The video tearing appeared only when the application used 2 buffers.
That's because, sometimes, the context loader could be armed after the
DB event came in the frame trace. That made a buffer submitted in frame
N end up on screen in frame N+2 because the context loader waits for the
next DB event. Since vblank events are sent at the end of the frame, by
the time the buffer lands on screen, the application will reuse it while
it's being displayed, hence the tearing effect.
This patch moves the CTXLD trigger moment all the way to the end of the
frame trace, just before DB event arrives. This will leave the
application plenty of time to submit new buffers.
In the event that the trigger moment is missed (application submits a
buffer right at the end of a frame trace), then we're not signalling the
next VBLANK event to application. This way, application will know that
the buffer is still needed and will not submit a new one.
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@nxp.com>
struct dcss_crtc *dcss_crtc = dev_id;
struct dcss_soc *dcss = dev_get_drvdata(dcss_crtc->dev->parent);
- drm_crtc_handle_vblank(&dcss_crtc->base);
+ if (dcss_ctxld_is_flushed(dcss))
+ drm_crtc_handle_vblank(&dcss_crtc->base);
dcss_vblank_irq_clear(dcss);
u8 current_ctx;
bool in_use;
- bool run_again;
+ bool armed;
spinlock_t lock; /* protects concurent access to private data */
};
!(irq_status & CTXLD_ENABLE) && priv->in_use) {
priv->in_use = false;
- if (priv->run_again) {
- priv->run_again = false;
- __dcss_ctxld_enable(priv);
- goto exit;
- }
if (priv->dcss->dcss_disable_callback) {
struct dcss_dtg_priv *dtg = priv->dcss->dtg_priv;
priv->ctx_size[priv->current_ctx ^ 1][CTX_SB_LP]);
}
-exit:
dcss_clr(irq_status & (CTXLD_IRQ_ERROR | CTXLD_IRQ_COMPLETION),
- priv->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
+ priv->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
return IRQ_HANDLED;
}
u32 db_base, sb_base, sb_count;
u32 sb_hp_cnt, sb_lp_cnt, db_cnt;
+ dcss_dpr_write_sysctrl(ctxld->dcss);
+ dcss_scaler_write_sclctrl(ctxld->dcss);
+
if (dcss_dtrc_is_running(ctxld->dcss, 1) ||
dcss_dtrc_is_running(ctxld->dcss, 2)) {
dcss_dtrc_switch_banks(ctxld->dcss);
- ctxld->run_again = true;
+ ctxld->armed = true;
}
sb_hp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_HP];
unsigned long flags;
spin_lock_irqsave(&ctxld->lock, flags);
- if (ctxld->in_use) {
- ctxld->run_again = true;
- spin_unlock_irqrestore(&ctxld->lock, flags);
- return 0;
- }
-
- __dcss_ctxld_enable(ctxld);
-
+ ctxld->armed = true;
spin_unlock_irqrestore(&ctxld->lock, flags);
return 0;
}
EXPORT_SYMBOL(dcss_ctxld_enable);
+void dcss_ctxld_kick(struct dcss_soc *dcss)
+{
+ struct dcss_ctxld_priv *ctxld = dcss->ctxld_priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctxld->lock, flags);
+ if (ctxld->armed) {
+ ctxld->armed = false;
+ __dcss_ctxld_enable(dcss->ctxld_priv);
+ }
+ spin_unlock_irqrestore(&ctxld->lock, flags);
+}
+EXPORT_SYMBOL(dcss_ctxld_kick);
+
void dcss_ctxld_write_irqsafe(struct dcss_soc *dcss, u32 ctx_id, u32 val,
u32 reg_ofs)
{
spin_unlock_irqrestore(&ctxld->lock, flags);
}
+bool dcss_ctxld_is_flushed(struct dcss_soc *dcss)
+{
+ struct dcss_ctxld_priv *ctxld = dcss->ctxld_priv;
+
+ return ctxld->ctx_size[ctxld->current_ctx][CTX_DB] == 0 &&
+ ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] == 0 &&
+ ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] == 0;
+}
+EXPORT_SYMBOL(dcss_ctxld_is_flushed);
+
int dcss_ctxld_resume(struct dcss_soc *dcss)
{
struct dcss_ctxld_priv *ctxld = dcss->ctxld_priv;
u32 sys_ctrl;
u32 rtram_ctrl;
+ bool sys_ctrl_chgd;
+
u32 pitch;
bool use_dtrc;
}
if (ch->sys_ctrl != sys_ctrl)
- dcss_dpr_write(dpr, ch_num, sys_ctrl,
- DCSS_DPR_SYSTEM_CTRL0);
+ ch->sys_ctrl_chgd = true;
ch->sys_ctrl = sys_ctrl;
}
dcss_dpr_rtram_set(dcss, ch_num, pix_format);
}
EXPORT_SYMBOL(dcss_dpr_format_set);
+
+void dcss_dpr_write_sysctrl(struct dcss_soc *dcss)
+{
+ int chnum;
+
+ for (chnum = 0; chnum < 3; chnum++) {
+ struct dcss_dpr_ch *ch = &dcss->dpr_priv->ch[chnum];
+
+ if (ch->sys_ctrl_chgd) {
+ dcss_ctxld_write_irqsafe(dcss, ch->ctx_id, ch->sys_ctrl,
+ ch->base_ofs +
+ DCSS_DPR_SYSTEM_CTRL0);
+ ch->sys_ctrl_chgd = false;
+ }
+ }
+}
#include <linux/clk.h>
#include <linux/seq_file.h>
#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
#include <drm/drm_fourcc.h>
#include <video/imx-dcss.h>
u32 alpha;
u32 use_global;
+ int ctxld_kick_irq;
+
/*
* This will be passed on by DRM CRTC so that we can signal when DTG has
* been successfully stopped. Otherwise, any modesetting while DTG is
}
#endif
+static irqreturn_t dcss_dtg_irq_handler(int irq, void *data)
+{
+ struct dcss_dtg_priv *dtg = data;
+ u32 status;
+
+ status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
+
+ dcss_ctxld_kick(dtg->dcss);
+
+ dcss_writel(status & LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
+
+ return IRQ_HANDLED;
+}
+
+static int dcss_dtg_irq_config(struct dcss_dtg_priv *dtg)
+{
+ struct dcss_soc *dcss = dtg->dcss;
+ struct platform_device *pdev = to_platform_device(dcss->dev);
+ int ret;
+
+ dtg->ctxld_kick_irq = platform_get_irq_byname(pdev, "ctxld_kick");
+ if (dtg->ctxld_kick_irq < 0) {
+ dev_err(dcss->dev, "dtg: can't get line2 irq number\n");
+ return dtg->ctxld_kick_irq;
+ }
+
+ ret = devm_request_irq(dcss->dev, dtg->ctxld_kick_irq,
+ dcss_dtg_irq_handler,
+ IRQF_TRIGGER_HIGH,
+ "dcss_ctxld_kick", dtg);
+ if (ret) {
+ dev_err(dcss->dev, "dtg: irq request failed.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
int dcss_dtg_init(struct dcss_soc *dcss, unsigned long dtg_base)
{
struct dcss_dtg_priv *dtg;
dtg->control_status |= OVL_DATA_MODE | BLENDER_VIDEO_ALPHA_SEL |
((dtg->alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK);
- return 0;
+ return dcss_dtg_irq_config(dtg);
}
void dcss_dtg_exit(struct dcss_soc *dcss)
u16 dtg_lrc_x, dtg_lrc_y;
u16 dis_ulc_x, dis_ulc_y;
u16 dis_lrc_x, dis_lrc_y;
+ u32 sb_ctxld_trig, db_ctxld_trig;
dev_dbg(dcss->dev, "hfront_porch = %d\n", vm->hfront_porch);
dev_dbg(dcss->dev, "hback_porch = %d\n", vm->hback_porch);
dtg->dis_ulc_x = dis_ulc_x;
dtg->dis_ulc_y = dis_ulc_y;
- /*
- * If the dis_ulc_y is too small, then the context loader will not have
- * time to load the DB context. This happens with LCD panels which have
- * small vfront_porch, vback_porch and/or vsync_len.
- */
- dcss_dtg_write(dtg, ((0 << TC_CTXLD_SB_Y_POS) & TC_CTXLD_SB_Y_MASK) |
- (dis_ulc_y < 50 ? 50 : dis_ulc_y),
- DCSS_DTG_TC_CTXLD);
+ sb_ctxld_trig = ((0 * dis_lrc_y / 100) << TC_CTXLD_SB_Y_POS) &
+ TC_CTXLD_SB_Y_MASK;
+ db_ctxld_trig = ((99 * dis_lrc_y / 100) << TC_CTXLD_DB_Y_POS) &
+ TC_CTXLD_DB_Y_MASK;
+
+ dcss_dtg_write(dtg, sb_ctxld_trig | db_ctxld_trig, DCSS_DTG_TC_CTXLD);
+
+ /* vblank trigger */
+ dcss_dtg_write(dtg, 0, DCSS_DTG_LINE0_INT);
+
+ /* CTXLD trigger */
+ dcss_dtg_write(dtg, ((98 * dis_lrc_y) / 100) << 16, DCSS_DTG_LINE1_INT);
}
EXPORT_SYMBOL(dcss_dtg_sync_set);
{
void __iomem *reg;
struct dcss_dtg_priv *dtg = dcss->dtg_priv;
- u32 val = en ? LINE0_IRQ : 0;
+ u32 val = en ? (LINE0_IRQ | LINE1_IRQ) : 0;
+
+ /* need to keep the CTXLD kick interrupt ON if DTRC is used */
+ if (!en && (dcss_dtrc_is_running(dcss, 1) ||
+ dcss_dtrc_is_running(dcss, 2)))
+ val |= LINE1_IRQ;
reg = dtg->base_reg + DCSS_DTG_INT_MASK;
- dcss_update(val, LINE0_IRQ, reg);
+ dcss_update(val, LINE0_IRQ | LINE1_IRQ, reg);
}
void dcss_dtg_vblank_irq_clear(struct dcss_soc *dcss)
int dcss_ctxld_suspend(struct dcss_soc *dcss);
void dcss_ctxld_write_irqsafe(struct dcss_soc *dcss, u32 ctx_id, u32 val,
u32 reg_ofs);
+void dcss_ctxld_kick(struct dcss_soc *dcss);
/* DPR */
int dcss_dpr_init(struct dcss_soc *dcss, unsigned long dpr_base);
void dcss_dpr_exit(struct dcss_soc *dcss);
+void dcss_dpr_write_sysctrl(struct dcss_soc *dcss);
/* DTG */
int dcss_dtg_init(struct dcss_soc *dcss, unsigned long dtg_base);
/* SCALER */
int dcss_scaler_init(struct dcss_soc *dcss, unsigned long scaler_base);
void dcss_scaler_exit(struct dcss_soc *dcss);
+void dcss_scaler_write_sclctrl(struct dcss_soc *dcss);
/* DTRC */
int dcss_dtrc_init(struct dcss_soc *dcss, unsigned long dtrc_base);
u32 sdata_ctrl;
u32 scaler_ctrl;
+ bool scaler_ctrl_chgd;
+
u32 c_vstart;
u32 c_hstart;
};
DCSS_SCALER_SDATA_CTRL);
if (ch->scaler_ctrl != scaler_ctrl)
- dcss_scaler_write(dcss->scaler_priv, ch_num, scaler_ctrl,
- DCSS_SCALER_CTRL);
+ ch->scaler_ctrl_chgd = true;
ch->scaler_ctrl = scaler_ctrl;
}
dst_yres, vrefresh_hz, wrscl_needed);
}
EXPORT_SYMBOL(dcss_scaler_setup);
+
+void dcss_scaler_write_sclctrl(struct dcss_soc *dcss)
+{
+ int chnum;
+
+ for (chnum = 0; chnum < 3; chnum++) {
+ struct dcss_scaler_ch *ch = &dcss->scaler_priv->ch[chnum];
+
+ if (ch->scaler_ctrl_chgd) {
+ dcss_ctxld_write_irqsafe(dcss, ch->ctx_id,
+ ch->scaler_ctrl,
+ ch->base_ofs + DCSS_SCALER_CTRL);
+ ch->scaler_ctrl_chgd = false;
+ }
+ }
+}
/* CTXLD */
int dcss_ctxld_enable(struct dcss_soc *dcss);
+bool dcss_ctxld_is_flushed(struct dcss_soc *dcss);
/* HDR10 */
enum dcss_hdr10_nonlinearity {