mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
video: rockchip: dvbm: support dvbm module
Signed-off-by: Yandong Lin <yandong.lin@rock-chips.com> Change-Id: I165a7b0eebb56da76ddc3ab82cb455aee28c0ffe
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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/
|
||||
|
||||
18
drivers/video/rockchip/dvbm/Kconfig
Normal file
18
drivers/video/rockchip/dvbm/Kconfig
Normal file
@@ -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
|
||||
5
drivers/video/rockchip/dvbm/Makefile
Normal file
5
drivers/video/rockchip/dvbm/Makefile
Normal file
@@ -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
|
||||
585
drivers/video/rockchip/dvbm/rockchip_dvbm.c
Normal file
585
drivers/video/rockchip/dvbm/rockchip_dvbm.c
Normal file
@@ -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 <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <soc/rockchip/rockchip_dvbm.h>
|
||||
|
||||
#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");
|
||||
205
drivers/video/rockchip/dvbm/rockchip_dvbm.h
Normal file
205
drivers/video/rockchip/dvbm/rockchip_dvbm.h
Normal file
@@ -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
|
||||
129
include/soc/rockchip/rockchip_dvbm.h
Normal file
129
include/soc/rockchip/rockchip_dvbm.h
Normal file
@@ -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 <linux/dma-buf.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user