From dc8498c00f6a41a28f01111a3d2ed9f179356a71 Mon Sep 17 00:00:00 2001 From: InKi Dae Date: Tue, 10 Aug 2010 18:02:32 -0700 Subject: s3c-fb: fix distortedness situation for the mode more then 24bpp It has been working fine at 16bpp but in case of pixel format more then 24bpp it would occur distortedness situation on that mode. so this patch set the word swap control bit of WINCONx to 1 as default value. but it should be set to 0 in case that each ENLOCAL bit of WINCON0 ~ 2 registers is enabled. this issue would be solved with local path feature soon. Signed-off-by: InKi Dae Reviewed-by: KyungMin Park Signed-off-by: Ben Dooks Signed-off-by: Pawel Osciak Cc: Marek Szyprowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/plat-samsung/include/plat/regs-fb.h | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/arm/plat-samsung/include/plat/regs-fb.h') diff --git a/arch/arm/plat-samsung/include/plat/regs-fb.h b/arch/arm/plat-samsung/include/plat/regs-fb.h index 0ef806e5034..41c68ac13a6 100644 --- a/arch/arm/plat-samsung/include/plat/regs-fb.h +++ b/arch/arm/plat-samsung/include/plat/regs-fb.h @@ -173,6 +173,7 @@ #define WINCONx_BITSWP (1 << 18) #define WINCONx_BYTSWP (1 << 17) #define WINCONx_HAWSWP (1 << 16) +#define WINCONx_WSWP (1 << 15) #define WINCONx_BURSTLEN_MASK (0x3 << 9) #define WINCONx_BURSTLEN_SHIFT (9) #define WINCONx_BURSTLEN_16WORD (0x0 << 9) -- cgit v1.2.3-70-g09d2 From c4bb6ffa7754e8d0f8b24decd91de259b549fda1 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 10 Aug 2010 18:02:34 -0700 Subject: s3c-fb: udpate to support s3c2416/s3c2443 style hardware Update the variant and window variant structures with the necessary changes to support the older style of hardware where these are not in the same place. Add the support for the s3c2443/s3c2416 hardware by using the platform-device s3c2443 to cover both, and add the initialisation data for these. Also change to including just the v4 header files for the moment until the last of the merging of these is sorted out. Signed-off-by: Ben Dooks Signed-off-by: Pawel Osciak Cc: InKi Dae Cc: KyungMin Park Cc: Marek Szyprowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/plat-samsung/include/plat/regs-fb-v4.h | 8 +- arch/arm/plat-samsung/include/plat/regs-fb.h | 4 + drivers/video/s3c-fb.c | 162 +++++++++++++++++++----- 3 files changed, 136 insertions(+), 38 deletions(-) (limited to 'arch/arm/plat-samsung/include/plat/regs-fb.h') diff --git a/arch/arm/plat-samsung/include/plat/regs-fb-v4.h b/arch/arm/plat-samsung/include/plat/regs-fb-v4.h index 0477e8aaf3d..8352f5dc8df 100644 --- a/arch/arm/plat-samsung/include/plat/regs-fb-v4.h +++ b/arch/arm/plat-samsung/include/plat/regs-fb-v4.h @@ -40,16 +40,10 @@ /* OSD1 and OSD4 do not have register D */ -#define VIDOSD_A(_win) (0x40 + ((_win) * 16)) -#define VIDOSD_B(_win) (0x44 + ((_win) * 16)) -#define VIDOSD_C(_win) (0x48 + ((_win) * 16)) -#define VIDOSD_D(_win) (0x4C + ((_win) * 16)) - +#define VIDOSD_BASE (0x40) #define VIDINTCON0 (0x130) -#define WxKEYCONy(_win, _con) ((0x140 + ((_win) * 8)) + ((_con) * 4)) - /* WINCONx */ #define WINCONx_CSCWIDTH_MASK (0x3 << 26) diff --git a/arch/arm/plat-samsung/include/plat/regs-fb.h b/arch/arm/plat-samsung/include/plat/regs-fb.h index 41c68ac13a6..f4259e52336 100644 --- a/arch/arm/plat-samsung/include/plat/regs-fb.h +++ b/arch/arm/plat-samsung/include/plat/regs-fb.h @@ -322,6 +322,10 @@ #define VIDINTCON1_INT_FIFO (1 << 0) /* Window colour-key control registers */ +#define WKEYCON (0x140) /* 6410,V210 */ + +#define WKEYCON0 (0x00) +#define WKEYCON1 (0x04) #define WxKEYCON0_KEYBL_EN (1 << 26) #define WxKEYCON0_KEYEN_F (1 << 25) diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index e700cfd5002..5c7ac01ab61 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -9,7 +9,7 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * published by the Free Software FoundatIon. */ #include @@ -23,7 +23,7 @@ #include #include -#include +#include #include /* This driver will export a number of framebuffer interfaces depending @@ -52,13 +52,38 @@ struct s3c_fb; #define VALID_BPP(x) (1 << ((x) - 1)) +#define OSD_BASE(win, variant) ((variant).osd + ((win) * (variant).osd_stride)) +#define VIDOSD_A(win, variant) (OSD_BASE(win, variant) + 0x00) +#define VIDOSD_B(win, variant) (OSD_BASE(win, variant) + 0x04) +#define VIDOSD_C(win, variant) (OSD_BASE(win, variant) + 0x08) +#define VIDOSD_D(win, variant) (OSD_BASE(win, variant) + 0x0C) + /** * struct s3c_fb_variant - fb variant information + * @is_2443: Set if S3C2443/S3C2416 style hardware. * @nr_windows: The number of windows. + * @vidtcon: The base for the VIDTCONx registers + * @wincon: The base for the WINxCON registers. + * @winmap: The base for the WINxMAP registers. + * @keycon: The abse for the WxKEYCON registers. + * @buf_start: Offset of buffer start registers. + * @buf_size: Offset of buffer size registers. + * @buf_end: Offset of buffer end registers. + * @osd: The base for the OSD registers. * @palette: Address of palette memory, or 0 if none. */ struct s3c_fb_variant { + unsigned int is_2443:1; unsigned short nr_windows; + unsigned short vidtcon; + unsigned short wincon; + unsigned short winmap; + unsigned short keycon; + unsigned short buf_start; + unsigned short buf_end; + unsigned short buf_size; + unsigned short osd; + unsigned short osd_stride; unsigned short palette[S3C_FB_MAX_WIN]; }; @@ -308,6 +333,7 @@ static int s3c_fb_set_par(struct fb_info *info) struct s3c_fb_win *win = info->par; struct s3c_fb *sfb = win->parent; void __iomem *regs = sfb->regs; + void __iomem *buf = regs; int win_no = win->index; u32 osdc_data = 0; u32 data; @@ -357,6 +383,9 @@ static int s3c_fb_set_par(struct fb_info *info) /* write the timing data to the panel */ + if (sfb->variant.is_2443) + data |= (1 << 5); + data |= VIDCON0_ENVID | VIDCON0_ENVID_F; writel(data, regs + VIDCON0); @@ -364,41 +393,45 @@ static int s3c_fb_set_par(struct fb_info *info) VIDTCON0_VFPD(var->lower_margin - 1) | VIDTCON0_VSPW(var->vsync_len - 1); - writel(data, regs + VIDTCON0); + writel(data, regs + sfb->variant.vidtcon); data = VIDTCON1_HBPD(var->left_margin - 1) | VIDTCON1_HFPD(var->right_margin - 1) | VIDTCON1_HSPW(var->hsync_len - 1); - writel(data, regs + VIDTCON1); + /* VIDTCON1 */ + writel(data, regs + sfb->variant.vidtcon + 4); data = VIDTCON2_LINEVAL(var->yres - 1) | VIDTCON2_HOZVAL(var->xres - 1); - writel(data, regs + VIDTCON2); + writel(data, regs +sfb->variant.vidtcon + 8 ); } /* write the buffer address */ - writel(info->fix.smem_start, regs + VIDW_BUF_START(win_no)); + /* start and end registers stride is 8 */ + buf = regs + win_no * 8; + + writel(info->fix.smem_start, buf + sfb->variant.buf_start); data = info->fix.smem_start + info->fix.line_length * var->yres; - writel(data, regs + VIDW_BUF_END(win_no)); + writel(data, buf + sfb->variant.buf_end); pagewidth = (var->xres * var->bits_per_pixel) >> 3; data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) | VIDW_BUF_SIZE_PAGEWIDTH(pagewidth); - writel(data, regs + VIDW_BUF_SIZE(win_no)); + writel(data, regs + sfb->variant.buf_size + (win_no * 4)); /* write 'OSD' registers to control position of framebuffer */ data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0); - writel(data, regs + VIDOSD_A(win_no)); + writel(data, regs + VIDOSD_A(win_no, sfb->variant)); data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel, var->xres - 1)) | VIDOSDxB_BOTRIGHT_Y(var->yres - 1); - writel(data, regs + VIDOSD_B(win_no)); + writel(data, regs + VIDOSD_B(win_no, sfb->variant)); data = var->xres * var->yres; @@ -407,10 +440,10 @@ static int s3c_fb_set_par(struct fb_info *info) VIDISD14C_ALPHA1_B(0xf); if (win->variant.has_osd_d) { - writel(data, regs + VIDOSD_D(win_no)); - writel(osdc_data, regs + VIDOSD_C(win_no)); + writel(data, regs + VIDOSD_D(win_no, sfb->variant)); + writel(osdc_data, regs + VIDOSD_C(win_no, sfb->variant)); } else - writel(data, regs + VIDOSD_C(win_no)); + writel(data, regs + VIDOSD_C(win_no, sfb->variant)); data = WINCONx_ENWIN; @@ -471,9 +504,10 @@ static int s3c_fb_set_par(struct fb_info *info) break; } - /* It has no color key control register for window0 */ + /* Enable the colour keying for the window below this one */ if (win_no > 0) { u32 keycon0_data = 0, keycon1_data = 0; + void __iomem *keycon = regs + sfb->variant.keycon; keycon0_data = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | @@ -481,12 +515,14 @@ static int s3c_fb_set_par(struct fb_info *info) keycon1_data = WxKEYCON1_COLVAL(0xffffff); - writel(keycon0_data, regs + WxKEYCONy(win_no-1, 0)); - writel(keycon1_data, regs + WxKEYCONy(win_no-1, 1)); + keycon += (win_no - 1) * 8; + + writel(keycon0_data, keycon + WKEYCON0); + writel(keycon1_data, keycon + WKEYCON1); } - writel(data, regs + WINCON(win_no)); - writel(0x0, regs + WINxMAP(win_no)); + writel(data, regs + sfb->variant.wincon + (win_no * 4)); + writel(0x0, regs + sfb->variant.winmap + (win_no * 4)); return 0; } @@ -634,7 +670,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) dev_dbg(sfb->dev, "blank mode %d\n", blank_mode); - wincon = readl(sfb->regs + WINCON(index)); + wincon = readl(sfb->regs + sfb->variant.wincon + (index * 4)); switch (blank_mode) { case FB_BLANK_POWERDOWN: @@ -645,11 +681,11 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) case FB_BLANK_NORMAL: /* disable the DMA and display 0x0 (black) */ writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0), - sfb->regs + WINxMAP(index)); + sfb->regs + sfb->variant.winmap + (index * 4)); break; case FB_BLANK_UNBLANK: - writel(0x0, sfb->regs + WINxMAP(index)); + writel(0x0, sfb->regs + sfb->variant.winmap + (index * 4)); wincon |= WINCONx_ENWIN; sfb->enabled |= (1 << index); break; @@ -660,7 +696,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) return 1; } - writel(wincon, sfb->regs + WINCON(index)); + writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4)); /* Check the enabled state to see if we need to be running the * main LCD interface, as if there are no active windows then @@ -796,7 +832,7 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, int palette_size; int ret; - dev_dbg(sfb->dev, "probing window %d\n", win_no); + dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant); palette_size = variant->palette_sz * 4; @@ -889,10 +925,10 @@ static void s3c_fb_clear_win(struct s3c_fb *sfb, int win) { void __iomem *regs = sfb->regs; - writel(0, regs + WINCON(win)); - writel(0, regs + VIDOSD_A(win)); - writel(0, regs + VIDOSD_B(win)); - writel(0, regs + VIDOSD_C(win)); + writel(0, regs + sfb->variant.wincon + (win * 4)); + writel(0, regs + VIDOSD_A(win, sfb->variant)); + writel(0, regs + VIDOSD_B(win, sfb->variant)); + writel(0, regs + VIDOSD_C(win, sfb->variant)); } static int __devinit s3c_fb_probe(struct platform_device *pdev) @@ -924,6 +960,8 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) return -ENOMEM; } + dev_dbg(dev, "allocate new framebuffer %p\n", sfb); + sfb->dev = dev; sfb->pdata = pd; sfb->variant = fbdrv->variant; @@ -973,8 +1011,11 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) /* initialise colour key controls */ for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) { - writel(0xffffff, sfb->regs + WxKEYCONy(win, 0)); - writel(0xffffff, sfb->regs + WxKEYCONy(win, 1)); + void __iomem *regs = sfb->regs + sfb->variant.keycon; + + regs += (win * 8); + writel(0xffffff, regs + WKEYCON0); + writel(0xffffff, regs + WKEYCON1); } /* we have the register setup, start allocating framebuffers */ @@ -1079,8 +1120,11 @@ static int s3c_fb_resume(struct platform_device *pdev) s3c_fb_clear_win(sfb, win_no); for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) { - writel(0xffffff, sfb->regs + WxKEYCONy(win_no, 1)); - writel(0xffffff, sfb->regs + WxKEYCONy(win_no, 1)); + void __iomem *regs = sfb->regs + sfb->variant.keycon; + + regs += (win_no * 8); + writel(0xffffff, regs + WKEYCON0); + writel(0xffffff, regs + WKEYCON1); } /* restore framebuffers */ @@ -1149,6 +1193,15 @@ static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] __devinitdata = { static struct s3c_fb_driverdata s3c_fb_data_64xx __devinitdata = { .variant = { .nr_windows = 5, + .vidtcon = VIDTCON0, + .wincon = WINCON(0), + .winmap = WINxMAP(0), + .keycon = WKEYCON, + .osd = VIDOSD_BASE, + .osd_stride = 16, + .buf_start = VIDW_BUF_START(0), + .buf_size = VIDW_BUF_SIZE(0), + .buf_end = VIDW_BUF_END(0), .palette = { [0] = 0x400, @@ -1168,6 +1221,15 @@ static struct s3c_fb_driverdata s3c_fb_data_64xx __devinitdata = { static struct s3c_fb_driverdata s3c_fb_data_s5p __devinitdata = { .variant = { .nr_windows = 5, + .vidtcon = VIDTCON0, + .wincon = WINCON(0), + .winmap = WINxMAP(0), + .keycon = WKEYCON, + .osd = VIDOSD_BASE, + .osd_stride = 16, + .buf_start = VIDW_BUF_START(0), + .buf_size = VIDW_BUF_SIZE(0), + .buf_end = VIDW_BUF_END(0), .palette = { [0] = 0x2400, @@ -1184,6 +1246,41 @@ static struct s3c_fb_driverdata s3c_fb_data_s5p __devinitdata = { .win[4] = &s3c_fb_data_64xx_wins[4], }; +/* S3C2443/S3C2416 style hardware */ +static struct s3c_fb_driverdata s3c_fb_data_s3c2443 __devinitdata = { + .variant = { + .nr_windows = 2, + .is_2443 = 1, + + .vidtcon = 0x08, + .wincon = 0x14, + .winmap = 0xd0, + .keycon = 0xb0, + .osd = 0x28, + .osd_stride = 12, + .buf_start = 0x64, + .buf_size = 0x94, + .buf_end = 0x7c, + + .palette = { + [0] = 0x400, + [1] = 0x800, + }, + }, + .win[0] = &(struct s3c_fb_win_variant) { + .palette_sz = 256, + .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24), + }, + .win[1] = &(struct s3c_fb_win_variant) { + .has_osd_c = 1, + .palette_sz = 256, + .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) | + VALID_BPP(18) | VALID_BPP(19) | + VALID_BPP(24) | VALID_BPP(25) | + VALID_BPP(28)), + }, +}; + static struct platform_device_id s3c_fb_driver_ids[] = { { .name = "s3c-fb", @@ -1191,6 +1288,9 @@ static struct platform_device_id s3c_fb_driver_ids[] = { }, { .name = "s5p-fb", .driver_data = (unsigned long)&s3c_fb_data_s5p, + }, { + .name = "s3c2443-fb", + .driver_data = (unsigned long)&s3c_fb_data_s3c2443, }, {}, }; -- cgit v1.2.3-70-g09d2 From 9fa424a40186ef3e8a6573aa1c1dd46c94c66012 Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Tue, 10 Aug 2010 18:02:36 -0700 Subject: s3c-fb: correct FRAMESEL1 bitfield defines for VIDINTCON0 register FRAMESEL1 bitfield starts on 13th bit, not on 14th. Signed-off-by: Pawel Osciak Signed-off-by: Kyungmin Park Acked-by: Ben Dooks Cc: InKi Dae Cc: Marek Szyprowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/plat-samsung/include/plat/regs-fb.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'arch/arm/plat-samsung/include/plat/regs-fb.h') diff --git a/arch/arm/plat-samsung/include/plat/regs-fb.h b/arch/arm/plat-samsung/include/plat/regs-fb.h index f4259e52336..ac10013959b 100644 --- a/arch/arm/plat-samsung/include/plat/regs-fb.h +++ b/arch/arm/plat-samsung/include/plat/regs-fb.h @@ -292,11 +292,11 @@ #define VIDINTCON0_FRAMESEL0_ACTIVE (0x2 << 15) #define VIDINTCON0_FRAMESEL0_FRONTPORCH (0x3 << 15) -#define VIDINTCON0_FRAMESEL1 (1 << 14) -#define VIDINTCON0_FRAMESEL1_NONE (0x0 << 14) -#define VIDINTCON0_FRAMESEL1_BACKPORCH (0x1 << 14) -#define VIDINTCON0_FRAMESEL1_VSYNC (0x2 << 14) -#define VIDINTCON0_FRAMESEL1_FRONTPORCH (0x3 << 14) +#define VIDINTCON0_FRAMESEL1 (1 << 13) +#define VIDINTCON0_FRAMESEL1_NONE (0x0 << 13) +#define VIDINTCON0_FRAMESEL1_BACKPORCH (0x1 << 13) +#define VIDINTCON0_FRAMESEL1_VSYNC (0x2 << 13) +#define VIDINTCON0_FRAMESEL1_FRONTPORCH (0x3 << 13) #define VIDINTCON0_INT_FRAME (1 << 12) #define VIDINTCON0_FIFIOSEL_MASK (0x7f << 5) -- cgit v1.2.3-70-g09d2 From 067b226b9e8e20463e0937344c93101ac8d8d2b1 Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Tue, 10 Aug 2010 18:02:38 -0700 Subject: s3c-fb: add support for display panning Supports all bpp modes. The PRTCON register is used to disable in-hardware updates of registers that store start and end addresses of framebuffer memory. This prevents display corruption in case we do not make it before VSYNC with updating them atomically. With this feature there is no need to wait for a VSYNC interrupt before each such update. Signed-off-by: Pawel Osciak Signed-off-by: Kyungmin Park Cc: InKi Dae Cc: Ben Dooks Cc: Marek Szyprowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/plat-samsung/include/plat/regs-fb.h | 7 +++ drivers/video/s3c-fb.c | 68 ++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) (limited to 'arch/arm/plat-samsung/include/plat/regs-fb.h') diff --git a/arch/arm/plat-samsung/include/plat/regs-fb.h b/arch/arm/plat-samsung/include/plat/regs-fb.h index ac10013959b..dbb6c0afef4 100644 --- a/arch/arm/plat-samsung/include/plat/regs-fb.h +++ b/arch/arm/plat-samsung/include/plat/regs-fb.h @@ -112,6 +112,13 @@ #define VIDCON2_ORGYCbCr (1 << 8) #define VIDCON2_YUVORDCrCb (1 << 7) +/* PRTCON (S3C6410, S5PC100) + * Might not be present in the S3C6410 documentation, + * but tests prove it's there almost for sure; shouldn't hurt in any case. + */ +#define PRTCON (0x0c) +#define PRTCON_PROTECT (1 << 11) + /* VIDTCON0 */ #define VIDTCON0_VBPDE_MASK (0xff << 24) diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 5f931b4933b..de427d928f4 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -71,6 +71,7 @@ struct s3c_fb; * @buf_end: Offset of buffer end registers. * @osd: The base for the OSD registers. * @palette: Address of palette memory, or 0 if none. + * @has_prtcon: Set if has PRTCON register. */ struct s3c_fb_variant { unsigned int is_2443:1; @@ -85,6 +86,8 @@ struct s3c_fb_variant { unsigned short osd; unsigned short osd_stride; unsigned short palette[S3C_FB_MAX_WIN]; + + unsigned int has_prtcon:1; }; /** @@ -379,6 +382,9 @@ static int s3c_fb_set_par(struct fb_info *info) info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; + info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0; + info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0; + /* disable the window whilst we update it */ writel(0, regs + WINCON(win_no)); @@ -735,6 +741,63 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) return 0; } +/** + * s3c_fb_pan_display() - Pan the display. + * + * Note that the offsets can be written to the device at any time, as their + * values are latched at each vsync automatically. This also means that only + * the last call to this function will have any effect on next vsync, but + * there is no need to sleep waiting for it to prevent tearing. + * + * @var: The screen information to verify. + * @info: The framebuffer device. + */ +static int s3c_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + void __iomem *buf = sfb->regs + win->index * 8; + unsigned int start_boff, end_boff; + + /* Offset in bytes to the start of the displayed area */ + start_boff = var->yoffset * info->fix.line_length; + /* X offset depends on the current bpp */ + if (info->var.bits_per_pixel >= 8) { + start_boff += var->xoffset * (info->var.bits_per_pixel >> 3); + } else { + switch (info->var.bits_per_pixel) { + case 4: + start_boff += var->xoffset >> 1; + break; + case 2: + start_boff += var->xoffset >> 2; + break; + case 1: + start_boff += var->xoffset >> 3; + break; + default: + dev_err(sfb->dev, "invalid bpp\n"); + return -EINVAL; + } + } + /* Offset in bytes to the end of the displayed area */ + end_boff = start_boff + var->yres * info->fix.line_length; + + /* Temporarily turn off per-vsync update from shadow registers until + * both start and end addresses are updated to prevent corruption */ + if (sfb->variant.has_prtcon) + writel(PRTCON_PROTECT, sfb->regs + PRTCON); + + writel(info->fix.smem_start + start_boff, buf + sfb->variant.buf_start); + writel(info->fix.smem_start + end_boff, buf + sfb->variant.buf_end); + + if (sfb->variant.has_prtcon) + writel(0, sfb->regs + PRTCON); + + return 0; +} + static struct fb_ops s3c_fb_ops = { .owner = THIS_MODULE, .fb_check_var = s3c_fb_check_var, @@ -744,6 +807,7 @@ static struct fb_ops s3c_fb_ops = { .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, + .fb_pan_display = s3c_fb_pan_display, }; /** @@ -1243,6 +1307,8 @@ static struct s3c_fb_driverdata s3c_fb_data_64xx __devinitdata = { [3] = 0x320, [4] = 0x340, }, + + .has_prtcon = 1, }, .win[0] = &s3c_fb_data_64xx_wins[0], .win[1] = &s3c_fb_data_64xx_wins[1], @@ -1271,6 +1337,8 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pc100 __devinitdata = { [3] = 0x3000, [4] = 0x3400, }, + + .has_prtcon = 1, }, .win[0] = &s3c_fb_data_64xx_wins[0], .win[1] = &s3c_fb_data_64xx_wins[1], -- cgit v1.2.3-70-g09d2 From efdc846d2f7190c8f3092c09975c0ebba30a95ba Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Tue, 10 Aug 2010 18:02:38 -0700 Subject: s3c-fb: add wait for VSYNC ioctl Add VSYNC interrupt support and an ioctl that allows waiting for it. Interrupts are turned on only when needed. Signed-off-by: Pawel Osciak Signed-off-by: Kyungmin Park Cc: InKi Dae Cc: Ben Dooks Cc: Marek Szyprowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/plat-samsung/include/plat/regs-fb.h | 1 + drivers/video/s3c-fb.c | 166 ++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 1 deletion(-) (limited to 'arch/arm/plat-samsung/include/plat/regs-fb.h') diff --git a/arch/arm/plat-samsung/include/plat/regs-fb.h b/arch/arm/plat-samsung/include/plat/regs-fb.h index dbb6c0afef4..564594e89ef 100644 --- a/arch/arm/plat-samsung/include/plat/regs-fb.h +++ b/arch/arm/plat-samsung/include/plat/regs-fb.h @@ -300,6 +300,7 @@ #define VIDINTCON0_FRAMESEL0_FRONTPORCH (0x3 << 15) #define VIDINTCON0_FRAMESEL1 (1 << 13) +#define VIDINTCON0_FRAMESEL1_MASK (0x3 << 13) #define VIDINTCON0_FRAMESEL1_NONE (0x0 << 13) #define VIDINTCON0_FRAMESEL1_BACKPORCH (0x1 << 13) #define VIDINTCON0_FRAMESEL1_VSYNC (0x2 << 13) diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index de427d928f4..2e82adf9ab6 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -48,6 +50,11 @@ __raw_writel(v, r); } while(0) #endif /* FB_S3C_DEBUG_REGWRITE */ +/* irq_flags bits */ +#define S3C_FB_VSYNC_IRQ_EN 0 + +#define VSYNC_TIMEOUT_MSEC 50 + struct s3c_fb; #define VALID_BPP(x) (1 << ((x) - 1)) @@ -155,6 +162,16 @@ struct s3c_fb_win { unsigned int index; }; +/** + * struct s3c_fb_vsync - vsync information + * @wait: a queue for processes waiting for vsync + * @count: vsync interrupt count + */ +struct s3c_fb_vsync { + wait_queue_head_t wait; + unsigned int count; +}; + /** * struct s3c_fb - overall hardware state of the hardware * @dev: The device that we bound to, for printing, etc. @@ -165,6 +182,9 @@ struct s3c_fb_win { * @enabled: A bitmask of enabled hardware windows. * @pdata: The platform configuration data passed with the device. * @windows: The hardware windows that have been claimed. + * @irq_no: IRQ line number + * @irq_flags: irq flags + * @vsync_info: VSYNC-related information (count, queues...) */ struct s3c_fb { struct device *dev; @@ -177,6 +197,10 @@ struct s3c_fb { struct s3c_fb_platdata *pdata; struct s3c_fb_win *windows[S3C_FB_MAX_WIN]; + + int irq_no; + unsigned long irq_flags; + struct s3c_fb_vsync vsync_info; }; /** @@ -798,6 +822,124 @@ static int s3c_fb_pan_display(struct fb_var_screeninfo *var, return 0; } +/** + * s3c_fb_enable_irq() - enable framebuffer interrupts + * @sfb: main hardware state + */ +static void s3c_fb_enable_irq(struct s3c_fb *sfb) +{ + void __iomem *regs = sfb->regs; + u32 irq_ctrl_reg; + + if (!test_and_set_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) { + /* IRQ disabled, enable it */ + irq_ctrl_reg = readl(regs + VIDINTCON0); + + irq_ctrl_reg |= VIDINTCON0_INT_ENABLE; + irq_ctrl_reg |= VIDINTCON0_INT_FRAME; + + irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL0_MASK; + irq_ctrl_reg |= VIDINTCON0_FRAMESEL0_VSYNC; + irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL1_MASK; + irq_ctrl_reg |= VIDINTCON0_FRAMESEL1_NONE; + + writel(irq_ctrl_reg, regs + VIDINTCON0); + } +} + +/** + * s3c_fb_disable_irq() - disable framebuffer interrupts + * @sfb: main hardware state + */ +static void s3c_fb_disable_irq(struct s3c_fb *sfb) +{ + void __iomem *regs = sfb->regs; + u32 irq_ctrl_reg; + + if (test_and_clear_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) { + /* IRQ enabled, disable it */ + irq_ctrl_reg = readl(regs + VIDINTCON0); + + irq_ctrl_reg &= ~VIDINTCON0_INT_FRAME; + irq_ctrl_reg &= ~VIDINTCON0_INT_ENABLE; + + writel(irq_ctrl_reg, regs + VIDINTCON0); + } +} + +static irqreturn_t s3c_fb_irq(int irq, void *dev_id) +{ + struct s3c_fb *sfb = dev_id; + void __iomem *regs = sfb->regs; + u32 irq_sts_reg; + + irq_sts_reg = readl(regs + VIDINTCON1); + + if (irq_sts_reg & VIDINTCON1_INT_FRAME) { + + /* VSYNC interrupt, accept it */ + writel(VIDINTCON1_INT_FRAME, regs + VIDINTCON1); + + sfb->vsync_info.count++; + wake_up_interruptible(&sfb->vsync_info.wait); + } + + /* We only support waiting for VSYNC for now, so it's safe + * to always disable irqs here. + */ + s3c_fb_disable_irq(sfb); + + return IRQ_HANDLED; +} + +/** + * s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout + * @sfb: main hardware state + * @crtc: head index. + */ +static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc) +{ + unsigned long count; + int ret; + + if (crtc != 0) + return -ENODEV; + + count = sfb->vsync_info.count; + s3c_fb_enable_irq(sfb); + ret = wait_event_interruptible_timeout(sfb->vsync_info.wait, + count != sfb->vsync_info.count, + msecs_to_jiffies(VSYNC_TIMEOUT_MSEC)); + if (ret == 0) + return -ETIMEDOUT; + + return 0; +} + +static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + int ret; + u32 crtc; + + switch (cmd) { + case FBIO_WAITFORVSYNC: + if (get_user(crtc, (u32 __user *)arg)) { + ret = -EFAULT; + break; + } + + ret = s3c_fb_wait_for_vsync(sfb, crtc); + break; + default: + ret = -ENOTTY; + } + + return ret; +} + static struct fb_ops s3c_fb_ops = { .owner = THIS_MODULE, .fb_check_var = s3c_fb_check_var, @@ -808,6 +950,7 @@ static struct fb_ops s3c_fb_ops = { .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, .fb_pan_display = s3c_fb_pan_display, + .fb_ioctl = s3c_fb_ioctl, }; /** @@ -914,6 +1057,8 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant); + init_waitqueue_head(&sfb->vsync_info.wait); + palette_size = variant->palette_sz * 4; fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) + @@ -1093,6 +1238,20 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) goto err_req_region; } + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "failed to acquire irq resource\n"); + ret = -ENOENT; + goto err_ioremap; + } + sfb->irq_no = res->start; + ret = request_irq(sfb->irq_no, s3c_fb_irq, + 0, "s3c_fb", sfb); + if (ret) { + dev_err(dev, "irq request failed\n"); + goto err_ioremap; + } + dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); /* setup gpio and output polarity controls */ @@ -1127,7 +1286,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) dev_err(dev, "failed to create window %d\n", win); for (; win >= 0; win--) s3c_fb_release_win(sfb, sfb->windows[win]); - goto err_ioremap; + goto err_irq; } } @@ -1135,6 +1294,9 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) return 0; +err_irq: + free_irq(sfb->irq_no, sfb); + err_ioremap: iounmap(sfb->regs); @@ -1167,6 +1329,8 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) if (sfb->windows[win]) s3c_fb_release_win(sfb, sfb->windows[win]); + free_irq(sfb->irq_no, sfb); + iounmap(sfb->regs); clk_disable(sfb->bus_clk); -- cgit v1.2.3-70-g09d2 From f5ec546f1f5e21bfc84ce7a1ac7408702082c65a Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Tue, 10 Aug 2010 18:02:40 -0700 Subject: s3c-fb: add SHADOWCON shadow register locking support for S5PV210 S5PV210 allows per-window locking of register value updates from shadow registers. Signed-off-by: Pawel Osciak Signed-off-by: Kyungmin Park Cc: InKi Dae Cc: Ben Dooks Cc: Marek Szyprowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/plat-samsung/include/plat/regs-fb.h | 3 +++ drivers/video/s3c-fb.c | 40 +++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) (limited to 'arch/arm/plat-samsung/include/plat/regs-fb.h') diff --git a/arch/arm/plat-samsung/include/plat/regs-fb.h b/arch/arm/plat-samsung/include/plat/regs-fb.h index 564594e89ef..4359918b58b 100644 --- a/arch/arm/plat-samsung/include/plat/regs-fb.h +++ b/arch/arm/plat-samsung/include/plat/regs-fb.h @@ -220,6 +220,9 @@ #define WINCON1_BPPMODE_25BPP_A1888 (0xd << 2) #define WINCON1_BPPMODE_28BPP_A4888 (0xd << 2) +/* S5PV210 */ +#define SHADOWCON (0x34) +#define SHADOWCON_WINx_PROTECT(_win) (1 << (10 + (_win))) #define VIDOSDxA_TOPLEFT_X_MASK (0x7ff << 11) #define VIDOSDxA_TOPLEFT_X_SHIFT (11) diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 6cbddc445f0..f76791180db 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -79,6 +79,7 @@ struct s3c_fb; * @osd: The base for the OSD registers. * @palette: Address of palette memory, or 0 if none. * @has_prtcon: Set if has PRTCON register. + * @has_shadowcon: Set if has SHADOWCON register. */ struct s3c_fb_variant { unsigned int is_2443:1; @@ -95,6 +96,7 @@ struct s3c_fb_variant { unsigned short palette[S3C_FB_MAX_WIN]; unsigned int has_prtcon:1; + unsigned int has_shadowcon:1; }; /** @@ -362,6 +364,36 @@ static int s3c_fb_align_word(unsigned int bpp, unsigned int pix) return ALIGN(pix, pix_per_word); } +/** + * shadow_protect_win() - disable updating values from shadow registers at vsync + * + * @win: window to protect registers for + * @protect: 1 to protect (disable updates) + */ +static void shadow_protect_win(struct s3c_fb_win *win, bool protect) +{ + struct s3c_fb *sfb = win->parent; + u32 reg; + + if (protect) { + if (sfb->variant.has_prtcon) { + writel(PRTCON_PROTECT, sfb->regs + PRTCON); + } else if (sfb->variant.has_shadowcon) { + reg = readl(sfb->regs + SHADOWCON); + writel(reg | SHADOWCON_WINx_PROTECT(win->index), + sfb->regs + SHADOWCON); + } + } else { + if (sfb->variant.has_prtcon) { + writel(0, sfb->regs + PRTCON); + } else if (sfb->variant.has_shadowcon) { + reg = readl(sfb->regs + SHADOWCON); + writel(reg & ~SHADOWCON_WINx_PROTECT(win->index), + sfb->regs + SHADOWCON); + } + } +} + /** * s3c_fb_set_par() - framebuffer request to set new framebuffer state. * @info: The framebuffer to change. @@ -810,14 +842,12 @@ static int s3c_fb_pan_display(struct fb_var_screeninfo *var, /* Temporarily turn off per-vsync update from shadow registers until * both start and end addresses are updated to prevent corruption */ - if (sfb->variant.has_prtcon) - writel(PRTCON_PROTECT, sfb->regs + PRTCON); + shadow_protect_win(win, 1); writel(info->fix.smem_start + start_boff, buf + sfb->variant.buf_start); writel(info->fix.smem_start + end_boff, buf + sfb->variant.buf_end); - if (sfb->variant.has_prtcon) - writel(0, sfb->regs + PRTCON); + shadow_protect_win(win, 0); return 0; } @@ -1530,6 +1560,8 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pv210 __devinitdata = { [3] = 0x3000, [4] = 0x3400, }, + + .has_shadowcon = 1, }, .win[0] = &s3c_fb_data_64xx_wins[0], .win[1] = &s3c_fb_data_64xx_wins[1], -- cgit v1.2.3-70-g09d2 From 04ab9ef97771ba88789672a1f0d0ddcf8dbc0924 Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Tue, 10 Aug 2010 18:02:43 -0700 Subject: s3c-fb: add support for DMA channel control on S5PV210 S5PV210 SoCs allow enabling/disabling DMA channels per window. For a window to display data from framebuffer memory, its channel has to be enabled. Signed-off-by: Pawel Osciak Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Cc: InKi Dae Cc: Ben Dooks Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/plat-samsung/include/plat/regs-fb.h | 4 ++++ drivers/video/s3c-fb.c | 15 +++++++++++++++ 2 files changed, 19 insertions(+) (limited to 'arch/arm/plat-samsung/include/plat/regs-fb.h') diff --git a/arch/arm/plat-samsung/include/plat/regs-fb.h b/arch/arm/plat-samsung/include/plat/regs-fb.h index 4359918b58b..8f39aa5b26e 100644 --- a/arch/arm/plat-samsung/include/plat/regs-fb.h +++ b/arch/arm/plat-samsung/include/plat/regs-fb.h @@ -223,6 +223,10 @@ /* S5PV210 */ #define SHADOWCON (0x34) #define SHADOWCON_WINx_PROTECT(_win) (1 << (10 + (_win))) +/* DMA channels (all windows) */ +#define SHADOWCON_CHx_ENABLE(_win) (1 << (_win)) +/* Local input channels (windows 0-2) */ +#define SHADOWCON_CHx_LOCAL_ENABLE(_win) (1 << (5 + (_win))) #define VIDOSDxA_TOPLEFT_X_MASK (0x7ff << 11) #define VIDOSDxA_TOPLEFT_X_SHIFT (11) diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index a95314ddf70..8ea974dd92c 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -634,6 +634,13 @@ static int s3c_fb_set_par(struct fb_info *info) writel(data, regs + sfb->variant.wincon + (win_no * 4)); writel(0x0, regs + sfb->variant.winmap + (win_no * 4)); + /* Enable DMA channel for this window */ + if (sfb->variant.has_shadowcon) { + data = readl(sfb->regs + SHADOWCON); + data |= SHADOWCON_CHx_ENABLE(win_no); + writel(data, sfb->regs + SHADOWCON); + } + shadow_protect_win(win, 0); return 0; @@ -1091,7 +1098,15 @@ static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win) */ static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win) { + u32 data; + if (win->fbinfo) { + if (sfb->variant.has_shadowcon) { + data = readl(sfb->regs + SHADOWCON); + data &= ~SHADOWCON_CHx_ENABLE(win->index); + data &= ~SHADOWCON_CHx_LOCAL_ENABLE(win->index); + writel(data, sfb->regs + SHADOWCON); + } unregister_framebuffer(win->fbinfo); if (win->fbinfo->cmap.len) fb_dealloc_cmap(&win->fbinfo->cmap); -- cgit v1.2.3-70-g09d2