diff options
author | Phil Edworthy <phil.edworthy@renesas.com> | 2009-09-15 08:00:18 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2009-09-15 08:08:21 -0400 |
commit | 9dd38819c2257375ea05bcb92b1f607a1d523c84 (patch) | |
tree | 6dc8d2a1f7004b9dd91265ba06731b497bc40b9a /drivers/video | |
parent | c8c2df9055074197ba12902c6d7e840667fb56d6 (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')
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 76 |
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 | ||
99 | struct sh_mobile_lcdc_priv; | 104 | struct sh_mobile_lcdc_priv; |
100 | struct sh_mobile_lcdc_chan { | 105 | struct 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 | ||
654 | static void sh_mobile_lcdc_fillrect(struct fb_info *info, | 680 | static 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 | ||
701 | static 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 | |||
675 | static struct fb_ops sh_mobile_lcdc_ops = { | 724 | static 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 | ||
684 | static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) | 735 | static 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); |