提交 deb95c6c 编写于 作者: M Manjunathappa, Prakash 提交者: Florian Tobias Schandinat

video: da8xx-fb: fix flicker due to 1 frame delay in updated frame

Flicker/tearing effect is observed with current FB driver.
Issue is because of 2 active DMA channels ping ponging among them
along with usage of 2 DDR ping pong buffers in driver. Application
unaware of active DMA channel keeps updating frame being displayed,
this leads to tearing effect.
Below steps describes the issue:
1)Initially assume both buffers FB0 and FB1 are programmed for buffer-0.
2)On EOF0: Program FB0 for buffer-1, indicate(wake up) application
 to fill up buffer-0. As FB1 is active and continues to DMA buffer-0
(which is being filled), leading to tearing/flickering issue.
3)On EOF1: Program FB1 for buffer-0, indicate(wake up) application to
 fill up buffer-1. As FB0 is active and continues to DMA buffer-1(which
 is being filled), leading to tearing/flickering issue.
4)On EOF0: Program FB0 for buffer-1, indicate(wake up) application to
fill up buffer-0. As FB1 is active and continues to DMA buffer-0(which is
being filled), leading to tearing/flickering issue.
...
Above steps depict that issue is because of 1 frame delay in frame
panned by application.

Patch fixes the issue by keeping track free DMA channel and configures
it in drivers PAN callback so that panned frame from application gets
displayed in next frame period.

Wiki below describes the issue in detail and it also has link to
application with which issue can be reproduced.
http://processors.wiki.ti.com/index.php/DA8xx_LCDC_Linux_FB_FAQsSigned-off-by: NNellutla, Aditya <aditya.n@ti.com>
Signed-off-by: NManjunathappa, Prakash <prakash.pm@ti.com>
Signed-off-by: NFlorian Tobias Schandinat <FlorianSchandinat@gmx.de>
上级 8a81dccd
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/console.h> #include <linux/console.h>
#include <linux/spinlock.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/lcm.h> #include <linux/lcm.h>
#include <video/da8xx-fb.h> #include <video/da8xx-fb.h>
...@@ -161,6 +162,13 @@ struct da8xx_fb_par { ...@@ -161,6 +162,13 @@ struct da8xx_fb_par {
wait_queue_head_t vsync_wait; wait_queue_head_t vsync_wait;
int vsync_flag; int vsync_flag;
int vsync_timeout; int vsync_timeout;
spinlock_t lock_for_chan_update;
/*
* LCDC has 2 ping pong DMA channels, channel 0
* and channel 1.
*/
unsigned int which_dma_channel_done;
#ifdef CONFIG_CPU_FREQ #ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition; struct notifier_block freq_transition;
unsigned int lcd_fck_rate; unsigned int lcd_fck_rate;
...@@ -741,6 +749,7 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg) ...@@ -741,6 +749,7 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
lcdc_write(stat, LCD_MASKED_STAT_REG); lcdc_write(stat, LCD_MASKED_STAT_REG);
if (stat & LCD_END_OF_FRAME0) { if (stat & LCD_END_OF_FRAME0) {
par->which_dma_channel_done = 0;
lcdc_write(par->dma_start, lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
lcdc_write(par->dma_end, lcdc_write(par->dma_end,
...@@ -750,6 +759,7 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg) ...@@ -750,6 +759,7 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
} }
if (stat & LCD_END_OF_FRAME1) { if (stat & LCD_END_OF_FRAME1) {
par->which_dma_channel_done = 1;
lcdc_write(par->dma_start, lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_1_REG); LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
lcdc_write(par->dma_end, lcdc_write(par->dma_end,
...@@ -796,6 +806,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg) ...@@ -796,6 +806,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg)
lcdc_write(stat, LCD_STAT_REG); lcdc_write(stat, LCD_STAT_REG);
if (stat & LCD_END_OF_FRAME0) { if (stat & LCD_END_OF_FRAME0) {
par->which_dma_channel_done = 0;
lcdc_write(par->dma_start, lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
lcdc_write(par->dma_end, lcdc_write(par->dma_end,
...@@ -805,6 +816,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg) ...@@ -805,6 +816,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg)
} }
if (stat & LCD_END_OF_FRAME1) { if (stat & LCD_END_OF_FRAME1) {
par->which_dma_channel_done = 1;
lcdc_write(par->dma_start, lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_1_REG); LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
lcdc_write(par->dma_end, lcdc_write(par->dma_end,
...@@ -1053,6 +1065,7 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var, ...@@ -1053,6 +1065,7 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var,
struct fb_fix_screeninfo *fix = &fbi->fix; struct fb_fix_screeninfo *fix = &fbi->fix;
unsigned int end; unsigned int end;
unsigned int start; unsigned int start;
unsigned long irq_flags;
if (var->xoffset != fbi->var.xoffset || if (var->xoffset != fbi->var.xoffset ||
var->yoffset != fbi->var.yoffset) { var->yoffset != fbi->var.yoffset) {
...@@ -1070,6 +1083,21 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var, ...@@ -1070,6 +1083,21 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var,
end = start + fbi->var.yres * fix->line_length - 1; end = start + fbi->var.yres * fix->line_length - 1;
par->dma_start = start; par->dma_start = start;
par->dma_end = end; par->dma_end = end;
spin_lock_irqsave(&par->lock_for_chan_update,
irq_flags);
if (par->which_dma_channel_done == 0) {
lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
lcdc_write(par->dma_end,
LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG);
} else if (par->which_dma_channel_done == 1) {
lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
lcdc_write(par->dma_end,
LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG);
}
spin_unlock_irqrestore(&par->lock_for_chan_update,
irq_flags);
} }
} }
...@@ -1299,6 +1327,8 @@ static int __devinit fb_probe(struct platform_device *device) ...@@ -1299,6 +1327,8 @@ static int __devinit fb_probe(struct platform_device *device)
/* initialize the vsync wait queue */ /* initialize the vsync wait queue */
init_waitqueue_head(&par->vsync_wait); init_waitqueue_head(&par->vsync_wait);
par->vsync_timeout = HZ / 5; par->vsync_timeout = HZ / 5;
par->which_dma_channel_done = -1;
spin_lock_init(&par->lock_for_chan_update);
/* Register the Frame Buffer */ /* Register the Frame Buffer */
if (register_framebuffer(da8xx_fb_info) < 0) { if (register_framebuffer(da8xx_fb_info) < 0) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册