media: rockchip: avsp: init driver for rv1126b

Change-Id: Idc461ba83d04b056249f025ca27bfbbd7fa4abbb
Signed-off-by: Zhizhen Zheng <zhizhen.zheng@rock-chips.com>
This commit is contained in:
Zhizhen Zheng
2025-03-03 09:53:23 +08:00
committed by Tao Huang
parent 7a30fcfc4b
commit 96d89865a2
7 changed files with 1089 additions and 0 deletions

View File

@@ -3,6 +3,7 @@
comment "Rockchip media platform drivers"
source "drivers/media/platform/rockchip/aiisp/Kconfig"
source "drivers/media/platform/rockchip/avsp/Kconfig"
source "drivers/media/platform/rockchip/cif/Kconfig"
source "drivers/media/platform/rockchip/fec/Kconfig"
source "drivers/media/platform/rockchip/flexbus_cif/Kconfig"

View File

@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += aiisp/
obj-y += avsp/
obj-y += cif/
obj-y += fec/
obj-y += flexbus_cif/

View File

@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
config VIDEO_ROCKCHIP_AVSP
tristate "Rockchip Any View Stitching Processor driver"
depends on ARCH_ROCKCHIP || COMPILE_TEST
default n
help
Support for AVSP on the rockchip SoC.
If you are using a Rockchip SoC with an integrated AVSP module
and require AVSP, say Y or M here.

View File

@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
video_rkavsp-objs := avsp.o
obj-$(CONFIG_VIDEO_ROCKCHIP_AVSP) += video_rkavsp.o

View File

