mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 11:26:02 +09:00
video: rockchip: dvbm: fix dvbm config issue
Signed-off-by: Yandong Lin <yandong.lin@rock-chips.com> Change-Id: Ia1abe5b1589fc6e02495c28b01e0ee83ef823f0f
This commit is contained in:
@@ -25,11 +25,11 @@ 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_DUMP 0x00000008
|
||||
|
||||
#define dvbm_debug(fmt, args...) \
|
||||
do { \
|
||||
@@ -49,6 +49,12 @@ static struct dvbm_ctx *g_ctx;
|
||||
pr_info(fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define dvbm_debug_dump(fmt, args...) \
|
||||
do { \
|
||||
if (unlikely(dvbm_debug & (DVBM_DEBUG_DUMP))) \
|
||||
pr_info(fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define dvbm_err(fmt, args...) \
|
||||
pr_err(fmt, ##args)
|
||||
|
||||
@@ -102,6 +108,8 @@ enum dvbm_flow {
|
||||
#define DVBM_ST 0x80
|
||||
#define DVBM_OVFL_ST 0x84
|
||||
|
||||
#define DVBM_REG_OFFSET 0x2c
|
||||
|
||||
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);
|
||||
@@ -141,12 +149,80 @@ static void dvbm2enc_callback(struct dvbm_ctx *ctx, enum dvbm_cb_event event, vo
|
||||
|
||||
static void rk_dvbm_dump_regs(struct dvbm_ctx *ctx)
|
||||
{
|
||||
u32 start = 0x4;
|
||||
u32 end = 0xb8;
|
||||
u32 start = ctx->dump_s;//0x80;
|
||||
u32 end = ctx->dump_e;//0xb8;
|
||||
u32 i;
|
||||
dvbm_debug_dump("=== %s ===\n", __func__);
|
||||
for (i = start; i <= end; i += 4)
|
||||
dvbm_debug_dump("reg[0x%0x] = 0x%08x\n", i, readl(ctx->reg_base + i));
|
||||
dvbm_debug_dump("=== %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static int rk_dvbm_clk_on(struct dvbm_ctx *ctx)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (ctx->clk)
|
||||
ret = clk_prepare_enable(ctx->clk);
|
||||
if (ret)
|
||||
dev_err(ctx->dev, "clk on failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rk_dvbm_clk_off(struct dvbm_ctx *ctx)
|
||||
{
|
||||
if (ctx->clk)
|
||||
clk_disable_unprepare(ctx->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void init_isp_infos(struct dvbm_ctx *ctx)
|
||||
{
|
||||
ctx->isp_frm_start = 0;
|
||||
ctx->isp_frm_end = 0;
|
||||
ctx->isp_frm_ns = 0;
|
||||
}
|
||||
|
||||
static int rk_dvbm_setup_iobuf(struct dvbm_ctx *ctx, struct rk_dvbm_base *addr_base)
|
||||
{
|
||||
u32 *data = (u32 *)addr_base;
|
||||
u32 i;
|
||||
|
||||
for (i = start; i <= end; i += 4)
|
||||
dvbm_debug("reg[0x%0x] = 0x%08x\n", i, readl(ctx->reg_base + i));
|
||||
for (i = 0; i < sizeof(struct rk_dvbm_base) / sizeof(u32); i++)
|
||||
rk_dvbm_set_reg(ctx, i * sizeof(u32) + DVBM_REG_OFFSET, data[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
struct dvbm_port *rk_dvbm_get_port(struct platform_device *pdev,
|
||||
@@ -190,7 +266,7 @@ int rk_dvbm_link(struct dvbm_port *port)
|
||||
struct dvbm_ctx *ctx;
|
||||
enum dvbm_port_dir dir;
|
||||
struct rk_dvbm_regs *reg;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (WARN_ON(!port))
|
||||
return -EINVAL;
|
||||
@@ -206,22 +282,19 @@ int rk_dvbm_link(struct dvbm_port *port)
|
||||
}
|
||||
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);
|
||||
if (!port->linked) {
|
||||
reg->vepu_cnct.vepu_cnct = 1;
|
||||
rk_dvbm_set_reg(ctx, DVBM_VEPU_CNCT, 0x1);
|
||||
}
|
||||
dvbm_debug_dump("=== vepu link ===\n");
|
||||
rk_dvbm_dump_regs(ctx);
|
||||
dvbm_debug_dump("=== vepu link ===\n");
|
||||
}
|
||||
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);
|
||||
|
||||
dvbm_debug("%s connect frm_cnt[%d : %d]\n",
|
||||
dir == DVBM_ISP_PORT ? "isp" : "vepu",
|
||||
ctx->isp_frm_start, ctx->isp_frm_end);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -285,7 +358,7 @@ 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))
|
||||
if (WARN_ON(!arg))
|
||||
return -EINVAL;
|
||||
if ((cmd < DVBM_ISP_CMD_BASE) || (cmd > DVBM_VEPU_CMD_BUTT)) {
|
||||
dvbm_err("%s input cmd invalid\n", __func__);
|
||||
@@ -321,13 +394,9 @@ int rk_dvbm_ctrl(struct dvbm_port *port, enum dvbm_cmd cmd, void *arg)
|
||||
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]);
|
||||
}
|
||||
rk_dvbm_setup_iobuf(ctx, addr_base);
|
||||
rk_dvbm_reg_init(ctx);
|
||||
init_isp_infos(ctx);
|
||||
} break;
|
||||
case DVBM_ISP_FRM_START: {
|
||||
ctx->isp_frm_start = *(u32 *)arg;
|
||||
@@ -367,6 +436,15 @@ int rk_dvbm_ctrl(struct dvbm_port *port, enum dvbm_cmd cmd, void *arg)
|
||||
reg->dvbm_cfg.auto_resyn ? "auto" : "soft");
|
||||
rk_dvbm_set_reg(ctx, DVBM_CFG, ((u32 *)®->dvbm_cfg)[0]);
|
||||
} break;
|
||||
case DVBM_VEPU_SET_CFG: {
|
||||
struct dvbm_vepu_cfg *cfg = (struct dvbm_vepu_cfg *)arg;
|
||||
|
||||
reg->dvbm_cfg.auto_resyn = cfg->auto_resyn;
|
||||
reg->dvbm_cfg.ignore_vepu_cnct_ack = cfg->ignore_vepu_cnct_ack;
|
||||
reg->dvbm_cfg.start_point_after_vepu_cnct = cfg->start_point_after_vepu_cnct;
|
||||
|
||||
rk_dvbm_set_reg(ctx, DVBM_CFG, ((u32 *)®->dvbm_cfg)[0]);
|
||||
} break;
|
||||
case DVBM_VEPU_DUMP_REGS: {
|
||||
rk_dvbm_dump_regs(ctx);
|
||||
} break;
|
||||
@@ -378,37 +456,6 @@ int rk_dvbm_ctrl(struct dvbm_port *port, enum dvbm_cmd cmd, void *arg)
|
||||
}
|
||||
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;
|
||||
@@ -418,7 +465,6 @@ static void dvbm_check_irq(struct dvbm_ctx *ctx)
|
||||
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__);
|
||||
@@ -428,16 +474,16 @@ static void dvbm_check_irq(struct dvbm_ctx *ctx)
|
||||
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)
|
||||
dvbm_debug_irq("%s buf overflow st 0x%08x auto_resync %d ignore %d\n",
|
||||
__func__, cur_st, ctx->regs.dvbm_cfg.auto_resyn, ctx->ignore_ovfl);
|
||||
|
||||
if (!ctx->regs.dvbm_cfg.auto_resyn && !ctx->ignore_ovfl)
|
||||
rk_dvbm_unlink(&ctx->port_vepu);
|
||||
}
|
||||
if (irq_st & (ISP_CNCT_TIMEOUT | VEPU_CNCT_TIMEOUT))
|
||||
@@ -454,6 +500,12 @@ static irqreturn_t rk_dvbm_irq(int irq, void *param)
|
||||
/* read irq st */
|
||||
irq_st = rk_dvbm_read_reg(ctx, DVBM_INT_ST);
|
||||
cur_st = rk_dvbm_read_reg(ctx, DVBM_ST);
|
||||
if (irq_st & BUF_OVERFLOW) {
|
||||
dvbm_debug_dump("=== dvbm overflow! dump reg st: 0x%08x===\n", irq_st);
|
||||
rk_dvbm_dump_regs(ctx);
|
||||
dvbm2enc_callback(ctx, DVBM_VEPU_NOTIFY_DUMP, NULL);
|
||||
dvbm_debug_dump("=== dvbm overflow! dump reg end===\n");
|
||||
}
|
||||
/* clr irq */
|
||||
rk_dvbm_set_reg(ctx, DVBM_INT_CLR, irq_st);
|
||||
rk_dvbm_set_reg(ctx, DVBM_INT_ST, 0);
|
||||
@@ -515,15 +567,35 @@ static int rk_dvbm_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ctx->reg_base = devm_ioremap_resource(dev, res);
|
||||
if (!ctx->reg_base) {
|
||||
if (IS_ERR_OR_NULL(ctx->reg_base)) {
|
||||
dev_err(dev, "ioremap failed for resource %pR\n", res);
|
||||
ret = -ENOMEM;
|
||||
ret = -ENODEV;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
ctx->clk = devm_clk_get(ctx->dev, "clk_core");
|
||||
if (IS_ERR_OR_NULL(ctx->clk)) {
|
||||
dev_err(dev, "clk_get failed for resource %pR\n", res);
|
||||
ret = -ENODEV;
|
||||
goto failed;
|
||||
}
|
||||
ctx->rst = devm_reset_control_get(ctx->dev, "dvbm_rst");
|
||||
if (IS_ERR_OR_NULL(ctx->rst)) {
|
||||
dev_err(dev, "clk_rst failed for resource %pR\n", res);
|
||||
ret = -ENODEV;
|
||||
goto failed;
|
||||
}
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret)
|
||||
dev_err(dev, "pm get failed!\n");
|
||||
g_ctx = ctx;
|
||||
ret = rk_dvbm_clk_on(ctx);
|
||||
if (ret)
|
||||
goto failed;
|
||||
rk_dvbm_reg_init(ctx);
|
||||
init_waitqueue_head(&ctx->link_wait);
|
||||
ctx->ignore_ovfl = 1;
|
||||
ctx->dump_s = 0x80;
|
||||
ctx->dump_e = 0xb8;
|
||||
ret = devm_request_threaded_irq(dev, ctx->irq,
|
||||
rk_dvbm_irq, rk_dvbm_isr,
|
||||
IRQF_ONESHOT, dev_name(dev), ctx);
|
||||
@@ -546,6 +618,8 @@ static int rk_dvbm_remove(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
dev_info(dev, "remove device\n");
|
||||
rk_dvbm_clk_off(g_ctx);
|
||||
pm_runtime_put(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#ifndef __ROCKCHIP_DVBM_H__
|
||||
#define __ROCKCHIP_DVBM_H__
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
struct rk_dvbm_base {
|
||||
/* 0x2c */
|
||||
u32 ybuf_bot;
|
||||
@@ -168,9 +171,11 @@ struct rk_dvbm_regs {
|
||||
};
|
||||
|
||||
struct dvbm_ctx {
|
||||
struct clk *clk;
|
||||
struct device *dev;
|
||||
void __iomem *reg_base;
|
||||
struct rk_dvbm_regs regs;
|
||||
struct reset_control *rst;
|
||||
|
||||
u32 isp_connet;
|
||||
u32 vepu_connet;
|
||||
@@ -179,27 +184,25 @@ struct dvbm_ctx {
|
||||
u32 dvbm_status;
|
||||
int irq;
|
||||
|
||||
struct dvbm_port port_isp;
|
||||
/* vepu infos */
|
||||
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;
|
||||
|
||||
/* isp infos */
|
||||
struct dvbm_port port_isp;
|
||||
struct dvbm_cb isp_cb;
|
||||
atomic_t isp_link;
|
||||
atomic_t isp_ref;
|
||||
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
|
||||
/* debug infos */
|
||||
u32 dump_s;
|
||||
u32 dump_e;
|
||||
u32 ignore_ovfl;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,7 @@ enum dvbm_cmd {
|
||||
|
||||
DVBM_VEPU_CMD_BASE = 0x10,
|
||||
DVBM_VEPU_SET_RESYNC,
|
||||
DVBM_VEPU_SET_CFG,
|
||||
DVBM_VEPU_GET_ADR,
|
||||
DVBM_VEPU_GET_CUR_ID,
|
||||
DVBM_VEPU_DUMP_REGS,
|
||||
@@ -34,6 +35,7 @@ enum dvbm_cb_event {
|
||||
|
||||
DVBM_VEPU_EVENT_BASE = 0x10,
|
||||
DVBM_VEPU_NOTIFY_ADDR,
|
||||
DVBM_VEPU_NOTIFY_DUMP,
|
||||
DVBM_VEPU_REQ_CONNECT,
|
||||
DVBM_VEPU_NOTIFY_FRM_STR,
|
||||
DVBM_VEPU_NOTIFY_FRM_END,
|
||||
@@ -74,6 +76,12 @@ struct dvbm_addr_cfg {
|
||||
u32 cbuf_bot;
|
||||
};
|
||||
|
||||
struct dvbm_vepu_cfg {
|
||||
u32 auto_resyn;
|
||||
u32 ignore_vepu_cnct_ack;
|
||||
u32 start_point_after_vepu_cnct;
|
||||
};
|
||||
|
||||
typedef int (*dvbm_callback)(void *ctx, enum dvbm_cb_event event, void *arg);
|
||||
|
||||
struct dvbm_cb {
|
||||
|
||||
Reference in New Issue
Block a user