Date:2012-06-05 20:25:23 (10 years 11 months ago)
Author:Paul Cercueil
Commit:a510b72114b3b9570598fad85cb01d0e83304b87
Message:MIPS: JZ4740: SLCD: wait for VSYNC before panning display.

This ensures that a perfect image is sent to the LCD (no tearing).
- On TV-out mode, wait until the screen is done rendering the previous image.
The introduced delay can be up to 1/60 = ~16ms.
- On SLCD mode, wait until the previous image has been sent to the controller
through DMA. The wait occurs only the framerate is higher than ~78fps.
Files: drivers/video/jz4740_slcd.h (1 diff)
drivers/video/jz4740_slcd_fb.c (16 diffs)

Change Details

drivers/video/jz4740_slcd.h
9999    dma_addr_t framedesc_phys;
100100
101101    struct jz4740_dma_chan *dma;
102    struct completion dma_completion;
103    unsigned refresh_on_pan:1;
102104
103105    struct clk *ldclk;
104106    struct clk *lpclk;
105107
106108    unsigned int tv_out;
109    unsigned tv_out_vsync:1;
107110
108111    unsigned is_enabled:1;
109112    struct mutex lock; /* Protecting against running enable/disable in paralell */
drivers/video/jz4740_slcd_fb.c
3434#include "jz4740_lcd.h"
3535#include "jz4740_slcd.h"
3636
37#define FB_A320TV_OFF 0
38#define FB_A320TV_NTSC 1
39#define FB_A320TV_PAL50 2
40#define FB_A320TV_PAL60 3
41#define FB_A320TV_PAL_M 4
42#define FB_A320TV_LAST 4
43
3744static const char *jzfb_tv_out_norm[] = {
3845    "off", "ntsc", "pal", "pal-60", "pal-m",
3946};
...... 
276283    mutex_lock(&jzfb->lock);
277284    if (jzfb->is_enabled) {
278285        if (1) {
279            jzfb_upload_frame_dma(jzfb);
280            /* The DMA complete callback will reschedule. */
286            int interval;
287
288            if (jzfb->dma_completion.done) {
289                if (jzfb->refresh_on_pan)
290                    interval = HZ / 5;
291                else
292                    interval = HZ / 60;
293                jzfb->refresh_on_pan = 0;
294
295                INIT_COMPLETION(jzfb->dma_completion);
296                jzfb_upload_frame_dma(jzfb);
297            } else
298                interval = HZ / 250;
299
300            schedule_delayed_work(&jzfb->refresh_work, interval);
281301        } else {
282302            jzfb_upload_frame_cpu(jzfb);
283303            schedule_delayed_work(&jzfb->refresh_work, HZ / 10);
...... 
290310        struct jz4740_dma_chan *dma, int res, void *dev)
291311{
292312    struct jzfb *jzfb = dev_get_drvdata(dev);
293    // TODO: Stick to refresh rate in mode description.
294    int interval = HZ / 60;
295
296    schedule_delayed_work(&jzfb->refresh_work, interval);
313    complete_all(&jzfb->dma_completion);
297314}
298315
299316static int jzfb_set_par(struct fb_info *info)
...... 
438455                    jzfb_num_data_pins(jzfb));
439456    }
440457    jzfb_disable_dma(jzfb);
458    complete_all(&jzfb->dma_completion);
441459    jzfb->panel->enable(jzfb);
442460
443461    ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL);
...... 
456474    /* Abort any DMA transfer that might be in progress and allow direct
457475       writes to the panel. */
458476    jzfb_disable_dma(jzfb);
477    complete_all(&jzfb->dma_completion);
459478
460479    jzfb->panel->disable(jzfb);
461480    jz_gpio_bulk_suspend(jz_slcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
...... 
497516    return ret;
498517}
499518
500static int jzfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
519static int jzfb_wait_for_vsync(struct fb_info *info)
501520{
502521    struct jzfb *jzfb = info->par;
503522
523    if (jzfb->tv_out != FB_A320TV_OFF &&
524                !jzfb->tv_out_vsync)
525        return 0;
526    return wait_for_completion_interruptible(&jzfb->dma_completion);
527}
528
529static int jzfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
530{
531    struct jzfb *jzfb = info->par;
504532    info->var.yoffset = var->yoffset;
533
534    /* Ensure that the data to be uploaded is in memory. */
535    dma_cache_sync(&jzfb->pdev->dev, jzfb->vidmem
536                + info->fix.line_length * var->yoffset,
537                info->fix.line_length * var->yres,
538                DMA_TO_DEVICE);
539
505540    /* update frame start address for TV-out mode */
506541    (*jzfb->framedesc)[1].addr = jzfb->vidmem_phys
507542                          + info->fix.line_length * var->yoffset;
508543
544    jzfb_wait_for_vsync(info);
545
546    jzfb->refresh_on_pan = 1;
547    flush_delayed_work(&jzfb->refresh_work);
509548    return 0;
510549}
511550
...... 
586625            jzfb->blackline_size / 4;
587626    (*jzfb->framedesc)[1].addr = jzfb->vidmem_phys;
588627    (*jzfb->framedesc)[1].id = 0xdeafbead;
589    (*jzfb->framedesc)[1].cmd = max_framesize / 4;
628    (*jzfb->framedesc)[1].cmd = (max_framesize / 4)
629            | JZ_LCD_CMD_EOF_IRQ | JZ_LCD_CMD_SOF_IRQ;
590630
591631    return 0;
592632
...... 
609649                jzfb->framedesc, jzfb->framedesc_phys);
610650}
611651
612#define FB_A320TV_OFF 0
613#define FB_A320TV_NTSC 1
614#define FB_A320TV_PAL50 2
615#define FB_A320TV_PAL60 3
616#define FB_A320TV_PAL_M 4
617#define FB_A320TV_LAST 4
618
619652static int jzfb_tv_out(struct jzfb *jzfb, unsigned int mode)
620653{
621654    int blank = jzfb->is_enabled ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
...... 
636669           allow direct writes to the panel. */
637670        jzfb_disable_dma(jzfb);
638671        jzfb->panel->disable(jzfb);
672        complete_all(&jzfb->dma_completion);
639673
640674        /* set up LCD controller for TV output */
641675
...... 
674708        writel(jzfb->framedesc_phys, jzfb->base + JZ_REG_LCD_DA0);
675709
676710        writel(JZ_LCD_CTRL_BURST_16 | JZ_LCD_CTRL_ENABLE |
677               JZ_LCD_CTRL_BPP_15_16,
678               jzfb->base + JZ_REG_LCD_CTRL);
711                    JZ_LCD_CTRL_BPP_15_16 |
712                    JZ_LCD_CTRL_EOF_IRQ | JZ_LCD_CTRL_SOF_IRQ,
713                    jzfb->base + JZ_REG_LCD_CTRL);
679714    } else {
715        /* disable EOF/SOF interrupts */
716        unsigned long ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL);
717        ctrl &= ~(JZ_LCD_CTRL_EOF_IRQ | JZ_LCD_CTRL_SOF_IRQ);
718        writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL);
719
680720        /* disable LCD controller and re-enable SLCD */
681721        writel(JZ_LCD_CFG_SLCD, jzfb->base + JZ_REG_LCD_CFG);
682722        jzfb->panel->enable(jzfb);
723
724        jzfb->refresh_on_pan = 0;
725        complete_all(&jzfb->dma_completion);
683726        schedule_delayed_work(&jzfb->refresh_work, 0);
684727    }
685728
...... 
721764
722765static DEVICE_ATTR(tv_out, 0644, jzfb_tv_out_show, jzfb_tv_out_store);
723766
767static ssize_t jzfb_vsync_show(struct device *dev, struct device_attribute *attr,
768            char *buf)
769{
770    struct jzfb *jzfb = dev_get_drvdata(dev);
771    return sprintf(buf, "%u\n", jzfb->tv_out_vsync);
772}
773
774static ssize_t jzfb_vsync_store(struct device *dev, struct device_attribute *attr,
775            const char *buf, size_t n)
776{
777    struct jzfb *jzfb = dev_get_drvdata(dev);
778    unsigned int vsync;
779
780    if (sscanf(buf, "%u", &vsync) < 1)
781        return -EINVAL;
782
783    jzfb->tv_out_vsync = vsync;
784    return n;
785}
786
787static DEVICE_ATTR(tv_out_vsync, 0644, jzfb_vsync_show, jzfb_vsync_store);
788
724789static ssize_t jzfb_panel_show(struct device *dev, struct device_attribute *attr,
725790            char *buf)
726791{
...... 
764829    .fb_mmap = jzfb_mmap,
765830};
766831
832static irqreturn_t jz4740_lcd_irq(int irq, void *dev_id)
833{
834    struct jzfb *jzfb = dev_id;
835    unsigned long state = readl(jzfb->base + JZ_REG_LCD_STATE);
836
837    if (state & JZ_LCD_STATE_SOF) {
838        INIT_COMPLETION(jzfb->dma_completion);
839        state &= ~JZ_LCD_STATE_SOF;
840    } else {
841        complete_all(&jzfb->dma_completion);
842        state &= ~JZ_LCD_STATE_EOF;
843    }
844
845    /* Acknowledge the interrupt */
846    writel(state, jzfb->base + JZ_REG_LCD_STATE);
847    return IRQ_HANDLED;
848}
849
767850static int __devinit jzfb_probe(struct platform_device *pdev)
768851{
769852    int ret;
...... 
808891    jzfb->mem = mem;
809892
810893    jzfb->tv_out = FB_A320TV_OFF;
894    jzfb->tv_out_vsync = 1;
895    jzfb->refresh_on_pan = 0;
896    init_completion(&jzfb->dma_completion);
897    complete_all(&jzfb->dma_completion);
811898
812899    jzfb->dma = jz4740_dma_request(&pdev->dev, dev_name(&pdev->dev), 0);
813900    if (!jzfb->dma) {
...... 
887974                     jzfb_num_data_pins(jzfb));
888975    }
889976
977    ret = request_irq(JZ4740_IRQ_LCD, jz4740_lcd_irq, 0, "LCD", jzfb);
978    if (ret) {
979        dev_err(&pdev->dev, "Failed to request IRQ\n");
980        goto err_free_devmem;
981    }
982
890983    jzfb->panel = jz_slcd_panels_probe(jzfb);
891984    if (!jzfb->panel) {
892985        dev_err(&pdev->dev, "Failed to find panel driver\n");
893986        ret = -ENOENT;
894        goto err_free_devmem;
987        goto err_free_irq;
895988    }
896989    jzfb_disable_dma(jzfb);
897990    jzfb->panel->init(jzfb);
991
898992    jzfb->panel->enable(jzfb);
899993
900994    ret = register_framebuffer(fb);
...... 
9151009        goto err_cancel_work;
9161010
9171011    ret = device_create_file(&pdev->dev, &dev_attr_tv_out);
1012    if (ret)
1013        goto err_remove_file_panel;
1014
1015    ret = device_create_file(&pdev->dev, &dev_attr_tv_out_vsync);
9181016    if (!ret)
9191017        return 0;
9201018
1019    device_remove_file(&pdev->dev, &dev_attr_tv_out);
1020err_remove_file_panel:
9211021    device_remove_file(&pdev->dev, &dev_attr_panel);
9221022err_cancel_work:
9231023    cancel_delayed_work_sync(&jzfb->refresh_work);
9241024err_free_panel:
9251025    jzfb->panel->exit(jzfb);
1026err_free_irq:
1027    free_irq(JZ4740_IRQ_LCD, jzfb);
9261028err_free_devmem:
9271029    jzfb_free_gpio_pins(jzfb);
9281030
...... 
9471049{
9481050    struct jzfb *jzfb = platform_get_drvdata(pdev);
9491051
1052    device_remove_file(&pdev->dev, &dev_attr_tv_out_vsync);
9501053    device_remove_file(&pdev->dev, &dev_attr_tv_out);
9511054    device_remove_file(&pdev->dev, &dev_attr_panel);
9521055    jzfb_blank(FB_BLANK_POWERDOWN, jzfb->fb);
9531056
1057    free_irq(JZ4740_IRQ_LCD, jzfb);
1058
9541059    /* Blanking will prevent future refreshes from behind scheduled.
9551060       Now wait for a possible refresh in progress to finish. */
9561061    cancel_delayed_work_sync(&jzfb->refresh_work);

Archive Download the corresponding diff file



interactive