aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/sh_mobile_lcdcfb.c
diff options
context:
space:
mode:
authorAndrea Bastoni <bastoni@cs.unc.edu>2010-05-30 19:16:45 -0400
committerAndrea Bastoni <bastoni@cs.unc.edu>2010-05-30 19:16:45 -0400
commitada47b5fe13d89735805b566185f4885f5a3f750 (patch)
tree644b88f8a71896307d71438e9b3af49126ffb22b /drivers/video/sh_mobile_lcdcfb.c
parent43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff)
parent3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff)
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'drivers/video/sh_mobile_lcdcfb.c')
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c160
1 files changed, 110 insertions, 50 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 3ad5157f9899..e8c769944812 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -19,6 +19,8 @@
19#include <linux/dma-mapping.h> 19#include <linux/dma-mapping.h>
20#include <linux/interrupt.h> 20#include <linux/interrupt.h>
21#include <linux/vmalloc.h> 21#include <linux/vmalloc.h>
22#include <linux/ioctl.h>
23#include <linux/slab.h>
22#include <video/sh_mobile_lcdc.h> 24#include <video/sh_mobile_lcdc.h>
23#include <asm/atomic.h> 25#include <asm/atomic.h>
24 26
@@ -106,6 +108,7 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
106#define LDRCNTR_SRC 0x00010000 108#define LDRCNTR_SRC 0x00010000
107#define LDRCNTR_MRS 0x00000002 109#define LDRCNTR_MRS 0x00000002
108#define LDRCNTR_MRC 0x00000001 110#define LDRCNTR_MRC 0x00000001
111#define LDSR_MRS 0x00000100
109 112
110struct sh_mobile_lcdc_priv; 113struct sh_mobile_lcdc_priv;
111struct sh_mobile_lcdc_chan { 114struct sh_mobile_lcdc_chan {
@@ -122,8 +125,8 @@ struct sh_mobile_lcdc_chan {
122 struct scatterlist *sglist; 125 struct scatterlist *sglist;
123 unsigned long frame_end; 126 unsigned long frame_end;
124 unsigned long pan_offset; 127 unsigned long pan_offset;
125 unsigned long new_pan_offset;
126 wait_queue_head_t frame_end_wait; 128 wait_queue_head_t frame_end_wait;
129 struct completion vsync_completion;
127}; 130};
128 131
129struct sh_mobile_lcdc_priv { 132struct sh_mobile_lcdc_priv {
@@ -281,18 +284,42 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
281 struct list_head *pagelist) 284 struct list_head *pagelist)
282{ 285{
283 struct sh_mobile_lcdc_chan *ch = info->par; 286 struct sh_mobile_lcdc_chan *ch = info->par;
284 unsigned int nr_pages; 287 struct sh_mobile_lcdc_board_cfg *bcfg = &ch->cfg.board_cfg;
285 288
286 /* enable clocks before accessing hardware */ 289 /* enable clocks before accessing hardware */
287 sh_mobile_lcdc_clk_on(ch->lcdc); 290 sh_mobile_lcdc_clk_on(ch->lcdc);
288 291
289 nr_pages = sh_mobile_lcdc_sginit(info, pagelist); 292 /*
290 dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); 293 * It's possible to get here without anything on the pagelist via
291 294 * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync()
292 /* trigger panel update */ 295 * invocation. In the former case, the acceleration routines are
293 lcdc_write_chan(ch, LDSM2R, 1); 296 * stepped in to when using the framebuffer console causing the
294 297 * workqueue to be scheduled without any dirty pages on the list.
295 dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); 298 *
299 * Despite this, a panel update is still needed given that the
300 * acceleration routines have their own methods for writing in
301 * that still need to be updated.
302 *
303 * The fsync() and empty pagelist case could be optimized for,
304 * but we don't bother, as any application exhibiting such
305 * behaviour is fundamentally broken anyways.
306 */
307 if (!list_empty(pagelist)) {
308 unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
309
310 /* trigger panel update */
311 dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
312 if (bcfg->start_transfer)
313 bcfg->start_transfer(bcfg->board_data, ch,
314 &sh_mobile_lcdc_sys_bus_ops);
315 lcdc_write_chan(ch, LDSM2R, 1);
316 dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
317 } else {
318 if (bcfg->start_transfer)
319 bcfg->start_transfer(bcfg->board_data, ch,
320 &sh_mobile_lcdc_sys_bus_ops);
321 lcdc_write_chan(ch, LDSM2R, 1);
322 }
296} 323}
297 324
298static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info) 325static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
@@ -342,19 +369,8 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
342 } 369 }
343 370
344 /* VSYNC End */ 371 /* VSYNC End */
345 if (ldintr & LDINTR_VES) { 372 if (ldintr & LDINTR_VES)
346 unsigned long ldrcntr = lcdc_read(priv, _LDRCNTR); 373 complete(&ch->vsync_completion);
347 /* Set the source address for the next refresh */
348 lcdc_write_chan_mirror(ch, LDSA1R, ch->dma_handle +
349 ch->new_pan_offset);
350 if (lcdc_chan_is_sublcd(ch))
351 lcdc_write(ch->lcdc, _LDRCNTR,
352 ldrcntr ^ LDRCNTR_SRS);
353 else
354 lcdc_write(ch->lcdc, _LDRCNTR,
355 ldrcntr ^ LDRCNTR_MRS);
356 ch->pan_offset = ch->new_pan_offset;
357 }
358 } 374 }
359 375
360 return IRQ_HANDLED; 376 return IRQ_HANDLED;
@@ -679,6 +695,7 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
679 * 1) Enable Runtime PM 695 * 1) Enable Runtime PM
680 * 2) Force Runtime PM Resume since hardware is accessed from probe() 696 * 2) Force Runtime PM Resume since hardware is accessed from probe()
681 */ 697 */
698 priv->dev = &pdev->dev;
682 pm_runtime_enable(priv->dev); 699 pm_runtime_enable(priv->dev);
683 pm_runtime_resume(priv->dev); 700 pm_runtime_resume(priv->dev);
684 return 0; 701 return 0;
@@ -743,25 +760,69 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
743 struct fb_info *info) 760 struct fb_info *info)
744{ 761{
745 struct sh_mobile_lcdc_chan *ch = info->par; 762 struct sh_mobile_lcdc_chan *ch = info->par;
763 struct sh_mobile_lcdc_priv *priv = ch->lcdc;
764 unsigned long ldrcntr;
765 unsigned long new_pan_offset;
766
767 new_pan_offset = (var->yoffset * info->fix.line_length) +
768 (var->xoffset * (info->var.bits_per_pixel / 8));
746 769
747 if (info->var.xoffset == var->xoffset && 770 if (new_pan_offset == ch->pan_offset)
748 info->var.yoffset == var->yoffset)
749 return 0; /* No change, do nothing */ 771 return 0; /* No change, do nothing */
750 772
751 ch->new_pan_offset = (var->yoffset * info->fix.line_length) + 773 ldrcntr = lcdc_read(priv, _LDRCNTR);
752 (var->xoffset * (info->var.bits_per_pixel / 8));
753 774
754 if (ch->new_pan_offset != ch->pan_offset) { 775 /* Set the source address for the next refresh */
755 unsigned long ldintr; 776 lcdc_write_chan_mirror(ch, LDSA1R, ch->dma_handle + new_pan_offset);
756 ldintr = lcdc_read(ch->lcdc, _LDINTR); 777 if (lcdc_chan_is_sublcd(ch))
757 ldintr |= LDINTR_VEE; 778 lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS);
758 lcdc_write(ch->lcdc, _LDINTR, ldintr); 779 else
759 sh_mobile_lcdc_deferred_io_touch(info); 780 lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_MRS);
760 } 781
782 ch->pan_offset = new_pan_offset;
783
784 sh_mobile_lcdc_deferred_io_touch(info);
761 785
762 return 0; 786 return 0;
763} 787}
764 788
789static int sh_mobile_wait_for_vsync(struct fb_info *info)
790{
791 struct sh_mobile_lcdc_chan *ch = info->par;
792 unsigned long ldintr;
793 int ret;
794
795 /* Enable VSync End interrupt */
796 ldintr = lcdc_read(ch->lcdc, _LDINTR);
797 ldintr |= LDINTR_VEE;
798 lcdc_write(ch->lcdc, _LDINTR, ldintr);
799
800 ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion,
801 msecs_to_jiffies(100));
802 if (!ret)
803 return -ETIMEDOUT;
804
805 return 0;
806}
807
808static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
809 unsigned long arg)
810{
811 int retval;
812
813 switch (cmd) {
814 case FBIO_WAITFORVSYNC:
815 retval = sh_mobile_wait_for_vsync(info);
816 break;
817
818 default:
819 retval = -ENOIOCTLCMD;
820 break;
821 }
822 return retval;
823}
824
825
765static struct fb_ops sh_mobile_lcdc_ops = { 826static struct fb_ops sh_mobile_lcdc_ops = {
766 .owner = THIS_MODULE, 827 .owner = THIS_MODULE,
767 .fb_setcolreg = sh_mobile_lcdc_setcolreg, 828 .fb_setcolreg = sh_mobile_lcdc_setcolreg,
@@ -771,6 +832,7 @@ static struct fb_ops sh_mobile_lcdc_ops = {
771 .fb_copyarea = sh_mobile_lcdc_copyarea, 832 .fb_copyarea = sh_mobile_lcdc_copyarea,
772 .fb_imageblit = sh_mobile_lcdc_imageblit, 833 .fb_imageblit = sh_mobile_lcdc_imageblit,
773 .fb_pan_display = sh_mobile_fb_pan_display, 834 .fb_pan_display = sh_mobile_fb_pan_display,
835 .fb_ioctl = sh_mobile_ioctl,
774}; 836};
775 837
776static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) 838static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
@@ -874,7 +936,7 @@ static int sh_mobile_lcdc_runtime_resume(struct device *dev)
874 return 0; 936 return 0;
875} 937}
876 938
877static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { 939static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
878 .suspend = sh_mobile_lcdc_suspend, 940 .suspend = sh_mobile_lcdc_suspend,
879 .resume = sh_mobile_lcdc_resume, 941 .resume = sh_mobile_lcdc_resume,
880 .runtime_suspend = sh_mobile_lcdc_runtime_suspend, 942 .runtime_suspend = sh_mobile_lcdc_runtime_suspend,
@@ -883,7 +945,7 @@ static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
883 945
884static int sh_mobile_lcdc_remove(struct platform_device *pdev); 946static int sh_mobile_lcdc_remove(struct platform_device *pdev);
885 947
886static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) 948static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
887{ 949{
888 struct fb_info *info; 950 struct fb_info *info;
889 struct sh_mobile_lcdc_priv *priv; 951 struct sh_mobile_lcdc_priv *priv;
@@ -896,25 +958,24 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
896 958
897 if (!pdev->dev.platform_data) { 959 if (!pdev->dev.platform_data) {
898 dev_err(&pdev->dev, "no platform data defined\n"); 960 dev_err(&pdev->dev, "no platform data defined\n");
899 error = -EINVAL; 961 return -EINVAL;
900 goto err0;
901 } 962 }
902 963
903 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 964 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
904 i = platform_get_irq(pdev, 0); 965 i = platform_get_irq(pdev, 0);
905 if (!res || i < 0) { 966 if (!res || i < 0) {
906 dev_err(&pdev->dev, "cannot get platform resources\n"); 967 dev_err(&pdev->dev, "cannot get platform resources\n");
907 error = -ENOENT; 968 return -ENOENT;
908 goto err0;
909 } 969 }
910 970
911 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 971 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
912 if (!priv) { 972 if (!priv) {
913 dev_err(&pdev->dev, "cannot allocate device data\n"); 973 dev_err(&pdev->dev, "cannot allocate device data\n");
914 error = -ENOMEM; 974 return -ENOMEM;
915 goto err0;
916 } 975 }
917 976
977 platform_set_drvdata(pdev, priv);
978
918 error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED, 979 error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED,
919 dev_name(&pdev->dev), priv); 980 dev_name(&pdev->dev), priv);
920 if (error) { 981 if (error) {
@@ -923,8 +984,6 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
923 } 984 }
924 985
925 priv->irq = i; 986 priv->irq = i;
926 priv->dev = &pdev->dev;
927 platform_set_drvdata(pdev, priv);
928 pdata = pdev->dev.platform_data; 987 pdata = pdev->dev.platform_data;
929 988
930 j = 0; 989 j = 0;
@@ -938,8 +997,8 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
938 goto err1; 997 goto err1;
939 } 998 }
940 init_waitqueue_head(&priv->ch[i].frame_end_wait); 999 init_waitqueue_head(&priv->ch[i].frame_end_wait);
1000 init_completion(&priv->ch[i].vsync_completion);
941 priv->ch[j].pan_offset = 0; 1001 priv->ch[j].pan_offset = 0;
942 priv->ch[j].new_pan_offset = 0;
943 1002
944 switch (pdata->ch[i].chan) { 1003 switch (pdata->ch[i].chan) {
945 case LCDC_CHAN_MAINLCD: 1004 case LCDC_CHAN_MAINLCD:
@@ -1038,9 +1097,9 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
1038 info = ch->info; 1097 info = ch->info;
1039 1098
1040 if (info->fbdefio) { 1099 if (info->fbdefio) {
1041 priv->ch->sglist = vmalloc(sizeof(struct scatterlist) * 1100 ch->sglist = vmalloc(sizeof(struct scatterlist) *
1042 info->fix.smem_len >> PAGE_SHIFT); 1101 info->fix.smem_len >> PAGE_SHIFT);
1043 if (!priv->ch->sglist) { 1102 if (!ch->sglist) {
1044 dev_err(&pdev->dev, "cannot allocate sglist\n"); 1103 dev_err(&pdev->dev, "cannot allocate sglist\n");
1045 goto err1; 1104 goto err1;
1046 } 1105 }
@@ -1065,9 +1124,9 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
1065 } 1124 }
1066 1125
1067 return 0; 1126 return 0;
1068 err1: 1127err1:
1069 sh_mobile_lcdc_remove(pdev); 1128 sh_mobile_lcdc_remove(pdev);
1070 err0: 1129
1071 return error; 1130 return error;
1072} 1131}
1073 1132
@@ -1078,7 +1137,7 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
1078 int i; 1137 int i;
1079 1138
1080 for (i = 0; i < ARRAY_SIZE(priv->ch); i++) 1139 for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
1081 if (priv->ch[i].info->dev) 1140 if (priv->ch[i].info && priv->ch[i].info->dev)
1082 unregister_framebuffer(priv->ch[i].info); 1141 unregister_framebuffer(priv->ch[i].info);
1083 1142
1084 sh_mobile_lcdc_stop(priv); 1143 sh_mobile_lcdc_stop(priv);
@@ -1101,7 +1160,8 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
1101 if (priv->dot_clk) 1160 if (priv->dot_clk)
1102 clk_put(priv->dot_clk); 1161 clk_put(priv->dot_clk);
1103 1162
1104 pm_runtime_disable(priv->dev); 1163 if (priv->dev)
1164 pm_runtime_disable(priv->dev);
1105 1165
1106 if (priv->base) 1166 if (priv->base)
1107 iounmap(priv->base); 1167 iounmap(priv->base);