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 | ||
---|---|---|
99 | 99 | dma_addr_t framedesc_phys; |
100 | 100 | |
101 | 101 | struct jz4740_dma_chan *dma; |
102 | struct completion dma_completion; | |
103 | unsigned refresh_on_pan:1; | |
102 | 104 | |
103 | 105 | struct clk *ldclk; |
104 | 106 | struct clk *lpclk; |
105 | 107 | |
106 | 108 | unsigned int tv_out; |
109 | unsigned tv_out_vsync:1; | |
107 | 110 | |
108 | 111 | unsigned is_enabled:1; |
109 | 112 | struct mutex lock; /* Protecting against running enable/disable in paralell */ |
drivers/video/jz4740_slcd_fb.c | ||
---|---|---|
34 | 34 | #include "jz4740_lcd.h" |
35 | 35 | #include "jz4740_slcd.h" |
36 | 36 | |
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 | ||
37 | 44 | static const char *jzfb_tv_out_norm[] = { |
38 | 45 | "off", "ntsc", "pal", "pal-60", "pal-m", |
39 | 46 | }; |
... | ... | |
276 | 283 | mutex_lock(&jzfb->lock); |
277 | 284 | if (jzfb->is_enabled) { |
278 | 285 | 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); | |
281 | 301 | } else { |
282 | 302 | jzfb_upload_frame_cpu(jzfb); |
283 | 303 | schedule_delayed_work(&jzfb->refresh_work, HZ / 10); |
... | ... | |
290 | 310 | struct jz4740_dma_chan *dma, int res, void *dev) |
291 | 311 | { |
292 | 312 | 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); | |
297 | 314 | } |
298 | 315 | |
299 | 316 | static int jzfb_set_par(struct fb_info *info) |
... | ... | |
438 | 455 | jzfb_num_data_pins(jzfb)); |
439 | 456 | } |
440 | 457 | jzfb_disable_dma(jzfb); |
458 | complete_all(&jzfb->dma_completion); | |
441 | 459 | jzfb->panel->enable(jzfb); |
442 | 460 | |
443 | 461 | ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL); |
... | ... | |
456 | 474 | /* Abort any DMA transfer that might be in progress and allow direct |
457 | 475 | writes to the panel. */ |
458 | 476 | jzfb_disable_dma(jzfb); |
477 | complete_all(&jzfb->dma_completion); | |
459 | 478 | |
460 | 479 | jzfb->panel->disable(jzfb); |
461 | 480 | jz_gpio_bulk_suspend(jz_slcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); |
... | ... | |
497 | 516 | return ret; |
498 | 517 | } |
499 | 518 | |
500 | static int jzfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) | |
519 | static int jzfb_wait_for_vsync(struct fb_info *info) | |
501 | 520 | { |
502 | 521 | struct jzfb *jzfb = info->par; |
503 | 522 | |
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 | ||
529 | static int jzfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) | |
530 | { | |
531 | struct jzfb *jzfb = info->par; | |
504 | 532 | 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 | ||
505 | 540 | /* update frame start address for TV-out mode */ |
506 | 541 | (*jzfb->framedesc)[1].addr = jzfb->vidmem_phys |
507 | 542 | + info->fix.line_length * var->yoffset; |
508 | 543 | |
544 | jzfb_wait_for_vsync(info); | |
545 | ||
546 | jzfb->refresh_on_pan = 1; | |
547 | flush_delayed_work(&jzfb->refresh_work); | |
509 | 548 | return 0; |
510 | 549 | } |
511 | 550 | |
... | ... | |
586 | 625 | jzfb->blackline_size / 4; |
587 | 626 | (*jzfb->framedesc)[1].addr = jzfb->vidmem_phys; |
588 | 627 | (*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; | |
590 | 630 | |
591 | 631 | return 0; |
592 | 632 | |
... | ... | |
609 | 649 | jzfb->framedesc, jzfb->framedesc_phys); |
610 | 650 | } |
611 | 651 | |
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 | ||
619 | 652 | static int jzfb_tv_out(struct jzfb *jzfb, unsigned int mode) |
620 | 653 | { |
621 | 654 | int blank = jzfb->is_enabled ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; |
... | ... | |
636 | 669 | allow direct writes to the panel. */ |
637 | 670 | jzfb_disable_dma(jzfb); |
638 | 671 | jzfb->panel->disable(jzfb); |
672 | complete_all(&jzfb->dma_completion); | |
639 | 673 | |
640 | 674 | /* set up LCD controller for TV output */ |
641 | 675 | |
... | ... | |
674 | 708 | writel(jzfb->framedesc_phys, jzfb->base + JZ_REG_LCD_DA0); |
675 | 709 | |
676 | 710 | 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); | |
679 | 714 | } 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 | ||
680 | 720 | /* disable LCD controller and re-enable SLCD */ |
681 | 721 | writel(JZ_LCD_CFG_SLCD, jzfb->base + JZ_REG_LCD_CFG); |
682 | 722 | jzfb->panel->enable(jzfb); |
723 | ||
724 | jzfb->refresh_on_pan = 0; | |
725 | complete_all(&jzfb->dma_completion); | |
683 | 726 | schedule_delayed_work(&jzfb->refresh_work, 0); |
684 | 727 | } |
685 | 728 | |
... | ... | |
721 | 764 | |
722 | 765 | static DEVICE_ATTR(tv_out, 0644, jzfb_tv_out_show, jzfb_tv_out_store); |
723 | 766 | |
767 | static 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 | ||
774 | static 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 | ||
787 | static DEVICE_ATTR(tv_out_vsync, 0644, jzfb_vsync_show, jzfb_vsync_store); | |
788 | ||
724 | 789 | static ssize_t jzfb_panel_show(struct device *dev, struct device_attribute *attr, |
725 | 790 | char *buf) |
726 | 791 | { |
... | ... | |
764 | 829 | .fb_mmap = jzfb_mmap, |
765 | 830 | }; |
766 | 831 | |
832 | static 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 | ||
767 | 850 | static int __devinit jzfb_probe(struct platform_device *pdev) |
768 | 851 | { |
769 | 852 | int ret; |
... | ... | |
808 | 891 | jzfb->mem = mem; |
809 | 892 | |
810 | 893 | 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); | |
811 | 898 | |
812 | 899 | jzfb->dma = jz4740_dma_request(&pdev->dev, dev_name(&pdev->dev), 0); |
813 | 900 | if (!jzfb->dma) { |
... | ... | |
887 | 974 | jzfb_num_data_pins(jzfb)); |
888 | 975 | } |
889 | 976 | |
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 | ||
890 | 983 | jzfb->panel = jz_slcd_panels_probe(jzfb); |
891 | 984 | if (!jzfb->panel) { |
892 | 985 | dev_err(&pdev->dev, "Failed to find panel driver\n"); |
893 | 986 | ret = -ENOENT; |
894 | goto err_free_devmem; | |
987 | goto err_free_irq; | |
895 | 988 | } |
896 | 989 | jzfb_disable_dma(jzfb); |
897 | 990 | jzfb->panel->init(jzfb); |
991 | ||
898 | 992 | jzfb->panel->enable(jzfb); |
899 | 993 | |
900 | 994 | ret = register_framebuffer(fb); |
... | ... | |
915 | 1009 | goto err_cancel_work; |
916 | 1010 | |
917 | 1011 | 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); | |
918 | 1016 | if (!ret) |
919 | 1017 | return 0; |
920 | 1018 | |
1019 | device_remove_file(&pdev->dev, &dev_attr_tv_out); | |
1020 | err_remove_file_panel: | |
921 | 1021 | device_remove_file(&pdev->dev, &dev_attr_panel); |
922 | 1022 | err_cancel_work: |
923 | 1023 | cancel_delayed_work_sync(&jzfb->refresh_work); |
924 | 1024 | err_free_panel: |
925 | 1025 | jzfb->panel->exit(jzfb); |
1026 | err_free_irq: | |
1027 | free_irq(JZ4740_IRQ_LCD, jzfb); | |
926 | 1028 | err_free_devmem: |
927 | 1029 | jzfb_free_gpio_pins(jzfb); |
928 | 1030 | |
... | ... | |
947 | 1049 | { |
948 | 1050 | struct jzfb *jzfb = platform_get_drvdata(pdev); |
949 | 1051 | |
1052 | device_remove_file(&pdev->dev, &dev_attr_tv_out_vsync); | |
950 | 1053 | device_remove_file(&pdev->dev, &dev_attr_tv_out); |
951 | 1054 | device_remove_file(&pdev->dev, &dev_attr_panel); |
952 | 1055 | jzfb_blank(FB_BLANK_POWERDOWN, jzfb->fb); |
953 | 1056 | |
1057 | free_irq(JZ4740_IRQ_LCD, jzfb); | |
1058 | ||
954 | 1059 | /* Blanking will prevent future refreshes from behind scheduled. |
955 | 1060 | Now wait for a possible refresh in progress to finish. */ |
956 | 1061 | cancel_delayed_work_sync(&jzfb->refresh_work); |
Branches:
ben-wpan
ben-wpan-stefan
5396a9238205f20f811ea57898980d3ca82df0b6
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9