@@ -0,0 +1,781 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */
#include <linux/version.h>
#include "avsp.h"
#include "regs.h"
int rkavsp_log_level;
module_param_named(debug, rkavsp_log_level, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-2)");
#if IS_LINUX_VERSION_AT_LEAST_6_1
#define GET_SG_TABLE(mem_ops, off_buf) mem_ops->cookie(&(off_buf)->vb, (off_buf)->mem)
#else
#define GET_SG_TABLE(mem_ops, off_buf) mem_ops->cookie((off_buf)->mem)
#endif
static void rkavsp_soft_reset(struct rkavsp_dev *hw);
#if IS_LINUX_VERSION_AT_LEAST_6_1
static void init_vb2(struct rkavsp_dev *avsp, struct rkavsp_buf *buf)
{
unsigned long attrs = DMA_ATTR_NO_KERNEL_MAPPING;
if (!buf)
return;
memset(&buf->vb, 0, sizeof(buf->vb));
memset(&buf->vb2_queue, 0, sizeof(buf->vb2_queue));
buf->vb2_queue.gfp_flags = GFP_KERNEL | GFP_DMA32;
buf->vb2_queue.dma_dir = DMA_BIDIRECTIONAL;
if (avsp->is_dma_config)
attrs |= DMA_ATTR_FORCE_CONTIGUOUS;
buf->vb2_queue.dma_attrs = attrs;
buf->vb.vb2_queue = &buf->vb2_queue;
}
#endif
static struct rkavsp_buf *avsp_buf_add(struct file *file, int fd)
{
struct rkavsp_dev *avsp = container_of(file->private_data, struct rkavsp_dev, mdev);
struct rkavsp_buf *buf = NULL, *next = NULL;
const struct vb2_mem_ops *ops = avsp->mem_ops;
struct dma_buf *dbuf = NULL;
void *mem = NULL;
bool need_add = true;
dbuf = dma_buf_get(fd);
if (IS_ERR_OR_NULL(dbuf)) {
RKAVSP_ERR("dma buf get err.\n");
return buf;
}
mutex_lock(&avsp->dev_lock);
list_for_each_entry_safe(buf, next, &avsp->list, list) {
if (buf->file == file && buf->fd == fd && buf->dbuf == dbuf) {
need_add = false;
break;
}
}
if (need_add) {
buf = kzalloc(sizeof(struct rkavsp_buf), GFP_KERNEL);
if (!buf)
goto err;
#if IS_LINUX_VERSION_AT_LEAST_6_1
init_vb2(avsp, buf);
mem = ops->attach_dmabuf(&buf->vb, avsp->dev, dbuf, dbuf->size);
#else
mem = ops->attach_dmabuf(avsp->dev, dbuf, dbuf->size,
DMA_BIDIRECTIONAL);
#endif
if (IS_ERR(mem)) {
RKAVSP_ERR("failed to attach dmabuf.\n");
goto err;
}
if (ops->map_dmabuf(mem)) {
RKAVSP_ERR("failed to map.\n");
ops->detach_dmabuf(mem);
mem = NULL;
goto err;
}
buf->fd = fd;
buf->file = file;
buf->dbuf = dbuf;
buf->mem = mem;
/* internal_alloc already add */
buf->alloc = false;
list_add_tail(&buf->list, &avsp->list);
RKAVSP_DBG("file:%p fd:%d dbuf:%p\n", file, fd, dbuf);
} else {
dma_buf_put(dbuf);
}
mutex_unlock(&avsp->dev_lock);
return buf;
err:
dma_buf_put(dbuf);
kfree(buf);
buf = NULL;
mutex_unlock(&avsp->dev_lock);
return buf;
}
static void avsp_buf_del(struct file *file, int fd, bool is_all)
{
struct rkavsp_dev *avsp = container_of(file->private_data, struct rkavsp_dev, mdev);
struct rkavsp_buf *buf, *next;
const struct vb2_mem_ops *ops = avsp->mem_ops;
mutex_lock(&avsp->dev_lock);
list_for_each_entry_safe(buf, next, &avsp->list, list) {
if (buf->file == file && (is_all || buf->fd == fd)) {
RKAVSP_DBG("file:%p fd:%d dbuf:%p\n", file, buf->fd, buf->dbuf);
if (!buf->alloc) {
ops->unmap_dmabuf(buf->mem);
ops->detach_dmabuf(buf->mem);
} else {
ops->put(buf->mem);
}
dma_buf_put(buf->dbuf);
buf->file = NULL;
buf->mem = NULL;
buf->dbuf = NULL;
buf->fd = -1;
list_del(&buf->list);
kfree(buf);
if (!is_all)
break;
}
}
mutex_unlock(&avsp->dev_lock);
}
static int avsp_dcp_run(struct file *file, struct rkavsp_dcp_in_out *buf)
{
struct rkavsp_dev *avsp = container_of(file->private_data, struct rkavsp_dev, mdev);
void __iomem *base = avsp->base;
const struct vb2_mem_ops *mem_ops = avsp->mem_ops;
struct sg_table *sgt;
struct rkavsp_buf *off_buf;
int ret = -EINVAL;
int pry_h[6], i;
u32 in_offs, out_offs, val;
u32 in_w = buf->in_width, in_h = buf->in_height;
u32 dcp_bypass = AVSP_BYPASS_OFF;
u32 bandnum = buf->bandnum;
u32 wr_mode = buf->dcp_wr_mode, rd_mode = buf->dcp_rd_mode;
u32 dcp_rd_stride_y = buf->dcp_rd_stride_y, dcp_rd_stride_c = buf->dcp_rd_stride_c;
mutex_lock(&avsp->dcp_lock);
if (rd_mode != AVSP_MODE_QUAD && rd_mode != AVSP_MODE_RASTER) {
RKAVSP_ERR("dcp rd_mode err.\n");
mutex_unlock(&avsp->dcp_lock);
return -EINVAL;
}
switch (buf->dcp_wr_mode) {
case AVSP_MODE_RASTER:
dcp_bypass = AVSP_BYPASS_OPEN;
break;
case AVSP_MODE_QUAD:
break;
default:
RKAVSP_ERR("no support dcp_wr_mode.\n");
mutex_unlock(&avsp->dcp_lock);
return -EINVAL;
}
// DCP CTL SET
val = SW_DCP_BYPASS(dcp_bypass) | SW_DCP_WR_MODE(wr_mode) |
SW_DCP_RD_MODE(rd_mode) | SW_DCP_BAND_NUM(bandnum);
writel(val, base + AVSP_DCP_CTRL);
val = SW_AVSP_SRC_WIDTH(in_w) | Sw_AVSP_SRC_HEIGHT(in_h);
writel(val, base + AVSP_DCP_SIZE);
val = AVSP_RD_VIR_STRIDE_Y(dcp_rd_stride_y) | AVSP_RD_VIR_STRIDE_C(dcp_rd_stride_c);
writel(val, base + AVSP_DCP_RD_VIR_SIZE);
// wr stride set
for (i = 0; i < bandnum; i++) {
val = AVSP_WR_VIR_STRIDE_Y(buf->dcp_wr_stride_y[i]) |
AVSP_WR_VIR_STRIDE_C(buf->dcp_wr_stride_c[i]);
writel(val, base + AVSP_DCP_WR_LV0_VIR_SIZE + i * 4);
}
/* input picture buf */
in_offs = dcp_rd_stride_y * in_h * 4;
off_buf = avsp_buf_add(file, buf->in_pic_fd);
if (!off_buf) {
mutex_unlock(&avsp->dcp_lock);
return -ENOMEM;
}
sgt = GET_SG_TABLE(mem_ops, off_buf);
if (!sgt)
goto free_buf;
val = sg_dma_address(sgt->sgl);
writel(val, base + AVSP_DCP_RD_Y_BASE);
if (rd_mode == AVSP_MODE_RASTER) {
val += in_offs;
writel(val, base + AVSP_DCP_RD_C_BASE);
}
/* output pyramid buf */
for (i = 0; i < bandnum; i++) {
pry_h[i] = in_h / (1 << i);
out_offs = (buf->dcp_wr_stride_y[i]) * pry_h[i] * 4;
off_buf = avsp_buf_add(file, buf->out_pry_fd[i]);
if (!off_buf) {
ret = -ENOMEM;
goto free_buf;
}
sgt = GET_SG_TABLE(mem_ops, off_buf);
if (!sgt)
goto free_buf;
val = sg_dma_address(sgt->sgl);
writel(val, base + AVSP_DCP_LV0_BASE_Y + i * 4);
if (wr_mode == AVSP_MODE_RASTER) {
val += out_offs;
writel(val, base + AVSP_DCP_LV0_BASE_C + i * 4);
}
}
writel(AVSP_FORCE_UPD, base + AVSP_DCP_UPDATE);
writel(AVSP_ST, base + AVSP_DCP_STRT);
RKAVSP_DBG("DCP: write start success.\n");
ret = wait_for_completion_timeout(&avsp->dcp_cmpl, msecs_to_jiffies(300));
if (!ret) {
RKAVSP_ERR("IOCTL AVSP_DCP work out time.\n");
ret = -EAGAIN;
rkavsp_soft_reset(avsp);
goto free_buf;
} else {
ret = 0;
}
mutex_unlock(&avsp->dcp_lock);
return ret;
free_buf:
RKAVSP_DBG("avsp_dcp free buf.\n");
avsp_buf_del(file, 0, true);
mutex_unlock(&avsp->dcp_lock);
return ret;
}
static int avsp_rcs_run(struct file *file, struct rkavsp_rcs_in_out *buf)
{
struct rkavsp_dev *avsp = container_of(file->private_data, struct rkavsp_dev, mdev);
void __iomem *base = avsp->base;
const struct vb2_mem_ops *mem_ops = avsp->mem_ops;
struct rkavsp_buf *off_buf;
struct sg_table *sgt;
int ret = -EINVAL;
int i;
u32 rd_mode = AVSP_MODE_QUAD;
u32 out_offs, val, c_addr;
u32 in_w = buf->in_width, in_h = buf->in_height;
u32 bandnum = buf->bandnum;
u32 wr_mode = buf->rcs_wr_mode;
u32 rcs_wr_stride_y = buf->rcs_wr_stride_y, rcs_wr_stride_c = buf->rcs_wr_stride_c;
u32 rcs_out_start_offset = buf->rcs_out_start_offset;
mutex_lock(&avsp->rcs_lock);
val = SW_RCS_BAND_NUM(bandnum) | SW_RCS_RD_MODE(rd_mode) | SW_RCS_WR_MODE(wr_mode);
if (wr_mode == AVSP_MODE_FBCE)
val |= SW_RCS_FBCE_CTL;
writel(val, base + AVSP_RCS_CTRL);
val = SW_AVSP_SRC_WIDTH(in_w) | Sw_AVSP_SRC_HEIGHT(in_h);
writel(val, base + AVSP_RCS_SIZE);
val = AVSP_WR_VIR_STRIDE_Y(rcs_wr_stride_y) | AVSP_WR_VIR_STRIDE_C(rcs_wr_stride_c);
writel(val, base + AVSP_RCS_WR_STRIDE);
// pry input0 buf add
for (i = 0; i < bandnum; i++) {
off_buf = avsp_buf_add(file, buf->in_pry0_fd[i]);
if (!off_buf) {
ret = -ENOMEM;
goto free_buf;
}
sgt = GET_SG_TABLE(mem_ops, off_buf);
if (!sgt)
goto free_buf;
val = sg_dma_address(sgt->sgl);
writel(val, base + AVSP_RCS_C0LV0_BASE + i * 4);
}
// pry input1 buf add
for (i = 0; i < bandnum; i++) {
off_buf = avsp_buf_add(file, buf->in_pry1_fd[i]);
if (!off_buf) {
ret = -ENOMEM;
goto free_buf;
}
sgt = GET_SG_TABLE(mem_ops, off_buf);
if (!sgt)
goto free_buf;
val = sg_dma_address(sgt->sgl);
writel(val, base + AVSP_RCS_C1LV0_BASE + i * 4);
}
// RCS DT_LVX
for (i = 0; i < bandnum; i++) {
off_buf = avsp_buf_add(file, buf->dt_pry_fd[i]);
if (!off_buf) {
ret = -ENOMEM;
goto free_buf;
}
sgt = GET_SG_TABLE(mem_ops, off_buf);
if (!sgt)
goto free_buf;
val = sg_dma_address(sgt->sgl);
writel(val, base + AVSP_RCS_DTLV0_BASE + i * 4);
}
off_buf = avsp_buf_add(file, buf->out_pic_fd);
if (!off_buf) {
ret = -ENOMEM;
goto free_buf;
}
sgt = GET_SG_TABLE(mem_ops, off_buf);
if (!sgt)
goto free_buf;
val = sg_dma_address(sgt->sgl);
switch (wr_mode) {
case AVSP_MODE_RASTER:
out_offs = rcs_wr_stride_y * in_h * 4;
val += rcs_out_start_offset;
writel(val, base + AVSP_RCS_WR_Y_BASE);
val += out_offs;
writel(val, base + AVSP_RCS_WR_C_BASE);
break;
case AVSP_MODE_FBCE:
out_offs = rcs_wr_stride_c * in_h;
c_addr = val + (rcs_out_start_offset / 64) * 16;
writel(c_addr, base + AVSP_RCS_WR_C_BASE);
val += ((rcs_out_start_offset / 64) * 384);
val += out_offs;
writel(val, base + AVSP_RCS_WR_Y_BASE);
break;
default:
val += (rcs_out_start_offset * 6);
writel(val, base + AVSP_RCS_WR_Y_BASE);
break;
}
writel(AVSP_FORCE_UPD, base + AVSP_RCS_UPDATE);
writel(AVSP_ST, base + AVSP_RCS_STRT);
ret = wait_for_completion_timeout(&avsp->rcs_cmpl, msecs_to_jiffies(300));
if (!ret) {
RKAVSP_ERR("IOCTL AVSP_RCS work out time.\n");
ret = -EAGAIN;
rkavsp_soft_reset(avsp);
goto free_buf;
} else {
ret = 0;
}
mutex_unlock(&avsp->rcs_lock);
return ret;
free_buf:
RKAVSP_DBG("avsp_rcs free buf.\n");
avsp_buf_del(file, 0, true);
mutex_unlock(&avsp->rcs_lock);
return ret;
}
static int avsp_open(struct inode *inode, struct file *file)
{
int ret;
struct rkavsp_dev *avsp = container_of(file->private_data, struct rkavsp_dev, mdev);
mutex_lock(&avsp->dev_lock);
ret = pm_runtime_get_sync(avsp->dev);
mutex_unlock(&avsp->dev_lock);
RKAVSP_INFO("avsp: device opened.\n");
return (ret > 0) ? 0 : ret;
}
static int avsp_release(struct inode *inode, struct file *file)
{
struct rkavsp_dev *avsp = container_of(file->private_data, struct rkavsp_dev, mdev);
avsp_buf_del(file, 0, true);
mutex_lock(&avsp->dev_lock);
pm_runtime_put_sync(avsp->dev);
mutex_unlock(&avsp->dev_lock);
RKAVSP_INFO("avsp: device released.\n");
return 0;
}
static long avsp_ioctl_default(struct file *file, unsigned int cmd, unsigned long arg)
{
long ret = 0;
struct rkavsp_dcp_in_out dcp_data;
struct rkavsp_rcs_in_out rcs_data;
if (!arg) {
ret = -EINVAL;
goto out;
}
switch (cmd) {
case RKAVSP_CMD_DCP:
if (copy_from_user(&dcp_data, (struct rkavsp_dcp_in_out __user *)arg,
sizeof(struct rkavsp_dcp_in_out))) {
ret = -EFAULT;
goto out;
}
ret = avsp_dcp_run(file, &dcp_data);
break;
case RKAVSP_CMD_RCS:
if (copy_from_user(&rcs_data, (struct rkavsp_rcs_in_out __user *)arg,
sizeof(struct rkavsp_rcs_in_out))) {
ret = -EFAULT;
goto out;
}
ret = avsp_rcs_run(file, &rcs_data);
break;
default:
ret = -EFAULT;
}
out:
return ret;
}
static const struct file_operations avsp_fops = {
.owner = THIS_MODULE,
.open = avsp_open,
.release = avsp_release,
.unlocked_ioctl = avsp_ioctl_default,
};
static irqreturn_t avsp_dcp_irq_hdl(int irq, void *dev_id)
{
struct device *dev = dev_id;
struct rkavsp_dev *avsp = dev_get_drvdata(dev);
void __iomem *base = avsp->base;
unsigned int mis_val;
mis_val = readl(base + AVSP_DCP_INT_MSK);
writel(mis_val, base + AVSP_DCP_INT_CLR);
if (mis_val & DCP_INT) {
mis_val &= (~DCP_INT);
if (!completion_done(&avsp->dcp_cmpl)) {
complete(&avsp->dcp_cmpl);
RKAVSP_DBG("misval: 0x%x\n", mis_val);
}
}
return IRQ_HANDLED;
}
static irqreturn_t avsp_rcs_irq_hdl(int irq, void *dev_id)
{
struct device *dev = dev_id;
struct rkavsp_dev *avsp = dev_get_drvdata(dev);
void __iomem *base = avsp->base;
unsigned int mis_val;
mis_val = readl(base + AVSP_RCS_INT_MSK1);
writel(mis_val, base + AVSP_RCS_INT_CLR0);
writel(mis_val, base + AVSP_RCS_INT_CLR1);
if (mis_val & RCS_INT) {
mis_val &= (~RCS_INT);
if (!completion_done(&avsp->rcs_cmpl)) {
complete(&avsp->rcs_cmpl);
RKAVSP_DBG("misval: 0x%x\n", mis_val);
}
}
return IRQ_HANDLED;
}
static const char * const rv1126b_avsp_clks[] = {
"aclk_avsp",
"hclk_avsp",
};
static void rkavsp_set_clk_rate(struct clk *clk, unsigned long rate)
{
clk_set_rate(clk, rate);
}
static void disable_sys_clk(struct rkavsp_dev *dev)
{
int i;
for (i = 0; i < dev->clks_num; i++)
clk_disable_unprepare(dev->clks[i]);
}
static int enable_sys_clk(struct rkavsp_dev *dev)
{
int i, ret;
for (i = 0; i < dev->clks_num; i++) {
ret = clk_prepare_enable(dev->clks[i]);
if (ret < 0)
goto err;
}
//tosee
rkavsp_set_clk_rate(dev->clks[0],
dev->clk_rate_tbl[dev->clk_rate_tbl_num - 1].clk_rate * 1000000);
return 0;
err:
for (--i; i >= 0; --i)
clk_disable_unprepare(dev->clks[i]);
return ret;
}
static int avsp_probe(struct platform_device *pdev)
{
const struct avsp_match_data *match_data;
struct rkavsp_dev *avsp;
struct device *dev = &pdev->dev;
struct resource *res;
int i, irq, ret = 0;
//bool is_mmu;
match_data = device_get_match_data(&pdev->dev);
if (!match_data)
return -ENODEV;
avsp = devm_kzalloc(&pdev->dev, sizeof(*avsp), GFP_KERNEL);
if (!avsp)
return -ENOMEM;
dev_set_drvdata(dev, avsp);
avsp->dev = &pdev->dev;
avsp->match_data = match_data;
/* map the registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
RKAVSP_ERR("get memory resource failed.\n");
ret = -EINVAL;
goto err;
}
avsp->base = devm_ioremap_resource(avsp->dev, res);
if (IS_ERR(avsp->base)) {
RKAVSP_ERR("ioremap failed\n");
ret = PTR_ERR(avsp->base);
goto err;
}
INIT_LIST_HEAD(&avsp->list);
init_completion(&avsp->dcp_cmpl);
init_completion(&avsp->rcs_cmpl);
avsp->mem_ops = &vb2_cma_sg_memops;
/* get the irq */
for (i = 0; i < match_data->num_irqs; i++) {
irq = platform_get_irq_byname(pdev, match_data->irqs[i].name);
if (irq < 0) {
RKAVSP_ERR("no irq %s in dts.\n", match_data->irqs[i].name);
ret = irq;
goto err;
}
ret = devm_request_irq(dev, irq, match_data->irqs[i].irq_hdl,
IRQF_SHARED, dev_driver_string(dev), dev);
if (ret < 0) {
RKAVSP_ERR("request %s failed: %d\n", match_data->irqs[i].name, ret);
goto err;
}
}
/* get the clk */
for (i = 0; i < match_data->clks_num; i++) {
struct clk *clk = devm_clk_get(dev, match_data->clks[i]);
if (IS_ERR(clk)) {
RKAVSP_ERR("failed to get %s\n", match_data->clks[i]);
ret = PTR_ERR(clk);
goto err;
}
avsp->clks[i] = clk;
}
avsp->clks_num = match_data->clks_num;
avsp->clk_rate_tbl = match_data->clk_rate_tbl;
avsp->clk_rate_tbl_num = match_data->clk_rate_tbl_num;
avsp->reset = devm_reset_control_array_get(dev, false, false);
if (IS_ERR(avsp->reset)) {
RKAVSP_INFO("failed to get cru reset\n");
avsp->reset = NULL;
}
mutex_init(&avsp->dev_lock);
mutex_init(&avsp->dcp_lock);
mutex_init(&avsp->rcs_lock);
avsp->is_dma_config = true;
// register misc device
avsp->mdev.minor = MISC_DYNAMIC_MINOR;
avsp->mdev.name = AVSP_NAME;
avsp->mdev.fops = &avsp_fops;
ret = misc_register(&avsp->mdev);
if (ret < 0) {
RKAVSP_ERR("avsp misc register failed.\n");
goto err;
}
pm_runtime_enable(&pdev->dev);
RKAVSP_INFO("avsp misc device probe success.\n");
return 0;
err:
return ret;
}
static int avsp_remove(struct platform_device *pdev)
{
/* misc device remove */
struct rkavsp_dev *avsp = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
misc_deregister(&avsp->mdev);
mutex_destroy(&avsp->rcs_lock);
mutex_destroy(&avsp->dcp_lock);
mutex_destroy(&avsp->dev_lock);
return 0;
}
static const struct avsp_clk_info rv1126b_avsp_clk_rate[] = {
{
.clk_rate = 300,
.refer_data = 1920,
}, {
.clk_rate = 400,
.refer_data = 2688,
}, {
.clk_rate = 500,
.refer_data = 3072,
}, {
.clk_rate = 600,
.refer_data = 3840,
}, {
.clk_rate = 702,
.refer_data = 4672,
}
};
static struct irqs_data rv1126b_avsp_irqs[] = {
{"dcp_irq", avsp_dcp_irq_hdl},
{"rcs_irq", avsp_rcs_irq_hdl},
};
static const struct avsp_match_data rv1126b_avsp_match_data = {
.clks = rv1126b_avsp_clks,
.clks_num = ARRAY_SIZE(rv1126b_avsp_clks),
.clk_rate_tbl = rv1126b_avsp_clk_rate,
.clk_rate_tbl_num = ARRAY_SIZE(rv1126b_avsp_clk_rate),
.irqs = rv1126b_avsp_irqs,
.num_irqs = ARRAY_SIZE(rv1126b_avsp_irqs),
};
static const struct of_device_id rockchip_avsp_match[] = {
{
.compatible = "rockchip,rv1126b-rkavsp",
.data = &rv1126b_avsp_match_data,
},
{},
};
MODULE_DEVICE_TABLE(of, rockchip_avsp_match);
static void rkavsp_soft_reset(struct rkavsp_dev *hw)
{
u32 val;
/* reset */
val = SYS_SOFT_RST_DCP;
writel(val, hw->base + AVSP_DCP_CLK_DIS);
udelay(10);
writel(SYS_SOFT_RST_VAL, hw->base + AVSP_DCP_CLK_DIS);
if (hw->reset) {
reset_control_assert(hw->reset);
udelay(20);
reset_control_deassert(hw->reset);
udelay(20);
}
/* refresh iommu after reset */
rockchip_iommu_disable(hw->dev);
rockchip_iommu_enable(hw->dev);
/* clk_dis */
val = SYS_DCP_LGC_CKG_DIS | SYS_DCP_RAM_CKG_DIS;
writel(val, hw->base + AVSP_DCP_CLK_DIS);
/* int en */
val = DCP_INT;
writel(val, hw->base + AVSP_DCP_INT_EN);
val = RCS_INT;
writel(val, hw->base + AVSP_RCS_INT_EN1);
}
static int __maybe_unused rkavsp_runtime_suspend(struct device *dev)
{
struct rkavsp_dev *avsp = dev_get_drvdata(dev);
if (dev->power.runtime_status) {
writel(0, avsp->base + AVSP_DCP_INT_EN);
writel(0, avsp->base + AVSP_RCS_INT_EN1);
}
disable_sys_clk(avsp);
return 0;
}
static int __maybe_unused rkavsp_runtime_resume(struct device *dev)
{
struct rkavsp_dev *avsp = dev_get_drvdata(dev);
enable_sys_clk(avsp);
rkavsp_soft_reset(avsp);
//if (dev->power.runtime_status)
return 0;
}
static const struct dev_pm_ops rkavsp_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(rkavsp_runtime_suspend,
rkavsp_runtime_resume, NULL)
};
static void rkavsp_shutdown(struct platform_device *pdev)
{
struct rkavsp_dev *avsp = platform_get_drvdata(pdev);
u32 val;
//hw_dev->is_shutdown = true;
if (pm_runtime_active(&pdev->dev)) {
writel(0, avsp->base + AVSP_DCP_INT_EN);
writel(0, avsp->base + AVSP_RCS_INT_EN1);
val = SYS_SOFT_RST_DCP;
writel(val, avsp->base + AVSP_DCP_CLK_DIS);
udelay(10);
writel(SYS_SOFT_RST_VAL, avsp->base + AVSP_DCP_CLK_DIS);
}
RKAVSP_INFO("shutdown.\n");
}
static struct platform_driver avsp_pdrv = {
.probe = avsp_probe,
.remove = avsp_remove,
.shutdown = rkavsp_shutdown,
.driver = {
.name = AVSP_NAME,
.pm = &rkavsp_pm_ops,
.of_match_table = of_match_ptr(rockchip_avsp_match),
},
};
module_platform_driver(avsp_pdrv);
MODULE_AUTHOR("Zhizhen Zheng <zhizhen.zheng@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip AVSP Module");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(DMA_BUF);

