diff --git a/drivers/media/platform/rockchip/Kconfig b/drivers/media/platform/rockchip/Kconfig index 51db7d94216f..1ccd565e1bba 100644 --- a/drivers/media/platform/rockchip/Kconfig +++ b/drivers/media/platform/rockchip/Kconfig @@ -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" diff --git a/drivers/media/platform/rockchip/Makefile b/drivers/media/platform/rockchip/Makefile index 9aec3025aa45..ff971bdfeb52 100644 --- a/drivers/media/platform/rockchip/Makefile +++ b/drivers/media/platform/rockchip/Makefile @@ -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/ diff --git a/drivers/media/platform/rockchip/avsp/Kconfig b/drivers/media/platform/rockchip/avsp/Kconfig new file mode 100644 index 000000000000..85e149eeb1b7 --- /dev/null +++ b/drivers/media/platform/rockchip/avsp/Kconfig @@ -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. diff --git a/drivers/media/platform/rockchip/avsp/Makefile b/drivers/media/platform/rockchip/avsp/Makefile new file mode 100644 index 000000000000..2e4362504914 --- /dev/null +++ b/drivers/media/platform/rockchip/avsp/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +video_rkavsp-objs := avsp.o + +obj-$(CONFIG_VIDEO_ROCKCHIP_AVSP) += video_rkavsp.o diff --git a/drivers/media/platform/rockchip/avsp/avsp.c b/drivers/media/platform/rockchip/avsp/avsp.c new file mode 100644 index 000000000000..3f9ad5a92676 --- /dev/null +++ b/drivers/media/platform/rockchip/avsp/avsp.c @@ -0,0 +1,781 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */ + +#include + +#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 "); +MODULE_DESCRIPTION("Rockchip AVSP Module"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(DMA_BUF); diff --git a/drivers/media/platform/rockchip/avsp/avsp.h b/drivers/media/platform/rockchip/avsp/avsp.h new file mode 100644 index 000000000000..a911c5836ee8 --- /dev/null +++ b/drivers/media/platform/rockchip/avsp/avsp.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */ + +#ifndef __RKAVSP_H__ +#define __RKAVSP_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 diff --git a/drivers/media/platform/rockchip/avsp/regs.h b/drivers/media/platform/rockchip/avsp/regs.h new file mode 100644 index 000000000000..f7de6baec75a --- /dev/null +++ b/drivers/media/platform/rockchip/avsp/regs.h @@ -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