aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVasily Khoruzhick <anarsoul@gmail.com>2011-03-11 04:20:47 -0500
committerEric Miao <eric.y.miao@gmail.com>2011-03-16 05:37:03 -0400
commit1b98d7c4491e5eaba7c403ec1bc5997e6596e569 (patch)
treecbd325caae3dd704373b6978a1e2368c7ad6e88c
parent1014cc38280f29ea0a39ec9a853fa0c3fdfebc7c (diff)
ARM: pxafb: rework pxafb overlay memory management
PXAFB overlay memory management is something messy: - it allocates memory dynamically on open/release, and it results in memory allocation failure after ~1h of uptime (system does not have 115k of physically contiguous memory) - in release callback it tries to free memory even if it was not allocated. Also driver touches FDADR1 on main plane reconfiguration, and it can cause problems if overlay1 is enabled. This patch attempts to fix those issues. Patch is based on Russell King's work. Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com> Signed-off-by: Eric Miao <eric.y.miao@gmail.com>
-rw-r--r--drivers/video/pxafb.c121
-rw-r--r--drivers/video/pxafb.h3
2 files changed, 81 insertions, 43 deletions
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index 825b665245b..0764759a2ee 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -627,7 +627,12 @@ static void overlay1fb_enable(struct pxafb_layer *ofb)
627 627
628static void overlay1fb_disable(struct pxafb_layer *ofb) 628static void overlay1fb_disable(struct pxafb_layer *ofb)
629{ 629{
630 uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5); 630 uint32_t lccr5;
631
632 if (!(lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN))
633 return;
634
635 lccr5 = lcd_readl(ofb->fbi, LCCR5);
631 636
632 lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] & ~OVLxC1_OEN); 637 lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] & ~OVLxC1_OEN);
633 638
@@ -685,7 +690,12 @@ static void overlay2fb_enable(struct pxafb_layer *ofb)
685 690
686static void overlay2fb_disable(struct pxafb_layer *ofb) 691static void overlay2fb_disable(struct pxafb_layer *ofb)
687{ 692{
688 uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5); 693 uint32_t lccr5;
694
695 if (!(lcd_readl(ofb->fbi, OVL2C1) & OVLxC1_OEN))
696 return;
697
698 lccr5 = lcd_readl(ofb->fbi, LCCR5);
689 699
690 lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] & ~OVLxC1_OEN); 700 lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] & ~OVLxC1_OEN);
691 701
@@ -720,12 +730,10 @@ static int overlayfb_open(struct fb_info *info, int user)
720 if (user == 0) 730 if (user == 0)
721 return -ENODEV; 731 return -ENODEV;
722 732
723 /* allow only one user at a time */ 733 if (ofb->usage++ == 0)
724 if (atomic_inc_and_test(&ofb->usage)) 734 /* unblank the base framebuffer */
725 return -EBUSY; 735 fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK);
726 736
727 /* unblank the base framebuffer */
728 fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK);
729 return 0; 737 return 0;
730} 738}
731 739
@@ -733,12 +741,15 @@ static int overlayfb_release(struct fb_info *info, int user)
733{ 741{
734 struct pxafb_layer *ofb = (struct pxafb_layer*) info; 742 struct pxafb_layer *ofb = (struct pxafb_layer*) info;
735 743
736 atomic_dec(&ofb->usage); 744 if (ofb->usage == 1) {
737 ofb->ops->disable(ofb); 745 ofb->ops->disable(ofb);
746 ofb->fb.var.height = -1;
747 ofb->fb.var.width = -1;
748 ofb->fb.var.xres = ofb->fb.var.xres_virtual = 0;
749 ofb->fb.var.yres = ofb->fb.var.yres_virtual = 0;
738 750
739 free_pages_exact(ofb->video_mem, ofb->video_mem_size); 751 ofb->usage--;
740 ofb->video_mem = NULL; 752 }
741 ofb->video_mem_size = 0;
742 return 0; 753 return 0;
743} 754}
744 755
@@ -794,7 +805,7 @@ static int overlayfb_check_var(struct fb_var_screeninfo *var,
794 return 0; 805 return 0;
795} 806}
796 807
797static int overlayfb_map_video_memory(struct pxafb_layer *ofb) 808static int overlayfb_check_video_memory(struct pxafb_layer *ofb)
798{ 809{
799 struct fb_var_screeninfo *var = &ofb->fb.var; 810 struct fb_var_screeninfo *var = &ofb->fb.var;
800 int pfor = NONSTD_TO_PFOR(var->nonstd); 811 int pfor = NONSTD_TO_PFOR(var->nonstd);
@@ -812,27 +823,11 @@ static int overlayfb_map_video_memory(struct pxafb_layer *ofb)
812 823
813 size = PAGE_ALIGN(ofb->fb.fix.line_length * var->yres_virtual); 824 size = PAGE_ALIGN(ofb->fb.fix.line_length * var->yres_virtual);
814 825
815 /* don't re-allocate if the original video memory is enough */
816 if (ofb->video_mem) { 826 if (ofb->video_mem) {
817 if (ofb->video_mem_size >= size) 827 if (ofb->video_mem_size >= size)
818 return 0; 828 return 0;
819
820 free_pages_exact(ofb->video_mem, ofb->video_mem_size);
821 } 829 }
822 830 return -EINVAL;
823 ofb->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
824 if (ofb->video_mem == NULL)
825 return -ENOMEM;
826
827 ofb->video_mem_phys = virt_to_phys(ofb->video_mem);
828 ofb->video_mem_size = size;
829
830 mutex_lock(&ofb->fb.mm_lock);
831 ofb->fb.fix.smem_start = ofb->video_mem_phys;
832 ofb->fb.fix.smem_len = ofb->fb.fix.line_length * var->yres_virtual;
833 mutex_unlock(&ofb->fb.mm_lock);
834 ofb->fb.screen_base = ofb->video_mem;
835 return 0;
836} 831}
837 832
838static int overlayfb_set_par(struct fb_info *info) 833static int overlayfb_set_par(struct fb_info *info)
@@ -841,7 +836,7 @@ static int overlayfb_set_par(struct fb_info *info)
841 struct fb_var_screeninfo *var = &info->var; 836 struct fb_var_screeninfo *var = &info->var;
842 int xpos, ypos, pfor, bpp, ret; 837 int xpos, ypos, pfor, bpp, ret;
843 838
844 ret = overlayfb_map_video_memory(ofb); 839 ret = overlayfb_check_video_memory(ofb);
845 if (ret) 840 if (ret)
846 return ret; 841 return ret;
847 842
@@ -891,7 +886,7 @@ static void __devinit init_pxafb_overlay(struct pxafb_info *fbi,
891 886
892 ofb->id = id; 887 ofb->id = id;
893 ofb->ops = &ofb_ops[id]; 888 ofb->ops = &ofb_ops[id];
894 atomic_set(&ofb->usage, 0); 889 ofb->usage = 0;
895 ofb->fbi = fbi; 890 ofb->fbi = fbi;
896 init_completion(&ofb->branch_done); 891 init_completion(&ofb->branch_done);
897} 892}
@@ -904,20 +899,54 @@ static inline int pxafb_overlay_supported(void)
904 return 0; 899 return 0;
905} 900}
906 901
907static int __devinit pxafb_overlay_init(struct pxafb_info *fbi) 902static int __devinit pxafb_overlay_map_video_memory(struct pxafb_info *pxafb,
903 struct pxafb_layer *ofb)
904{
905 /* We assume that user will use at most video_mem_size for overlay fb,
906 * anyway, it's useless to use 16bpp main plane and 24bpp overlay
907 */
908 ofb->video_mem = alloc_pages_exact(PAGE_ALIGN(pxafb->video_mem_size),
909 GFP_KERNEL | __GFP_ZERO);
910 if (ofb->video_mem == NULL)
911 return -ENOMEM;
912
913 ofb->video_mem_phys = virt_to_phys(ofb->video_mem);
914 ofb->video_mem_size = PAGE_ALIGN(pxafb->video_mem_size);
915
916 mutex_lock(&ofb->fb.mm_lock);
917 ofb->fb.fix.smem_start = ofb->video_mem_phys;
918 ofb->fb.fix.smem_len = pxafb->video_mem_size;
919 mutex_unlock(&ofb->fb.mm_lock);
920
921 ofb->fb.screen_base = ofb->video_mem;
922
923 return 0;
924}
925
926static void __devinit pxafb_overlay_init(struct pxafb_info *fbi)
908{ 927{
909 int i, ret; 928 int i, ret;
910 929
911 if (!pxafb_overlay_supported()) 930 if (!pxafb_overlay_supported())
912 return 0; 931 return;
913 932
914 for (i = 0; i < 2; i++) { 933 for (i = 0; i < 2; i++) {
915 init_pxafb_overlay(fbi, &fbi->overlay[i], i); 934 struct pxafb_layer *ofb = &fbi->overlay[i];
916 ret = register_framebuffer(&fbi->overlay[i].fb); 935 init_pxafb_overlay(fbi, ofb, i);
936 ret = register_framebuffer(&ofb->fb);
917 if (ret) { 937 if (ret) {
918 dev_err(fbi->dev, "failed to register overlay %d\n", i); 938 dev_err(fbi->dev, "failed to register overlay %d\n", i);
919 return ret; 939 continue;
940 }
941 ret = pxafb_overlay_map_video_memory(fbi, ofb);
942 if (ret) {
943 dev_err(fbi->dev,
944 "failed to map video memory for overlay %d\n",
945 i);
946 unregister_framebuffer(&ofb->fb);
947 continue;
920 } 948 }
949 ofb->registered = 1;
921 } 950 }
922 951
923 /* mask all IU/BS/EOF/SOF interrupts */ 952 /* mask all IU/BS/EOF/SOF interrupts */
@@ -926,7 +955,6 @@ static int __devinit pxafb_overlay_init(struct pxafb_info *fbi)
926 /* place overlay(s) on top of base */ 955 /* place overlay(s) on top of base */
927 fbi->lccr0 |= LCCR0_OUC; 956 fbi->lccr0 |= LCCR0_OUC;
928 pr_info("PXA Overlay driver loaded successfully!\n"); 957 pr_info("PXA Overlay driver loaded successfully!\n");
929 return 0;
930} 958}
931 959
932static void __devexit pxafb_overlay_exit(struct pxafb_info *fbi) 960static void __devexit pxafb_overlay_exit(struct pxafb_info *fbi)
@@ -936,8 +964,15 @@ static void __devexit pxafb_overlay_exit(struct pxafb_info *fbi)
936 if (!pxafb_overlay_supported()) 964 if (!pxafb_overlay_supported())
937 return; 965 return;
938 966
939 for (i = 0; i < 2; i++) 967 for (i = 0; i < 2; i++) {
940 unregister_framebuffer(&fbi->overlay[i].fb); 968 struct pxafb_layer *ofb = &fbi->overlay[i];
969 if (ofb->registered) {
970 if (ofb->video_mem)
971 free_pages_exact(ofb->video_mem,
972 ofb->video_mem_size);
973 unregister_framebuffer(&ofb->fb);
974 }
975 }
941} 976}
942#else 977#else
943static inline void pxafb_overlay_init(struct pxafb_info *fbi) {} 978static inline void pxafb_overlay_init(struct pxafb_info *fbi) {}
@@ -1368,7 +1403,8 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var,
1368 (lcd_readl(fbi, LCCR3) != fbi->reg_lccr3) || 1403 (lcd_readl(fbi, LCCR3) != fbi->reg_lccr3) ||
1369 (lcd_readl(fbi, LCCR4) != fbi->reg_lccr4) || 1404 (lcd_readl(fbi, LCCR4) != fbi->reg_lccr4) ||
1370 (lcd_readl(fbi, FDADR0) != fbi->fdadr[0]) || 1405 (lcd_readl(fbi, FDADR0) != fbi->fdadr[0]) ||
1371 (lcd_readl(fbi, FDADR1) != fbi->fdadr[1])) 1406 ((fbi->lccr0 & LCCR0_SDS) &&
1407 (lcd_readl(fbi, FDADR1) != fbi->fdadr[1])))
1372 pxafb_schedule_work(fbi, C_REENABLE); 1408 pxafb_schedule_work(fbi, C_REENABLE);
1373 1409
1374 return 0; 1410 return 0;
@@ -1420,7 +1456,8 @@ static void pxafb_enable_controller(struct pxafb_info *fbi)
1420 lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB); 1456 lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB);
1421 1457
1422 lcd_writel(fbi, FDADR0, fbi->fdadr[0]); 1458 lcd_writel(fbi, FDADR0, fbi->fdadr[0]);
1423 lcd_writel(fbi, FDADR1, fbi->fdadr[1]); 1459 if (fbi->lccr0 & LCCR0_SDS)
1460 lcd_writel(fbi, FDADR1, fbi->fdadr[1]);
1424 lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB); 1461 lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB);
1425} 1462}
1426 1463
diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h
index 2353521c5c8..26ba9fa3f73 100644
--- a/drivers/video/pxafb.h
+++ b/drivers/video/pxafb.h
@@ -92,7 +92,8 @@ struct pxafb_layer_ops {
92struct pxafb_layer { 92struct pxafb_layer {
93 struct fb_info fb; 93 struct fb_info fb;
94 int id; 94 int id;
95 atomic_t usage; 95 int registered;
96 uint32_t usage;
96 uint32_t control[2]; 97 uint32_t control[2];
97 98
98 struct pxafb_layer_ops *ops; 99 struct pxafb_layer_ops *ops;