diff --git a/drivers/video/rockchip/dvbm/rockchip_dvbm.c b/drivers/video/rockchip/dvbm/rockchip_dvbm.c index c769c94fae08..546a284afbfd 100644 --- a/drivers/video/rockchip/dvbm/rockchip_dvbm.c +++ b/drivers/video/rockchip/dvbm/rockchip_dvbm.c @@ -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; diff --git a/drivers/video/rockchip/dvbm/rockchip_dvbm.h b/drivers/video/rockchip/dvbm/rockchip_dvbm.h index 76d7e703ec5d..69cb5dbad63f 100644 --- a/drivers/video/rockchip/dvbm/rockchip_dvbm.h +++ b/drivers/video/rockchip/dvbm/rockchip_dvbm.h @@ -5,6 +5,9 @@ #ifndef __ROCKCHIP_DVBM_H__ #define __ROCKCHIP_DVBM_H__ +#include +#include + 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 diff --git a/include/soc/rockchip/rockchip_dvbm.h b/include/soc/rockchip/rockchip_dvbm.h index 8b0eec7903ce..d82ba0207a9b 100644 --- a/include/soc/rockchip/rockchip_dvbm.h +++ b/include/soc/rockchip/rockchip_dvbm.h @@ -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 {