aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/sh_mobile_lcdcfb.c
diff options
context:
space:
mode:
authorPhil Edworthy <phil.edworthy@renesas.com>2009-09-15 08:00:18 -0400
committerPaul Mundt <lethal@linux-sh.org>2009-09-15 08:08:21 -0400
commit9dd38819c2257375ea05bcb92b1f607a1d523c84 (patch)
tree6dc8d2a1f7004b9dd91265ba06731b497bc40b9a /drivers/video/sh_mobile_lcdcfb.c
parentc8c2df9055074197ba12902c6d7e840667fb56d6 (diff)
video: sh_mobile_lcdcfb: implement display panning
Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/video/sh_mobile_lcdcfb.c')
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c76
1 files changed, 66 insertions, 10 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 1cb5213c1a03..7f30cb33a203 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -31,6 +31,7 @@
31#define _LDSR 0x46c 31#define _LDSR 0x46c
32#define _LDCNT1R 0x470 32#define _LDCNT1R 0x470
33#define _LDCNT2R 0x474 33#define _LDCNT2R 0x474
34#define _LDRCNTR 0x478
34#define _LDDDSR 0x47c 35#define _LDDDSR 0x47c
35#define _LDDWD0R 0x800 36#define _LDDWD0R 0x800
36#define _LDDRDR 0x840 37#define _LDDRDR 0x840
@@ -94,7 +95,11 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
94#define DISPLAY_BEU 0x00000008 95#define DISPLAY_BEU 0x00000008
95#define LCDC_ENABLE 0x00000001 96#define LCDC_ENABLE 0x00000001
96#define LDINTR_FE 0x00000400 97#define LDINTR_FE 0x00000400
98#define LDINTR_VSE 0x00000200
99#define LDINTR_VEE 0x00000100
97#define LDINTR_FS 0x00000004 100#define LDINTR_FS 0x00000004
101#define LDINTR_VSS 0x00000002
102#define LDINTR_VES 0x00000001
98 103
99struct sh_mobile_lcdc_priv; 104struct sh_mobile_lcdc_priv;
100struct sh_mobile_lcdc_chan { 105struct sh_mobile_lcdc_chan {
@@ -110,6 +115,8 @@ struct sh_mobile_lcdc_chan {
110 struct fb_deferred_io defio; 115 struct fb_deferred_io defio;
111 struct scatterlist *sglist; 116 struct scatterlist *sglist;
112 unsigned long frame_end; 117 unsigned long frame_end;
118 unsigned long pan_offset;
119 unsigned long new_pan_offset;
113 wait_queue_head_t frame_end_wait; 120 wait_queue_head_t frame_end_wait;
114}; 121};
115 122
@@ -266,30 +273,46 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
266 struct sh_mobile_lcdc_priv *priv = data; 273 struct sh_mobile_lcdc_priv *priv = data;
267 struct sh_mobile_lcdc_chan *ch; 274 struct sh_mobile_lcdc_chan *ch;
268 unsigned long tmp; 275 unsigned long tmp;
276 unsigned long ldintr;
269 int is_sub; 277 int is_sub;
270 int k; 278 int k;
271 279
272 /* acknowledge interrupt */ 280 /* acknowledge interrupt */
273 tmp = lcdc_read(priv, _LDINTR); 281 ldintr = tmp = lcdc_read(priv, _LDINTR);
274 tmp &= 0xffffff00; /* mask in high 24 bits */ 282 /*
275 tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */ 283 * disable further VSYNC End IRQs, preserve all other enabled IRQs,
284 * write 0 to bits 0-6 to ack all triggered IRQs.
285 */
286 tmp &= 0xffffff00 & ~LDINTR_VEE;
276 lcdc_write(priv, _LDINTR, tmp); 287 lcdc_write(priv, _LDINTR, tmp);
277 288
278 /* figure out if this interrupt is for main or sub lcd */ 289 /* figure out if this interrupt is for main or sub lcd */
279 is_sub = (lcdc_read(priv, _LDSR) & (1 << 10)) ? 1 : 0; 290 is_sub = (lcdc_read(priv, _LDSR) & (1 << 10)) ? 1 : 0;
280 291
281 /* wake up channel and disable clocks*/ 292 /* wake up channel and disable clocks */
282 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 293 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
283 ch = &priv->ch[k]; 294 ch = &priv->ch[k];
284 295
285 if (!ch->enabled) 296 if (!ch->enabled)
286 continue; 297 continue;
287 298
288 if (is_sub == lcdc_chan_is_sublcd(ch)) { 299 /* Frame Start */
289 ch->frame_end = 1; 300 if (ldintr & LDINTR_FS) {
290 wake_up(&ch->frame_end_wait); 301 if (is_sub == lcdc_chan_is_sublcd(ch)) {
302 ch->frame_end = 1;
303 wake_up(&ch->frame_end_wait);
291 304
292 sh_mobile_lcdc_clk_off(priv); 305 sh_mobile_lcdc_clk_off(priv);
306 }
307 }
308
309 /* VSYNC End */
310 if (ldintr & LDINTR_VES) {
311 /* Set the source address for the next refresh */
312 lcdc_write_chan(ch, LDSA1R, ch->dma_handle +
313 ch->new_pan_offset);
314 lcdc_write(ch->lcdc, _LDRCNTR, 0);
315 ch->pan_offset = ch->new_pan_offset;
293 } 316 }
294 } 317 }
295 318
@@ -649,6 +672,9 @@ static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
649 .type = FB_TYPE_PACKED_PIXELS, 672 .type = FB_TYPE_PACKED_PIXELS,
650 .visual = FB_VISUAL_TRUECOLOR, 673 .visual = FB_VISUAL_TRUECOLOR,
651 .accel = FB_ACCEL_NONE, 674 .accel = FB_ACCEL_NONE,
675 .xpanstep = 0,
676 .ypanstep = 1,
677 .ywrapstep = 0,
652}; 678};
653 679
654static void sh_mobile_lcdc_fillrect(struct fb_info *info, 680static void sh_mobile_lcdc_fillrect(struct fb_info *info,
@@ -672,13 +698,38 @@ static void sh_mobile_lcdc_imageblit(struct fb_info *info,
672 sh_mobile_lcdc_deferred_io_touch(info); 698 sh_mobile_lcdc_deferred_io_touch(info);
673} 699}
674 700
701static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
702 struct fb_info *info)
703{
704 struct sh_mobile_lcdc_chan *ch = info->par;
705
706 if (info->var.xoffset == var->xoffset &&
707 info->var.yoffset == var->yoffset)
708 return 0; /* No change, do nothing */
709
710 ch->new_pan_offset = (var->yoffset * info->fix.line_length) +
711 (var->xoffset * (info->var.bits_per_pixel / 8));
712
713 if (ch->new_pan_offset != ch->pan_offset) {
714 unsigned long ldintr;
715 ldintr = lcdc_read(ch->lcdc, _LDINTR);
716 ldintr |= LDINTR_VEE;
717 lcdc_write(ch->lcdc, _LDINTR, ldintr);
718 sh_mobile_lcdc_deferred_io_touch(info);
719 }
720
721 return 0;
722}
723
675static struct fb_ops sh_mobile_lcdc_ops = { 724static struct fb_ops sh_mobile_lcdc_ops = {
725 .owner = THIS_MODULE,
676 .fb_setcolreg = sh_mobile_lcdc_setcolreg, 726 .fb_setcolreg = sh_mobile_lcdc_setcolreg,
677 .fb_read = fb_sys_read, 727 .fb_read = fb_sys_read,
678 .fb_write = fb_sys_write, 728 .fb_write = fb_sys_write,
679 .fb_fillrect = sh_mobile_lcdc_fillrect, 729 .fb_fillrect = sh_mobile_lcdc_fillrect,
680 .fb_copyarea = sh_mobile_lcdc_copyarea, 730 .fb_copyarea = sh_mobile_lcdc_copyarea,
681 .fb_imageblit = sh_mobile_lcdc_imageblit, 731 .fb_imageblit = sh_mobile_lcdc_imageblit,
732 .fb_pan_display = sh_mobile_fb_pan_display,
682}; 733};
683 734
684static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) 735static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
@@ -846,6 +897,8 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
846 goto err1; 897 goto err1;
847 } 898 }
848 init_waitqueue_head(&priv->ch[i].frame_end_wait); 899 init_waitqueue_head(&priv->ch[i].frame_end_wait);
900 priv->ch[j].pan_offset = 0;
901 priv->ch[j].new_pan_offset = 0;
849 902
850 switch (pdata->ch[i].chan) { 903 switch (pdata->ch[i].chan) {
851 case LCDC_CHAN_MAINLCD: 904 case LCDC_CHAN_MAINLCD:
@@ -888,7 +941,9 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
888 info = priv->ch[i].info; 941 info = priv->ch[i].info;
889 info->fbops = &sh_mobile_lcdc_ops; 942 info->fbops = &sh_mobile_lcdc_ops;
890 info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres; 943 info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres;
891 info->var.yres = info->var.yres_virtual = cfg->lcd_cfg.yres; 944 info->var.yres = cfg->lcd_cfg.yres;
945 /* Default Y virtual resolution is 2x panel size */
946 info->var.yres_virtual = info->var.yres * 2;
892 info->var.width = cfg->lcd_size_cfg.width; 947 info->var.width = cfg->lcd_size_cfg.width;
893 info->var.height = cfg->lcd_size_cfg.height; 948 info->var.height = cfg->lcd_size_cfg.height;
894 info->var.activate = FB_ACTIVATE_NOW; 949 info->var.activate = FB_ACTIVATE_NOW;
@@ -898,7 +953,8 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
898 953
899 info->fix = sh_mobile_lcdc_fix; 954 info->fix = sh_mobile_lcdc_fix;
900 info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8); 955 info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8);
901 info->fix.smem_len = info->fix.line_length * cfg->lcd_cfg.yres; 956 info->fix.smem_len = info->fix.line_length *
957 info->var.yres_virtual;
902 958
903 buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len, 959 buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len,
904 &priv->ch[i].dma_handle, GFP_KERNEL); 960 &priv->ch[i].dma_handle, GFP_KERNEL);