diff options
author | Eric Miao <ycmiao@ycmiao-hp520.(none)> | 2008-12-23 04:49:43 -0500 |
---|---|---|
committer | Eric Miao <eric.miao@marvell.com> | 2008-12-29 05:00:04 -0500 |
commit | 198fc108ee4c2cd3f08954eae6a819c81c03214b (patch) | |
tree | 153fdb793142ef5ee8e0ab6198dcde32866b062c /drivers/video | |
parent | 3f16ff608a75c8bf28c8cafed12e076d67a3602a (diff) |
[ARM] pxafb: add support for overlay1 and overlay2 as framebuffer devices
PXA27x and later processors support overlay1 and overlay2 on-top of the
base framebuffer (although under-neath the base is also possible). They
support palette and no-palette RGB formats, as well as YUV formats (only
available on overlay2). These overlays have dedicated DMA channels and
behave in a similar way as a framebuffer.
This heavily simplified and re-structured work is based on the original
pxafb_overlay.c (which is pending for mainline merge for a long time).
The major problems with this pxafb_overlay.c are (if you are interested
in the history):
1. heavily redundant (the control logics for overlay1 and overlay2 are
actually identical except for some small operations, which are now
abstracted into a 'pxafb_layer_ops' structure)
2. a lot of useless and un-tested code (two workarounds which are now
fixed on mature silicons)
3. cursorfb is actually useless, hardware cursor should not be used
this way, and the code was actually un-tested for a long time.
The code in this patch should be self-explanatory, I tried to add minimum
comments. As said, this is basically simplified, there are several things
still on the pending list:
1. palette mode is un-supported and un-tested (although re-using the
palette code of the base framebuffer is actually very easy now with
previous clean-up patches)
2. fb_pan_display for overlay(s) is un-supported
3. the base framebuffer can actually be abstracted by 'pxafb_layer' as
well, which will help further re-use of the code and keep a better
and consistent structure. (This is the reason I named it 'pxafb_layer'
instead of 'pxafb_overlay' or something alike)
See Documentation/fb/pxafb.txt for additional usage information.
Signed-off-by: Eric Miao <eric.miao@marvell.com>
Cc: Rodolfo Giometti <giometti@linux.it>
Signed-off-by: Eric Miao <ycmiao@ycmiao-hp520.(none)>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/Kconfig | 5 | ||||
-rw-r--r-- | drivers/video/pxafb.c | 364 | ||||
-rw-r--r-- | drivers/video/pxafb.h | 45 |
3 files changed, 407 insertions, 7 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3f3ce13fef43..486d81ca02a3 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -1817,6 +1817,11 @@ config FB_PXA | |||
1817 | 1817 | ||
1818 | If unsure, say N. | 1818 | If unsure, say N. |
1819 | 1819 | ||
1820 | config FB_PXA_OVERLAY | ||
1821 | bool "Support PXA27x/PXA3xx Overlay(s) as framebuffer" | ||
1822 | default n | ||
1823 | depends on FB_PXA && (PXA27x || PXA3xx) | ||
1824 | |||
1820 | config FB_PXA_SMARTPANEL | 1825 | config FB_PXA_SMARTPANEL |
1821 | bool "PXA Smartpanel LCD support" | 1826 | bool "PXA Smartpanel LCD support" |
1822 | default n | 1827 | default n |
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 7935706a7565..3a41ea10e8ec 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c | |||
@@ -20,6 +20,16 @@ | |||
20 | * | 20 | * |
21 | * linux-arm-kernel@lists.arm.linux.org.uk | 21 | * linux-arm-kernel@lists.arm.linux.org.uk |
22 | * | 22 | * |
23 | * Add support for overlay1 and overlay2 based on pxafb_overlay.c: | ||
24 | * | ||
25 | * Copyright (C) 2004, Intel Corporation | ||
26 | * | ||
27 | * 2003/08/27: <yu.tang@intel.com> | ||
28 | * 2004/03/10: <stanley.cai@intel.com> | ||
29 | * 2004/10/28: <yan.yin@intel.com> | ||
30 | * | ||
31 | * Copyright (C) 2006-2008 Marvell International Ltd. | ||
32 | * All Rights Reserved | ||
23 | */ | 33 | */ |
24 | 34 | ||
25 | #include <linux/module.h> | 35 | #include <linux/module.h> |
@@ -72,6 +82,8 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, | |||
72 | struct pxafb_info *); | 82 | struct pxafb_info *); |
73 | static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); | 83 | static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); |
74 | static void setup_base_frame(struct pxafb_info *fbi, int branch); | 84 | static void setup_base_frame(struct pxafb_info *fbi, int branch); |
85 | static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, | ||
86 | unsigned long offset, size_t size); | ||
75 | 87 | ||
76 | static unsigned long video_mem_size = 0; | 88 | static unsigned long video_mem_size = 0; |
77 | 89 | ||
@@ -581,6 +593,330 @@ static struct fb_ops pxafb_ops = { | |||
581 | .fb_blank = pxafb_blank, | 593 | .fb_blank = pxafb_blank, |
582 | }; | 594 | }; |
583 | 595 | ||
596 | #ifdef CONFIG_FB_PXA_OVERLAY | ||
597 | static void overlay1fb_setup(struct pxafb_layer *ofb) | ||
598 | { | ||
599 | int size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual; | ||
600 | unsigned long start = ofb->video_mem_phys; | ||
601 | setup_frame_dma(ofb->fbi, DMA_OV1, PAL_NONE, start, size); | ||
602 | } | ||
603 | |||
604 | /* Depending on the enable status of overlay1/2, the DMA should be | ||
605 | * updated from FDADRx (when disabled) or FBRx (when enabled). | ||
606 | */ | ||
607 | static void overlay1fb_enable(struct pxafb_layer *ofb) | ||
608 | { | ||
609 | int enabled = lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN; | ||
610 | uint32_t fdadr1 = ofb->fbi->fdadr[DMA_OV1] | (enabled ? 0x1 : 0); | ||
611 | |||
612 | lcd_writel(ofb->fbi, enabled ? FBR1 : FDADR1, fdadr1); | ||
613 | lcd_writel(ofb->fbi, OVL1C2, ofb->control[1]); | ||
614 | lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] | OVLxC1_OEN); | ||
615 | } | ||
616 | |||
617 | static void overlay1fb_disable(struct pxafb_layer *ofb) | ||
618 | { | ||
619 | uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5); | ||
620 | |||
621 | lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] & ~OVLxC1_OEN); | ||
622 | |||
623 | lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(1)); | ||
624 | lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(1)); | ||
625 | lcd_writel(ofb->fbi, FBR1, ofb->fbi->fdadr[DMA_OV1] | 0x3); | ||
626 | |||
627 | if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0) | ||
628 | pr_warning("%s: timeout disabling overlay1\n", __func__); | ||
629 | |||
630 | lcd_writel(ofb->fbi, LCCR5, lccr5); | ||
631 | } | ||
632 | |||
633 | static void overlay2fb_setup(struct pxafb_layer *ofb) | ||
634 | { | ||
635 | int size, div = 1, pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd); | ||
636 | unsigned long start[3] = { ofb->video_mem_phys, 0, 0 }; | ||
637 | |||
638 | if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED) { | ||
639 | size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual; | ||
640 | setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size); | ||
641 | } else { | ||
642 | size = ofb->fb.var.xres_virtual * ofb->fb.var.yres_virtual; | ||
643 | switch (pfor) { | ||
644 | case OVERLAY_FORMAT_YUV444_PLANAR: div = 1; break; | ||
645 | case OVERLAY_FORMAT_YUV422_PLANAR: div = 2; break; | ||
646 | case OVERLAY_FORMAT_YUV420_PLANAR: div = 4; break; | ||
647 | } | ||
648 | start[1] = start[0] + size; | ||
649 | start[2] = start[1] + size / div; | ||
650 | setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size); | ||
651 | setup_frame_dma(ofb->fbi, DMA_OV2_Cb, -1, start[1], size / div); | ||
652 | setup_frame_dma(ofb->fbi, DMA_OV2_Cr, -1, start[2], size / div); | ||
653 | } | ||
654 | } | ||
655 | |||
656 | static void overlay2fb_enable(struct pxafb_layer *ofb) | ||
657 | { | ||
658 | int pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd); | ||
659 | int enabled = lcd_readl(ofb->fbi, OVL2C1) & OVLxC1_OEN; | ||
660 | uint32_t fdadr2 = ofb->fbi->fdadr[DMA_OV2_Y] | (enabled ? 0x1 : 0); | ||
661 | uint32_t fdadr3 = ofb->fbi->fdadr[DMA_OV2_Cb] | (enabled ? 0x1 : 0); | ||
662 | uint32_t fdadr4 = ofb->fbi->fdadr[DMA_OV2_Cr] | (enabled ? 0x1 : 0); | ||
663 | |||
664 | if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED) | ||
665 | lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2); | ||
666 | else { | ||
667 | lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2); | ||
668 | lcd_writel(ofb->fbi, enabled ? FBR3 : FDADR3, fdadr3); | ||
669 | lcd_writel(ofb->fbi, enabled ? FBR4 : FDADR4, fdadr4); | ||
670 | } | ||
671 | lcd_writel(ofb->fbi, OVL2C2, ofb->control[1]); | ||
672 | lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] | OVLxC1_OEN); | ||
673 | } | ||
674 | |||
675 | static void overlay2fb_disable(struct pxafb_layer *ofb) | ||
676 | { | ||
677 | uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5); | ||
678 | |||
679 | lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] & ~OVLxC1_OEN); | ||
680 | |||
681 | lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(2)); | ||
682 | lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(2)); | ||
683 | lcd_writel(ofb->fbi, FBR2, ofb->fbi->fdadr[DMA_OV2_Y] | 0x3); | ||
684 | lcd_writel(ofb->fbi, FBR3, ofb->fbi->fdadr[DMA_OV2_Cb] | 0x3); | ||
685 | lcd_writel(ofb->fbi, FBR4, ofb->fbi->fdadr[DMA_OV2_Cr] | 0x3); | ||
686 | |||
687 | if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0) | ||
688 | pr_warning("%s: timeout disabling overlay2\n", __func__); | ||
689 | } | ||
690 | |||
691 | static struct pxafb_layer_ops ofb_ops[] = { | ||
692 | [0] = { | ||
693 | .enable = overlay1fb_enable, | ||
694 | .disable = overlay1fb_disable, | ||
695 | .setup = overlay1fb_setup, | ||
696 | }, | ||
697 | [1] = { | ||
698 | .enable = overlay2fb_enable, | ||
699 | .disable = overlay2fb_disable, | ||
700 | .setup = overlay2fb_setup, | ||
701 | }, | ||
702 | }; | ||
703 | |||
704 | static int overlayfb_open(struct fb_info *info, int user) | ||
705 | { | ||
706 | struct pxafb_layer *ofb = (struct pxafb_layer *)info; | ||
707 | |||
708 | /* no support for framebuffer console on overlay */ | ||
709 | if (user == 0) | ||
710 | return -ENODEV; | ||
711 | |||
712 | /* allow only one user at a time */ | ||
713 | if (atomic_inc_and_test(&ofb->usage)) | ||
714 | return -EBUSY; | ||
715 | |||
716 | /* unblank the base framebuffer */ | ||
717 | fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK); | ||
718 | return 0; | ||
719 | } | ||
720 | |||
721 | static int overlayfb_release(struct fb_info *info, int user) | ||
722 | { | ||
723 | struct pxafb_layer *ofb = (struct pxafb_layer*) info; | ||
724 | |||
725 | atomic_dec(&ofb->usage); | ||
726 | ofb->ops->disable(ofb); | ||
727 | |||
728 | free_pages_exact(ofb->video_mem, ofb->video_mem_size); | ||
729 | ofb->video_mem = NULL; | ||
730 | ofb->video_mem_size = 0; | ||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | static int overlayfb_check_var(struct fb_var_screeninfo *var, | ||
735 | struct fb_info *info) | ||
736 | { | ||
737 | struct pxafb_layer *ofb = (struct pxafb_layer *)info; | ||
738 | struct fb_var_screeninfo *base_var = &ofb->fbi->fb.var; | ||
739 | int xpos, ypos, pfor, bpp; | ||
740 | |||
741 | xpos = NONSTD_TO_XPOS(var->nonstd); | ||
742 | ypos = NONSTD_TO_XPOS(var->nonstd); | ||
743 | pfor = NONSTD_TO_PFOR(var->nonstd); | ||
744 | |||
745 | bpp = pxafb_var_to_bpp(var); | ||
746 | if (bpp < 0) | ||
747 | return -EINVAL; | ||
748 | |||
749 | /* no support for YUV format on overlay1 */ | ||
750 | if (ofb->id == OVERLAY1 && pfor != 0) | ||
751 | return -EINVAL; | ||
752 | |||
753 | /* for YUV packed formats, bpp = 'minimum bpp of YUV components' */ | ||
754 | switch (pfor) { | ||
755 | case OVERLAY_FORMAT_RGB: | ||
756 | bpp = pxafb_var_to_bpp(var); | ||
757 | if (bpp < 0) | ||
758 | return -EINVAL; | ||
759 | |||
760 | pxafb_set_pixfmt(var, var_to_depth(var)); | ||
761 | break; | ||
762 | case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break; | ||
763 | case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 8; break; | ||
764 | case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 4; break; | ||
765 | case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 2; break; | ||
766 | default: | ||
767 | return -EINVAL; | ||
768 | } | ||
769 | |||
770 | /* each line must start at a 32-bit word boundary */ | ||
771 | if ((xpos * bpp) % 32) | ||
772 | return -EINVAL; | ||
773 | |||
774 | /* xres must align on 32-bit word boundary */ | ||
775 | var->xres = roundup(var->xres * bpp, 32) / bpp; | ||
776 | |||
777 | if ((xpos + var->xres > base_var->xres) || | ||
778 | (ypos + var->yres > base_var->yres)) | ||
779 | return -EINVAL; | ||
780 | |||
781 | var->xres_virtual = var->xres; | ||
782 | var->yres_virtual = max(var->yres, var->yres_virtual); | ||
783 | return 0; | ||
784 | } | ||
785 | |||
786 | static int overlayfb_map_video_memory(struct pxafb_layer *ofb) | ||
787 | { | ||
788 | struct fb_var_screeninfo *var = &ofb->fb.var; | ||
789 | int pfor = NONSTD_TO_PFOR(var->nonstd); | ||
790 | int size, bpp = 0; | ||
791 | |||
792 | switch (pfor) { | ||
793 | case OVERLAY_FORMAT_RGB: bpp = var->bits_per_pixel; break; | ||
794 | case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break; | ||
795 | case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 24; break; | ||
796 | case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 16; break; | ||
797 | case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 12; break; | ||
798 | } | ||
799 | |||
800 | ofb->fb.fix.line_length = var->xres_virtual * bpp / 8; | ||
801 | |||
802 | size = PAGE_ALIGN(ofb->fb.fix.line_length * var->yres_virtual); | ||
803 | |||
804 | /* don't re-allocate if the original video memory is enough */ | ||
805 | if (ofb->video_mem) { | ||
806 | if (ofb->video_mem_size >= size) | ||
807 | return 0; | ||
808 | |||
809 | free_pages_exact(ofb->video_mem, ofb->video_mem_size); | ||
810 | } | ||
811 | |||
812 | ofb->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); | ||
813 | if (ofb->video_mem == NULL) | ||
814 | return -ENOMEM; | ||
815 | |||
816 | ofb->video_mem_phys = virt_to_phys(ofb->video_mem); | ||
817 | ofb->video_mem_size = size; | ||
818 | |||
819 | ofb->fb.fix.smem_start = ofb->video_mem_phys; | ||
820 | ofb->fb.fix.smem_len = ofb->fb.fix.line_length * var->yres_virtual; | ||
821 | ofb->fb.screen_base = ofb->video_mem; | ||
822 | return 0; | ||
823 | } | ||
824 | |||
825 | static int overlayfb_set_par(struct fb_info *info) | ||
826 | { | ||
827 | struct pxafb_layer *ofb = (struct pxafb_layer *)info; | ||
828 | struct fb_var_screeninfo *var = &info->var; | ||
829 | int xpos, ypos, pfor, bpp, ret; | ||
830 | |||
831 | ret = overlayfb_map_video_memory(ofb); | ||
832 | if (ret) | ||
833 | return ret; | ||
834 | |||
835 | bpp = pxafb_var_to_bpp(var); | ||
836 | xpos = NONSTD_TO_XPOS(var->nonstd); | ||
837 | ypos = NONSTD_TO_XPOS(var->nonstd); | ||
838 | pfor = NONSTD_TO_PFOR(var->nonstd); | ||
839 | |||
840 | ofb->control[0] = OVLxC1_PPL(var->xres) | OVLxC1_LPO(var->yres) | | ||
841 | OVLxC1_BPP(bpp); | ||
842 | ofb->control[1] = OVLxC2_XPOS(xpos) | OVLxC2_YPOS(ypos); | ||
843 | |||
844 | if (ofb->id == OVERLAY2) | ||
845 | ofb->control[1] |= OVL2C2_PFOR(pfor); | ||
846 | |||
847 | ofb->ops->setup(ofb); | ||
848 | ofb->ops->enable(ofb); | ||
849 | return 0; | ||
850 | } | ||
851 | |||
852 | static struct fb_ops overlay_fb_ops = { | ||
853 | .owner = THIS_MODULE, | ||
854 | .fb_open = overlayfb_open, | ||
855 | .fb_release = overlayfb_release, | ||
856 | .fb_check_var = overlayfb_check_var, | ||
857 | .fb_set_par = overlayfb_set_par, | ||
858 | }; | ||
859 | |||
860 | static void __devinit init_pxafb_overlay(struct pxafb_info *fbi, | ||
861 | struct pxafb_layer *ofb, int id) | ||
862 | { | ||
863 | sprintf(ofb->fb.fix.id, "overlay%d", id + 1); | ||
864 | |||
865 | ofb->fb.fix.type = FB_TYPE_PACKED_PIXELS; | ||
866 | ofb->fb.fix.xpanstep = 0; | ||
867 | ofb->fb.fix.ypanstep = 1; | ||
868 | |||
869 | ofb->fb.var.activate = FB_ACTIVATE_NOW; | ||
870 | ofb->fb.var.height = -1; | ||
871 | ofb->fb.var.width = -1; | ||
872 | ofb->fb.var.vmode = FB_VMODE_NONINTERLACED; | ||
873 | |||
874 | ofb->fb.fbops = &overlay_fb_ops; | ||
875 | ofb->fb.flags = FBINFO_FLAG_DEFAULT; | ||
876 | ofb->fb.node = -1; | ||
877 | ofb->fb.pseudo_palette = NULL; | ||
878 | |||
879 | ofb->id = id; | ||
880 | ofb->ops = &ofb_ops[id]; | ||
881 | atomic_set(&ofb->usage, 0); | ||
882 | ofb->fbi = fbi; | ||
883 | init_completion(&ofb->branch_done); | ||
884 | } | ||
885 | |||
886 | static int __devinit pxafb_overlay_init(struct pxafb_info *fbi) | ||
887 | { | ||
888 | int i, ret; | ||
889 | |||
890 | for (i = 0; i < 2; i++) { | ||
891 | init_pxafb_overlay(fbi, &fbi->overlay[i], i); | ||
892 | ret = register_framebuffer(&fbi->overlay[i].fb); | ||
893 | if (ret) { | ||
894 | dev_err(fbi->dev, "failed to register overlay %d\n", i); | ||
895 | return ret; | ||
896 | } | ||
897 | } | ||
898 | |||
899 | /* mask all IU/BS/EOF/SOF interrupts */ | ||
900 | lcd_writel(fbi, LCCR5, ~0); | ||
901 | |||
902 | /* place overlay(s) on top of base */ | ||
903 | fbi->lccr0 |= LCCR0_OUC; | ||
904 | pr_info("PXA Overlay driver loaded successfully!\n"); | ||
905 | return 0; | ||
906 | } | ||
907 | |||
908 | static void __devexit pxafb_overlay_exit(struct pxafb_info *fbi) | ||
909 | { | ||
910 | int i; | ||
911 | |||
912 | for (i = 0; i < 2; i++) | ||
913 | unregister_framebuffer(&fbi->overlay[i].fb); | ||
914 | } | ||
915 | #else | ||
916 | static inline void pxafb_overlay_init(struct pxafb_info *fbi) {} | ||
917 | static inline void pxafb_overlay_exit(struct pxafb_info *fbi) {} | ||
918 | #endif /* CONFIG_FB_PXA_OVERLAY */ | ||
919 | |||
584 | /* | 920 | /* |
585 | * Calculate the PCD value from the clock rate (in picoseconds). | 921 | * Calculate the PCD value from the clock rate (in picoseconds). |
586 | * We take account of the PPCR clock setting. | 922 | * We take account of the PPCR clock setting. |
@@ -660,7 +996,7 @@ unsigned long pxafb_get_hsync_time(struct device *dev) | |||
660 | EXPORT_SYMBOL(pxafb_get_hsync_time); | 996 | EXPORT_SYMBOL(pxafb_get_hsync_time); |
661 | 997 | ||
662 | static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, | 998 | static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, |
663 | unsigned int offset, size_t size) | 999 | unsigned long start, size_t size) |
664 | { | 1000 | { |
665 | struct pxafb_dma_descriptor *dma_desc, *pal_desc; | 1001 | struct pxafb_dma_descriptor *dma_desc, *pal_desc; |
666 | unsigned int dma_desc_off, pal_desc_off; | 1002 | unsigned int dma_desc_off, pal_desc_off; |
@@ -671,7 +1007,7 @@ static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, | |||
671 | dma_desc = &fbi->dma_buff->dma_desc[dma]; | 1007 | dma_desc = &fbi->dma_buff->dma_desc[dma]; |
672 | dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]); | 1008 | dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]); |
673 | 1009 | ||
674 | dma_desc->fsadr = fbi->video_mem_phys + offset; | 1010 | dma_desc->fsadr = start; |
675 | dma_desc->fidr = 0; | 1011 | dma_desc->fidr = 0; |
676 | dma_desc->ldcmd = size; | 1012 | dma_desc->ldcmd = size; |
677 | 1013 | ||
@@ -705,14 +1041,14 @@ static void setup_base_frame(struct pxafb_info *fbi, int branch) | |||
705 | { | 1041 | { |
706 | struct fb_var_screeninfo *var = &fbi->fb.var; | 1042 | struct fb_var_screeninfo *var = &fbi->fb.var; |
707 | struct fb_fix_screeninfo *fix = &fbi->fb.fix; | 1043 | struct fb_fix_screeninfo *fix = &fbi->fb.fix; |
708 | unsigned int nbytes, offset; | 1044 | int nbytes, dma, pal, bpp = var->bits_per_pixel; |
709 | int dma, pal, bpp = var->bits_per_pixel; | 1045 | unsigned long offset; |
710 | 1046 | ||
711 | dma = DMA_BASE + (branch ? DMA_MAX : 0); | 1047 | dma = DMA_BASE + (branch ? DMA_MAX : 0); |
712 | pal = (bpp >= 16) ? PAL_NONE : PAL_BASE + (branch ? PAL_MAX : 0); | 1048 | pal = (bpp >= 16) ? PAL_NONE : PAL_BASE + (branch ? PAL_MAX : 0); |
713 | 1049 | ||
714 | nbytes = fix->line_length * var->yres; | 1050 | nbytes = fix->line_length * var->yres; |
715 | offset = fix->line_length * var->yoffset; | 1051 | offset = fix->line_length * var->yoffset + fbi->video_mem_phys; |
716 | 1052 | ||
717 | if (fbi->lccr0 & LCCR0_SDS) { | 1053 | if (fbi->lccr0 & LCCR0_SDS) { |
718 | nbytes = nbytes / 2; | 1054 | nbytes = nbytes / 2; |
@@ -1090,8 +1426,9 @@ static void pxafb_disable_controller(struct pxafb_info *fbi) | |||
1090 | static irqreturn_t pxafb_handle_irq(int irq, void *dev_id) | 1426 | static irqreturn_t pxafb_handle_irq(int irq, void *dev_id) |
1091 | { | 1427 | { |
1092 | struct pxafb_info *fbi = dev_id; | 1428 | struct pxafb_info *fbi = dev_id; |
1093 | unsigned int lccr0, lcsr = lcd_readl(fbi, LCSR); | 1429 | unsigned int lccr0, lcsr, lcsr1; |
1094 | 1430 | ||
1431 | lcsr = lcd_readl(fbi, LCSR); | ||
1095 | if (lcsr & LCSR_LDD) { | 1432 | if (lcsr & LCSR_LDD) { |
1096 | lccr0 = lcd_readl(fbi, LCCR0); | 1433 | lccr0 = lcd_readl(fbi, LCCR0); |
1097 | lcd_writel(fbi, LCCR0, lccr0 | LCCR0_LDM); | 1434 | lcd_writel(fbi, LCCR0, lccr0 | LCCR0_LDM); |
@@ -1102,8 +1439,18 @@ static irqreturn_t pxafb_handle_irq(int irq, void *dev_id) | |||
1102 | if (lcsr & LCSR_CMD_INT) | 1439 | if (lcsr & LCSR_CMD_INT) |
1103 | complete(&fbi->command_done); | 1440 | complete(&fbi->command_done); |
1104 | #endif | 1441 | #endif |
1105 | |||
1106 | lcd_writel(fbi, LCSR, lcsr); | 1442 | lcd_writel(fbi, LCSR, lcsr); |
1443 | |||
1444 | #ifdef CONFIG_FB_PXA_OVERLAY | ||
1445 | lcsr1 = lcd_readl(fbi, LCSR1); | ||
1446 | if (lcsr1 & LCSR1_BS(1)) | ||
1447 | complete(&fbi->overlay[0].branch_done); | ||
1448 | |||
1449 | if (lcsr1 & LCSR1_BS(2)) | ||
1450 | complete(&fbi->overlay[1].branch_done); | ||
1451 | |||
1452 | lcd_writel(fbi, LCSR1, lcsr1); | ||
1453 | #endif | ||
1107 | return IRQ_HANDLED; | 1454 | return IRQ_HANDLED; |
1108 | } | 1455 | } |
1109 | 1456 | ||
@@ -1802,6 +2149,8 @@ static int __devinit pxafb_probe(struct platform_device *dev) | |||
1802 | goto failed_free_cmap; | 2149 | goto failed_free_cmap; |
1803 | } | 2150 | } |
1804 | 2151 | ||
2152 | pxafb_overlay_init(fbi); | ||
2153 | |||
1805 | #ifdef CONFIG_CPU_FREQ | 2154 | #ifdef CONFIG_CPU_FREQ |
1806 | fbi->freq_transition.notifier_call = pxafb_freq_transition; | 2155 | fbi->freq_transition.notifier_call = pxafb_freq_transition; |
1807 | fbi->freq_policy.notifier_call = pxafb_freq_policy; | 2156 | fbi->freq_policy.notifier_call = pxafb_freq_policy; |
@@ -1852,6 +2201,7 @@ static int __devexit pxafb_remove(struct platform_device *dev) | |||
1852 | 2201 | ||
1853 | info = &fbi->fb; | 2202 | info = &fbi->fb; |
1854 | 2203 | ||
2204 | pxafb_overlay_exit(fbi); | ||
1855 | unregister_framebuffer(info); | 2205 | unregister_framebuffer(info); |
1856 | 2206 | ||
1857 | pxafb_disable_controller(fbi); | 2207 | pxafb_disable_controller(fbi); |
diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h index ae3cbc1ca64f..2353521c5c8c 100644 --- a/drivers/video/pxafb.h +++ b/drivers/video/pxafb.h | |||
@@ -64,6 +64,47 @@ struct pxafb_dma_buff { | |||
64 | struct pxafb_dma_descriptor dma_desc[DMA_MAX * 2]; | 64 | struct pxafb_dma_descriptor dma_desc[DMA_MAX * 2]; |
65 | }; | 65 | }; |
66 | 66 | ||
67 | enum { | ||
68 | OVERLAY1, | ||
69 | OVERLAY2, | ||
70 | }; | ||
71 | |||
72 | enum { | ||
73 | OVERLAY_FORMAT_RGB = 0, | ||
74 | OVERLAY_FORMAT_YUV444_PACKED, | ||
75 | OVERLAY_FORMAT_YUV444_PLANAR, | ||
76 | OVERLAY_FORMAT_YUV422_PLANAR, | ||
77 | OVERLAY_FORMAT_YUV420_PLANAR, | ||
78 | }; | ||
79 | |||
80 | #define NONSTD_TO_XPOS(x) (((x) >> 0) & 0x3ff) | ||
81 | #define NONSTD_TO_YPOS(x) (((x) >> 10) & 0x3ff) | ||
82 | #define NONSTD_TO_PFOR(x) (((x) >> 20) & 0x7) | ||
83 | |||
84 | struct pxafb_layer; | ||
85 | |||
86 | struct pxafb_layer_ops { | ||
87 | void (*enable)(struct pxafb_layer *); | ||
88 | void (*disable)(struct pxafb_layer *); | ||
89 | void (*setup)(struct pxafb_layer *); | ||
90 | }; | ||
91 | |||
92 | struct pxafb_layer { | ||
93 | struct fb_info fb; | ||
94 | int id; | ||
95 | atomic_t usage; | ||
96 | uint32_t control[2]; | ||
97 | |||
98 | struct pxafb_layer_ops *ops; | ||
99 | |||
100 | void __iomem *video_mem; | ||
101 | unsigned long video_mem_phys; | ||
102 | size_t video_mem_size; | ||
103 | struct completion branch_done; | ||
104 | |||
105 | struct pxafb_info *fbi; | ||
106 | }; | ||
107 | |||
67 | struct pxafb_info { | 108 | struct pxafb_info { |
68 | struct fb_info fb; | 109 | struct fb_info fb; |
69 | struct device *dev; | 110 | struct device *dev; |
@@ -114,6 +155,10 @@ struct pxafb_info { | |||
114 | struct task_struct *smart_thread; | 155 | struct task_struct *smart_thread; |
115 | #endif | 156 | #endif |
116 | 157 | ||
158 | #ifdef CONFIG_FB_PXA_OVERLAY | ||
159 | struct pxafb_layer overlay[2]; | ||
160 | #endif | ||
161 | |||
117 | #ifdef CONFIG_CPU_FREQ | 162 | #ifdef CONFIG_CPU_FREQ |
118 | struct notifier_block freq_transition; | 163 | struct notifier_block freq_transition; |
119 | struct notifier_block freq_policy; | 164 | struct notifier_block freq_policy; |