diff options
| -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); |
