diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig index 5779065b968a..3315d4bdc29e 100644 --- a/drivers/video/rockchip/Kconfig +++ b/drivers/video/rockchip/Kconfig @@ -5,3 +5,4 @@ source "drivers/video/rockchip/rga3/Kconfig" source "drivers/video/rockchip/rve/Kconfig" source "drivers/video/rockchip/iep/Kconfig" source "drivers/video/rockchip/mpp/Kconfig" +source "drivers/video/rockchip/dvbm/Kconfig" diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile index f5801d44fb33..69fb54a5676f 100644 --- a/drivers/video/rockchip/Makefile +++ b/drivers/video/rockchip/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_ROCKCHIP_MULTI_RGA) += rga3/ obj-$(CONFIG_ROCKCHIP_RVE) += rve/ obj-$(CONFIG_IEP) += iep/ obj-$(CONFIG_ROCKCHIP_MPP_SERVICE) += mpp/ +obj-$(CONFIG_ROCKCHIP_DVBM) += dvbm/ diff --git a/drivers/video/rockchip/dvbm/Kconfig b/drivers/video/rockchip/dvbm/Kconfig new file mode 100644 index 000000000000..bfbd396b2706 --- /dev/null +++ b/drivers/video/rockchip/dvbm/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +menuconfig ROCKCHIP_DVBM + tristate "RK Direct Video Buffer Manager driver" + depends on ARCH_ROCKCHIP + help + rockchip dvbm module. + +if ROCKCHIP_DVBM + +config ROCKCHIP_DVBM_PROC_FS + bool "enable dvbm procfs" + depends on PROC_FS + default y + help + rockchip dvbm procfs. + +endif diff --git a/drivers/video/rockchip/dvbm/Makefile b/drivers/video/rockchip/dvbm/Makefile new file mode 100644 index 000000000000..9096c34720ec --- /dev/null +++ b/drivers/video/rockchip/dvbm/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +rk_dvbm-objs := rockchip_dvbm.o + +obj-$(CONFIG_ROCKCHIP_DVBM) += rk_dvbm.o diff --git a/drivers/video/rockchip/dvbm/rockchip_dvbm.c b/drivers/video/rockchip/dvbm/rockchip_dvbm.c new file mode 100644 index 000000000000..c769c94fae08 --- /dev/null +++ b/drivers/video/rockchip/dvbm/rockchip_dvbm.c @@ -0,0 +1,585 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd + * + * author: + * Yandong Lin, yandong.lin@rock-chips.com + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rockchip_dvbm.h" + +#define RK_DVBM "rk_dvbm" + +unsigned int dvbm_debug; +module_param(dvbm_debug, uint, 0644); +MODULE_PARM_DESC(dvbm_debug, "bit switch for dvbm debug information"); + +static struct dvbm_ctx *g_ctx; +#define DVBM_LINK_WAIT_TIMEOUT_MS (1000) + +#define DVBM_DEBUG 0x00000001 +#define DVBM_DEBUG_IRQ 0x00000002 +#define DVBM_DEBUG_REG 0x00000004 + +#define dvbm_debug(fmt, args...) \ + do { \ + if (unlikely(dvbm_debug & (DVBM_DEBUG))) \ + pr_info(fmt, ##args); \ + } while (0) + +#define dvbm_debug_reg(fmt, args...) \ + do { \ + if (unlikely(dvbm_debug & (DVBM_DEBUG_REG))) \ + pr_info(fmt, ##args); \ + } while (0) + +#define dvbm_debug_irq(fmt, args...) \ + do { \ + if (unlikely(dvbm_debug & (DVBM_DEBUG_IRQ))) \ + pr_info(fmt, ##args); \ + } while (0) + +#define dvbm_err(fmt, args...) \ + pr_err(fmt, ##args) + +enum dvbm_flow { + ISP_CFG = 1, + ISP_CONNECT = 2, + VEPU_CFG = 3, + VEPU_CONNECT = 4, +}; +/* dvbm status reg bit value define */ +#define BUF_OVERFLOW BIT(0) +#define RESYNC_FINISH BIT(1) +#define ISP_CNCT_TIMEOUT BIT(2) +#define VEPU_CNCT_TIMEOUT BIT(3) +#define VEPU_HANDSHAKE_TIMEOUT BIT(4) +#define ISP_CNCT BIT(5) +#define ISP_DISCNCT BIT(6) +#define VEPU_CNCT BIT(7) +#define VEPU_DISCNCT BIT(8) + +/* dvbm reg addr define */ +#define DVBM_VERSION 0x0 +#define DVBM_ISP_CNCT 0x4 +#define DVBM_VEPU_CNCT 0x8 +/* cfg regs */ +#define DVBM_CFG 0xC +#define DVBM_WDG_CFG0 0x10 +#define DVBM_WDG_CFG1 0x14 +#define DVBM_WDG_CFG2 0x18 +/* interrupt regs */ +#define DVBM_INT_EN 0x1c +#define DVBM_INT_MSK 0x20 +#define DVBM_INT_CLR 0x24 +#define DVBM_INT_ST 0x28 +/* addr regs */ +#define DVBM_YBUF_BOT 0x2c +#define DVBM_YBUF_TOP 0x30 +#define DVBM_YBUF_SADR 0x34 +#define DVBM_YBUF_LSTD 0x38 +#define DVBM_YBUF_FSTD 0x3c +#define DVBM_CBUF_BOT 0x40 +#define DVBM_CBUF_TOP 0x44 +#define DVBM_CBUF_SADR 0x48 +#define DVBM_CBUF_LSTD 0x4c +#define DVBM_CBUF_FSTD 0x50 +#define DVBM_AFUL_THDY 0x54 +#define DVBM_AFUL_THDC 0x58 +#define DVBM_OVFL_THDY 0x5c +#define DVBM_OVFL_THDC 0x60 +/* status regs */ +#define DVBM_ST 0x80 +#define DVBM_OVFL_ST 0x84 + +static void rk_dvbm_set_reg(struct dvbm_ctx *ctx, u32 offset, u32 val) +{ + dvbm_debug_reg("write reg[%d] 0x%x = 0x%08x\n", offset >> 2, offset, val); + writel(val, ctx->reg_base + offset); +} + +static u32 rk_dvbm_read_reg(struct dvbm_ctx *ctx, u32 offset) +{ + u32 val = readl(ctx->reg_base + offset); + + dvbm_debug_reg("read reg[%d] 0x%x = 0x%08x\n", offset >> 2, offset, val); + return val; +} + +static struct dvbm_ctx *port_to_ctx(struct dvbm_port *port) +{ + struct dvbm_ctx *ctx = NULL; + + if (IS_ERR_OR_NULL(port)) + return g_ctx; + if (port->dir == DVBM_ISP_PORT) + ctx = container_of(port, struct dvbm_ctx, port_isp); + else if (port->dir == DVBM_VEPU_PORT) + ctx = container_of(port, struct dvbm_ctx, port_vepu); + + return ctx; +} + +static void dvbm2enc_callback(struct dvbm_ctx *ctx, enum dvbm_cb_event event, void *arg) +{ + struct dvbm_cb *callback = &ctx->vepu_cb; + dvbm_callback cb = callback->cb; + + if (cb) + cb(callback->ctx, event, arg); +} + +static void rk_dvbm_dump_regs(struct dvbm_ctx *ctx) +{ + u32 start = 0x4; + u32 end = 0xb8; + u32 i; + + for (i = start; i <= end; i += 4) + dvbm_debug("reg[0x%0x] = 0x%08x\n", i, readl(ctx->reg_base + i)); +} + +struct dvbm_port *rk_dvbm_get_port(struct platform_device *pdev, + enum dvbm_port_dir dir) +{ + struct dvbm_ctx *ctx = NULL; + struct dvbm_port *port = NULL; + + if (WARN_ON(!pdev)) + return NULL; + + ctx = (struct dvbm_ctx *)platform_get_drvdata(pdev); + WARN_ON(!ctx); + dvbm_debug("%s dir %d\n", __func__, dir); + if (dir == DVBM_ISP_PORT) + port = &ctx->port_isp; + else if (dir == DVBM_VEPU_PORT) + port = &ctx->port_vepu; + + return port; +} +EXPORT_SYMBOL(rk_dvbm_get_port); + +int rk_dvbm_put(struct dvbm_port *port) +{ + struct dvbm_ctx *ctx = NULL; + + if (WARN_ON(!port)) + return -EINVAL; + + ctx = port_to_ctx(port); + + if (!ctx) + return -EINVAL; + return 0; +} +EXPORT_SYMBOL(rk_dvbm_put); + +int rk_dvbm_link(struct dvbm_port *port) +{ + struct dvbm_ctx *ctx; + enum dvbm_port_dir dir; + struct rk_dvbm_regs *reg; + int ret; + + if (WARN_ON(!port)) + return -EINVAL; + + ctx = port_to_ctx(port); + dir = port->dir; + reg = &ctx->regs; + + if (dir == DVBM_ISP_PORT) { + if (port->linked) { + rk_dvbm_unlink(port); + udelay(5); + } + reg->isp_cnct.isp_cnct = 1; + rk_dvbm_set_reg(ctx, DVBM_ISP_CNCT, 0x1); + + + } else if (dir == DVBM_VEPU_PORT) { + reg->vepu_cnct.vepu_cnct = 1; + rk_dvbm_set_reg(ctx, DVBM_VEPU_CNCT, 0x1); + } + ret = wait_event_timeout(ctx->link_wait, port->linked, + msecs_to_jiffies(DVBM_LINK_WAIT_TIMEOUT_MS)); + if (ret > 0) + dvbm_debug("%s connect frm_cnt[%d : %d]\n", + dir == DVBM_ISP_PORT ? "isp" : "vepu", + ctx->isp_frm_start, ctx->isp_frm_end); + else + dvbm_err("%s connect timeout!!! frm cnt[%d : %d]\n", + dir == DVBM_ISP_PORT ? "isp" : "vepu", + ctx->isp_frm_start, ctx->isp_frm_end); + + return ret; +} +EXPORT_SYMBOL(rk_dvbm_link); + +int rk_dvbm_unlink(struct dvbm_port *port) +{ + struct dvbm_ctx *ctx; + enum dvbm_port_dir dir; + struct rk_dvbm_regs *reg; + + if (WARN_ON(!port)) + return -EINVAL; + + ctx = port_to_ctx(port); + dir = port->dir; + reg = &ctx->regs; + + if (dir == DVBM_ISP_PORT) { + reg->isp_cnct.isp_cnct = 0; + rk_dvbm_set_reg(ctx, DVBM_ISP_CNCT, 0); + } else if (dir == DVBM_VEPU_PORT) { + reg->vepu_cnct.vepu_cnct = 0; + rk_dvbm_set_reg(ctx, DVBM_VEPU_CNCT, 0); + if (!ctx->regs.dvbm_cfg.auto_resyn) { + u32 connect = 0; + + dvbm2enc_callback(ctx, DVBM_VEPU_REQ_CONNECT, &connect); + } + } + dvbm_debug("%s disconnect\n", dir == DVBM_ISP_PORT ? "isp" : "vepu"); + + return 0; +} +EXPORT_SYMBOL(rk_dvbm_unlink); + +int rk_dvbm_set_cb(struct dvbm_port *port, struct dvbm_cb *cb) +{ + struct dvbm_ctx *ctx; + enum dvbm_port_dir dir; + + if (WARN_ON(!port) || WARN_ON(!cb)) + return -EINVAL; + + ctx = port_to_ctx(port); + dir = port->dir; + + if (dir == DVBM_ISP_PORT) { + + } else if (dir == DVBM_VEPU_PORT) { + ctx->vepu_cb.cb = cb->cb; + ctx->vepu_cb.ctx = cb->ctx; + } + + return 0; +} +EXPORT_SYMBOL(rk_dvbm_set_cb); + +int rk_dvbm_ctrl(struct dvbm_port *port, enum dvbm_cmd cmd, void *arg) +{ + struct dvbm_ctx *ctx; + struct rk_dvbm_regs *reg; + + if (WARN_ON(!port) || WARN_ON(!arg)) + return -EINVAL; + if ((cmd < DVBM_ISP_CMD_BASE) || (cmd > DVBM_VEPU_CMD_BUTT)) { + dvbm_err("%s input cmd invalid\n", __func__); + return -EINVAL; + } + + ctx = port_to_ctx(port); + reg = &ctx->regs; + + switch (cmd) { + case DVBM_ISP_SET_CFG: { + struct dvbm_isp_cfg_t *cfg = (struct dvbm_isp_cfg_t *)arg; + struct rk_dvbm_base *addr_base = ®->addr_base; + + addr_base->ybuf_bot = cfg->dma_addr + cfg->ybuf_bot; + addr_base->ybuf_top = cfg->dma_addr + cfg->ybuf_top; + addr_base->ybuf_sadr = cfg->dma_addr + cfg->ybuf_bot; + addr_base->ybuf_fstd = cfg->ybuf_fstd; + addr_base->ybuf_lstd = cfg->ybuf_lstd; + + addr_base->cbuf_bot = cfg->dma_addr + cfg->cbuf_bot; + addr_base->cbuf_top = cfg->dma_addr + cfg->cbuf_top; + addr_base->cbuf_sadr = cfg->dma_addr + cfg->cbuf_bot; + addr_base->cbuf_fstd = cfg->cbuf_fstd; + addr_base->cbuf_lstd = cfg->cbuf_lstd; + + addr_base->aful_thdy = cfg->ybuf_lstd; + addr_base->aful_thdc = cfg->ybuf_lstd; + addr_base->oful_thdy = cfg->ybuf_lstd; + addr_base->oful_thdc = cfg->ybuf_lstd; + dvbm_debug("dma_addr 0x%08x y_lstd %d y_fstd %d\n", + cfg->dma_addr, cfg->ybuf_lstd, cfg->ybuf_fstd); + dvbm_debug("ybot 0x%x top 0x%x cbuf bot 0x%x top 0x%x\n", + addr_base->ybuf_bot, addr_base->ybuf_top, + addr_base->cbuf_bot, addr_base->cbuf_top); + { + u32 *data = (u32 *)®->addr_base; + u32 i; + + for (i = 0; i < sizeof(struct rk_dvbm_base) / sizeof(u32); i++) + rk_dvbm_set_reg(ctx, i * sizeof(u32), data[i]); + } + } break; + case DVBM_ISP_FRM_START: { + ctx->isp_frm_start = *(u32 *)arg; + dvbm_debug("isp frame start[%d : %d]\n", + ctx->isp_frm_start, ctx->isp_frm_end); + dvbm2enc_callback(ctx, DVBM_VEPU_NOTIFY_FRM_STR, &ctx->isp_frm_start); + } break; + case DVBM_ISP_FRM_END: { + u64 ns; + + ns = ktime_get_ns(); + ctx->isp_frm_end = *(u32 *)arg; + if (ctx->isp_frm_ns) { + u64 interval = ns - ctx->isp_frm_ns; + + dvbm_debug("isp frame end[%d : %d] times %lld ns\n", + ctx->isp_frm_start, ctx->isp_frm_end, interval); + } + ctx->isp_frm_ns = ns; + dvbm2enc_callback(ctx, DVBM_VEPU_NOTIFY_FRM_END, &ctx->isp_frm_end); + } break; + case DVBM_VEPU_GET_ADR: { + struct dvbm_addr_cfg *dvbm_adr = (struct dvbm_addr_cfg *)arg; + struct rk_dvbm_base *addr_base = ®->addr_base; + + dvbm_adr->ybuf_top = addr_base->ybuf_top; + dvbm_adr->ybuf_bot = addr_base->ybuf_bot; + dvbm_adr->cbuf_top = addr_base->cbuf_top; + dvbm_adr->cbuf_bot = addr_base->cbuf_bot; + } break; + case DVBM_VEPU_GET_CUR_ID: { + *(u32 *)arg = ctx->isp_frm_start; + } break; + case DVBM_VEPU_SET_RESYNC: { + reg->dvbm_cfg.auto_resyn = *(u32 *)arg; + dev_info(ctx->dev, "change resync %s\n", + reg->dvbm_cfg.auto_resyn ? "auto" : "soft"); + rk_dvbm_set_reg(ctx, DVBM_CFG, ((u32 *)®->dvbm_cfg)[0]); + } break; + case DVBM_VEPU_DUMP_REGS: { + rk_dvbm_dump_regs(ctx); + } break; + default: { + } break; + } + + return 0; +} +EXPORT_SYMBOL(rk_dvbm_ctrl); + +static void rk_dvbm_reg_init(struct dvbm_ctx *ctx) +{ + struct rk_dvbm_regs *reg = &ctx->regs; + u32 *val = (u32 *)reg; + + reg->int_en.buf_ovfl = 1; + reg->int_en.isp_cnct = 1; + reg->int_en.vepu_cnct = 1; + reg->int_en.vepu_discnct = 1; + reg->int_en.isp_discnct = 1; + reg->int_en.resync_finish = 1; + reg->int_en.isp_cnct_timeout = 1; + reg->int_en.vepu_cnct_timeout = 1; + reg->int_en.vepu_handshake_timeout = 1; + + reg->dvbm_cfg.fmt = 0; + reg->dvbm_cfg.auto_resyn = 0; + reg->dvbm_cfg.ignore_vepu_cnct_ack = 0; + reg->dvbm_cfg.start_point_after_vepu_cnct = 0; + + reg->wdg_cfg0.wdg_isp_cnct_timeout = 0xfffff; + reg->wdg_cfg1.wdg_vepu_cnct_timeout = 0xfffff; + reg->wdg_cfg2.wdg_vepu_handshake_timeout = 0xfffff; + + rk_dvbm_set_reg(ctx, DVBM_WDG_CFG0, val[DVBM_WDG_CFG0 >> 2]); + rk_dvbm_set_reg(ctx, DVBM_WDG_CFG1, val[DVBM_WDG_CFG1 >> 2]); + rk_dvbm_set_reg(ctx, DVBM_WDG_CFG2, val[DVBM_WDG_CFG2 >> 2]); + rk_dvbm_set_reg(ctx, DVBM_CFG, val[DVBM_CFG >> 2]); + rk_dvbm_set_reg(ctx, DVBM_INT_EN, val[DVBM_INT_EN >> 2]); +} + +static void dvbm_check_irq(struct dvbm_ctx *ctx) +{ + u32 irq_st = ctx->irq_status; + u32 cur_st = ctx->dvbm_status; + + if (irq_st & ISP_CNCT) { + dvbm_debug_irq("%s isp connect success! st 0x%08x\n", + __func__, cur_st); + ctx->port_isp.linked = 1; + wake_up(&ctx->link_wait); + } + if (irq_st & ISP_DISCNCT) { + dvbm_debug_irq("%s isp disconnect success!\n", __func__); + ctx->port_isp.linked = 0; + } + if (irq_st & VEPU_CNCT) { + dvbm_debug_irq("%s vepu connect success! st 0x%08x\n", + __func__, cur_st); + ctx->port_vepu.linked = 1; + wake_up(&ctx->link_wait); + } + if (irq_st & VEPU_DISCNCT) { + dvbm_debug_irq("%s vepu disconnect success! st 0x%08x\n", __func__, cur_st); + ctx->port_vepu.linked = 0; + } + if (irq_st & BUF_OVERFLOW) { + dvbm_debug_irq("%s buffer overflow st 0x%08x auto_resync %d\n", + __func__, cur_st, ctx->regs.dvbm_cfg.auto_resyn); + if (!ctx->regs.dvbm_cfg.auto_resyn) + rk_dvbm_unlink(&ctx->port_vepu); + } + if (irq_st & (ISP_CNCT_TIMEOUT | VEPU_CNCT_TIMEOUT)) + rk_dvbm_dump_regs(ctx); +} + +static irqreturn_t rk_dvbm_irq(int irq, void *param) +{ + struct dvbm_ctx *ctx = param; + u32 irq_st = 0; + u32 cur_st = 0; + + if (ctx->reg_base) { + /* read irq st */ + irq_st = rk_dvbm_read_reg(ctx, DVBM_INT_ST); + cur_st = rk_dvbm_read_reg(ctx, DVBM_ST); + /* clr irq */ + rk_dvbm_set_reg(ctx, DVBM_INT_CLR, irq_st); + rk_dvbm_set_reg(ctx, DVBM_INT_ST, 0); + } + ctx->irq_status = irq_st; + ctx->dvbm_status = cur_st; + + dvbm_debug_irq("%s irq status 0x%08x\n", __func__, irq_st); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t rk_dvbm_isr(int irq, void *param) +{ + struct dvbm_ctx *ctx = param; + + dvbm_check_irq(ctx); + + return IRQ_HANDLED; +} + +static int rk_dvbm_probe(struct platform_device *pdev) +{ + int ret; + struct dvbm_ctx *ctx = NULL; + struct device *dev = &pdev->dev; + struct resource *res = NULL; + + dev_info(dev, "probe start\n"); + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + dev_info(dev, "dvbm ctx %p\n", ctx); + + ctx->dev = dev; + + atomic_set(&ctx->isp_ref, 0); + atomic_set(&ctx->vepu_ref, 0); + ctx->port_isp.dir = DVBM_ISP_PORT; + ctx->port_vepu.dir = DVBM_VEPU_PORT; + + platform_set_drvdata(pdev, ctx); + + pm_runtime_enable(dev); + + /* get irq */ + ctx->irq = platform_get_irq(pdev, 0); + if (ctx->irq < 0) { + dev_err(&pdev->dev, "no interrupt resource found\n"); + ret = -ENODEV; + goto failed; + } + /* get mem resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no memory resource defined\n"); + ret = -ENODEV; + goto failed; + } + + ctx->reg_base = devm_ioremap_resource(dev, res); + if (!ctx->reg_base) { + dev_err(dev, "ioremap failed for resource %pR\n", res); + ret = -ENOMEM; + goto failed; + } + + g_ctx = ctx; + rk_dvbm_reg_init(ctx); + init_waitqueue_head(&ctx->link_wait); + ret = devm_request_threaded_irq(dev, ctx->irq, + rk_dvbm_irq, rk_dvbm_isr, + IRQF_ONESHOT, dev_name(dev), ctx); + if (ret) { + dev_err(dev, "register interrupter failed\n"); + goto failed; + } + dev_info(dev, "probe success\n"); + + return 0; + +failed: + pm_runtime_disable(dev); + + return ret; +} + +static int rk_dvbm_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + dev_info(dev, "remove device\n"); + pm_runtime_disable(dev); + + return 0; +} + +static const struct of_device_id rk_dvbm_dt_ids[] = { + { + .compatible = "rockchip,rk-dvbm", + }, + { }, +}; + +static struct platform_driver rk_dvbm_driver = { + .probe = rk_dvbm_probe, + .remove = rk_dvbm_remove, + .driver = { + .name = "rk_dvbm", + .of_match_table = of_match_ptr(rk_dvbm_dt_ids), + }, +}; + +static int __init rk_dvbm_init(void) +{ + return platform_driver_register(&rk_dvbm_driver); +} + +static __exit void rk_dvbm_exit(void) +{ + platform_driver_unregister(&rk_dvbm_driver); +} + +subsys_initcall(rk_dvbm_init); +module_exit(rk_dvbm_exit); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Yandong Lin yandong.lin@rock-chips.com"); +MODULE_DESCRIPTION("Rockchip dvbm driver"); diff --git a/drivers/video/rockchip/dvbm/rockchip_dvbm.h b/drivers/video/rockchip/dvbm/rockchip_dvbm.h new file mode 100644 index 000000000000..76d7e703ec5d --- /dev/null +++ b/drivers/video/rockchip/dvbm/rockchip_dvbm.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd + */ +#ifndef __ROCKCHIP_DVBM_H__ +#define __ROCKCHIP_DVBM_H__ + +struct rk_dvbm_base { + /* 0x2c */ + u32 ybuf_bot; + /* 0x30 */ + u32 ybuf_top; + /* 0x34 */ + u32 ybuf_sadr; + /* 0x38 */ + u32 ybuf_lstd; + /* 0x3c */ + u32 ybuf_fstd; + /* 0x40 */ + u32 cbuf_bot; + /* 0x44 */ + u32 cbuf_top; + /* 0x48 */ + u32 cbuf_sadr; + /* 0x4c */ + u32 cbuf_lstd; + /* 0x50 */ + u32 cbuf_fstd; + /* 0x54 */ + u32 aful_thdy; + /* 0x58 */ + u32 aful_thdc; + /* 0x5c */ + u32 oful_thdy; + /* 0x60 */ + u32 oful_thdc; +}; + +struct rk_dvbm_regs { + /* 0x0 */ + u32 version; + + /* 0x4 */ + struct { + u32 isp_cnct : 1; + u32 reserved : 31; + } isp_cnct; + + /* 0x8 */ + struct { + u32 vepu_cnct : 1; + u32 reserved : 31; + } vepu_cnct; + + /* 0xc */ + struct { + u32 auto_resyn : 1; + u32 ignore_vepu_cnct_ack : 1; + /* + * 1’b0 : the current ISP frame + * 1’b1 : the next ISP frame + */ + u32 start_point_after_vepu_cnct : 1; + u32 reserved0 : 5; + /* only support yuv420sp 4'h0 */ + u32 fmt : 4; + u32 reserved1 : 20; + } dvbm_cfg; + + /* 0x10 */ + struct { + u32 wdg_isp_cnct_timeout : 22; + u32 reserved : 10; + } wdg_cfg0; + + /* 0x14 */ + struct { + u32 wdg_vepu_cnct_timeout : 22; + u32 reserved : 10; + } wdg_cfg1; + + /* 0x18 */ + struct { + u32 wdg_vepu_handshake_timeout : 22; + u32 reserved : 10; + } wdg_cfg2; + + /* 0x1c */ + struct { + u32 buf_ovfl : 1; + u32 resync_finish : 1; + u32 isp_cnct_timeout : 1; + u32 vepu_cnct_timeout : 1; + + u32 vepu_handshake_timeout : 1; + u32 isp_cnct : 1; + u32 isp_discnct : 1; + u32 vepu_cnct : 1; + + u32 vepu_discnct : 1; + u32 reserved : 23; + } int_en; + + /* 0x20 */ + struct { + u32 buf_ovfl : 1; + u32 resync_finish : 1; + u32 isp_cnct_timeout : 1; + u32 vepu_cnct_timeout : 1; + + u32 vepu_handshake_timeout : 1; + u32 isp_cnct : 1; + u32 isp_discnct : 1; + u32 vepu_cnct : 1; + + u32 vepu_discnct : 1; + u32 reserved : 23; + } int_msk; + + /* 0x24 */ + struct { + u32 buf_ovfl : 1; + u32 resync_finish : 1; + u32 isp_cnct_timeout : 1; + u32 vepu_cnct_timeout : 1; + + u32 vepu_handshake_timeout : 1; + u32 isp_cnct : 1; + u32 isp_discnct : 1; + u32 vepu_cnct : 1; + + u32 vepu_discnct : 1; + u32 reserved : 23; + } int_clr; + + /* 0x28 */ + struct { + u32 buf_ovfl : 1; + u32 resync_finish : 1; + u32 isp_cnct_timeout : 1; + u32 vepu_cnct_timeout : 1; + + u32 vepu_handshake_timeout : 1; + u32 isp_cnct : 1; + u32 isp_discnct : 1; + u32 vepu_cnct : 1; + + u32 vepu_discnct : 1; + u32 reserved : 23; + } int_st; + struct rk_dvbm_base addr_base; + /* 0x64 - 0x7c */ + u32 reserved[7]; + + /* 0x80 */ + struct { + u32 isp_connection : 1; + u32 vepu_connection : 1; + u32 resynchronization : 1; + u32 y_buf_ovfl : 1; + + u32 c_buf_ovfl : 1; + u32 reserved : 27; + } dvbm_st; + + /* 0x84 */ + u32 ovfl_st; +}; + +struct dvbm_ctx { + struct device *dev; + void __iomem *reg_base; + struct rk_dvbm_regs regs; + + u32 isp_connet; + u32 vepu_connet; + u32 buf_overflow; + u32 irq_status; + u32 dvbm_status; + int irq; + + struct dvbm_port port_isp; + struct dvbm_port port_vepu; + + atomic_t isp_ref; + atomic_t vepu_ref; + + atomic_t isp_link; + atomic_t vepu_link; + + struct dvbm_cb vepu_cb; + struct dvbm_cb isp_cb; + + u32 isp_frm_start; + u32 isp_frm_end; + u64 isp_frm_ns; + + wait_queue_head_t link_wait; + +#ifdef CONFIG_ROCKCHIP_MPP_PROC_FS + struct proc_dir_entry *procfs; +#endif +}; + +#endif diff --git a/include/soc/rockchip/rockchip_dvbm.h b/include/soc/rockchip/rockchip_dvbm.h new file mode 100644 index 000000000000..8b0eec7903ce --- /dev/null +++ b/include/soc/rockchip/rockchip_dvbm.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd + */ +#ifndef __SOC_ROCKCHIP_DVBM_H +#define __SOC_ROCKCHIP_DVBM_H + +#include +#include + +enum dvbm_port_dir { + DVBM_ISP_PORT, + DVBM_VEPU_PORT, +}; + +enum dvbm_cmd { + DVBM_ISP_CMD_BASE = 0, + DVBM_ISP_SET_CFG, + DVBM_ISP_FRM_START, + DVBM_ISP_FRM_END, + DVBM_ISP_CMD_BUTT, + + DVBM_VEPU_CMD_BASE = 0x10, + DVBM_VEPU_SET_RESYNC, + DVBM_VEPU_GET_ADR, + DVBM_VEPU_GET_CUR_ID, + DVBM_VEPU_DUMP_REGS, + DVBM_VEPU_CMD_BUTT, +}; + +enum dvbm_cb_event { + DVBM_ISP_EVENT_BASE = 0, + DVBM_ISP_EVENT_BUTT, + + DVBM_VEPU_EVENT_BASE = 0x10, + DVBM_VEPU_NOTIFY_ADDR, + DVBM_VEPU_REQ_CONNECT, + DVBM_VEPU_NOTIFY_FRM_STR, + DVBM_VEPU_NOTIFY_FRM_END, + DVBM_VEPU_EVENT_BUTT, +}; + +struct dvbm_port { + enum dvbm_port_dir dir; + u32 linked; +}; + +struct dvbm_isp_cfg_t { + u32 fmt; + u32 timeout; + + struct dmabuf *buf; + dma_addr_t dma_addr; + u32 ybuf_top; + u32 ybuf_bot; + u32 ybuf_lstd; + u32 ybuf_fstd; + u32 cbuf_top; + u32 cbuf_bot; + u32 cbuf_lstd; + u32 cbuf_fstd; +}; + +struct dvbm_isp_frm_cfg { + s32 frm_idx; + u32 ybuf_start; + u32 cbuf_start; +}; + +struct dvbm_addr_cfg { + u32 ybuf_top; + u32 ybuf_bot; + u32 cbuf_top; + u32 cbuf_bot; +}; + +typedef int (*dvbm_callback)(void *ctx, enum dvbm_cb_event event, void *arg); + +struct dvbm_cb { + dvbm_callback cb; + void *ctx; + int event; +}; + +#if IS_ENABLED(CONFIG_ROCKCHIP_DVBM) + +struct dvbm_port *rk_dvbm_get_port(struct platform_device *pdev, + enum dvbm_port_dir dir); +int rk_dvbm_put(struct dvbm_port *port); +int rk_dvbm_link(struct dvbm_port *port); +int rk_dvbm_unlink(struct dvbm_port *port); +int rk_dvbm_set_cb(struct dvbm_port *port, struct dvbm_cb *cb); +int rk_dvbm_ctrl(struct dvbm_port *port, enum dvbm_cmd cmd, void *arg); + +#else + +static inline struct dvbm_port *rk_dvbm_get_port(struct platform_device *pdev, + enum dvbm_port_dir dir) +{ + return ERR_PTR(-ENODEV); +} + +static inline int rk_dvbm_put(struct dvbm_port *port) +{ + return -ENODEV; +} + +static inline int rk_dvbm_link(struct dvbm_port *port) +{ + return -ENODEV; +} +static inline int rk_dvbm_unlink(struct dvbm_port *port) +{ + return -ENODEV; +} + +static inline int rk_dvbm_set_cb(struct dvbm_port *port, struct dvbm_cb *cb) +{ + return -ENODEV; +} + +static inline int rk_dvbm_ctrl(struct dvbm_port *port, enum dvbm_cmd cmd, void *arg) +{ + return -ENODEV; +} + +#endif + +#endif