summaryrefslogtreecommitdiff
path: root/drivers/gpu/imx
diff options
context:
space:
mode:
authorLaurentiu Palcu <laurentiu.palcu@nxp.com>2018-06-04 14:47:36 +0300
committerLeonard Crestez <leonard.crestez@nxp.com>2018-08-24 12:41:33 +0300
commit73d8c809ded8e4e00bd9e3f1f1183f51b3d014d7 (patch)
tree74365e99d4ccddfcee2a0ef1b6773c9715481989 /drivers/gpu/imx
parent3b58b2de81fe59707480028bb801b0863adb85ec (diff)
MLK-17925: drm: imx: dcss: fix tearing
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>
Diffstat (limited to 'drivers/gpu/imx')
-rw-r--r--drivers/gpu/imx/dcss/dcss-ctxld.c48
-rw-r--r--drivers/gpu/imx/dcss/dcss-dpr.c21
-rw-r--r--drivers/gpu/imx/dcss/dcss-dtg.c74
-rw-r--r--drivers/gpu/imx/dcss/dcss-prv.h3
-rw-r--r--drivers/gpu/imx/dcss/dcss-scaler.c21
5 files changed, 135 insertions, 32 deletions
diff --git a/drivers/gpu/imx/dcss/dcss-ctxld.c b/drivers/gpu/imx/dcss/dcss-ctxld.c
index c6c4eb3a1014..a42a969a253f 100644
--- a/drivers/gpu/imx/dcss/dcss-ctxld.c
+++ b/drivers/gpu/imx/dcss/dcss-ctxld.c
@@ -106,7 +106,7 @@ struct dcss_ctxld_priv {
u8 current_ctx;
bool in_use;
- bool run_again;
+ bool armed;
spinlock_t lock; /* protects concurent access to private data */
};
@@ -141,11 +141,6 @@ static irqreturn_t dcss_ctxld_irq_handler(int irq, void *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;
@@ -165,9 +160,8 @@ static irqreturn_t dcss_ctxld_irq_handler(int irq, void *data)
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;
}
@@ -291,10 +285,13 @@ static int __dcss_ctxld_enable(struct dcss_ctxld_priv *ctxld)
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];
@@ -360,20 +357,27 @@ int dcss_ctxld_enable(struct dcss_soc *dcss)
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)
{
@@ -404,6 +408,16 @@ void dcss_ctxld_write(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;
diff --git a/drivers/gpu/imx/dcss/dcss-dpr.c b/drivers/gpu/imx/dcss/dcss-dpr.c
index ba89a37c4247..ebf2b06957b5 100644
--- a/drivers/gpu/imx/dcss/dcss-dpr.c
+++ b/drivers/gpu/imx/dcss/dcss-dpr.c
@@ -111,6 +111,8 @@ struct dcss_dpr_ch {
u32 sys_ctrl;
u32 rtram_ctrl;
+ bool sys_ctrl_chgd;
+
u32 pitch;
bool use_dtrc;
@@ -431,8 +433,7 @@ void dcss_dpr_enable(struct dcss_soc *dcss, int ch_num, bool en)
}
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;
}
@@ -651,3 +652,19 @@ void dcss_dpr_format_set(struct dcss_soc *dcss, int ch_num, u32 pix_format,
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;
+ }
+ }
+}
diff --git a/drivers/gpu/imx/dcss/dcss-dtg.c b/drivers/gpu/imx/dcss/dcss-dtg.c
index c93925653fef..9b85c53237ec 100644
--- a/drivers/gpu/imx/dcss/dcss-dtg.c
+++ b/drivers/gpu/imx/dcss/dcss-dtg.c
@@ -17,6 +17,8 @@
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
#include <drm/drm_fourcc.h>
#include <video/imx-dcss.h>
@@ -136,6 +138,8 @@ struct dcss_dtg_priv {
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
@@ -169,6 +173,44 @@ void dcss_dtg_dump_regs(struct seq_file *s, void *data)
}
#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;
@@ -198,7 +240,7 @@ int dcss_dtg_init(struct dcss_soc *dcss, unsigned long dtg_base)
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)
@@ -215,6 +257,7 @@ void dcss_dtg_sync_set(struct dcss_soc *dcss, struct videomode *vm)
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);
@@ -253,14 +296,18 @@ void dcss_dtg_sync_set(struct dcss_soc *dcss, struct videomode *vm)
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);
@@ -424,11 +471,16 @@ void dcss_dtg_vblank_irq_enable(struct dcss_soc *dcss, bool en)
{
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)
diff --git a/drivers/gpu/imx/dcss/dcss-prv.h b/drivers/gpu/imx/dcss/dcss-prv.h
index ae9551a8953f..1911076fd09d 100644
--- a/drivers/gpu/imx/dcss/dcss-prv.h
+++ b/drivers/gpu/imx/dcss/dcss-prv.h
@@ -80,10 +80,12 @@ int dcss_ctxld_resume(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);
@@ -103,6 +105,7 @@ void dcss_hdr10_cfg(struct dcss_soc *dcss);
/* 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);
diff --git a/drivers/gpu/imx/dcss/dcss-scaler.c b/drivers/gpu/imx/dcss/dcss-scaler.c
index b948a96a7a4c..586dc1a0cffd 100644
--- a/drivers/gpu/imx/dcss/dcss-scaler.c
+++ b/drivers/gpu/imx/dcss/dcss-scaler.c
@@ -107,6 +107,8 @@ struct dcss_scaler_ch {
u32 sdata_ctrl;
u32 scaler_ctrl;
+ bool scaler_ctrl_chgd;
+
u32 c_vstart;
u32 c_hstart;
};
@@ -397,8 +399,7 @@ void dcss_scaler_enable(struct dcss_soc *dcss, int ch_num, bool en)
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;
}
@@ -975,3 +976,19 @@ void dcss_scaler_setup(struct dcss_soc *dcss, int ch_num, u32 pix_format,
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;
+ }
+ }
+}