View File

@@ -0,0 +1,149 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */
#ifndef __RKAVSP_H__
#define __RKAVSP_H__
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <media/videobuf2-cma-sg.h>
#include <media/videobuf2-dma-sg.h>
#include <linux/iommu.h>
#include <soc/rockchip/rockchip_iommu.h>
extern int rkavsp_log_level;
#define RKAVSP_LEVEL_ERR 0
#define RKAVSP_LEVEL_INFO 1
#define RKAVSP_LEVEL_DBG 2
#define RKAVSP_PRINT(level, fmt, args...) \
do { \
if (rkavsp_log_level >= level) { \
if (level == RKAVSP_LEVEL_ERR) \
dev_err(avsp->dev, "%s:%d " fmt, __func__, __LINE__, ##args); \
else if (level == RKAVSP_LEVEL_INFO) \
dev_info(avsp->dev, "%s:%d " fmt, __func__, __LINE__, ##args); \
else if (level == RKAVSP_LEVEL_DBG) \
dev_dbg(avsp->dev, "%s:%d " fmt, __func__, __LINE__, ##args); \
} \
} while (0)
#define RKAVSP_ERR(fmt, args...) RKAVSP_PRINT(RKAVSP_LEVEL_ERR, fmt, ##args)
#define RKAVSP_INFO(fmt, args...) RKAVSP_PRINT(RKAVSP_LEVEL_INFO, fmt, ##args)
#define RKAVSP_DBG(fmt, args...) RKAVSP_PRINT(RKAVSP_LEVEL_DBG, fmt, ##args)
#define RKAVSP_CMD_DCP \
_IOW('V', 192 + 20, struct rkavsp_dcp_in_out)
#define RKAVSP_CMD_RCS \
_IOW('V', 192 + 21, struct rkavsp_rcs_in_out)
#define AVSP_NAME "rockchip_avsp"
#define AVSP_MAX_BUS_CLK 3
struct rkavsp_buf {
int fd;
struct file *file;
struct list_head list;
struct vb2_buffer vb;
struct vb2_queue vb2_queue;
struct dma_buf *dbuf;
struct dma_buf_attachment *dba;
void *mem;
bool alloc;
};
struct avsp_match_data {
int clks_num;
const char * const *clks;
int clk_rate_tbl_num;
const struct avsp_clk_info *clk_rate_tbl;
struct irqs_data *irqs;
int num_irqs;
};
struct rkavsp_dev {
struct device *dev;
struct regmap *grf;
struct completion dcp_cmpl;
struct completion rcs_cmpl;
struct list_head list;
const struct vb2_mem_ops *mem_ops;
void __iomem *base;
struct reset_control *reset;
const struct avsp_match_data *match_data;
const struct avsp_clk_info *clk_rate_tbl;
struct clk *clks[AVSP_MAX_BUS_CLK];
int clk_rate_tbl_num;
int clks_num;
spinlock_t dcp_irq_lock;
spinlock_t rcs_irq_lock;
struct mutex dev_lock;
struct mutex dcp_lock;
struct mutex rcs_lock;
struct miscdevice mdev;
bool is_dma_config;
};
struct rkavsp_dcp_in_out {
int in_width;
int in_height;
int bandnum;
int dcp_rd_mode;
int dcp_wr_mode;
int dcp_rd_stride_y;
int dcp_rd_stride_c;
int dcp_wr_stride_y[6];
int dcp_wr_stride_c[6];
int in_pic_fd;
int out_pry_fd[6];
};
struct rkavsp_rcs_in_out {
int in_width;
int in_height;
int bandnum;
int rcs_wr_mode;
int rcs_wr_stride_y;
int rcs_wr_stride_c;
int in_pry0_fd[6];
int in_pry1_fd[6];
int dt_pry_fd[6];
int out_pic_fd;
int rcs_out_start_offset;
};
struct avsp_clk_info {
u32 clk_rate;
u32 refer_data;
};
struct irqs_data {
const char *name;
irqreturn_t (*irq_hdl)(int irq, void *ctx);
};
#ifndef IS_LINUX_VERSION_AT_LEAST_6_1
#define IS_LINUX_VERSION_AT_LEAST_6_1 (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE)
#endif
#endif

View File

@@ -0,0 +1,143 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */
#ifndef _RKAVSP_REGS_H
#define _RKAVSP_REGS_H
#define ALIGN_RASTER(w) (((w + 15) / 16) * 16)
#define ALIGN_QUAD(w) (((w * 3 + 15) / 16) * 16)
#define DCP_STD_MSK 0x3FFF
#define AVSP_FORCE_UPD 0x1
#define AVSP_ST 0x1
#define AVSP_BYPASS_OPEN 0x1
#define AVSP_BYPASS_OFF 0x0
/* AVSP FMT DEFINE */
#define AVSP_MODE_RASTER 0x0
#define AVSP_MODE_TILE 0x1
#define AVSP_MODE_FBCE 0x2
#define AVSP_MODE_QUAD 0x3
/* AVSP_DCP_CORE_CTRL */
#define SW_DCP_BYPASS(x) ((x & 0x1) << 7)
#define SW_DCP_WR_MODE(x) ((x & 0x3) << 5)
#define SW_DCP_RD_MODE(x) ((x & 0x3) << 3)
#define SW_DCP_BAND_NUM(x) (x & 0x7)
/* AVSP_SRC_SIZE */
#define SW_AVSP_SRC_WIDTH(x) ((x) & 0x07ff)
#define Sw_AVSP_SRC_HEIGHT(x) (((x) & 0x1fff) << 16)
/* AVSP_RD_VIR_STRIDE */
#define AVSP_RD_VIR_STRIDE_Y(x) ((x) & 0x3fff)
#define AVSP_RD_VIR_STRIDE_C(x) (((x) & 0x3fff) << 16)
/* AVSP_WR_VIR_STRIDE */
#define AVSP_WR_VIR_STRIDE_Y(x) ((x) & 0x3fff)
#define AVSP_WR_VIR_STRIDE_C(x) (((x) & 0x3fff) << 16)
/* AVSP_RCS_CORE_CTRL */
#define SW_RCS_WR_MODE(x) ((x & 0x3) << 5)
#define SW_RCS_RD_MODE(x) ((x & 0x3) << 3)
#define SW_RCS_BAND_NUM(x) (x & 0x7)
#define SW_RCS_FBCE_CTL (0x6 << 9)
// AVSP INT
#define DCP_INT (0x1 << 25)
#define RCS_INT 0x00010000
#define SYS_SOFT_RST_DCP 0x00004000
#define SYS_SOFT_RST_VAL 0x00000000
#define SYS_DCP_LGC_CKG_DIS 0x00000001
#define SYS_DCP_RAM_CKG_DIS 0x00000002
/* AVSP_REGS */
#define AVSP_BASE 0x00000000
#define AVSP_DCP_STRT (AVSP_BASE + 0x00300)
#define AVSP_DCP_UPDATE (AVSP_BASE + 0x00304)
#define AVSP_DCP_CLK_DIS (AVSP_BASE + 0x00308)
#define AVSP_DCP_CTRL (AVSP_BASE + 0x00310)
#define AVSP_DCP_SIZE (AVSP_BASE + 0x00314)
#define AVSP_DCP_RD_VIR_SIZE (AVSP_BASE + 0x00318)
#define AVSP_DCP_WR_LV0_VIR_SIZE (AVSP_BASE + 0x0031c)
#define AVSP_DCP_WR_LV1_VIR_SIZE (AVSP_BASE + 0x00320)
#define AVSP_DCP_WR_LV2_VIR_SIZE (AVSP_BASE + 0x00324)
#define AVSP_DCP_WR_LV3_VIR_SIZE (AVSP_BASE + 0x00328)
#define AVSP_DCP_WR_LV4_VIR_SIZE (AVSP_BASE + 0x0032c)
#define AVSP_DCP_WR_LV5_VIR_SIZE (AVSP_BASE + 0x00330)
#define AVSP_DCP_RD_Y_BASE (AVSP_BASE + 0x00334)
#define AVSP_DCP_RD_C_BASE (AVSP_BASE + 0x00338)
#define AVSP_DCP_LV0_BASE_Y (AVSP_BASE + 0x0033c)
#define AVSP_DCP_LV1_BASE_Y (AVSP_BASE + 0x00340)
#define AVSP_DCP_LV2_BASE_Y (AVSP_BASE + 0x00344)
#define AVSP_DCP_LV3_BASE_Y (AVSP_BASE + 0x00348)
#define AVSP_DCP_LV4_BASE_Y (AVSP_BASE + 0x0034c)
#define AVSP_DCP_LV5_BASE_Y (AVSP_BASE + 0x00350)
#define AVSP_DCP_LV0_BASE_C (AVSP_BASE + 0x00354)
#define AVSP_DCP_LV1_BASE_C (AVSP_BASE + 0x00358)
#define AVSP_DCP_LV2_BASE_C (AVSP_BASE + 0x0035c)
#define AVSP_DCP_LV3_BASE_C (AVSP_BASE + 0x00360)
#define AVSP_DCP_LV4_BASE_C (AVSP_BASE + 0x00364)
#define AVSP_DCP_LV5_BASE_C (AVSP_BASE + 0x00368)
#define AVSP_DCP_INT_EN (AVSP_BASE + 0x00390)
#define AVSP_DCP_INT_CLR (AVSP_BASE + 0x00394)
#define AVSP_DCP_INT_RAW (AVSP_BASE + 0x00398)
#define AVSP_DCP_INT_MSK (AVSP_BASE + 0x0039c)
#define AVSP_DCP_STATUS0 (AVSP_BASE + 0x003a0)
#define AVSP_DCP_STATUS1 (AVSP_BASE + 0x003a4)
#define AVSP_DCP_STATUS2 (AVSP_BASE + 0x003a8)
#define AVSP_DCP_STATUS3 (AVSP_BASE + 0x003ac)
#define AVSP_DCP_STATUS4 (AVSP_BASE + 0x003b0)
#define AVSP_RCS_STRT (AVSP_BASE + 0x00400)
#define AVSP_RCS_UPDATE (AVSP_BASE + 0x00404)
#define AVSP_RCS_CLK_DIS (AVSP_BASE + 0x00408)
#define AVSP_RCS_CTRL (AVSP_BASE + 0x00410)
#define AVSP_RCS_SIZE (AVSP_BASE + 0x00414)
#define AVSP_RCS_WR_STRIDE (AVSP_BASE + 0x00430)
#define AVSP_RCS_C0LV0_BASE (AVSP_BASE + 0x00434)
#define AVSP_RCS_C0LV1_BASE (AVSP_BASE + 0x00438)
#define AVSP_RCS_C0LV2_BASE (AVSP_BASE + 0x0043c)
#define AVSP_RCS_C0LV3_BASE (AVSP_BASE + 0x00440)
#define AVSP_RCS_C0LV4_BASE (AVSP_BASE + 0x00444)
#define AVSP_RCS_C0LV5_BASE (AVSP_BASE + 0x00448)
#define AVSP_RCS_C1LV0_BASE (AVSP_BASE + 0x0044c)
#define AVSP_RCS_C1LV2_BASE (AVSP_BASE + 0x00450)
#define AVSP_RCS_C1LV1_BASE (AVSP_BASE + 0x00454)
#define AVSP_RCS_C1LV3_BASE (AVSP_BASE + 0x00458)
#define AVSP_RCS_C1LV4_BASE (AVSP_BASE + 0x0045c)
#define AVSP_RCS_C1LV5_BASE (AVSP_BASE + 0x00460)
#define AVSP_RCS_DTLV0_BASE (AVSP_BASE + 0x00464)
#define AVSP_RCS_DTLV1_BASE (AVSP_BASE + 0x00468)
#define AVSP_RCS_DTLV2_BASE (AVSP_BASE + 0x0046c)
#define AVSP_RCS_DTLV3_BASE (AVSP_BASE + 0x00470)
#define AVSP_RCS_DTLV4_BASE (AVSP_BASE + 0x00474)
#define AVSP_RCS_DTLV5_BASE (AVSP_BASE + 0x00478)
#define AVSP_RCS_WR_Y_BASE (AVSP_BASE + 0x0047c)
#define AVSP_RCS_WR_C_BASE (AVSP_BASE + 0x00480)
#define AVSP_RCS_WR_FBCE_HEAD_OFFSET (AVSP_BASE + 0x00484)
#define AVSP_RCS_INT_EN0 (AVSP_BASE + 0x00490)
#define AVSP_RCS_INT_CLR0 (AVSP_BASE + 0x00494)
#define AVSP_RCS_INT_RAW0 (AVSP_BASE + 0x00498)
#define AVSP_RCS_INT_MSK0 (AVSP_BASE + 0x0049c)
#define AVSP_RCS_INT_EN1 (AVSP_BASE + 0x004a0)
#define AVSP_RCS_INT_CLR1 (AVSP_BASE + 0x004a4)
#define AVSP_RCS_INT_RAW1 (AVSP_BASE + 0x004a8)
#define AVSP_RCS_INT_MSK1 (AVSP_BASE + 0x004ac)
#define AVSP_RCS_STATUS0 (AVSP_BASE + 0x004b0)
#define AVSP_RCS_STATUS1 (AVSP_BASE + 0x004b4)
#define AVSP_RCS_STATUS2 (AVSP_BASE + 0x004b8)
#define AVSP_RCS_STATUS3 (AVSP_BASE + 0x004bc)
#define MMU_DTE_ADDR (AVSP_BASE + 0x00f00)
#define MMU_STATUS (AVSP_BASE + 0x00f04)
#define MMU_COMMAND (AVSP_BASE + 0x00f08)
#define MMU_PAGE_FAULT_ADDR (AVSP_BASE + 0x00f0c)
#define MMU_ZAP_ONE_LINE (AVSP_BASE + 0x00f10)
#define MMU_INT_RAWSTAT (AVSP_BASE + 0x00f14)
#define MMU_INT_CLEAR (AVSP_BASE + 0x00f18)
#define MMU_INT_MASK (AVSP_BASE + 0x00f1c)
#define MMU_INT_STATUS (AVSP_BASE + 0x00f20)
#define MMU_AUTO_GATING (AVSP_BASE + 0x00f24)
#define MMU_REG_LOAD_EN (AVSP_BASE + 0x00f28)
#endif