You can subscribe to this list here.
2001 |
Jan
|
Feb
|
Mar
(1) |
Apr
(104) |
May
(81) |
Jun
(248) |
Jul
(133) |
Aug
(33) |
Sep
(53) |
Oct
(82) |
Nov
(166) |
Dec
(71) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2002 |
Jan
(121) |
Feb
(42) |
Mar
(39) |
Apr
(84) |
May
(87) |
Jun
(58) |
Jul
(97) |
Aug
(130) |
Sep
(32) |
Oct
(139) |
Nov
(108) |
Dec
(216) |
2003 |
Jan
(299) |
Feb
(136) |
Mar
(392) |
Apr
(141) |
May
(137) |
Jun
(107) |
Jul
(94) |
Aug
(262) |
Sep
(300) |
Oct
(216) |
Nov
(72) |
Dec
(94) |
2004 |
Jan
(174) |
Feb
(192) |
Mar
(215) |
Apr
(314) |
May
(319) |
Jun
(293) |
Jul
(205) |
Aug
(161) |
Sep
(192) |
Oct
(226) |
Nov
(308) |
Dec
(89) |
2005 |
Jan
(127) |
Feb
(269) |
Mar
(588) |
Apr
(106) |
May
(77) |
Jun
(77) |
Jul
(161) |
Aug
(239) |
Sep
(86) |
Oct
(112) |
Nov
(153) |
Dec
(145) |
2006 |
Jan
(87) |
Feb
(57) |
Mar
(129) |
Apr
(109) |
May
(102) |
Jun
(232) |
Jul
(97) |
Aug
(69) |
Sep
(67) |
Oct
(69) |
Nov
(214) |
Dec
(82) |
2007 |
Jan
(133) |
Feb
(307) |
Mar
(121) |
Apr
(171) |
May
(229) |
Jun
(156) |
Jul
(185) |
Aug
(160) |
Sep
(122) |
Oct
(130) |
Nov
(78) |
Dec
(27) |
2008 |
Jan
(105) |
Feb
(137) |
Mar
(146) |
Apr
(148) |
May
(239) |
Jun
(208) |
Jul
(157) |
Aug
(244) |
Sep
(119) |
Oct
(125) |
Nov
(189) |
Dec
(225) |
2009 |
Jan
(157) |
Feb
(139) |
Mar
(106) |
Apr
(130) |
May
(246) |
Jun
(189) |
Jul
(128) |
Aug
(127) |
Sep
(88) |
Oct
(86) |
Nov
(216) |
Dec
(9) |
2010 |
Jan
(5) |
Feb
|
Mar
(11) |
Apr
(31) |
May
(3) |
Jun
|
Jul
(7) |
Aug
|
Sep
(1) |
Oct
|
Nov
(1) |
Dec
|
2012 |
Jan
|
Feb
|
Mar
(3) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2013 |
Jan
(1) |
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: Ali G. R. <al...@ru...> - 2009-07-13 20:06:22
|
Greg KH <gr...@su...> wrote: > On Mon, Jul 13, 2009 at 07:47:09PM +0430, Ali Gholami Rudi wrote: > > Is there any reason for not adding these ioctls to fbdev? I searched > > the net and couldn't any. Anyway, these patches simply implement those > > ioctls. > > Have you also added the needed 32 vs. 64 bit handlers for these ioctls? I guess Arnd's patch will do. By the way, it should check for overflows, too? I mean should I change this: > + if (copy->dx + copy->width > info->var.xres || > + copy->sx + copy->width > info->var.xres || > + copy->dy + copy->height > info->var.yres || > + copy->sy + copy->height > info->var.yres) { #define ISSUMLESSTHAN(a, b, s) (((a) <= (s)) && ((b) <= (s)) && \ ((a) + (b) <= (s))) if (!ISSUMLESSTHAN(copy->dx, copy->width, info->var.xres) || ... Ali |
From: Ali G. R. <al...@ru...> - 2009-07-13 19:51:08
|
Arnd Bergmann <ar...@ar...> wrote: > On Monday 13 July 2009, Greg KH wrote: > > On Mon, Jul 13, 2009 at 07:47:09PM +0430, Ali Gholami Rudi wrote: > > > Is there any reason for not adding these ioctls to fbdev? I searched > > > the net and couldn't any. Anyway, these patches simply implement those > > > ioctls. > > > > Have you also added the needed 32 vs. 64 bit handlers for these ioctls? > > > > They are not there, but could be trivially added with > > diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c > index f8a09bf..ce107c4 100644 > --- a/drivers/video/fbmem.c > +++ b/drivers/video/fbmem.c > @@ -1285,6 +1285,8 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, > case FBIOPAN_DISPLAY: > case FBIOGET_CON2FBMAP: > case FBIOPUT_CON2FBMAP: > + case FBIOCOPYAREA: > + case FBIOFILLRECT: > arg = (unsigned long) compat_ptr(arg); > case FBIOBLANK: > ret = do_fb_ioctl(info, cmd, arg); Thanks, I'll update the patches. Ali |
From: Arnd B. <ar...@ar...> - 2009-07-13 18:53:31
|
On Monday 13 July 2009, Greg KH wrote: > On Mon, Jul 13, 2009 at 07:47:09PM +0430, Ali Gholami Rudi wrote: > > Hi, > > > > Is there any reason for not adding these ioctls to fbdev? I searched > > the net and couldn't any. Anyway, these patches simply implement those > > ioctls. > > Have you also added the needed 32 vs. 64 bit handlers for these ioctls? > They are not there, but could be trivially added with diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index f8a09bf..ce107c4 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1285,6 +1285,8 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, case FBIOPAN_DISPLAY: case FBIOGET_CON2FBMAP: case FBIOPUT_CON2FBMAP: + case FBIOCOPYAREA: + case FBIOFILLRECT: arg = (unsigned long) compat_ptr(arg); case FBIOBLANK: ret = do_fb_ioctl(info, cmd, arg); Unfortunately, it would not be as easy to add an FBIOIMAGE, which sounds equally useful. Arnd <>< |
From: Arjan v. de V. <ar...@in...> - 2009-07-13 16:50:20
|
On Mon, 13 Jul 2009 19:47:09 +0430 Ali Gholami Rudi <al...@ru...> wrote: > Hi, > > Is there any reason for not adding these ioctls to fbdev? I searched > the net and couldn't any. Anyway, these patches simply implement > those ioctls. > can we turn this around, is there a reason to add them? or in other words, how / where would these be used ? -- Arjan van de Ven Intel Open Source Technology Centre For development, discussion and tips for power savings, visit http://www.lesswatts.org |
From: Greg KH <gr...@su...> - 2009-07-13 16:38:51
|
On Mon, Jul 13, 2009 at 07:47:09PM +0430, Ali Gholami Rudi wrote: > Hi, > > Is there any reason for not adding these ioctls to fbdev? I searched > the net and couldn't any. Anyway, these patches simply implement those > ioctls. Have you also added the needed 32 vs. 64 bit handlers for these ioctls? thanks, greg k-h |
From: Ali G. R. <al...@ru...> - 2009-07-13 16:24:15
|
Arjan van de Ven <ar...@in...> wrote: > On Mon, 13 Jul 2009 19:47:09 +0430 > Ali Gholami Rudi <al...@ru...> wrote: > > Is there any reason for not adding these ioctls to fbdev? I searched > > the net and couldn't any. Anyway, these patches simply implement > > those ioctls. > > > > can we turn this around, is there a reason to add them? > or in other words, how / where would these be used ? User-space programs that use framebuffer directly can use them. I was writing a simple framebuffer virtual terminal (using libfreetype for fonts; like fbterm); scrolling and painting boxes would be faster if there was someway of using hardware accelerated operations. I think other similar programs can benefit, too. Ali |
From: Ali G. R. <al...@ru...> - 2009-07-13 15:19:22
|
Signed-off-by: Ali Gholami Rudi <al...@ru...> --- drivers/video/fbmem.c | 25 +++++++++++++++++++++++++ include/linux/fb.h | 1 + 2 files changed, 26 insertions(+), 0 deletions(-) diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index a90cd0f..518119a 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1035,6 +1035,25 @@ out: return ret; } +static int fb_copyarea_user(struct fb_info *info, + struct fb_copyarea *copy) +{ + int ret = 0; + if (!lock_fb_info(info)) + return -ENODEV; + if (copy->dx + copy->width > info->var.xres || + copy->sx + copy->width > info->var.xres || + copy->dy + copy->height > info->var.yres || + copy->sy + copy->height > info->var.yres) { + ret = -EINVAL; + goto out; + } + info->fbops->fb_copyarea(info, copy); +out: + unlock_fb_info(info); + return ret; +} + static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { @@ -1046,6 +1065,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, struct fb_cmap_user cmap; struct fb_event event; struct fb_fillrect fill; + struct fb_copyarea copy; void __user *argp = (void __user *)arg; long ret = 0; @@ -1158,6 +1178,11 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, return -EFAULT; ret = fb_fillrect_user(info, &fill); break; + case FBIOCOPYAREA: + if (copy_from_user(©, argp, sizeof(copy))) + return -EFAULT; + ret = fb_copyarea_user(info, ©); + break; default: if (!lock_fb_info(info)) return -ENODEV; diff --git a/include/linux/fb.h b/include/linux/fb.h index 40cc99a..f1cf8df 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -38,6 +38,7 @@ struct dentry; #define FBIOPUT_MODEINFO 0x4617 #define FBIOGET_DISPINFO 0x4618 #define FBIOFILLRECT 0x4619 +#define FBIOCOPYAREA 0x461A #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ |
From: Ali G. R. <al...@ru...> - 2009-07-13 15:19:20
|
Signed-off-by: Ali Gholami Rudi <al...@ru...> --- drivers/video/fbmem.c | 25 +++++++++++++++++++++++++ include/linux/fb.h | 1 + 2 files changed, 26 insertions(+), 0 deletions(-) diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index a85c818..a90cd0f 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1016,6 +1016,25 @@ fb_blank(struct fb_info *info, int blank) return ret; } +static int fb_fillrect_user(struct fb_info *info, + struct fb_fillrect *fill) +{ + int ret = 0; + if (fill->rop != ROP_COPY && fill->rop != ROP_XOR) + return -EINVAL; + if (!lock_fb_info(info)) + return -ENODEV; + if (fill->dx + fill->width > info->var.xres || + fill->dy + fill->height > info->var.yres) { + ret = -EINVAL; + goto out; + } + info->fbops->fb_fillrect(info, fill); +out: + unlock_fb_info(info); + return ret; +} + static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { @@ -1026,6 +1045,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, struct fb_cmap cmap_from; struct fb_cmap_user cmap; struct fb_event event; + struct fb_fillrect fill; void __user *argp = (void __user *)arg; long ret = 0; @@ -1133,6 +1153,11 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, release_console_sem(); unlock_fb_info(info); break; + case FBIOFILLRECT: + if (copy_from_user(&fill, argp, sizeof(fill))) + return -EFAULT; + ret = fb_fillrect_user(info, &fill); + break; default: if (!lock_fb_info(info)) return -ENODEV; diff --git a/include/linux/fb.h b/include/linux/fb.h index f847df9..40cc99a 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -37,6 +37,7 @@ struct dentry; #define FBIOGET_HWCINFO 0x4616 #define FBIOPUT_MODEINFO 0x4617 #define FBIOGET_DISPINFO 0x4618 +#define FBIOFILLRECT 0x4619 #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ |
From: Ali G. R. <al...@ru...> - 2009-07-13 15:19:13
|
Hi, Is there any reason for not adding these ioctls to fbdev? I searched the net and couldn't any. Anyway, these patches simply implement those ioctls. Ali Gholami Rudi (2): fbdev: add FBIOFILLRECT ioctl fbdev: add FBIOCOPYAREA ioctl drivers/video/fbmem.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fb.h | 2 + 2 files changed, 52 insertions(+), 0 deletions(-) |
From: Uwe Kleine-K. <u.k...@pe...> - 2009-07-11 21:18:46
|
A pointer to acornfb_probe is passed to the core via platform_driver_register and so the function must not disappear when the .init sections are discarded. Otherwise (if also having HOTPLUG=y) unbinding and binding a device to the driver via sysfs will result in an oops as does a device being registered late. An alternative to this patch is using platform_driver_probe instead of platform_driver_register plus removing the pointer to the probe function from the struct platform_driver. Signed-off-by: Uwe Kleine-König <u.k...@pe...> Cc: Christoph Hellwig <hc...@ls...> Cc: Antonino Daplas <ad...@po...> Cc: lin...@li... Cc: Andrew Morton <ak...@li...> --- drivers/video/acornfb.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/drivers/video/acornfb.c b/drivers/video/acornfb.c index 0bcc59e..43d7d50 100644 --- a/drivers/video/acornfb.c +++ b/drivers/video/acornfb.c @@ -1221,7 +1221,7 @@ free_unused_pages(unsigned int virtual_start, unsigned int virtual_end) printk("acornfb: freed %dK memory\n", mb_freed); } -static int __init acornfb_probe(struct platform_device *dev) +static int __devinit acornfb_probe(struct platform_device *dev) { unsigned long size; u_int h_sync, v_sync; -- 1.6.3.1 |
From: Krzysztof H. <krz...@po...> - 2009-07-10 14:34:26
|
On Thu, 9 Jul 2009 19:02:28 +0100 Russell King - ARM Linux <li...@ar...> wrote: > On Thu, Jul 09, 2009 at 07:49:12PM +0200, Krzysztof Helt wrote: > > On Thu, 09 Jul 2009 17:09:00 +0200 > > Pawel Osciak <p.o...@sa...> wrote: > > > diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c > > > index ff0b1a3..77b77a2 100644 > > > --- a/drivers/video/s3c-fb.c > > > +++ b/drivers/video/s3c-fb.c > > > @@ -294,6 +294,8 @@ 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; > > > > > > > These two lines are not need. If the xres_virtual > xres there is no > > panning possible (the same is true for yres and yres_virtual). > > Erm, I think you have that backwards. > Right. The given conditions (xres_virtual < xres) are already checked by a higher layer so there is no need for this condition here. > xres,yres is the visibly displayed dimentions. > xres_virtual,yres_virtual is the size of the underlying framebuffer. > > If the underlying framebuffer is larger than the visible display, you can > pan the visible display around within the underlying framebuffer. > > It is common for unaccelerated framebuffers used in text mode to have > xres_virtual = xres, yres_virtual >> yres so that Y panning (or if your > video driver supports it, wrapping) can occur to speed line scrolling. > You are right, of course. Regards, Krzysztof ---------------------------------------------------------------------- Ubezpieczenie komunikacyjne wraz z ubezpieczeniem kosztow leczenia za granic±. http://link.interia.pl/f222b |
From: Kai J. <b1...@fr...> - 2009-07-10 08:42:28
|
Florian Tobias Schandinat, I think your solution is better. It seems much simple in your way:) Best Regards, Kai Jiang > > I only wanted to highlight, that as far as I can see the same behavior > you want to archive can be archived by changing the current code to: > > fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) > { > ...... > if (err || !info->fbops->fb_pan_display || > var->yoffset > info->var.yres_virtual - yres || > var->xoffset > info->var.xres_virtual - info->var.xres) > return -EINVAL; > ...... > } |
From: Andrey P. <pa...@ce...> - 2009-07-10 08:11:01
|
On 191, 07 10, 2009 at 02:30:19AM -0400, Sudhakar Rajashekhara wrote: > Adds LCD controller (LCDC) driver for TI's DA8xx/OMAP-L1xx > architecture. LCDC specifications can be found at > http://www.ti.com/litv/pdf/sprufm0a. > > LCDC on DA8xx consists of two independent controllers, the > Raster Controller and the LCD Interface Display Driver (LIDD) > controller. LIDD further supports character and graphic displays. > > This patch adds support for the graphic display (Sharp LQ035Q3DG01) > found on the DA830 based EVM. The EVM details can be found at: > http://support.spectrumdigital.com/boards/dskda830/revc/. > > Signed-off-by: Sudhakar Rajashekhara <sud...@ti...> > Signed-off-by: Pavel Kiryukhin <pki...@ru...> > Signed-off-by: Steve Chen <sc...@mv...> > Acked-by: Krzysztof Helt <krz...@wp...> > --- > This patch applies to Linus's Kernel tree. > > Since the previous version, return value of ioremap is being > checked and mapped region has been unmapped using the iounmap > call. Please add iounmap(da8xx_fb_reg_base) to fb_remove() function too, otherwise mapped region will leak on module unload. > > drivers/video/Kconfig | 11 + > drivers/video/Makefile | 1 + > drivers/video/da8xx-fb.c | 907 ++++++++++++++++++++++++++++++++++++++++++++++ > include/video/da8xx-fb.h | 106 ++++++ > 4 files changed, 1025 insertions(+), 0 deletions(-) > create mode 100644 drivers/video/da8xx-fb.c > create mode 100644 include/video/da8xx-fb.h > > diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig > index 8afcf08..d048b7e 100644 > --- a/drivers/video/Kconfig > +++ b/drivers/video/Kconfig > @@ -2038,6 +2038,17 @@ config FB_SH7760 > and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for > panels <= 320 pixel horizontal resolution. > > +config FB_DA8XX > + tristate "DA8xx/OMAP-L1xx Framebuffer support" > + depends on FB && ARCH_DAVINCI_DA830 > + select FB_CFB_FILLRECT > + select FB_CFB_COPYAREA > + select FB_CFB_IMAGEBLIT > + ---help--- > + This is the frame buffer device driver for the TI LCD controller > + found on DA8xx/OMAP-L1xx SoCs. > + If unsure, say N. > + > config FB_VIRTUAL > tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" > depends on FB > diff --git a/drivers/video/Makefile b/drivers/video/Makefile > index 01a819f..288d9b0 100644 > --- a/drivers/video/Makefile > +++ b/drivers/video/Makefile > @@ -136,6 +136,7 @@ obj-$(CONFIG_FB_OF) += offb.o > obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o > obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o > obj-$(CONFIG_FB_MX3) += mx3fb.o > +obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o > > # the test framebuffer is last > obj-$(CONFIG_FB_VIRTUAL) += vfb.o > diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c > new file mode 100644 > index 0000000..fafd82c > --- /dev/null > +++ b/drivers/video/da8xx-fb.c > @@ -0,0 +1,907 @@ > +/* > + * Copyright (C) 2008-2009 MontaVista Software Inc. > + * Copyright (C) 2008-2009 Texas Instruments Inc > + * > + * Based on the LCD driver for TI Avalanche processors written by > + * Ajay Singh and Shalom Hai. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option)any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + */ > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/fb.h> > +#include <linux/dma-mapping.h> > +#include <linux/device.h> > +#include <linux/platform_device.h> > +#include <linux/uaccess.h> > +#include <linux/device.h> > +#include <linux/interrupt.h> > +#include <linux/clk.h> > +#include <video/da8xx-fb.h> > + > +#define DRIVER_NAME "da8xx_lcdc" > + > +/* LCD Status Register */ > +#define LCD_END_OF_FRAME0 BIT(8) > +#define LCD_FIFO_UNDERFLOW BIT(5) > +#define LCD_SYNC_LOST BIT(2) > + > +/* LCD DMA Control Register */ > +#define LCD_DMA_BURST_SIZE(x) ((x) << 4) > +#define LCD_DMA_BURST_1 0x0 > +#define LCD_DMA_BURST_2 0x1 > +#define LCD_DMA_BURST_4 0x2 > +#define LCD_DMA_BURST_8 0x3 > +#define LCD_DMA_BURST_16 0x4 > +#define LCD_END_OF_FRAME_INT_ENA BIT(2) > +#define LCD_DUAL_FRAME_BUFFER_ENABLE BIT(0) > + > +/* LCD Control Register */ > +#define LCD_CLK_DIVISOR(x) ((x) << 8) > +#define LCD_RASTER_MODE 0x01 > + > +/* LCD Raster Control Register */ > +#define LCD_PALETTE_LOAD_MODE(x) ((x) << 20) > +#define PALETTE_AND_DATA 0x00 > +#define PALETTE_ONLY 0x01 > + > +#define LCD_MONO_8BIT_MODE BIT(9) > +#define LCD_RASTER_ORDER BIT(8) > +#define LCD_TFT_MODE BIT(7) > +#define LCD_UNDERFLOW_INT_ENA BIT(6) > +#define LCD_MONOCHROME_MODE BIT(1) > +#define LCD_RASTER_ENABLE BIT(0) > +#define LCD_TFT_ALT_ENABLE BIT(23) > +#define LCD_STN_565_ENABLE BIT(24) > + > +/* LCD Raster Timing 2 Register */ > +#define LCD_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16) > +#define LCD_AC_BIAS_FREQUENCY(x) ((x) << 8) > +#define LCD_SYNC_CTRL BIT(25) > +#define LCD_SYNC_EDGE BIT(24) > +#define LCD_INVERT_PIXEL_CLOCK BIT(22) > +#define LCD_INVERT_LINE_CLOCK BIT(21) > +#define LCD_INVERT_FRAME_CLOCK BIT(20) > + > +/* LCD Block */ > +#define LCD_CTRL_REG 0x4 > +#define LCD_STAT_REG 0x8 > +#define LCD_RASTER_CTRL_REG 0x28 > +#define LCD_RASTER_TIMING_0_REG 0x2C > +#define LCD_RASTER_TIMING_1_REG 0x30 > +#define LCD_RASTER_TIMING_2_REG 0x34 > +#define LCD_DMA_CTRL_REG 0x40 > +#define LCD_DMA_FRM_BUF_BASE_ADDR_0_REG 0x44 > +#define LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG 0x48 > + > +#define WSI_TIMEOUT 50 > +#define PALETTE_SIZE 256 > +#define LEFT_MARGIN 64 > +#define RIGHT_MARGIN 64 > +#define UPPER_MARGIN 32 > +#define LOWER_MARGIN 32 > + > +static resource_size_t da8xx_fb_reg_base; > + > +static inline unsigned int lcdc_read(unsigned int addr) > +{ > + return (unsigned int)__raw_readl(da8xx_fb_reg_base + (addr)); > +} > + > +static inline void lcdc_write(unsigned int val, unsigned int addr) > +{ > + __raw_writel(val, da8xx_fb_reg_base + (addr)); > +} > + > +struct da8xx_fb_par { > + wait_queue_head_t da8xx_wq; > + resource_size_t p_palette_base; > + unsigned char *v_palette_base; > + struct clk *lcdc_clk; > + unsigned int irq; > + unsigned short pseudo_palette[16]; > + unsigned int databuf_sz; > + unsigned int palette_sz; > +}; > + > +/* Variable Screen Information */ > +static struct fb_var_screeninfo da8xx_fb_var __devinitdata = { > + .xoffset = 0, > + .yoffset = 0, > + .transp = {0, 0, 0}, > + .nonstd = 0, > + .activate = 0, > + .height = -1, > + .width = -1, > + .pixclock = 46666, /* 46us - AUO display */ > + .accel_flags = 0, > + .left_margin = LEFT_MARGIN, > + .right_margin = RIGHT_MARGIN, > + .upper_margin = UPPER_MARGIN, > + .lower_margin = LOWER_MARGIN, > + .sync = 0, > + .vmode = FB_VMODE_NONINTERLACED > +}; > + > +static struct fb_fix_screeninfo da8xx_fb_fix __devinitdata = { > + .id = "DA8xx FB Drv", > + .type = FB_TYPE_PACKED_PIXELS, > + .type_aux = 0, > + .visual = FB_VISUAL_PSEUDOCOLOR, > + .xpanstep = 1, > + .ypanstep = 1, > + .ywrapstep = 1, > + .accel = FB_ACCEL_NONE > +}; > + > +struct da8xx_panel { > + const char name[25]; /* Full name <vendor>_<model> */ > + unsigned short width; > + unsigned short height; > + int hfp; /* Horizontal front porch */ > + int hbp; /* Horizontal back porch */ > + int hsw; /* Horizontal Sync Pulse Width */ > + int vfp; /* Vertical front porch */ > + int vbp; /* Vertical back porch */ > + int vsw; /* Vertical Sync Pulse Width */ > + int pxl_clk; /* Pixel clock */ > +}; > + > +static struct da8xx_panel known_lcd_panels[] = { > + /* Sharp LCD035Q3DG01 */ > + [0] = { > + .name = "Sharp_LCD035Q3DG01", > + .width = 320, > + .height = 240, > + .hfp = 8, > + .hbp = 6, > + .hsw = 0, > + .vfp = 2, > + .vbp = 2, > + .vsw = 0, > + .pxl_clk = 0x10, > + }, > + /* Sharp LK043T1DG01 */ > + [1] = { > + .name = "Sharp_LK043T1DG01", > + .width = 480, > + .height = 272, > + .hfp = 2, > + .hbp = 2, > + .hsw = 41, > + .vfp = 2, > + .vbp = 2, > + .vsw = 10, > + .pxl_clk = 0x12, > + }, > +}; > + > +/* Disable the Raster Engine of the LCD Controller */ > +static int lcd_disable_raster(struct da8xx_fb_par *par) > +{ > + int ret = 0; > + u32 reg; > + > + reg = lcdc_read(LCD_RASTER_CTRL_REG); > + if (reg & LCD_RASTER_ENABLE) { > + lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); > + ret = wait_event_interruptible_timeout(par->da8xx_wq, > + !lcdc_read(LCD_STAT_REG) & > + LCD_END_OF_FRAME0, WSI_TIMEOUT); > + } > + > + if (ret < 0) > + return ret; > + if (ret == 0) > + return -ETIMEDOUT; > + > + return 0; > +} > + > +static void lcd_blit(int load_mode, struct da8xx_fb_par *par) > +{ > + u32 tmp = par->p_palette_base + par->databuf_sz - 4; > + u32 reg; > + > + /* Update the databuf in the hw. */ > + lcdc_write(par->p_palette_base, LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); > + lcdc_write(tmp, LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG); > + > + /* Start the DMA. */ > + reg = lcdc_read(LCD_RASTER_CTRL_REG); > + reg &= ~(3 << 20); > + if (load_mode == LOAD_DATA) > + reg |= LCD_PALETTE_LOAD_MODE(PALETTE_AND_DATA); > + else if (load_mode == LOAD_PALETTE) > + reg |= LCD_PALETTE_LOAD_MODE(PALETTE_ONLY); > + > + lcdc_write(reg, LCD_RASTER_CTRL_REG); > +} > + > +/* Configure the Burst Size of DMA */ > +static int lcd_cfg_dma(int burst_size) > +{ > + u32 reg; > + > + reg = lcdc_read(LCD_DMA_CTRL_REG) & 0x00000001; > + switch (burst_size) { > + case 1: > + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_1); > + break; > + case 2: > + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_2); > + break; > + case 4: > + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_4); > + break; > + case 8: > + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_8); > + break; > + case 16: > + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_16); > + break; > + default: > + return -EINVAL; > + } > + lcdc_write(reg | LCD_END_OF_FRAME_INT_ENA, LCD_DMA_CTRL_REG); > + > + return 0; > +} > + > +static void lcd_cfg_ac_bias(int period, int transitions_per_int) > +{ > + u32 reg; > + > + /* Set the AC Bias Period and Number of Transisitons per Interrupt */ > + reg = lcdc_read(LCD_RASTER_TIMING_2_REG) & 0xFFF00000; > + reg |= LCD_AC_BIAS_FREQUENCY(period) | > + LCD_AC_BIAS_TRANSITIONS_PER_INT(transitions_per_int); > + lcdc_write(reg, LCD_RASTER_TIMING_2_REG); > +} > + > +static void lcd_cfg_horizontal_sync(int back_porch, int pulse_width, > + int front_porch) > +{ > + u32 reg; > + > + reg = lcdc_read(LCD_RASTER_TIMING_0_REG) & 0xf; > + reg |= ((back_porch & 0xff) << 24) > + | ((front_porch & 0xff) << 16) > + | ((pulse_width & 0x3f) << 10); > + lcdc_write(reg, LCD_RASTER_TIMING_0_REG); > +} > + > +static void lcd_cfg_vertical_sync(int back_porch, int pulse_width, > + int front_porch) > +{ > + u32 reg; > + > + reg = lcdc_read(LCD_RASTER_TIMING_1_REG) & 0x3ff; > + reg |= ((back_porch & 0xff) << 24) > + | ((front_porch & 0xff) << 16) > + | ((pulse_width & 0x3f) << 10); > + lcdc_write(reg, LCD_RASTER_TIMING_1_REG); > +} > + > +static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) > +{ > + u32 reg; > + > + reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(LCD_TFT_MODE | > + LCD_MONO_8BIT_MODE | > + LCD_MONOCHROME_MODE); > + > + switch (cfg->p_disp_panel->panel_shade) { > + case MONOCHROME: > + reg |= LCD_MONOCHROME_MODE; > + if (cfg->mono_8bit_mode) > + reg |= LCD_MONO_8BIT_MODE; > + break; > + case COLOR_ACTIVE: > + reg |= LCD_TFT_MODE; > + if (cfg->tft_alt_mode) > + reg |= LCD_TFT_ALT_ENABLE; > + break; > + > + case COLOR_PASSIVE: > + if (cfg->stn_565_mode) > + reg |= LCD_STN_565_ENABLE; > + break; > + > + default: > + return -EINVAL; > + } > + > + /* enable additional interrupts here */ > + reg |= LCD_UNDERFLOW_INT_ENA; > + > + lcdc_write(reg, LCD_RASTER_CTRL_REG); > + > + reg = lcdc_read(LCD_RASTER_TIMING_2_REG); > + > + if (cfg->sync_ctrl) > + reg |= LCD_SYNC_CTRL; > + else > + reg &= ~LCD_SYNC_CTRL; > + > + if (cfg->sync_edge) > + reg |= LCD_SYNC_EDGE; > + else > + reg &= ~LCD_SYNC_EDGE; > + > + if (cfg->invert_pxl_clock) > + reg |= LCD_INVERT_PIXEL_CLOCK; > + else > + reg &= ~LCD_INVERT_PIXEL_CLOCK; > + > + if (cfg->invert_line_clock) > + reg |= LCD_INVERT_LINE_CLOCK; > + else > + reg &= ~LCD_INVERT_LINE_CLOCK; > + > + if (cfg->invert_frm_clock) > + reg |= LCD_INVERT_FRAME_CLOCK; > + else > + reg &= ~LCD_INVERT_FRAME_CLOCK; > + > + lcdc_write(reg, LCD_RASTER_TIMING_2_REG); > + > + return 0; > +} > + > +static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, > + u32 bpp, u32 raster_order) > +{ > + u32 bpl, reg; > + > + /* Disable Dual Frame Buffer. */ > + reg = lcdc_read(LCD_DMA_CTRL_REG); > + lcdc_write(reg & ~LCD_DUAL_FRAME_BUFFER_ENABLE, > + LCD_DMA_CTRL_REG); > + /* Set the Panel Width */ > + /* Pixels per line = (PPL + 1)*16 */ > + /*0x3F in bits 4..9 gives max horisontal resolution = 1024 pixels*/ > + width &= 0x3f0; > + reg = lcdc_read(LCD_RASTER_TIMING_0_REG); > + reg &= 0xfffffc00; > + reg |= ((width >> 4) - 1) << 4; > + lcdc_write(reg, LCD_RASTER_TIMING_0_REG); > + > + /* Set the Panel Height */ > + reg = lcdc_read(LCD_RASTER_TIMING_1_REG); > + reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00); > + lcdc_write(reg, LCD_RASTER_TIMING_1_REG); > + > + /* Set the Raster Order of the Frame Buffer */ > + reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(1 << 8); > + if (raster_order) > + reg |= LCD_RASTER_ORDER; > + lcdc_write(reg, LCD_RASTER_CTRL_REG); > + > + switch (bpp) { > + case 1: > + case 2: > + case 4: > + case 16: > + par->palette_sz = 16 * 2; > + break; > + > + case 8: > + par->palette_sz = 256 * 2; > + break; > + > + default: > + return -EINVAL; > + } > + > + bpl = width * bpp / 8; > + par->databuf_sz = height * bpl + par->palette_sz; > + > + return 0; > +} > + > +static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, > + unsigned blue, unsigned transp, > + struct fb_info *info) > +{ > + struct da8xx_fb_par *par = info->par; > + unsigned short *palette = (unsigned short *)par->v_palette_base; > + u_short pal; > + > + if (regno > 255) > + return 1; > + > + if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) > + return 1; > + > + if (info->var.bits_per_pixel == 8) { > + red >>= 4; > + green >>= 8; > + blue >>= 12; > + > + pal = (red & 0x0f00); > + pal |= (green & 0x00f0); > + pal |= (blue & 0x000f); > + > + palette[regno] = pal; > + > + } else if ((info->var.bits_per_pixel == 16) && regno < 16) { > + red >>= (16 - info->var.red.length); > + red <<= info->var.red.offset; > + > + green >>= (16 - info->var.green.length); > + green <<= info->var.green.offset; > + > + blue >>= (16 - info->var.blue.length); > + blue <<= info->var.blue.offset; > + > + par->pseudo_palette[regno] = red | green | blue; > + > + palette[0] = 0x4000; > + } > + > + return 0; > +} > + > +static int lcd_reset(struct da8xx_fb_par *par) > +{ > + int ret = 0; > + > + /* Disable the Raster if previously Enabled */ > + if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE) > + ret = lcd_disable_raster(par); > + > + /* DMA has to be disabled */ > + lcdc_write(0, LCD_DMA_CTRL_REG); > + lcdc_write(0, LCD_RASTER_CTRL_REG); > + > + return ret; > +} > + > +static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, > + struct da8xx_panel *panel) > +{ > + u32 bpp, ret = 0; > + > + ret = lcd_reset(par); > + if (ret != 0) > + return ret; > + > + /* Configure the LCD clock divisor. */ > + lcdc_write(LCD_CLK_DIVISOR(panel->pxl_clk) | > + (LCD_RASTER_MODE & 0x1), LCD_CTRL_REG); > + > + /* Configure the DMA burst size. */ > + ret = lcd_cfg_dma(cfg->dma_burst_sz); > + if (ret < 0) > + return ret; > + > + /* Configure the AC bias properties. */ > + lcd_cfg_ac_bias(cfg->ac_bias, cfg->ac_bias_intrpt); > + > + /* Configure the vertical and horizontal sync properties. */ > + lcd_cfg_vertical_sync(panel->vbp, panel->vsw, panel->vfp); > + lcd_cfg_horizontal_sync(panel->hbp, panel->hsw, panel->hfp); > + > + /* Configure for disply */ > + ret = lcd_cfg_display(cfg); > + if (ret < 0) > + return ret; > + > + if (QVGA != cfg->p_disp_panel->panel_type) > + return -EINVAL; > + > + if (cfg->bpp <= cfg->p_disp_panel->max_bpp && > + cfg->bpp >= cfg->p_disp_panel->min_bpp) > + bpp = cfg->bpp; > + else > + bpp = cfg->p_disp_panel->max_bpp; > + if (bpp == 12) > + bpp = 16; > + ret = lcd_cfg_frame_buffer(par, (unsigned int)panel->width, > + (unsigned int)panel->height, bpp, > + cfg->raster_order); > + if (ret < 0) > + return ret; > + > + /* Configure FDD */ > + lcdc_write((lcdc_read(LCD_RASTER_CTRL_REG) & 0xfff00fff) | > + (cfg->fdd << 12), LCD_RASTER_CTRL_REG); > + > + return 0; > +} > + > +static irqreturn_t lcdc_irq_handler(int irq, void *arg) > +{ > + u32 stat = lcdc_read(LCD_STAT_REG); > + struct da8xx_fb_par *par = arg; > + u32 reg; > + > + if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { > + reg = lcdc_read(LCD_RASTER_CTRL_REG); > + lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); > + lcdc_write(stat, LCD_STAT_REG); > + lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); > + } else > + lcdc_write(stat, LCD_STAT_REG); > + > + wake_up_interruptible(&par->da8xx_wq); > + return IRQ_HANDLED; > +} > + > +static int fb_check_var(struct fb_var_screeninfo *var, > + struct fb_info *info) > +{ > + int err = 0; > + > + switch (var->bits_per_pixel) { > + case 1: > + case 8: > + var->red.offset = 0; > + var->red.length = 8; > + var->green.offset = 0; > + var->green.length = 8; > + var->blue.offset = 0; > + var->blue.length = 8; > + var->transp.offset = 0; > + var->transp.length = 0; > + break; > + case 4: > + var->red.offset = 0; > + var->red.length = 4; > + var->green.offset = 0; > + var->green.length = 4; > + var->blue.offset = 0; > + var->blue.length = 4; > + var->transp.offset = 0; > + var->transp.length = 0; > + break; > + case 16: /* RGB 565 */ > + var->red.offset = 0; > + var->red.length = 5; > + var->green.offset = 5; > + var->green.length = 6; > + var->blue.offset = 11; > + var->blue.length = 5; > + var->transp.offset = 0; > + var->transp.length = 0; > + break; > + default: > + err = -EINVAL; > + } > + > + var->red.msb_right = 0; > + var->green.msb_right = 0; > + var->blue.msb_right = 0; > + var->transp.msb_right = 0; > + return err; > +} > + > +static int __devexit fb_remove(struct platform_device *dev) > +{ > + struct fb_info *info = dev_get_drvdata(&dev->dev); > + int ret = 0; > + > + if (info) { > + struct da8xx_fb_par *par = info->par; > + > + if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE) > + ret = lcd_disable_raster(par); > + lcdc_write(0, LCD_RASTER_CTRL_REG); > + > + /* disable DMA */ > + lcdc_write(0, LCD_DMA_CTRL_REG); > + > + unregister_framebuffer(info); > + fb_dealloc_cmap(&info->cmap); > + dma_free_coherent(NULL, par->databuf_sz + PAGE_SIZE, > + info->screen_base, > + info->fix.smem_start); > + free_irq(par->irq, NULL); > + clk_disable(par->lcdc_clk); > + clk_put(par->lcdc_clk); > + framebuffer_release(info); > + > + } > + return ret; > +} > + > +static int fb_ioctl(struct fb_info *info, unsigned int cmd, > + unsigned long arg) > +{ > + struct lcd_sync_arg sync_arg; > + > + switch (cmd) { > + case FBIOGET_CONTRAST: > + case FBIOPUT_CONTRAST: > + case FBIGET_BRIGHTNESS: > + case FBIPUT_BRIGHTNESS: > + case FBIGET_COLOR: > + case FBIPUT_COLOR: > + return -EINVAL; > + case FBIPUT_HSYNC: > + if (copy_from_user(&sync_arg, (char *)arg, > + sizeof(struct lcd_sync_arg))) > + return -EINVAL; > + lcd_cfg_horizontal_sync(sync_arg.back_porch, > + sync_arg.pulse_width, > + sync_arg.front_porch); > + break; > + case FBIPUT_VSYNC: > + if (copy_from_user(&sync_arg, (char *)arg, > + sizeof(struct lcd_sync_arg))) > + return -EINVAL; > + lcd_cfg_vertical_sync(sync_arg.back_porch, > + sync_arg.pulse_width, > + sync_arg.front_porch); > + break; > + default: > + return -EINVAL; > + } > + return 0; > +} > + > +static struct fb_ops da8xx_fb_ops = { > + .owner = THIS_MODULE, > + .fb_check_var = fb_check_var, > + .fb_setcolreg = fb_setcolreg, > + .fb_ioctl = fb_ioctl, > + .fb_fillrect = cfb_fillrect, > + .fb_copyarea = cfb_copyarea, > + .fb_imageblit = cfb_imageblit, > +}; > + > +static int __init fb_probe(struct platform_device *device) > +{ > + struct da8xx_lcdc_platform_data *fb_pdata = > + device->dev.platform_data; > + struct lcd_ctrl_config *lcd_cfg; > + struct da8xx_panel *lcdc_info; > + struct fb_info *da8xx_fb_info; > + struct resource *lcdc_regs; > + struct clk *fb_clk = NULL; > + struct da8xx_fb_par *par; > + resource_size_t len; > + int ret, i; > + > + if (fb_pdata == NULL) { > + dev_err(&device->dev, "Can not get platform data\n"); > + return -ENOENT; > + } > + > + lcdc_regs = platform_get_resource(device, IORESOURCE_MEM, 0); > + if (!lcdc_regs) { > + dev_err(&device->dev, > + "Can not get memory resource for LCD controller\n"); > + return -ENOENT; > + } > + > + len = lcdc_regs->end - lcdc_regs->start + 1; > + > + lcdc_regs = request_mem_region(lcdc_regs->start, len, lcdc_regs->name); > + if (!lcdc_regs) > + return -EBUSY; > + > + da8xx_fb_reg_base = (resource_size_t)ioremap(lcdc_regs->start, len); > + if (!da8xx_fb_reg_base) { > + ret = -EBUSY; > + goto err_request_mem; > + } > + > + fb_clk = clk_get(&device->dev, NULL); > + if (IS_ERR(fb_clk)) { > + dev_err(&device->dev, "Can not get device clock\n"); > + ret = -ENODEV; > + goto err_ioremap; > + } > + ret = clk_enable(fb_clk); > + if (ret) > + goto err_clk_put; > + > + for (i = 0, lcdc_info = known_lcd_panels; > + i < ARRAY_SIZE(known_lcd_panels); > + i++, lcdc_info++) { > + if (strcmp(fb_pdata->type, lcdc_info->name) == 0) > + break; > + } > + > + if (i == ARRAY_SIZE(known_lcd_panels)) { > + dev_err(&device->dev, "GLCD: No valid panel found\n"); > + ret = ENODEV; > + goto err_clk_disable; > + } else > + dev_info(&device->dev, "GLCD: Found %s panel\n", > + fb_pdata->type); > + > + lcd_cfg = (struct lcd_ctrl_config *)fb_pdata->controller_data; > + > + da8xx_fb_info = framebuffer_alloc(sizeof(struct da8xx_fb_par), > + &device->dev); > + if (!da8xx_fb_info) { > + dev_dbg(&device->dev, "Memory allocation failed for fb_info\n"); > + ret = -ENOMEM; > + goto err_clk_disable; > + } > + > + par = da8xx_fb_info->par; > + > + if (lcd_init(par, lcd_cfg, lcdc_info) < 0) { > + dev_err(&device->dev, "lcd_init failed\n"); > + ret = -EFAULT; > + goto err_release_fb; > + } > + > + /* allocate frame buffer */ > + da8xx_fb_info->screen_base = dma_alloc_coherent(NULL, > + par->databuf_sz + PAGE_SIZE, > + (resource_size_t *) > + &da8xx_fb_info->fix.smem_start, > + GFP_KERNEL | GFP_DMA); > + > + if (!da8xx_fb_info->screen_base) { > + dev_err(&device->dev, > + "GLCD: kmalloc for frame buffer failed\n"); > + ret = -EINVAL; > + goto err_release_fb; > + } > + > + /* move palette base pointer by (PAGE_SIZE - palette_sz) bytes */ > + par->v_palette_base = da8xx_fb_info->screen_base + > + (PAGE_SIZE - par->palette_sz); > + par->p_palette_base = da8xx_fb_info->fix.smem_start + > + (PAGE_SIZE - par->palette_sz); > + > + /* the rest of the frame buffer is pixel data */ > + da8xx_fb_info->screen_base = par->v_palette_base + par->palette_sz; > + da8xx_fb_fix.smem_start = par->p_palette_base + par->palette_sz; > + da8xx_fb_fix.smem_len = par->databuf_sz - par->palette_sz; > + da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8; > + > + par->lcdc_clk = fb_clk; > + > + init_waitqueue_head(&par->da8xx_wq); > + > + par->irq = platform_get_irq(device, 0); > + if (par->irq < 0) { > + ret = -ENOENT; > + goto err_release_fb_mem; > + } > + > + ret = request_irq(par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par); > + if (ret) > + goto err_release_fb_mem; > + > + /* Initialize par */ > + da8xx_fb_info->var.bits_per_pixel = lcd_cfg->bpp; > + > + da8xx_fb_var.xres = lcdc_info->width; > + da8xx_fb_var.xres_virtual = lcdc_info->width; > + > + da8xx_fb_var.yres = lcdc_info->height; > + da8xx_fb_var.yres_virtual = lcdc_info->height; > + > + da8xx_fb_var.grayscale = > + lcd_cfg->p_disp_panel->panel_shade == MONOCHROME ? 1 : 0; > + da8xx_fb_var.bits_per_pixel = lcd_cfg->bpp; > + > + da8xx_fb_var.hsync_len = lcdc_info->hsw; > + da8xx_fb_var.vsync_len = lcdc_info->vsw; > + > + /* Initialize fbinfo */ > + da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT; > + da8xx_fb_info->fix = da8xx_fb_fix; > + da8xx_fb_info->var = da8xx_fb_var; > + da8xx_fb_info->fbops = &da8xx_fb_ops; > + da8xx_fb_info->pseudo_palette = par->pseudo_palette; > + > + ret = fb_alloc_cmap(&da8xx_fb_info->cmap, PALETTE_SIZE, 0); > + if (ret) > + goto err_free_irq; > + > + /* First palette_sz byte of the frame buffer is the palette */ > + da8xx_fb_info->cmap.len = par->palette_sz; > + > + /* Flush the buffer to the screen. */ > + lcd_blit(LOAD_DATA, par); > + > + /* initialize var_screeninfo */ > + da8xx_fb_var.activate = FB_ACTIVATE_FORCE; > + fb_set_var(da8xx_fb_info, &da8xx_fb_var); > + > + dev_set_drvdata(&device->dev, da8xx_fb_info); > + /* Register the Frame Buffer */ > + if (register_framebuffer(da8xx_fb_info) < 0) { > + dev_err(&device->dev, > + "GLCD: Frame Buffer Registration Failed!\n"); > + ret = -EINVAL; > + goto err_dealloc_cmap; > + } > + > + /* enable raster engine */ > + lcdc_write(lcdc_read(LCD_RASTER_CTRL_REG) | > + LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); > + > + return 0; > + > +err_dealloc_cmap: > + fb_dealloc_cmap(&da8xx_fb_info->cmap); > + > +err_free_irq: > + free_irq(par->irq, NULL); > + > +err_release_fb_mem: > + dma_free_coherent(NULL, par->databuf_sz + PAGE_SIZE, > + da8xx_fb_info->screen_base, > + da8xx_fb_info->fix.smem_start); > + > +err_release_fb: > + framebuffer_release(da8xx_fb_info); > + > +err_clk_disable: > + clk_disable(fb_clk); > + > +err_clk_put: > + clk_put(fb_clk); > + > +err_ioremap: > + iounmap(da8xx_fb_reg_base); > + > +err_request_mem: > + release_mem_region(lcdc_regs->start, len);; > + > + return ret; > +} > + > +#ifdef CONFIG_PM > +static int fb_suspend(struct platform_device *dev, pm_message_t state) > +{ > + return -EBUSY; > +} > +static int fb_resume(struct platform_device *dev) > +{ > + return -EBUSY; > +} > +#else > +#define fb_suspend NULL > +#define fb_resume NULL > +#endif > + > +static struct platform_driver da8xx_fb_driver = { > + .probe = fb_probe, > + .remove = fb_remove, > + .suspend = fb_suspend, > + .resume = fb_resume, > + .driver = { > + .name = DRIVER_NAME, > + .owner = THIS_MODULE, > + }, > +}; > + > +static int __init da8xx_fb_init(void) > +{ > + return platform_driver_register(&da8xx_fb_driver); > +} > + > +static void __exit da8xx_fb_cleanup(void) > +{ > + platform_driver_unregister(&da8xx_fb_driver); > +} > + > +module_init(da8xx_fb_init); > +module_exit(da8xx_fb_cleanup); > + > +MODULE_DESCRIPTION("Framebuffer driver for TI da8xx/omap-l1xx"); > +MODULE_AUTHOR("Texas Instruments"); > +MODULE_LICENSE("GPL"); > diff --git a/include/video/da8xx-fb.h b/include/video/da8xx-fb.h > new file mode 100644 > index 0000000..5f77675 > --- /dev/null > +++ b/include/video/da8xx-fb.h > @@ -0,0 +1,106 @@ > +/* > + * Header file for TI DA8XX LCD controller platform data. > + * > + * Copyright (C) 2008-2009 MontaVista Software Inc. > + * Copyright (C) 2008-2009 Texas Instruments Inc > + * > + * This file is licensed under the terms of the GNU General Public License > + * version 2. This program is licensed "as is" without any warranty of any > + * kind, whether express or implied. > + */ > + > +#ifndef DA8XX_FB_H > +#define DA8XX_FB_H > + > +enum panel_type { > + QVGA = 0 > +}; > + > +enum panel_shade { > + MONOCHROME = 0, > + COLOR_ACTIVE, > + COLOR_PASSIVE, > +}; > + > +enum raster_load_mode { > + LOAD_DATA = 1, > + LOAD_PALETTE, > +}; > + > +struct display_panel { > + enum panel_type panel_type; /* QVGA */ > + int max_bpp; > + int min_bpp; > + enum panel_shade panel_shade; > +}; > + > +struct da8xx_lcdc_platform_data { > + const char manu_name[10]; > + void *controller_data; > + const char type[25]; > +}; > + > +struct lcd_ctrl_config { > + const struct display_panel *p_disp_panel; > + > + /* AC Bias Pin Frequency */ > + int ac_bias; > + > + /* AC Bias Pin Transitions per Interrupt */ > + int ac_bias_intrpt; > + > + /* DMA burst size */ > + int dma_burst_sz; > + > + /* Bits per pixel */ > + int bpp; > + > + /* FIFO DMA Request Delay */ > + int fdd; > + > + /* TFT Alternative Signal Mapping (Only for active) */ > + unsigned char tft_alt_mode; > + > + /* 12 Bit Per Pixel (5-6-5) Mode (Only for passive) */ > + unsigned char stn_565_mode; > + > + /* Mono 8-bit Mode: 1=D0-D7 or 0=D0-D3 */ > + unsigned char mono_8bit_mode; > + > + /* Invert pixel clock */ > + unsigned char invert_pxl_clock; > + > + /* Invert line clock */ > + unsigned char invert_line_clock; > + > + /* Invert frame clock */ > + unsigned char invert_frm_clock; > + > + /* Horizontal and Vertical Sync Edge: 0=rising 1=falling */ > + unsigned char sync_edge; > + > + /* Horizontal and Vertical Sync: Control: 0=ignore */ > + unsigned char sync_ctrl; > + > + /* Raster Data Order Select: 1=Most-to-least 0=Least-to-most */ > + unsigned char raster_order; > +}; > + > +struct lcd_sync_arg { > + int back_porch; > + int front_porch; > + int pulse_width; > +}; > + > +/* ioctls */ > +#define FBIOGET_CONTRAST _IOR('F', 1, int) > +#define FBIOPUT_CONTRAST _IOW('F', 2, int) > +#define FBIGET_BRIGHTNESS _IOR('F', 3, int) > +#define FBIPUT_BRIGHTNESS _IOW('F', 3, int) > +#define FBIGET_COLOR _IOR('F', 5, int) > +#define FBIPUT_COLOR _IOW('F', 6, int) > +#define FBIPUT_HSYNC _IOW('F', 9, int) > +#define FBIPUT_VSYNC _IOW('F', 10, int) > + > +#endif /* ifndef DA8XX_FB_H */ > + > -- > 1.5.6 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to maj...@vg... > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ > |
From: Sudhakar R. <sud...@ti...> - 2009-07-10 07:25:53
|
On Fri, Jul 10, 2009 at 12:01:23, Andrey Panin wrote: > On 191, 07 10, 2009 at 01:19:34AM -0400, Sudhakar Rajashekhara wrote: > > Adds LCD controller (LCDC) driver for TI's DA8xx/OMAP-L1xx > > architecture. LCDC specifications can be found at > > http://www.ti.com/litv/pdf/sprufm0a. > > > > LCDC on DA8xx consists of two independent controllers, the > > Raster Controller and the LCD Interface Display Driver (LIDD) > > controller. LIDD further supports character and graphic displays. > > > > This patch adds support for the graphic display (Sharp LQ035Q3DG01) > > found on the DA830 based EVM. The EVM details can be found at: > > http://support.spectrumdigital.com/boards/dskda830/revc/. > > > > Signed-off-by: Sudhakar Rajashekhara <sud...@ti...> > > Signed-off-by: Pavel Kiryukhin <pki...@ru...> > > Signed-off-by: Steve Chen <sc...@mv...> > > Acked-by: Krzysztof Helt <krz...@wp...> > > --- > > This patch applies to Linus's Kernel tree. > > > <SNIP> > > > +static int __init fb_probe(struct platform_device *device) > > +{ > > + struct da8xx_lcdc_platform_data *fb_pdata = > > + device->dev.platform_data; > > + struct lcd_ctrl_config *lcd_cfg; > > + struct da8xx_panel *lcdc_info; > > + struct fb_info *da8xx_fb_info; > > + struct resource *lcdc_regs; > > + struct clk *fb_clk = NULL; > > + struct da8xx_fb_par *par; > > + resource_size_t len; > > + int ret, i; > > + > > + if (fb_pdata == NULL) { > > + dev_err(&device->dev, "Can not get platform data\n"); > > + return -ENOENT; > > + } > > + > > + lcdc_regs = platform_get_resource(device, IORESOURCE_MEM, 0); > > + if (!lcdc_regs) { > > + dev_err(&device->dev, > > + "Can not get memory resource for LCD controller\n"); > > + return -ENOENT; > > + } > > + > > + len = lcdc_regs->end - lcdc_regs->start + 1; > > + > > + lcdc_regs = request_mem_region(lcdc_regs->start, len, lcdc_regs->name); > > + if (!lcdc_regs) > > + return -EBUSY; > > + > > + da8xx_fb_reg_base = (resource_size_t)ioremap(lcdc_regs->start, len); > > 1. ioremap() can return NULL, so check is needed here; > 2. ioremapped region is leaked in case of error and on module unload too. > Agreed. Will add checks and submit an updated version of the patch. Thanks, Sudhakar |
From: Sudhakar R. <sud...@ti...> - 2009-07-10 07:23:51
|
Adds LCD controller (LCDC) driver for TI's DA8xx/OMAP-L1xx architecture. LCDC specifications can be found at http://www.ti.com/litv/pdf/sprufm0a. LCDC on DA8xx consists of two independent controllers, the Raster Controller and the LCD Interface Display Driver (LIDD) controller. LIDD further supports character and graphic displays. This patch adds support for the graphic display (Sharp LQ035Q3DG01) found on the DA830 based EVM. The EVM details can be found at: http://support.spectrumdigital.com/boards/dskda830/revc/. Signed-off-by: Sudhakar Rajashekhara <sud...@ti...> Signed-off-by: Pavel Kiryukhin <pki...@ru...> Signed-off-by: Steve Chen <sc...@mv...> Acked-by: Krzysztof Helt <krz...@wp...> --- This patch applies to Linus's Kernel tree. Since the previous version, return value of ioremap is being checked and mapped region has been unmapped using the iounmap call. drivers/video/Kconfig | 11 + drivers/video/Makefile | 1 + drivers/video/da8xx-fb.c | 907 ++++++++++++++++++++++++++++++++++++++++++++++ include/video/da8xx-fb.h | 106 ++++++ 4 files changed, 1025 insertions(+), 0 deletions(-) create mode 100644 drivers/video/da8xx-fb.c create mode 100644 include/video/da8xx-fb.h diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 8afcf08..d048b7e 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2038,6 +2038,17 @@ config FB_SH7760 and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for panels <= 320 pixel horizontal resolution. +config FB_DA8XX + tristate "DA8xx/OMAP-L1xx Framebuffer support" + depends on FB && ARCH_DAVINCI_DA830 + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + This is the frame buffer device driver for the TI LCD controller + found on DA8xx/OMAP-L1xx SoCs. + If unsure, say N. + config FB_VIRTUAL tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" depends on FB diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 01a819f..288d9b0 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -136,6 +136,7 @@ obj-$(CONFIG_FB_OF) += offb.o obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o obj-$(CONFIG_FB_MX3) += mx3fb.o +obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c new file mode 100644 index 0000000..fafd82c --- /dev/null +++ b/drivers/video/da8xx-fb.c @@ -0,0 +1,907 @@ +/* + * Copyright (C) 2008-2009 MontaVista Software Inc. + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * Based on the LCD driver for TI Avalanche processors written by + * Ajay Singh and Shalom Hai. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option)any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/fb.h> +#include <linux/dma-mapping.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <video/da8xx-fb.h> + +#define DRIVER_NAME "da8xx_lcdc" + +/* LCD Status Register */ +#define LCD_END_OF_FRAME0 BIT(8) +#define LCD_FIFO_UNDERFLOW BIT(5) +#define LCD_SYNC_LOST BIT(2) + +/* LCD DMA Control Register */ +#define LCD_DMA_BURST_SIZE(x) ((x) << 4) +#define LCD_DMA_BURST_1 0x0 +#define LCD_DMA_BURST_2 0x1 +#define LCD_DMA_BURST_4 0x2 +#define LCD_DMA_BURST_8 0x3 +#define LCD_DMA_BURST_16 0x4 +#define LCD_END_OF_FRAME_INT_ENA BIT(2) +#define LCD_DUAL_FRAME_BUFFER_ENABLE BIT(0) + +/* LCD Control Register */ +#define LCD_CLK_DIVISOR(x) ((x) << 8) +#define LCD_RASTER_MODE 0x01 + +/* LCD Raster Control Register */ +#define LCD_PALETTE_LOAD_MODE(x) ((x) << 20) +#define PALETTE_AND_DATA 0x00 +#define PALETTE_ONLY 0x01 + +#define LCD_MONO_8BIT_MODE BIT(9) +#define LCD_RASTER_ORDER BIT(8) +#define LCD_TFT_MODE BIT(7) +#define LCD_UNDERFLOW_INT_ENA BIT(6) +#define LCD_MONOCHROME_MODE BIT(1) +#define LCD_RASTER_ENABLE BIT(0) +#define LCD_TFT_ALT_ENABLE BIT(23) +#define LCD_STN_565_ENABLE BIT(24) + +/* LCD Raster Timing 2 Register */ +#define LCD_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16) +#define LCD_AC_BIAS_FREQUENCY(x) ((x) << 8) +#define LCD_SYNC_CTRL BIT(25) +#define LCD_SYNC_EDGE BIT(24) +#define LCD_INVERT_PIXEL_CLOCK BIT(22) +#define LCD_INVERT_LINE_CLOCK BIT(21) +#define LCD_INVERT_FRAME_CLOCK BIT(20) + +/* LCD Block */ +#define LCD_CTRL_REG 0x4 +#define LCD_STAT_REG 0x8 +#define LCD_RASTER_CTRL_REG 0x28 +#define LCD_RASTER_TIMING_0_REG 0x2C +#define LCD_RASTER_TIMING_1_REG 0x30 +#define LCD_RASTER_TIMING_2_REG 0x34 +#define LCD_DMA_CTRL_REG 0x40 +#define LCD_DMA_FRM_BUF_BASE_ADDR_0_REG 0x44 +#define LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG 0x48 + +#define WSI_TIMEOUT 50 +#define PALETTE_SIZE 256 +#define LEFT_MARGIN 64 +#define RIGHT_MARGIN 64 +#define UPPER_MARGIN 32 +#define LOWER_MARGIN 32 + +static resource_size_t da8xx_fb_reg_base; + +static inline unsigned int lcdc_read(unsigned int addr) +{ + return (unsigned int)__raw_readl(da8xx_fb_reg_base + (addr)); +} + +static inline void lcdc_write(unsigned int val, unsigned int addr) +{ + __raw_writel(val, da8xx_fb_reg_base + (addr)); +} + +struct da8xx_fb_par { + wait_queue_head_t da8xx_wq; + resource_size_t p_palette_base; + unsigned char *v_palette_base; + struct clk *lcdc_clk; + unsigned int irq; + unsigned short pseudo_palette[16]; + unsigned int databuf_sz; + unsigned int palette_sz; +}; + +/* Variable Screen Information */ +static struct fb_var_screeninfo da8xx_fb_var __devinitdata = { + .xoffset = 0, + .yoffset = 0, + .transp = {0, 0, 0}, + .nonstd = 0, + .activate = 0, + .height = -1, + .width = -1, + .pixclock = 46666, /* 46us - AUO display */ + .accel_flags = 0, + .left_margin = LEFT_MARGIN, + .right_margin = RIGHT_MARGIN, + .upper_margin = UPPER_MARGIN, + .lower_margin = LOWER_MARGIN, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED +}; + +static struct fb_fix_screeninfo da8xx_fb_fix __devinitdata = { + .id = "DA8xx FB Drv", + .type = FB_TYPE_PACKED_PIXELS, + .type_aux = 0, + .visual = FB_VISUAL_PSEUDOCOLOR, + .xpanstep = 1, + .ypanstep = 1, + .ywrapstep = 1, + .accel = FB_ACCEL_NONE +}; + +struct da8xx_panel { + const char name[25]; /* Full name <vendor>_<model> */ + unsigned short width; + unsigned short height; + int hfp; /* Horizontal front porch */ + int hbp; /* Horizontal back porch */ + int hsw; /* Horizontal Sync Pulse Width */ + int vfp; /* Vertical front porch */ + int vbp; /* Vertical back porch */ + int vsw; /* Vertical Sync Pulse Width */ + int pxl_clk; /* Pixel clock */ +}; + +static struct da8xx_panel known_lcd_panels[] = { + /* Sharp LCD035Q3DG01 */ + [0] = { + .name = "Sharp_LCD035Q3DG01", + .width = 320, + .height = 240, + .hfp = 8, + .hbp = 6, + .hsw = 0, + .vfp = 2, + .vbp = 2, + .vsw = 0, + .pxl_clk = 0x10, + }, + /* Sharp LK043T1DG01 */ + [1] = { + .name = "Sharp_LK043T1DG01", + .width = 480, + .height = 272, + .hfp = 2, + .hbp = 2, + .hsw = 41, + .vfp = 2, + .vbp = 2, + .vsw = 10, + .pxl_clk = 0x12, + }, +}; + +/* Disable the Raster Engine of the LCD Controller */ +static int lcd_disable_raster(struct da8xx_fb_par *par) +{ + int ret = 0; + u32 reg; + + reg = lcdc_read(LCD_RASTER_CTRL_REG); + if (reg & LCD_RASTER_ENABLE) { + lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); + ret = wait_event_interruptible_timeout(par->da8xx_wq, + !lcdc_read(LCD_STAT_REG) & + LCD_END_OF_FRAME0, WSI_TIMEOUT); + } + + if (ret < 0) + return ret; + if (ret == 0) + return -ETIMEDOUT; + + return 0; +} + +static void lcd_blit(int load_mode, struct da8xx_fb_par *par) +{ + u32 tmp = par->p_palette_base + par->databuf_sz - 4; + u32 reg; + + /* Update the databuf in the hw. */ + lcdc_write(par->p_palette_base, LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); + lcdc_write(tmp, LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG); + + /* Start the DMA. */ + reg = lcdc_read(LCD_RASTER_CTRL_REG); + reg &= ~(3 << 20); + if (load_mode == LOAD_DATA) + reg |= LCD_PALETTE_LOAD_MODE(PALETTE_AND_DATA); + else if (load_mode == LOAD_PALETTE) + reg |= LCD_PALETTE_LOAD_MODE(PALETTE_ONLY); + + lcdc_write(reg, LCD_RASTER_CTRL_REG); +} + +/* Configure the Burst Size of DMA */ +static int lcd_cfg_dma(int burst_size) +{ + u32 reg; + + reg = lcdc_read(LCD_DMA_CTRL_REG) & 0x00000001; + switch (burst_size) { + case 1: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_1); + break; + case 2: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_2); + break; + case 4: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_4); + break; + case 8: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_8); + break; + case 16: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_16); + break; + default: + return -EINVAL; + } + lcdc_write(reg | LCD_END_OF_FRAME_INT_ENA, LCD_DMA_CTRL_REG); + + return 0; +} + +static void lcd_cfg_ac_bias(int period, int transitions_per_int) +{ + u32 reg; + + /* Set the AC Bias Period and Number of Transisitons per Interrupt */ + reg = lcdc_read(LCD_RASTER_TIMING_2_REG) & 0xFFF00000; + reg |= LCD_AC_BIAS_FREQUENCY(period) | + LCD_AC_BIAS_TRANSITIONS_PER_INT(transitions_per_int); + lcdc_write(reg, LCD_RASTER_TIMING_2_REG); +} + +static void lcd_cfg_horizontal_sync(int back_porch, int pulse_width, + int front_porch) +{ + u32 reg; + + reg = lcdc_read(LCD_RASTER_TIMING_0_REG) & 0xf; + reg |= ((back_porch & 0xff) << 24) + | ((front_porch & 0xff) << 16) + | ((pulse_width & 0x3f) << 10); + lcdc_write(reg, LCD_RASTER_TIMING_0_REG); +} + +static void lcd_cfg_vertical_sync(int back_porch, int pulse_width, + int front_porch) +{ + u32 reg; + + reg = lcdc_read(LCD_RASTER_TIMING_1_REG) & 0x3ff; + reg |= ((back_porch & 0xff) << 24) + | ((front_porch & 0xff) << 16) + | ((pulse_width & 0x3f) << 10); + lcdc_write(reg, LCD_RASTER_TIMING_1_REG); +} + +static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) +{ + u32 reg; + + reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(LCD_TFT_MODE | + LCD_MONO_8BIT_MODE | + LCD_MONOCHROME_MODE); + + switch (cfg->p_disp_panel->panel_shade) { + case MONOCHROME: + reg |= LCD_MONOCHROME_MODE; + if (cfg->mono_8bit_mode) + reg |= LCD_MONO_8BIT_MODE; + break; + case COLOR_ACTIVE: + reg |= LCD_TFT_MODE; + if (cfg->tft_alt_mode) + reg |= LCD_TFT_ALT_ENABLE; + break; + + case COLOR_PASSIVE: + if (cfg->stn_565_mode) + reg |= LCD_STN_565_ENABLE; + break; + + default: + return -EINVAL; + } + + /* enable additional interrupts here */ + reg |= LCD_UNDERFLOW_INT_ENA; + + lcdc_write(reg, LCD_RASTER_CTRL_REG); + + reg = lcdc_read(LCD_RASTER_TIMING_2_REG); + + if (cfg->sync_ctrl) + reg |= LCD_SYNC_CTRL; + else + reg &= ~LCD_SYNC_CTRL; + + if (cfg->sync_edge) + reg |= LCD_SYNC_EDGE; + else + reg &= ~LCD_SYNC_EDGE; + + if (cfg->invert_pxl_clock) + reg |= LCD_INVERT_PIXEL_CLOCK; + else + reg &= ~LCD_INVERT_PIXEL_CLOCK; + + if (cfg->invert_line_clock) + reg |= LCD_INVERT_LINE_CLOCK; + else + reg &= ~LCD_INVERT_LINE_CLOCK; + + if (cfg->invert_frm_clock) + reg |= LCD_INVERT_FRAME_CLOCK; + else + reg &= ~LCD_INVERT_FRAME_CLOCK; + + lcdc_write(reg, LCD_RASTER_TIMING_2_REG); + + return 0; +} + +static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, + u32 bpp, u32 raster_order) +{ + u32 bpl, reg; + + /* Disable Dual Frame Buffer. */ + reg = lcdc_read(LCD_DMA_CTRL_REG); + lcdc_write(reg & ~LCD_DUAL_FRAME_BUFFER_ENABLE, + LCD_DMA_CTRL_REG); + /* Set the Panel Width */ + /* Pixels per line = (PPL + 1)*16 */ + /*0x3F in bits 4..9 gives max horisontal resolution = 1024 pixels*/ + width &= 0x3f0; + reg = lcdc_read(LCD_RASTER_TIMING_0_REG); + reg &= 0xfffffc00; + reg |= ((width >> 4) - 1) << 4; + lcdc_write(reg, LCD_RASTER_TIMING_0_REG); + + /* Set the Panel Height */ + reg = lcdc_read(LCD_RASTER_TIMING_1_REG); + reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00); + lcdc_write(reg, LCD_RASTER_TIMING_1_REG); + + /* Set the Raster Order of the Frame Buffer */ + reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(1 << 8); + if (raster_order) + reg |= LCD_RASTER_ORDER; + lcdc_write(reg, LCD_RASTER_CTRL_REG); + + switch (bpp) { + case 1: + case 2: + case 4: + case 16: + par->palette_sz = 16 * 2; + break; + + case 8: + par->palette_sz = 256 * 2; + break; + + default: + return -EINVAL; + } + + bpl = width * bpp / 8; + par->databuf_sz = height * bpl + par->palette_sz; + + return 0; +} + +static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct da8xx_fb_par *par = info->par; + unsigned short *palette = (unsigned short *)par->v_palette_base; + u_short pal; + + if (regno > 255) + return 1; + + if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) + return 1; + + if (info->var.bits_per_pixel == 8) { + red >>= 4; + green >>= 8; + blue >>= 12; + + pal = (red & 0x0f00); + pal |= (green & 0x00f0); + pal |= (blue & 0x000f); + + palette[regno] = pal; + + } else if ((info->var.bits_per_pixel == 16) && regno < 16) { + red >>= (16 - info->var.red.length); + red <<= info->var.red.offset; + + green >>= (16 - info->var.green.length); + green <<= info->var.green.offset; + + blue >>= (16 - info->var.blue.length); + blue <<= info->var.blue.offset; + + par->pseudo_palette[regno] = red | green | blue; + + palette[0] = 0x4000; + } + + return 0; +} + +static int lcd_reset(struct da8xx_fb_par *par) +{ + int ret = 0; + + /* Disable the Raster if previously Enabled */ + if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE) + ret = lcd_disable_raster(par); + + /* DMA has to be disabled */ + lcdc_write(0, LCD_DMA_CTRL_REG); + lcdc_write(0, LCD_RASTER_CTRL_REG); + + return ret; +} + +static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, + struct da8xx_panel *panel) +{ + u32 bpp, ret = 0; + + ret = lcd_reset(par); + if (ret != 0) + return ret; + + /* Configure the LCD clock divisor. */ + lcdc_write(LCD_CLK_DIVISOR(panel->pxl_clk) | + (LCD_RASTER_MODE & 0x1), LCD_CTRL_REG); + + /* Configure the DMA burst size. */ + ret = lcd_cfg_dma(cfg->dma_burst_sz); + if (ret < 0) + return ret; + + /* Configure the AC bias properties. */ + lcd_cfg_ac_bias(cfg->ac_bias, cfg->ac_bias_intrpt); + + /* Configure the vertical and horizontal sync properties. */ + lcd_cfg_vertical_sync(panel->vbp, panel->vsw, panel->vfp); + lcd_cfg_horizontal_sync(panel->hbp, panel->hsw, panel->hfp); + + /* Configure for disply */ + ret = lcd_cfg_display(cfg); + if (ret < 0) + return ret; + + if (QVGA != cfg->p_disp_panel->panel_type) + return -EINVAL; + + if (cfg->bpp <= cfg->p_disp_panel->max_bpp && + cfg->bpp >= cfg->p_disp_panel->min_bpp) + bpp = cfg->bpp; + else + bpp = cfg->p_disp_panel->max_bpp; + if (bpp == 12) + bpp = 16; + ret = lcd_cfg_frame_buffer(par, (unsigned int)panel->width, + (unsigned int)panel->height, bpp, + cfg->raster_order); + if (ret < 0) + return ret; + + /* Configure FDD */ + lcdc_write((lcdc_read(LCD_RASTER_CTRL_REG) & 0xfff00fff) | + (cfg->fdd << 12), LCD_RASTER_CTRL_REG); + + return 0; +} + +static irqreturn_t lcdc_irq_handler(int irq, void *arg) +{ + u32 stat = lcdc_read(LCD_STAT_REG); + struct da8xx_fb_par *par = arg; + u32 reg; + + if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { + reg = lcdc_read(LCD_RASTER_CTRL_REG); + lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); + lcdc_write(stat, LCD_STAT_REG); + lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); + } else + lcdc_write(stat, LCD_STAT_REG); + + wake_up_interruptible(&par->da8xx_wq); + return IRQ_HANDLED; +} + +static int fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + int err = 0; + + switch (var->bits_per_pixel) { + case 1: + case 8: + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + break; + case 4: + var->red.offset = 0; + var->red.length = 4; + var->green.offset = 0; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + var->transp.offset = 0; + var->transp.length = 0; + break; + case 16: /* RGB 565 */ + var->red.offset = 0; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 11; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + break; + default: + err = -EINVAL; + } + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + var->transp.msb_right = 0; + return err; +} + +static int __devexit fb_remove(struct platform_device *dev) +{ + struct fb_info *info = dev_get_drvdata(&dev->dev); + int ret = 0; + + if (info) { + struct da8xx_fb_par *par = info->par; + + if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE) + ret = lcd_disable_raster(par); + lcdc_write(0, LCD_RASTER_CTRL_REG); + + /* disable DMA */ + lcdc_write(0, LCD_DMA_CTRL_REG); + + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + dma_free_coherent(NULL, par->databuf_sz + PAGE_SIZE, + info->screen_base, + info->fix.smem_start); + free_irq(par->irq, NULL); + clk_disable(par->lcdc_clk); + clk_put(par->lcdc_clk); + framebuffer_release(info); + + } + return ret; +} + +static int fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct lcd_sync_arg sync_arg; + + switch (cmd) { + case FBIOGET_CONTRAST: + case FBIOPUT_CONTRAST: + case FBIGET_BRIGHTNESS: + case FBIPUT_BRIGHTNESS: + case FBIGET_COLOR: + case FBIPUT_COLOR: + return -EINVAL; + case FBIPUT_HSYNC: + if (copy_from_user(&sync_arg, (char *)arg, + sizeof(struct lcd_sync_arg))) + return -EINVAL; + lcd_cfg_horizontal_sync(sync_arg.back_porch, + sync_arg.pulse_width, + sync_arg.front_porch); + break; + case FBIPUT_VSYNC: + if (copy_from_user(&sync_arg, (char *)arg, + sizeof(struct lcd_sync_arg))) + return -EINVAL; + lcd_cfg_vertical_sync(sync_arg.back_porch, + sync_arg.pulse_width, + sync_arg.front_porch); + break; + default: + return -EINVAL; + } + return 0; +} + +static struct fb_ops da8xx_fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = fb_check_var, + .fb_setcolreg = fb_setcolreg, + .fb_ioctl = fb_ioctl, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static int __init fb_probe(struct platform_device *device) +{ + struct da8xx_lcdc_platform_data *fb_pdata = + device->dev.platform_data; + struct lcd_ctrl_config *lcd_cfg; + struct da8xx_panel *lcdc_info; + struct fb_info *da8xx_fb_info; + struct resource *lcdc_regs; + struct clk *fb_clk = NULL; + struct da8xx_fb_par *par; + resource_size_t len; + int ret, i; + + if (fb_pdata == NULL) { + dev_err(&device->dev, "Can not get platform data\n"); + return -ENOENT; + } + + lcdc_regs = platform_get_resource(device, IORESOURCE_MEM, 0); + if (!lcdc_regs) { + dev_err(&device->dev, + "Can not get memory resource for LCD controller\n"); + return -ENOENT; + } + + len = lcdc_regs->end - lcdc_regs->start + 1; + + lcdc_regs = request_mem_region(lcdc_regs->start, len, lcdc_regs->name); + if (!lcdc_regs) + return -EBUSY; + + da8xx_fb_reg_base = (resource_size_t)ioremap(lcdc_regs->start, len); + if (!da8xx_fb_reg_base) { + ret = -EBUSY; + goto err_request_mem; + } + + fb_clk = clk_get(&device->dev, NULL); + if (IS_ERR(fb_clk)) { + dev_err(&device->dev, "Can not get device clock\n"); + ret = -ENODEV; + goto err_ioremap; + } + ret = clk_enable(fb_clk); + if (ret) + goto err_clk_put; + + for (i = 0, lcdc_info = known_lcd_panels; + i < ARRAY_SIZE(known_lcd_panels); + i++, lcdc_info++) { + if (strcmp(fb_pdata->type, lcdc_info->name) == 0) + break; + } + + if (i == ARRAY_SIZE(known_lcd_panels)) { + dev_err(&device->dev, "GLCD: No valid panel found\n"); + ret = ENODEV; + goto err_clk_disable; + } else + dev_info(&device->dev, "GLCD: Found %s panel\n", + fb_pdata->type); + + lcd_cfg = (struct lcd_ctrl_config *)fb_pdata->controller_data; + + da8xx_fb_info = framebuffer_alloc(sizeof(struct da8xx_fb_par), + &device->dev); + if (!da8xx_fb_info) { + dev_dbg(&device->dev, "Memory allocation failed for fb_info\n"); + ret = -ENOMEM; + goto err_clk_disable; + } + + par = da8xx_fb_info->par; + + if (lcd_init(par, lcd_cfg, lcdc_info) < 0) { + dev_err(&device->dev, "lcd_init failed\n"); + ret = -EFAULT; + goto err_release_fb; + } + + /* allocate frame buffer */ + da8xx_fb_info->screen_base = dma_alloc_coherent(NULL, + par->databuf_sz + PAGE_SIZE, + (resource_size_t *) + &da8xx_fb_info->fix.smem_start, + GFP_KERNEL | GFP_DMA); + + if (!da8xx_fb_info->screen_base) { + dev_err(&device->dev, + "GLCD: kmalloc for frame buffer failed\n"); + ret = -EINVAL; + goto err_release_fb; + } + + /* move palette base pointer by (PAGE_SIZE - palette_sz) bytes */ + par->v_palette_base = da8xx_fb_info->screen_base + + (PAGE_SIZE - par->palette_sz); + par->p_palette_base = da8xx_fb_info->fix.smem_start + + (PAGE_SIZE - par->palette_sz); + + /* the rest of the frame buffer is pixel data */ + da8xx_fb_info->screen_base = par->v_palette_base + par->palette_sz; + da8xx_fb_fix.smem_start = par->p_palette_base + par->palette_sz; + da8xx_fb_fix.smem_len = par->databuf_sz - par->palette_sz; + da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8; + + par->lcdc_clk = fb_clk; + + init_waitqueue_head(&par->da8xx_wq); + + par->irq = platform_get_irq(device, 0); + if (par->irq < 0) { + ret = -ENOENT; + goto err_release_fb_mem; + } + + ret = request_irq(par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par); + if (ret) + goto err_release_fb_mem; + + /* Initialize par */ + da8xx_fb_info->var.bits_per_pixel = lcd_cfg->bpp; + + da8xx_fb_var.xres = lcdc_info->width; + da8xx_fb_var.xres_virtual = lcdc_info->width; + + da8xx_fb_var.yres = lcdc_info->height; + da8xx_fb_var.yres_virtual = lcdc_info->height; + + da8xx_fb_var.grayscale = + lcd_cfg->p_disp_panel->panel_shade == MONOCHROME ? 1 : 0; + da8xx_fb_var.bits_per_pixel = lcd_cfg->bpp; + + da8xx_fb_var.hsync_len = lcdc_info->hsw; + da8xx_fb_var.vsync_len = lcdc_info->vsw; + + /* Initialize fbinfo */ + da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT; + da8xx_fb_info->fix = da8xx_fb_fix; + da8xx_fb_info->var = da8xx_fb_var; + da8xx_fb_info->fbops = &da8xx_fb_ops; + da8xx_fb_info->pseudo_palette = par->pseudo_palette; + + ret = fb_alloc_cmap(&da8xx_fb_info->cmap, PALETTE_SIZE, 0); + if (ret) + goto err_free_irq; + + /* First palette_sz byte of the frame buffer is the palette */ + da8xx_fb_info->cmap.len = par->palette_sz; + + /* Flush the buffer to the screen. */ + lcd_blit(LOAD_DATA, par); + + /* initialize var_screeninfo */ + da8xx_fb_var.activate = FB_ACTIVATE_FORCE; + fb_set_var(da8xx_fb_info, &da8xx_fb_var); + + dev_set_drvdata(&device->dev, da8xx_fb_info); + /* Register the Frame Buffer */ + if (register_framebuffer(da8xx_fb_info) < 0) { + dev_err(&device->dev, + "GLCD: Frame Buffer Registration Failed!\n"); + ret = -EINVAL; + goto err_dealloc_cmap; + } + + /* enable raster engine */ + lcdc_write(lcdc_read(LCD_RASTER_CTRL_REG) | + LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); + + return 0; + +err_dealloc_cmap: + fb_dealloc_cmap(&da8xx_fb_info->cmap); + +err_free_irq: + free_irq(par->irq, NULL); + +err_release_fb_mem: + dma_free_coherent(NULL, par->databuf_sz + PAGE_SIZE, + da8xx_fb_info->screen_base, + da8xx_fb_info->fix.smem_start); + +err_release_fb: + framebuffer_release(da8xx_fb_info); + +err_clk_disable: + clk_disable(fb_clk); + +err_clk_put: + clk_put(fb_clk); + +err_ioremap: + iounmap(da8xx_fb_reg_base); + +err_request_mem: + release_mem_region(lcdc_regs->start, len);; + + return ret; +} + +#ifdef CONFIG_PM +static int fb_suspend(struct platform_device *dev, pm_message_t state) +{ + return -EBUSY; +} +static int fb_resume(struct platform_device *dev) +{ + return -EBUSY; +} +#else +#define fb_suspend NULL +#define fb_resume NULL +#endif + +static struct platform_driver da8xx_fb_driver = { + .probe = fb_probe, + .remove = fb_remove, + .suspend = fb_suspend, + .resume = fb_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init da8xx_fb_init(void) +{ + return platform_driver_register(&da8xx_fb_driver); +} + +static void __exit da8xx_fb_cleanup(void) +{ + platform_driver_unregister(&da8xx_fb_driver); +} + +module_init(da8xx_fb_init); +module_exit(da8xx_fb_cleanup); + +MODULE_DESCRIPTION("Framebuffer driver for TI da8xx/omap-l1xx"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_LICENSE("GPL"); diff --git a/include/video/da8xx-fb.h b/include/video/da8xx-fb.h new file mode 100644 index 0000000..5f77675 --- /dev/null +++ b/include/video/da8xx-fb.h @@ -0,0 +1,106 @@ +/* + * Header file for TI DA8XX LCD controller platform data. + * + * Copyright (C) 2008-2009 MontaVista Software Inc. + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef DA8XX_FB_H +#define DA8XX_FB_H + +enum panel_type { + QVGA = 0 +}; + +enum panel_shade { + MONOCHROME = 0, + COLOR_ACTIVE, + COLOR_PASSIVE, +}; + +enum raster_load_mode { + LOAD_DATA = 1, + LOAD_PALETTE, +}; + +struct display_panel { + enum panel_type panel_type; /* QVGA */ + int max_bpp; + int min_bpp; + enum panel_shade panel_shade; +}; + +struct da8xx_lcdc_platform_data { + const char manu_name[10]; + void *controller_data; + const char type[25]; +}; + +struct lcd_ctrl_config { + const struct display_panel *p_disp_panel; + + /* AC Bias Pin Frequency */ + int ac_bias; + + /* AC Bias Pin Transitions per Interrupt */ + int ac_bias_intrpt; + + /* DMA burst size */ + int dma_burst_sz; + + /* Bits per pixel */ + int bpp; + + /* FIFO DMA Request Delay */ + int fdd; + + /* TFT Alternative Signal Mapping (Only for active) */ + unsigned char tft_alt_mode; + + /* 12 Bit Per Pixel (5-6-5) Mode (Only for passive) */ + unsigned char stn_565_mode; + + /* Mono 8-bit Mode: 1=D0-D7 or 0=D0-D3 */ + unsigned char mono_8bit_mode; + + /* Invert pixel clock */ + unsigned char invert_pxl_clock; + + /* Invert line clock */ + unsigned char invert_line_clock; + + /* Invert frame clock */ + unsigned char invert_frm_clock; + + /* Horizontal and Vertical Sync Edge: 0=rising 1=falling */ + unsigned char sync_edge; + + /* Horizontal and Vertical Sync: Control: 0=ignore */ + unsigned char sync_ctrl; + + /* Raster Data Order Select: 1=Most-to-least 0=Least-to-most */ + unsigned char raster_order; +}; + +struct lcd_sync_arg { + int back_porch; + int front_porch; + int pulse_width; +}; + +/* ioctls */ +#define FBIOGET_CONTRAST _IOR('F', 1, int) +#define FBIOPUT_CONTRAST _IOW('F', 2, int) +#define FBIGET_BRIGHTNESS _IOR('F', 3, int) +#define FBIPUT_BRIGHTNESS _IOW('F', 3, int) +#define FBIGET_COLOR _IOR('F', 5, int) +#define FBIPUT_COLOR _IOW('F', 6, int) +#define FBIPUT_HSYNC _IOW('F', 9, int) +#define FBIPUT_VSYNC _IOW('F', 10, int) + +#endif /* ifndef DA8XX_FB_H */ + -- 1.5.6 |
From: Andrey P. <pa...@ce...> - 2009-07-10 06:43:45
|
On 191, 07 10, 2009 at 01:19:34AM -0400, Sudhakar Rajashekhara wrote: > Adds LCD controller (LCDC) driver for TI's DA8xx/OMAP-L1xx > architecture. LCDC specifications can be found at > http://www.ti.com/litv/pdf/sprufm0a. > > LCDC on DA8xx consists of two independent controllers, the > Raster Controller and the LCD Interface Display Driver (LIDD) > controller. LIDD further supports character and graphic displays. > > This patch adds support for the graphic display (Sharp LQ035Q3DG01) > found on the DA830 based EVM. The EVM details can be found at: > http://support.spectrumdigital.com/boards/dskda830/revc/. > > Signed-off-by: Sudhakar Rajashekhara <sud...@ti...> > Signed-off-by: Pavel Kiryukhin <pki...@ru...> > Signed-off-by: Steve Chen <sc...@mv...> > Acked-by: Krzysztof Helt <krz...@wp...> > --- > This patch applies to Linus's Kernel tree. <SNIP> > +static int __init fb_probe(struct platform_device *device) > +{ > + struct da8xx_lcdc_platform_data *fb_pdata = > + device->dev.platform_data; > + struct lcd_ctrl_config *lcd_cfg; > + struct da8xx_panel *lcdc_info; > + struct fb_info *da8xx_fb_info; > + struct resource *lcdc_regs; > + struct clk *fb_clk = NULL; > + struct da8xx_fb_par *par; > + resource_size_t len; > + int ret, i; > + > + if (fb_pdata == NULL) { > + dev_err(&device->dev, "Can not get platform data\n"); > + return -ENOENT; > + } > + > + lcdc_regs = platform_get_resource(device, IORESOURCE_MEM, 0); > + if (!lcdc_regs) { > + dev_err(&device->dev, > + "Can not get memory resource for LCD controller\n"); > + return -ENOENT; > + } > + > + len = lcdc_regs->end - lcdc_regs->start + 1; > + > + lcdc_regs = request_mem_region(lcdc_regs->start, len, lcdc_regs->name); > + if (!lcdc_regs) > + return -EBUSY; > + > + da8xx_fb_reg_base = (resource_size_t)ioremap(lcdc_regs->start, len); 1. ioremap() can return NULL, so check is needed here; 2. ioremapped region is leaked in case of error and on module unload too. > + fb_clk = clk_get(&device->dev, NULL); > + if (IS_ERR(fb_clk)) { > + dev_err(&device->dev, "Can not get device clock\n"); > + ret = -ENODEV; > + goto err_request_mem; > + } > + ret = clk_enable(fb_clk); > + if (ret) > + goto err_clk_put; > + > + for (i = 0, lcdc_info = known_lcd_panels; > + i < ARRAY_SIZE(known_lcd_panels); > + i++, lcdc_info++) { > + if (strcmp(fb_pdata->type, lcdc_info->name) == 0) > + break; > + } > + > + if (i == ARRAY_SIZE(known_lcd_panels)) { > + dev_err(&device->dev, "GLCD: No valid panel found\n"); > + ret = ENODEV; > + goto err_clk_disable; > + } else > + dev_info(&device->dev, "GLCD: Found %s panel\n", > + fb_pdata->type); > + > + lcd_cfg = (struct lcd_ctrl_config *)fb_pdata->controller_data; > + > + da8xx_fb_info = framebuffer_alloc(sizeof(struct da8xx_fb_par), > + &device->dev); > + if (!da8xx_fb_info) { > + dev_dbg(&device->dev, "Memory allocation failed for fb_info\n"); > + ret = -ENOMEM; > + goto err_clk_disable; > + } > + > + par = da8xx_fb_info->par; > + > + if (lcd_init(par, lcd_cfg, lcdc_info) < 0) { > + dev_err(&device->dev, "lcd_init failed\n"); > + ret = -EFAULT; > + goto err_release_fb; > + } > + > + /* allocate frame buffer */ > + da8xx_fb_info->screen_base = dma_alloc_coherent(NULL, > + par->databuf_sz + PAGE_SIZE, > + (resource_size_t *) > + &da8xx_fb_info->fix.smem_start, > + GFP_KERNEL | GFP_DMA); > + > + if (!da8xx_fb_info->screen_base) { > + dev_err(&device->dev, > + "GLCD: kmalloc for frame buffer failed\n"); > + ret = -EINVAL; > + goto err_release_fb; > + } > + > + /* move palette base pointer by (PAGE_SIZE - palette_sz) bytes */ > + par->v_palette_base = da8xx_fb_info->screen_base + > + (PAGE_SIZE - par->palette_sz); > + par->p_palette_base = da8xx_fb_info->fix.smem_start + > + (PAGE_SIZE - par->palette_sz); > + > + /* the rest of the frame buffer is pixel data */ > + da8xx_fb_info->screen_base = par->v_palette_base + par->palette_sz; > + da8xx_fb_fix.smem_start = par->p_palette_base + par->palette_sz; > + da8xx_fb_fix.smem_len = par->databuf_sz - par->palette_sz; > + da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8; > + > + par->lcdc_clk = fb_clk; > + > + init_waitqueue_head(&par->da8xx_wq); > + > + par->irq = platform_get_irq(device, 0); > + if (par->irq < 0) { > + ret = -ENOENT; > + goto err_release_fb_mem; > + } > + > + ret = request_irq(par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par); > + if (ret) > + goto err_release_fb_mem; > + > + /* Initialize par */ > + da8xx_fb_info->var.bits_per_pixel = lcd_cfg->bpp; > + > + da8xx_fb_var.xres = lcdc_info->width; > + da8xx_fb_var.xres_virtual = lcdc_info->width; > + > + da8xx_fb_var.yres = lcdc_info->height; > + da8xx_fb_var.yres_virtual = lcdc_info->height; > + > + da8xx_fb_var.grayscale = > + lcd_cfg->p_disp_panel->panel_shade == MONOCHROME ? 1 : 0; > + da8xx_fb_var.bits_per_pixel = lcd_cfg->bpp; > + > + da8xx_fb_var.hsync_len = lcdc_info->hsw; > + da8xx_fb_var.vsync_len = lcdc_info->vsw; > + > + /* Initialize fbinfo */ > + da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT; > + da8xx_fb_info->fix = da8xx_fb_fix; > + da8xx_fb_info->var = da8xx_fb_var; > + da8xx_fb_info->fbops = &da8xx_fb_ops; > + da8xx_fb_info->pseudo_palette = par->pseudo_palette; > + > + ret = fb_alloc_cmap(&da8xx_fb_info->cmap, PALETTE_SIZE, 0); > + if (ret) > + goto err_free_irq; > + > + /* First palette_sz byte of the frame buffer is the palette */ > + da8xx_fb_info->cmap.len = par->palette_sz; > + > + /* Flush the buffer to the screen. */ > + lcd_blit(LOAD_DATA, par); > + > + /* initialize var_screeninfo */ > + da8xx_fb_var.activate = FB_ACTIVATE_FORCE; > + fb_set_var(da8xx_fb_info, &da8xx_fb_var); > + > + dev_set_drvdata(&device->dev, da8xx_fb_info); > + /* Register the Frame Buffer */ > + if (register_framebuffer(da8xx_fb_info) < 0) { > + dev_err(&device->dev, > + "GLCD: Frame Buffer Registration Failed!\n"); > + ret = -EINVAL; > + goto err_dealloc_cmap; > + } > + > + /* enable raster engine */ > + lcdc_write(lcdc_read(LCD_RASTER_CTRL_REG) | > + LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); > + > + return 0; > + > +err_dealloc_cmap: > + fb_dealloc_cmap(&da8xx_fb_info->cmap); > + > +err_free_irq: > + free_irq(par->irq, NULL); > + > +err_release_fb_mem: > + dma_free_coherent(NULL, par->databuf_sz + PAGE_SIZE, > + da8xx_fb_info->screen_base, > + da8xx_fb_info->fix.smem_start); > + > +err_release_fb: > + framebuffer_release(da8xx_fb_info); > + > +err_clk_disable: > + clk_disable(fb_clk); > + > +err_clk_put: > + clk_put(fb_clk); > + > +err_request_mem: > + release_mem_region(lcdc_regs->start, len);; > + > + return ret; > +} > + > +#ifdef CONFIG_PM > +static int fb_suspend(struct platform_device *dev, pm_message_t state) > +{ > + return -EBUSY; > +} > +static int fb_resume(struct platform_device *dev) > +{ > + return -EBUSY; > +} > +#else > +#define fb_suspend NULL > +#define fb_resume NULL > +#endif > + > +static struct platform_driver da8xx_fb_driver = { > + .probe = fb_probe, > + .remove = fb_remove, > + .suspend = fb_suspend, > + .resume = fb_resume, > + .driver = { > + .name = DRIVER_NAME, > + .owner = THIS_MODULE, > + }, > +}; > + > +static int __init da8xx_fb_init(void) > +{ > + return platform_driver_register(&da8xx_fb_driver); > +} > + > +static void __exit da8xx_fb_cleanup(void) > +{ > + platform_driver_unregister(&da8xx_fb_driver); > +} > + > +module_init(da8xx_fb_init); > +module_exit(da8xx_fb_cleanup); > + > +MODULE_DESCRIPTION("Framebuffer driver for TI da8xx/omap-l1xx"); > +MODULE_AUTHOR("Texas Instruments"); > +MODULE_LICENSE("GPL"); > diff --git a/include/video/da8xx-fb.h b/include/video/da8xx-fb.h > new file mode 100644 > index 0000000..5f77675 > --- /dev/null > +++ b/include/video/da8xx-fb.h > @@ -0,0 +1,106 @@ > +/* > + * Header file for TI DA8XX LCD controller platform data. > + * > + * Copyright (C) 2008-2009 MontaVista Software Inc. > + * Copyright (C) 2008-2009 Texas Instruments Inc > + * > + * This file is licensed under the terms of the GNU General Public License > + * version 2. This program is licensed "as is" without any warranty of any > + * kind, whether express or implied. > + */ > + > +#ifndef DA8XX_FB_H > +#define DA8XX_FB_H > + > +enum panel_type { > + QVGA = 0 > +}; > + > +enum panel_shade { > + MONOCHROME = 0, > + COLOR_ACTIVE, > + COLOR_PASSIVE, > +}; > + > +enum raster_load_mode { > + LOAD_DATA = 1, > + LOAD_PALETTE, > +}; > + > +struct display_panel { > + enum panel_type panel_type; /* QVGA */ > + int max_bpp; > + int min_bpp; > + enum panel_shade panel_shade; > +}; > + > +struct da8xx_lcdc_platform_data { > + const char manu_name[10]; > + void *controller_data; > + const char type[25]; > +}; > + > +struct lcd_ctrl_config { > + const struct display_panel *p_disp_panel; > + > + /* AC Bias Pin Frequency */ > + int ac_bias; > + > + /* AC Bias Pin Transitions per Interrupt */ > + int ac_bias_intrpt; > + > + /* DMA burst size */ > + int dma_burst_sz; > + > + /* Bits per pixel */ > + int bpp; > + > + /* FIFO DMA Request Delay */ > + int fdd; > + > + /* TFT Alternative Signal Mapping (Only for active) */ > + unsigned char tft_alt_mode; > + > + /* 12 Bit Per Pixel (5-6-5) Mode (Only for passive) */ > + unsigned char stn_565_mode; > + > + /* Mono 8-bit Mode: 1=D0-D7 or 0=D0-D3 */ > + unsigned char mono_8bit_mode; > + > + /* Invert pixel clock */ > + unsigned char invert_pxl_clock; > + > + /* Invert line clock */ > + unsigned char invert_line_clock; > + > + /* Invert frame clock */ > + unsigned char invert_frm_clock; > + > + /* Horizontal and Vertical Sync Edge: 0=rising 1=falling */ > + unsigned char sync_edge; > + > + /* Horizontal and Vertical Sync: Control: 0=ignore */ > + unsigned char sync_ctrl; > + > + /* Raster Data Order Select: 1=Most-to-least 0=Least-to-most */ > + unsigned char raster_order; > +}; > + > +struct lcd_sync_arg { > + int back_porch; > + int front_porch; > + int pulse_width; > +}; > + > +/* ioctls */ > +#define FBIOGET_CONTRAST _IOR('F', 1, int) > +#define FBIOPUT_CONTRAST _IOW('F', 2, int) > +#define FBIGET_BRIGHTNESS _IOR('F', 3, int) > +#define FBIPUT_BRIGHTNESS _IOW('F', 3, int) > +#define FBIGET_COLOR _IOR('F', 5, int) > +#define FBIPUT_COLOR _IOW('F', 6, int) > +#define FBIPUT_HSYNC _IOW('F', 9, int) > +#define FBIPUT_VSYNC _IOW('F', 10, int) > + > +#endif /* ifndef DA8XX_FB_H */ > + > -- > 1.5.6 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to maj...@vg... > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ > |
From: Sudhakar R. <sud...@ti...> - 2009-07-10 06:19:56
|
Adds LCD controller (LCDC) driver for TI's DA8xx/OMAP-L1xx architecture. LCDC specifications can be found at http://www.ti.com/litv/pdf/sprufm0a. LCDC on DA8xx consists of two independent controllers, the Raster Controller and the LCD Interface Display Driver (LIDD) controller. LIDD further supports character and graphic displays. This patch adds support for the graphic display (Sharp LQ035Q3DG01) found on the DA830 based EVM. The EVM details can be found at: http://support.spectrumdigital.com/boards/dskda830/revc/. Signed-off-by: Sudhakar Rajashekhara <sud...@ti...> Signed-off-by: Pavel Kiryukhin <pki...@ru...> Signed-off-by: Steve Chen <sc...@mv...> Acked-by: Krzysztof Helt <krz...@wp...> --- This patch applies to Linus's Kernel tree. drivers/video/Kconfig | 11 + drivers/video/Makefile | 1 + drivers/video/da8xx-fb.c | 900 ++++++++++++++++++++++++++++++++++++++++++++++ include/video/da8xx-fb.h | 106 ++++++ 4 files changed, 1018 insertions(+), 0 deletions(-) create mode 100644 drivers/video/da8xx-fb.c create mode 100644 include/video/da8xx-fb.h diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 8afcf08..d048b7e 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2038,6 +2038,17 @@ config FB_SH7760 and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for panels <= 320 pixel horizontal resolution. +config FB_DA8XX + tristate "DA8xx/OMAP-L1xx Framebuffer support" + depends on FB && ARCH_DAVINCI_DA830 + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + This is the frame buffer device driver for the TI LCD controller + found on DA8xx/OMAP-L1xx SoCs. + If unsure, say N. + config FB_VIRTUAL tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" depends on FB diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 01a819f..288d9b0 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -136,6 +136,7 @@ obj-$(CONFIG_FB_OF) += offb.o obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o obj-$(CONFIG_FB_MX3) += mx3fb.o +obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c new file mode 100644 index 0000000..4b9d80b --- /dev/null +++ b/drivers/video/da8xx-fb.c @@ -0,0 +1,900 @@ +/* + * Copyright (C) 2008-2009 MontaVista Software Inc. + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * Based on the LCD driver for TI Avalanche processors written by + * Ajay Singh and Shalom Hai. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option)any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/fb.h> +#include <linux/dma-mapping.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <video/da8xx-fb.h> + +#define DRIVER_NAME "da8xx_lcdc" + +/* LCD Status Register */ +#define LCD_END_OF_FRAME0 BIT(8) +#define LCD_FIFO_UNDERFLOW BIT(5) +#define LCD_SYNC_LOST BIT(2) + +/* LCD DMA Control Register */ +#define LCD_DMA_BURST_SIZE(x) ((x) << 4) +#define LCD_DMA_BURST_1 0x0 +#define LCD_DMA_BURST_2 0x1 +#define LCD_DMA_BURST_4 0x2 +#define LCD_DMA_BURST_8 0x3 +#define LCD_DMA_BURST_16 0x4 +#define LCD_END_OF_FRAME_INT_ENA BIT(2) +#define LCD_DUAL_FRAME_BUFFER_ENABLE BIT(0) + +/* LCD Control Register */ +#define LCD_CLK_DIVISOR(x) ((x) << 8) +#define LCD_RASTER_MODE 0x01 + +/* LCD Raster Control Register */ +#define LCD_PALETTE_LOAD_MODE(x) ((x) << 20) +#define PALETTE_AND_DATA 0x00 +#define PALETTE_ONLY 0x01 + +#define LCD_MONO_8BIT_MODE BIT(9) +#define LCD_RASTER_ORDER BIT(8) +#define LCD_TFT_MODE BIT(7) +#define LCD_UNDERFLOW_INT_ENA BIT(6) +#define LCD_MONOCHROME_MODE BIT(1) +#define LCD_RASTER_ENABLE BIT(0) +#define LCD_TFT_ALT_ENABLE BIT(23) +#define LCD_STN_565_ENABLE BIT(24) + +/* LCD Raster Timing 2 Register */ +#define LCD_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16) +#define LCD_AC_BIAS_FREQUENCY(x) ((x) << 8) +#define LCD_SYNC_CTRL BIT(25) +#define LCD_SYNC_EDGE BIT(24) +#define LCD_INVERT_PIXEL_CLOCK BIT(22) +#define LCD_INVERT_LINE_CLOCK BIT(21) +#define LCD_INVERT_FRAME_CLOCK BIT(20) + +/* LCD Block */ +#define LCD_CTRL_REG 0x4 +#define LCD_STAT_REG 0x8 +#define LCD_RASTER_CTRL_REG 0x28 +#define LCD_RASTER_TIMING_0_REG 0x2C +#define LCD_RASTER_TIMING_1_REG 0x30 +#define LCD_RASTER_TIMING_2_REG 0x34 +#define LCD_DMA_CTRL_REG 0x40 +#define LCD_DMA_FRM_BUF_BASE_ADDR_0_REG 0x44 +#define LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG 0x48 + +#define WSI_TIMEOUT 50 +#define PALETTE_SIZE 256 +#define LEFT_MARGIN 64 +#define RIGHT_MARGIN 64 +#define UPPER_MARGIN 32 +#define LOWER_MARGIN 32 + +static resource_size_t da8xx_fb_reg_base; + +static inline unsigned int lcdc_read(unsigned int addr) +{ + return (unsigned int)__raw_readl(da8xx_fb_reg_base + (addr)); +} + +static inline void lcdc_write(unsigned int val, unsigned int addr) +{ + __raw_writel(val, da8xx_fb_reg_base + (addr)); +} + +struct da8xx_fb_par { + wait_queue_head_t da8xx_wq; + resource_size_t p_palette_base; + unsigned char *v_palette_base; + struct clk *lcdc_clk; + unsigned int irq; + unsigned short pseudo_palette[16]; + unsigned int databuf_sz; + unsigned int palette_sz; +}; + +/* Variable Screen Information */ +static struct fb_var_screeninfo da8xx_fb_var __devinitdata = { + .xoffset = 0, + .yoffset = 0, + .transp = {0, 0, 0}, + .nonstd = 0, + .activate = 0, + .height = -1, + .width = -1, + .pixclock = 46666, /* 46us - AUO display */ + .accel_flags = 0, + .left_margin = LEFT_MARGIN, + .right_margin = RIGHT_MARGIN, + .upper_margin = UPPER_MARGIN, + .lower_margin = LOWER_MARGIN, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED +}; + +static struct fb_fix_screeninfo da8xx_fb_fix __devinitdata = { + .id = "DA8xx FB Drv", + .type = FB_TYPE_PACKED_PIXELS, + .type_aux = 0, + .visual = FB_VISUAL_PSEUDOCOLOR, + .xpanstep = 1, + .ypanstep = 1, + .ywrapstep = 1, + .accel = FB_ACCEL_NONE +}; + +struct da8xx_panel { + const char name[25]; /* Full name <vendor>_<model> */ + unsigned short width; + unsigned short height; + int hfp; /* Horizontal front porch */ + int hbp; /* Horizontal back porch */ + int hsw; /* Horizontal Sync Pulse Width */ + int vfp; /* Vertical front porch */ + int vbp; /* Vertical back porch */ + int vsw; /* Vertical Sync Pulse Width */ + int pxl_clk; /* Pixel clock */ +}; + +static struct da8xx_panel known_lcd_panels[] = { + /* Sharp LCD035Q3DG01 */ + [0] = { + .name = "Sharp_LCD035Q3DG01", + .width = 320, + .height = 240, + .hfp = 8, + .hbp = 6, + .hsw = 0, + .vfp = 2, + .vbp = 2, + .vsw = 0, + .pxl_clk = 0x10, + }, + /* Sharp LK043T1DG01 */ + [1] = { + .name = "Sharp_LK043T1DG01", + .width = 480, + .height = 272, + .hfp = 2, + .hbp = 2, + .hsw = 41, + .vfp = 2, + .vbp = 2, + .vsw = 10, + .pxl_clk = 0x12, + }, +}; + +/* Disable the Raster Engine of the LCD Controller */ +static int lcd_disable_raster(struct da8xx_fb_par *par) +{ + int ret = 0; + u32 reg; + + reg = lcdc_read(LCD_RASTER_CTRL_REG); + if (reg & LCD_RASTER_ENABLE) { + lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); + ret = wait_event_interruptible_timeout(par->da8xx_wq, + !lcdc_read(LCD_STAT_REG) & + LCD_END_OF_FRAME0, WSI_TIMEOUT); + } + + if (ret < 0) + return ret; + if (ret == 0) + return -ETIMEDOUT; + + return 0; +} + +static void lcd_blit(int load_mode, struct da8xx_fb_par *par) +{ + u32 tmp = par->p_palette_base + par->databuf_sz - 4; + u32 reg; + + /* Update the databuf in the hw. */ + lcdc_write(par->p_palette_base, LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); + lcdc_write(tmp, LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG); + + /* Start the DMA. */ + reg = lcdc_read(LCD_RASTER_CTRL_REG); + reg &= ~(3 << 20); + if (load_mode == LOAD_DATA) + reg |= LCD_PALETTE_LOAD_MODE(PALETTE_AND_DATA); + else if (load_mode == LOAD_PALETTE) + reg |= LCD_PALETTE_LOAD_MODE(PALETTE_ONLY); + + lcdc_write(reg, LCD_RASTER_CTRL_REG); +} + +/* Configure the Burst Size of DMA */ +static int lcd_cfg_dma(int burst_size) +{ + u32 reg; + + reg = lcdc_read(LCD_DMA_CTRL_REG) & 0x00000001; + switch (burst_size) { + case 1: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_1); + break; + case 2: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_2); + break; + case 4: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_4); + break; + case 8: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_8); + break; + case 16: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_16); + break; + default: + return -EINVAL; + } + lcdc_write(reg | LCD_END_OF_FRAME_INT_ENA, LCD_DMA_CTRL_REG); + + return 0; +} + +static void lcd_cfg_ac_bias(int period, int transitions_per_int) +{ + u32 reg; + + /* Set the AC Bias Period and Number of Transisitons per Interrupt */ + reg = lcdc_read(LCD_RASTER_TIMING_2_REG) & 0xFFF00000; + reg |= LCD_AC_BIAS_FREQUENCY(period) | + LCD_AC_BIAS_TRANSITIONS_PER_INT(transitions_per_int); + lcdc_write(reg, LCD_RASTER_TIMING_2_REG); +} + +static void lcd_cfg_horizontal_sync(int back_porch, int pulse_width, + int front_porch) +{ + u32 reg; + + reg = lcdc_read(LCD_RASTER_TIMING_0_REG) & 0xf; + reg |= ((back_porch & 0xff) << 24) + | ((front_porch & 0xff) << 16) + | ((pulse_width & 0x3f) << 10); + lcdc_write(reg, LCD_RASTER_TIMING_0_REG); +} + +static void lcd_cfg_vertical_sync(int back_porch, int pulse_width, + int front_porch) +{ + u32 reg; + + reg = lcdc_read(LCD_RASTER_TIMING_1_REG) & 0x3ff; + reg |= ((back_porch & 0xff) << 24) + | ((front_porch & 0xff) << 16) + | ((pulse_width & 0x3f) << 10); + lcdc_write(reg, LCD_RASTER_TIMING_1_REG); +} + +static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) +{ + u32 reg; + + reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(LCD_TFT_MODE | + LCD_MONO_8BIT_MODE | + LCD_MONOCHROME_MODE); + + switch (cfg->p_disp_panel->panel_shade) { + case MONOCHROME: + reg |= LCD_MONOCHROME_MODE; + if (cfg->mono_8bit_mode) + reg |= LCD_MONO_8BIT_MODE; + break; + case COLOR_ACTIVE: + reg |= LCD_TFT_MODE; + if (cfg->tft_alt_mode) + reg |= LCD_TFT_ALT_ENABLE; + break; + + case COLOR_PASSIVE: + if (cfg->stn_565_mode) + reg |= LCD_STN_565_ENABLE; + break; + + default: + return -EINVAL; + } + + /* enable additional interrupts here */ + reg |= LCD_UNDERFLOW_INT_ENA; + + lcdc_write(reg, LCD_RASTER_CTRL_REG); + + reg = lcdc_read(LCD_RASTER_TIMING_2_REG); + + if (cfg->sync_ctrl) + reg |= LCD_SYNC_CTRL; + else + reg &= ~LCD_SYNC_CTRL; + + if (cfg->sync_edge) + reg |= LCD_SYNC_EDGE; + else + reg &= ~LCD_SYNC_EDGE; + + if (cfg->invert_pxl_clock) + reg |= LCD_INVERT_PIXEL_CLOCK; + else + reg &= ~LCD_INVERT_PIXEL_CLOCK; + + if (cfg->invert_line_clock) + reg |= LCD_INVERT_LINE_CLOCK; + else + reg &= ~LCD_INVERT_LINE_CLOCK; + + if (cfg->invert_frm_clock) + reg |= LCD_INVERT_FRAME_CLOCK; + else + reg &= ~LCD_INVERT_FRAME_CLOCK; + + lcdc_write(reg, LCD_RASTER_TIMING_2_REG); + + return 0; +} + +static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, + u32 bpp, u32 raster_order) +{ + u32 bpl, reg; + + /* Disable Dual Frame Buffer. */ + reg = lcdc_read(LCD_DMA_CTRL_REG); + lcdc_write(reg & ~LCD_DUAL_FRAME_BUFFER_ENABLE, + LCD_DMA_CTRL_REG); + /* Set the Panel Width */ + /* Pixels per line = (PPL + 1)*16 */ + /*0x3F in bits 4..9 gives max horisontal resolution = 1024 pixels*/ + width &= 0x3f0; + reg = lcdc_read(LCD_RASTER_TIMING_0_REG); + reg &= 0xfffffc00; + reg |= ((width >> 4) - 1) << 4; + lcdc_write(reg, LCD_RASTER_TIMING_0_REG); + + /* Set the Panel Height */ + reg = lcdc_read(LCD_RASTER_TIMING_1_REG); + reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00); + lcdc_write(reg, LCD_RASTER_TIMING_1_REG); + + /* Set the Raster Order of the Frame Buffer */ + reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(1 << 8); + if (raster_order) + reg |= LCD_RASTER_ORDER; + lcdc_write(reg, LCD_RASTER_CTRL_REG); + + switch (bpp) { + case 1: + case 2: + case 4: + case 16: + par->palette_sz = 16 * 2; + break; + + case 8: + par->palette_sz = 256 * 2; + break; + + default: + return -EINVAL; + } + + bpl = width * bpp / 8; + par->databuf_sz = height * bpl + par->palette_sz; + + return 0; +} + +static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct da8xx_fb_par *par = info->par; + unsigned short *palette = (unsigned short *)par->v_palette_base; + u_short pal; + + if (regno > 255) + return 1; + + if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) + return 1; + + if (info->var.bits_per_pixel == 8) { + red >>= 4; + green >>= 8; + blue >>= 12; + + pal = (red & 0x0f00); + pal |= (green & 0x00f0); + pal |= (blue & 0x000f); + + palette[regno] = pal; + + } else if ((info->var.bits_per_pixel == 16) && regno < 16) { + red >>= (16 - info->var.red.length); + red <<= info->var.red.offset; + + green >>= (16 - info->var.green.length); + green <<= info->var.green.offset; + + blue >>= (16 - info->var.blue.length); + blue <<= info->var.blue.offset; + + par->pseudo_palette[regno] = red | green | blue; + + palette[0] = 0x4000; + } + + return 0; +} + +static int lcd_reset(struct da8xx_fb_par *par) +{ + int ret = 0; + + /* Disable the Raster if previously Enabled */ + if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE) + ret = lcd_disable_raster(par); + + /* DMA has to be disabled */ + lcdc_write(0, LCD_DMA_CTRL_REG); + lcdc_write(0, LCD_RASTER_CTRL_REG); + + return ret; +} + +static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, + struct da8xx_panel *panel) +{ + u32 bpp, ret = 0; + + ret = lcd_reset(par); + if (ret != 0) + return ret; + + /* Configure the LCD clock divisor. */ + lcdc_write(LCD_CLK_DIVISOR(panel->pxl_clk) | + (LCD_RASTER_MODE & 0x1), LCD_CTRL_REG); + + /* Configure the DMA burst size. */ + ret = lcd_cfg_dma(cfg->dma_burst_sz); + if (ret < 0) + return ret; + + /* Configure the AC bias properties. */ + lcd_cfg_ac_bias(cfg->ac_bias, cfg->ac_bias_intrpt); + + /* Configure the vertical and horizontal sync properties. */ + lcd_cfg_vertical_sync(panel->vbp, panel->vsw, panel->vfp); + lcd_cfg_horizontal_sync(panel->hbp, panel->hsw, panel->hfp); + + /* Configure for disply */ + ret = lcd_cfg_display(cfg); + if (ret < 0) + return ret; + + if (QVGA != cfg->p_disp_panel->panel_type) + return -EINVAL; + + if (cfg->bpp <= cfg->p_disp_panel->max_bpp && + cfg->bpp >= cfg->p_disp_panel->min_bpp) + bpp = cfg->bpp; + else + bpp = cfg->p_disp_panel->max_bpp; + if (bpp == 12) + bpp = 16; + ret = lcd_cfg_frame_buffer(par, (unsigned int)panel->width, + (unsigned int)panel->height, bpp, + cfg->raster_order); + if (ret < 0) + return ret; + + /* Configure FDD */ + lcdc_write((lcdc_read(LCD_RASTER_CTRL_REG) & 0xfff00fff) | + (cfg->fdd << 12), LCD_RASTER_CTRL_REG); + + return 0; +} + +static irqreturn_t lcdc_irq_handler(int irq, void *arg) +{ + u32 stat = lcdc_read(LCD_STAT_REG); + struct da8xx_fb_par *par = arg; + u32 reg; + + if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { + reg = lcdc_read(LCD_RASTER_CTRL_REG); + lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); + lcdc_write(stat, LCD_STAT_REG); + lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); + } else + lcdc_write(stat, LCD_STAT_REG); + + wake_up_interruptible(&par->da8xx_wq); + return IRQ_HANDLED; +} + +static int fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + int err = 0; + + switch (var->bits_per_pixel) { + case 1: + case 8: + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + break; + case 4: + var->red.offset = 0; + var->red.length = 4; + var->green.offset = 0; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + var->transp.offset = 0; + var->transp.length = 0; + break; + case 16: /* RGB 565 */ + var->red.offset = 0; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 11; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + break; + default: + err = -EINVAL; + } + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + var->transp.msb_right = 0; + return err; +} + +static int __devexit fb_remove(struct platform_device *dev) +{ + struct fb_info *info = dev_get_drvdata(&dev->dev); + int ret = 0; + + if (info) { + struct da8xx_fb_par *par = info->par; + + if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE) + ret = lcd_disable_raster(par); + lcdc_write(0, LCD_RASTER_CTRL_REG); + + /* disable DMA */ + lcdc_write(0, LCD_DMA_CTRL_REG); + + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + dma_free_coherent(NULL, par->databuf_sz + PAGE_SIZE, + info->screen_base, + info->fix.smem_start); + free_irq(par->irq, NULL); + clk_disable(par->lcdc_clk); + clk_put(par->lcdc_clk); + framebuffer_release(info); + + } + return ret; +} + +static int fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct lcd_sync_arg sync_arg; + + switch (cmd) { + case FBIOGET_CONTRAST: + case FBIOPUT_CONTRAST: + case FBIGET_BRIGHTNESS: + case FBIPUT_BRIGHTNESS: + case FBIGET_COLOR: + case FBIPUT_COLOR: + return -EINVAL; + case FBIPUT_HSYNC: + if (copy_from_user(&sync_arg, (char *)arg, + sizeof(struct lcd_sync_arg))) + return -EINVAL; + lcd_cfg_horizontal_sync(sync_arg.back_porch, + sync_arg.pulse_width, + sync_arg.front_porch); + break; + case FBIPUT_VSYNC: + if (copy_from_user(&sync_arg, (char *)arg, + sizeof(struct lcd_sync_arg))) + return -EINVAL; + lcd_cfg_vertical_sync(sync_arg.back_porch, + sync_arg.pulse_width, + sync_arg.front_porch); + break; + default: + return -EINVAL; + } + return 0; +} + +static struct fb_ops da8xx_fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = fb_check_var, + .fb_setcolreg = fb_setcolreg, + .fb_ioctl = fb_ioctl, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static int __init fb_probe(struct platform_device *device) +{ + struct da8xx_lcdc_platform_data *fb_pdata = + device->dev.platform_data; + struct lcd_ctrl_config *lcd_cfg; + struct da8xx_panel *lcdc_info; + struct fb_info *da8xx_fb_info; + struct resource *lcdc_regs; + struct clk *fb_clk = NULL; + struct da8xx_fb_par *par; + resource_size_t len; + int ret, i; + + if (fb_pdata == NULL) { + dev_err(&device->dev, "Can not get platform data\n"); + return -ENOENT; + } + + lcdc_regs = platform_get_resource(device, IORESOURCE_MEM, 0); + if (!lcdc_regs) { + dev_err(&device->dev, + "Can not get memory resource for LCD controller\n"); + return -ENOENT; + } + + len = lcdc_regs->end - lcdc_regs->start + 1; + + lcdc_regs = request_mem_region(lcdc_regs->start, len, lcdc_regs->name); + if (!lcdc_regs) + return -EBUSY; + + da8xx_fb_reg_base = (resource_size_t)ioremap(lcdc_regs->start, len); + + fb_clk = clk_get(&device->dev, NULL); + if (IS_ERR(fb_clk)) { + dev_err(&device->dev, "Can not get device clock\n"); + ret = -ENODEV; + goto err_request_mem; + } + ret = clk_enable(fb_clk); + if (ret) + goto err_clk_put; + + for (i = 0, lcdc_info = known_lcd_panels; + i < ARRAY_SIZE(known_lcd_panels); + i++, lcdc_info++) { + if (strcmp(fb_pdata->type, lcdc_info->name) == 0) + break; + } + + if (i == ARRAY_SIZE(known_lcd_panels)) { + dev_err(&device->dev, "GLCD: No valid panel found\n"); + ret = ENODEV; + goto err_clk_disable; + } else + dev_info(&device->dev, "GLCD: Found %s panel\n", + fb_pdata->type); + + lcd_cfg = (struct lcd_ctrl_config *)fb_pdata->controller_data; + + da8xx_fb_info = framebuffer_alloc(sizeof(struct da8xx_fb_par), + &device->dev); + if (!da8xx_fb_info) { + dev_dbg(&device->dev, "Memory allocation failed for fb_info\n"); + ret = -ENOMEM; + goto err_clk_disable; + } + + par = da8xx_fb_info->par; + + if (lcd_init(par, lcd_cfg, lcdc_info) < 0) { + dev_err(&device->dev, "lcd_init failed\n"); + ret = -EFAULT; + goto err_release_fb; + } + + /* allocate frame buffer */ + da8xx_fb_info->screen_base = dma_alloc_coherent(NULL, + par->databuf_sz + PAGE_SIZE, + (resource_size_t *) + &da8xx_fb_info->fix.smem_start, + GFP_KERNEL | GFP_DMA); + + if (!da8xx_fb_info->screen_base) { + dev_err(&device->dev, + "GLCD: kmalloc for frame buffer failed\n"); + ret = -EINVAL; + goto err_release_fb; + } + + /* move palette base pointer by (PAGE_SIZE - palette_sz) bytes */ + par->v_palette_base = da8xx_fb_info->screen_base + + (PAGE_SIZE - par->palette_sz); + par->p_palette_base = da8xx_fb_info->fix.smem_start + + (PAGE_SIZE - par->palette_sz); + + /* the rest of the frame buffer is pixel data */ + da8xx_fb_info->screen_base = par->v_palette_base + par->palette_sz; + da8xx_fb_fix.smem_start = par->p_palette_base + par->palette_sz; + da8xx_fb_fix.smem_len = par->databuf_sz - par->palette_sz; + da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8; + + par->lcdc_clk = fb_clk; + + init_waitqueue_head(&par->da8xx_wq); + + par->irq = platform_get_irq(device, 0); + if (par->irq < 0) { + ret = -ENOENT; + goto err_release_fb_mem; + } + + ret = request_irq(par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par); + if (ret) + goto err_release_fb_mem; + + /* Initialize par */ + da8xx_fb_info->var.bits_per_pixel = lcd_cfg->bpp; + + da8xx_fb_var.xres = lcdc_info->width; + da8xx_fb_var.xres_virtual = lcdc_info->width; + + da8xx_fb_var.yres = lcdc_info->height; + da8xx_fb_var.yres_virtual = lcdc_info->height; + + da8xx_fb_var.grayscale = + lcd_cfg->p_disp_panel->panel_shade == MONOCHROME ? 1 : 0; + da8xx_fb_var.bits_per_pixel = lcd_cfg->bpp; + + da8xx_fb_var.hsync_len = lcdc_info->hsw; + da8xx_fb_var.vsync_len = lcdc_info->vsw; + + /* Initialize fbinfo */ + da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT; + da8xx_fb_info->fix = da8xx_fb_fix; + da8xx_fb_info->var = da8xx_fb_var; + da8xx_fb_info->fbops = &da8xx_fb_ops; + da8xx_fb_info->pseudo_palette = par->pseudo_palette; + + ret = fb_alloc_cmap(&da8xx_fb_info->cmap, PALETTE_SIZE, 0); + if (ret) + goto err_free_irq; + + /* First palette_sz byte of the frame buffer is the palette */ + da8xx_fb_info->cmap.len = par->palette_sz; + + /* Flush the buffer to the screen. */ + lcd_blit(LOAD_DATA, par); + + /* initialize var_screeninfo */ + da8xx_fb_var.activate = FB_ACTIVATE_FORCE; + fb_set_var(da8xx_fb_info, &da8xx_fb_var); + + dev_set_drvdata(&device->dev, da8xx_fb_info); + /* Register the Frame Buffer */ + if (register_framebuffer(da8xx_fb_info) < 0) { + dev_err(&device->dev, + "GLCD: Frame Buffer Registration Failed!\n"); + ret = -EINVAL; + goto err_dealloc_cmap; + } + + /* enable raster engine */ + lcdc_write(lcdc_read(LCD_RASTER_CTRL_REG) | + LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); + + return 0; + +err_dealloc_cmap: + fb_dealloc_cmap(&da8xx_fb_info->cmap); + +err_free_irq: + free_irq(par->irq, NULL); + +err_release_fb_mem: + dma_free_coherent(NULL, par->databuf_sz + PAGE_SIZE, + da8xx_fb_info->screen_base, + da8xx_fb_info->fix.smem_start); + +err_release_fb: + framebuffer_release(da8xx_fb_info); + +err_clk_disable: + clk_disable(fb_clk); + +err_clk_put: + clk_put(fb_clk); + +err_request_mem: + release_mem_region(lcdc_regs->start, len);; + + return ret; +} + +#ifdef CONFIG_PM +static int fb_suspend(struct platform_device *dev, pm_message_t state) +{ + return -EBUSY; +} +static int fb_resume(struct platform_device *dev) +{ + return -EBUSY; +} +#else +#define fb_suspend NULL +#define fb_resume NULL +#endif + +static struct platform_driver da8xx_fb_driver = { + .probe = fb_probe, + .remove = fb_remove, + .suspend = fb_suspend, + .resume = fb_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init da8xx_fb_init(void) +{ + return platform_driver_register(&da8xx_fb_driver); +} + +static void __exit da8xx_fb_cleanup(void) +{ + platform_driver_unregister(&da8xx_fb_driver); +} + +module_init(da8xx_fb_init); +module_exit(da8xx_fb_cleanup); + +MODULE_DESCRIPTION("Framebuffer driver for TI da8xx/omap-l1xx"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_LICENSE("GPL"); diff --git a/include/video/da8xx-fb.h b/include/video/da8xx-fb.h new file mode 100644 index 0000000..5f77675 --- /dev/null +++ b/include/video/da8xx-fb.h @@ -0,0 +1,106 @@ +/* + * Header file for TI DA8XX LCD controller platform data. + * + * Copyright (C) 2008-2009 MontaVista Software Inc. + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef DA8XX_FB_H +#define DA8XX_FB_H + +enum panel_type { + QVGA = 0 +}; + +enum panel_shade { + MONOCHROME = 0, + COLOR_ACTIVE, + COLOR_PASSIVE, +}; + +enum raster_load_mode { + LOAD_DATA = 1, + LOAD_PALETTE, +}; + +struct display_panel { + enum panel_type panel_type; /* QVGA */ + int max_bpp; + int min_bpp; + enum panel_shade panel_shade; +}; + +struct da8xx_lcdc_platform_data { + const char manu_name[10]; + void *controller_data; + const char type[25]; +}; + +struct lcd_ctrl_config { + const struct display_panel *p_disp_panel; + + /* AC Bias Pin Frequency */ + int ac_bias; + + /* AC Bias Pin Transitions per Interrupt */ + int ac_bias_intrpt; + + /* DMA burst size */ + int dma_burst_sz; + + /* Bits per pixel */ + int bpp; + + /* FIFO DMA Request Delay */ + int fdd; + + /* TFT Alternative Signal Mapping (Only for active) */ + unsigned char tft_alt_mode; + + /* 12 Bit Per Pixel (5-6-5) Mode (Only for passive) */ + unsigned char stn_565_mode; + + /* Mono 8-bit Mode: 1=D0-D7 or 0=D0-D3 */ + unsigned char mono_8bit_mode; + + /* Invert pixel clock */ + unsigned char invert_pxl_clock; + + /* Invert line clock */ + unsigned char invert_line_clock; + + /* Invert frame clock */ + unsigned char invert_frm_clock; + + /* Horizontal and Vertical Sync Edge: 0=rising 1=falling */ + unsigned char sync_edge; + + /* Horizontal and Vertical Sync: Control: 0=ignore */ + unsigned char sync_ctrl; + + /* Raster Data Order Select: 1=Most-to-least 0=Least-to-most */ + unsigned char raster_order; +}; + +struct lcd_sync_arg { + int back_porch; + int front_porch; + int pulse_width; +}; + +/* ioctls */ +#define FBIOGET_CONTRAST _IOR('F', 1, int) +#define FBIOPUT_CONTRAST _IOW('F', 2, int) +#define FBIGET_BRIGHTNESS _IOR('F', 3, int) +#define FBIPUT_BRIGHTNESS _IOW('F', 3, int) +#define FBIGET_COLOR _IOR('F', 5, int) +#define FBIPUT_COLOR _IOW('F', 6, int) +#define FBIPUT_HSYNC _IOW('F', 9, int) +#define FBIPUT_VSYNC _IOW('F', 10, int) + +#endif /* ifndef DA8XX_FB_H */ + -- 1.5.6 |
From: Russell K. - A. L. <li...@ar...> - 2009-07-09 18:19:55
|
On Thu, Jul 09, 2009 at 07:49:12PM +0200, Krzysztof Helt wrote: > On Thu, 09 Jul 2009 17:09:00 +0200 > Pawel Osciak <p.o...@sa...> wrote: > > diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c > > index ff0b1a3..77b77a2 100644 > > --- a/drivers/video/s3c-fb.c > > +++ b/drivers/video/s3c-fb.c > > @@ -294,6 +294,8 @@ 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; > > > > These two lines are not need. If the xres_virtual > xres there is no > panning possible (the same is true for yres and yres_virtual). Erm, I think you have that backwards. xres,yres is the visibly displayed dimentions. xres_virtual,yres_virtual is the size of the underlying framebuffer. If the underlying framebuffer is larger than the visible display, you can pan the visible display around within the underlying framebuffer. It is common for unaccelerated framebuffers used in text mode to have xres_virtual = xres, yres_virtual >> yres so that Y panning (or if your video driver supports it, wrapping) can occur to speed line scrolling. |
From: Krzysztof H. <krz...@po...> - 2009-07-09 18:09:21
|
On Thu, 09 Jul 2009 17:09:00 +0200 Pawel Osciak <p.o...@sa...> wrote: > Added support for panning the display in all directions. > > > Reviewed-by: Marek Szyprowski <m.s...@sa...> > Reviewed-by: Kyungmin Park <kyu...@sa...> > Signed-off-by: Pawel Osciak <p.o...@sa...> > > > > diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c > index ff0b1a3..77b77a2 100644 > --- a/drivers/video/s3c-fb.c > +++ b/drivers/video/s3c-fb.c > @@ -294,6 +294,8 @@ 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; > These two lines are not need. If the xres_virtual > xres there is no panning possible (the same is true for yres and yres_virtual). > /* disable the window whilst we update it */ > writel(0, regs + WINCON(win_no)); > @@ -640,6 +642,38 @@ 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; > + unsigned int start_byte_offset, end_byte_offset; > + > + /* Offset in bytes to the start of the displayed area */ > + start_byte_offset = var->yoffset * info->fix.line_length > + + var->xoffset * (info->var.bits_per_pixel) / 8; > + writel(info->fix.smem_start + start_byte_offset, > + sfb->regs + VIDW_BUF_START(win->index)); > + Check out carefully what units are used for VIDW_BUF_START (pixels, bytes, words, dwords). This x panning may not work on all depths supported by the driver (18-bit, 19-bit, 1-bit, etc). Do you really want panning in the x direction? Please test it in all depths. Regards, Krzysztof ---------------------------------------------------------------------- Rowerem do pracy? Czemu nie! Kliknij >>> http://link.interia.pl/f2256 |
From: Krzysztof H. <krz...@po...> - 2009-07-09 18:04:16
|
On Thu, 09 Jul 2009 17:09:03 +0200 Pawel Osciak <p.o...@sa...> wrote: > Added VSYNC interrupt support. > Added FBIO_WAITFORVSYNC ioctl that allows sleeping on vsync > waitqueue. > > > Reviewed-by: Marek Szyprowski <m.s...@sa...> > Reviewed-by: Kyungmin Park <kyu...@sa...> > Signed-off-by: Pawel Osciak <p.o...@sa...> > > > > diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c > index 77b77a2..0c064c2 100644 > --- a/drivers/video/s3c-fb.c > +++ b/drivers/video/s3c-fb.c > @@ -21,6 +21,8 @@ > #include <linux/clk.h> > #include <linux/fb.h> > #include <linux/io.h> > +#include <linux/uaccess.h> > +#include <linux/interrupt.h> > > #include <mach/map.h> > #include <mach/regs-fb.h> > @@ -48,6 +50,13 @@ > __raw_writel(v, r); } while(0) > #endif /* FB_S3C_DEBUG_REGWRITE */ > > +/* Bit in irq_flags used to mark that interrupts are activated in device */ > +#define S3C_FB_IRQ_ENABLED_BIT 0 > + > +#ifndef FBIO_WAITFORVSYNC > +#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) > +#endif > + > struct s3c_fb; > > /** > @@ -59,6 +68,7 @@ struct s3c_fb; > * @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/ > * @index: The window number of this window. > * @palette: The bitfields for changing r/g/b into a hardware palette entry. > + * @vsync: Vsync handling helper. > */ > struct s3c_fb_win { > struct s3c_fb_pd_win *windata; > @@ -72,14 +82,27 @@ struct s3c_fb_win { > }; > > /** > + * struct s3c_fb_vsync - vsync information for display. > + * @vsync_queue: A queue for processes waiting for vsync. > + * @vsync_count: Vsync interrupt count. > + */ > +struct s3c_fb_vsync { > + wait_queue_head_t vsync_queue; > + unsigned long vsync_count; > +}; > + > +/** > * struct s3c_fb - overall hardware state of the hardware > * @dev: The device that we bound to, for printing, etc. > * @regs_res: The resource we claimed for the IO registers. > * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk. > * @regs: The mapped hardware registers. > * @enabled: A bitmask of enabled hardware windows. > + * @open_instances: How many times the driver has been opened. > * @pdata: The platform configuration data passed with the device. > * @windows: The hardware windows that have been claimed. > + * @irq_no: IRQ number for the device (acquired from platform data). > + * @vsync_info: VSYNC-related information (count, queues...) > */ > struct s3c_fb { > struct device *dev; > @@ -87,10 +110,16 @@ struct s3c_fb { > struct clk *bus_clk; > void __iomem *regs; > > - unsigned char enabled; > + unsigned char enabled; > + atomic_t open_instances; > > struct s3c_fb_platdata *pdata; > struct s3c_fb_win *windows[S3C_FB_MAX_WIN]; > + > + unsigned int irq_no; > + unsigned long irq_flags; > + > + struct s3c_fb_vsync vsync_info; > }; > > /** > @@ -643,6 +672,104 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) > } > > /** > + * s3c_fb_enable_irq() - enable interrupts in hardware. > + * @sfb: The base resources for the hardware. > + */ > +static int s3c_fb_enable_irq(struct s3c_fb *sfb) > +{ > + u32 irq_ctrl_reg; > + void __iomem *regs = sfb->regs; > + > + if (!test_and_set_bit(S3C_FB_IRQ_ENABLED_BIT, &sfb->irq_flags)) { > + /* IRQ was 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); > + } > + > + return 0; > +} > + > +/** > + * s3c_fb_disable_irq() - disable interrupts in hardware. > + * @sfb: The base resources for the hardware. > + */ > +static int s3c_fb_disable_irq(struct s3c_fb *sfb) > +{ > + u32 irq_ctrl_reg; > + void __iomem *regs = sfb->regs; > + > + if (test_and_clear_bit(S3C_FB_IRQ_ENABLED_BIT, &sfb->irq_flags)) { > + /* IRQ was 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); > + } > + > + return 0; > +} > + > +static irqreturn_t s3c_fb_interrupt(int irq, void *dev_id) > +{ > + u32 irq_sts_reg; > + struct s3c_fb *sfb = dev_id; > + void __iomem *regs = sfb->regs; > + > + 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.vsync_count++; > + wake_up_interruptible(&sfb->vsync_info.vsync_queue); > + } > + > + /* We support only waiting for VSYNC interrupt for now, > + * so it's safe to disable irqs here. */ > + s3c_fb_disable_irq(sfb); > + > + return IRQ_HANDLED; > +} > + > +/** > + * s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout. > + * @sfb: The base resources for the hardware. > + * @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; > + > + s3c_fb_enable_irq(sfb); > + count = sfb->vsync_info.vsync_count; > + ret = wait_event_interruptible_timeout(sfb->vsync_info.vsync_queue, > + count != sfb->vsync_info.vsync_count, > + HZ / 10); > + if (ret == 0) > + return -ETIMEDOUT; > + > + If you disable the irq here there is no need for open and release functions. You can also remove this disabling from all other places. Much less code then. > + return 0; > +} > + Regards, Krzysztof ---------------------------------------------------------------------- Sprawdz promocje ubezpieczen komunikacyjnych w Ergo Hestia http://link.interia.pl/f222c |
From: Pawel O. <p.o...@sa...> - 2009-07-09 15:39:42
|
Added VSYNC interrupt support. Added FBIO_WAITFORVSYNC ioctl that allows sleeping on vsync waitqueue. Reviewed-by: Marek Szyprowski <m.s...@sa...> Reviewed-by: Kyungmin Park <kyu...@sa...> Signed-off-by: Pawel Osciak <p.o...@sa...> diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 77b77a2..0c064c2 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -21,6 +21,8 @@ #include <linux/clk.h> #include <linux/fb.h> #include <linux/io.h> +#include <linux/uaccess.h> +#include <linux/interrupt.h> #include <mach/map.h> #include <mach/regs-fb.h> @@ -48,6 +50,13 @@ __raw_writel(v, r); } while(0) #endif /* FB_S3C_DEBUG_REGWRITE */ +/* Bit in irq_flags used to mark that interrupts are activated in device */ +#define S3C_FB_IRQ_ENABLED_BIT 0 + +#ifndef FBIO_WAITFORVSYNC +#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) +#endif + struct s3c_fb; /** @@ -59,6 +68,7 @@ struct s3c_fb; * @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/ * @index: The window number of this window. * @palette: The bitfields for changing r/g/b into a hardware palette entry. + * @vsync: Vsync handling helper. */ struct s3c_fb_win { struct s3c_fb_pd_win *windata; @@ -72,14 +82,27 @@ struct s3c_fb_win { }; /** + * struct s3c_fb_vsync - vsync information for display. + * @vsync_queue: A queue for processes waiting for vsync. + * @vsync_count: Vsync interrupt count. + */ +struct s3c_fb_vsync { + wait_queue_head_t vsync_queue; + unsigned long vsync_count; +}; + +/** * struct s3c_fb - overall hardware state of the hardware * @dev: The device that we bound to, for printing, etc. * @regs_res: The resource we claimed for the IO registers. * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk. * @regs: The mapped hardware registers. * @enabled: A bitmask of enabled hardware windows. + * @open_instances: How many times the driver has been opened. * @pdata: The platform configuration data passed with the device. * @windows: The hardware windows that have been claimed. + * @irq_no: IRQ number for the device (acquired from platform data). + * @vsync_info: VSYNC-related information (count, queues...) */ struct s3c_fb { struct device *dev; @@ -87,10 +110,16 @@ struct s3c_fb { struct clk *bus_clk; void __iomem *regs; - unsigned char enabled; + unsigned char enabled; + atomic_t open_instances; struct s3c_fb_platdata *pdata; struct s3c_fb_win *windows[S3C_FB_MAX_WIN]; + + unsigned int irq_no; + unsigned long irq_flags; + + struct s3c_fb_vsync vsync_info; }; /** @@ -643,6 +672,104 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) } /** + * s3c_fb_enable_irq() - enable interrupts in hardware. + * @sfb: The base resources for the hardware. + */ +static int s3c_fb_enable_irq(struct s3c_fb *sfb) +{ + u32 irq_ctrl_reg; + void __iomem *regs = sfb->regs; + + if (!test_and_set_bit(S3C_FB_IRQ_ENABLED_BIT, &sfb->irq_flags)) { + /* IRQ was 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); + } + + return 0; +} + +/** + * s3c_fb_disable_irq() - disable interrupts in hardware. + * @sfb: The base resources for the hardware. + */ +static int s3c_fb_disable_irq(struct s3c_fb *sfb) +{ + u32 irq_ctrl_reg; + void __iomem *regs = sfb->regs; + + if (test_and_clear_bit(S3C_FB_IRQ_ENABLED_BIT, &sfb->irq_flags)) { + /* IRQ was 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); + } + + return 0; +} + +static irqreturn_t s3c_fb_interrupt(int irq, void *dev_id) +{ + u32 irq_sts_reg; + struct s3c_fb *sfb = dev_id; + void __iomem *regs = sfb->regs; + + 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.vsync_count++; + wake_up_interruptible(&sfb->vsync_info.vsync_queue); + } + + /* We support only waiting for VSYNC interrupt for now, + * so it's safe to disable irqs here. */ + s3c_fb_disable_irq(sfb); + + return IRQ_HANDLED; +} + +/** + * s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout. + * @sfb: The base resources for the hardware. + * @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; + + s3c_fb_enable_irq(sfb); + count = sfb->vsync_info.vsync_count; + ret = wait_event_interruptible_timeout(sfb->vsync_info.vsync_queue, + count != sfb->vsync_info.vsync_count, + HZ / 10); + if (ret == 0) + return -ETIMEDOUT; + + + return 0; +} + +/** * s3c_fb_pan_display() - Pan the display. * * Note, that the offsets can be written to the device at any time, as their @@ -674,8 +801,53 @@ static int s3c_fb_pan_display(struct fb_var_screeninfo *var, 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; + + switch (cmd) { + case FBIO_WAITFORVSYNC: + { + u32 crtc; + if (get_user(crtc, (u32 __user *)arg)) + return -EFAULT; + + return s3c_fb_wait_for_vsync(sfb, crtc); + } + default: + return -ENOTTY; + } + + return 0; +} + +static int s3c_fb_open(struct fb_info *info, int user) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + + atomic_inc(&sfb->open_instances); + + return 0; +} + +static int s3c_fb_release(struct fb_info *info, int user) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + + if (atomic_dec_and_test(&sfb->open_instances)) + s3c_fb_disable_irq(sfb); + + return 0; +} + static struct fb_ops s3c_fb_ops = { .owner = THIS_MODULE, + .fb_open = s3c_fb_open, + .fb_release = s3c_fb_release, .fb_check_var = s3c_fb_check_var, .fb_set_par = s3c_fb_set_par, .fb_blank = s3c_fb_blank, @@ -683,6 +855,7 @@ static struct fb_ops s3c_fb_ops = { .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, + .fb_ioctl = s3c_fb_ioctl, .fb_pan_display = s3c_fb_pan_display, }; @@ -908,6 +1081,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) sfb->dev = dev; sfb->pdata = pd; + init_waitqueue_head(&sfb->vsync_info.vsync_queue); sfb->bus_clk = clk_get(dev, "lcd"); if (IS_ERR(sfb->bus_clk)) { @@ -939,6 +1113,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_interrupt, + 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 */ @@ -963,7 +1151,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) dev_err(dev, "failed to create window %d\n", win); for ( win = win - 1; win >= 0; win--) s3c_fb_release_win(sfb, sfb->windows[win]); - goto err_ioremap; + goto err_irq; } } @@ -971,6 +1159,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); @@ -1003,6 +1194,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); |
From: Pawel O. <p.o...@sa...> - 2009-07-09 15:39:30
|
Added support for panning the display in all directions. Reviewed-by: Marek Szyprowski <m.s...@sa...> Reviewed-by: Kyungmin Park <kyu...@sa...> Signed-off-by: Pawel Osciak <p.o...@sa...> diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index ff0b1a3..77b77a2 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -294,6 +294,8 @@ 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)); @@ -640,6 +642,38 @@ 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; + unsigned int start_byte_offset, end_byte_offset; + + /* Offset in bytes to the start of the displayed area */ + start_byte_offset = var->yoffset * info->fix.line_length + + var->xoffset * (info->var.bits_per_pixel) / 8; + writel(info->fix.smem_start + start_byte_offset, + sfb->regs + VIDW_BUF_START(win->index)); + + /* Offset in bytes to the end of the displayed area */ + end_byte_offset = start_byte_offset + var->yres * info->fix.line_length; + writel(info->fix.smem_start + end_byte_offset, + sfb->regs + VIDW_BUF_END(win->index)); + + return 0; +} + static struct fb_ops s3c_fb_ops = { .owner = THIS_MODULE, .fb_check_var = s3c_fb_check_var, @@ -649,6 +683,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, }; /** |
From: Pawel O. <p.o...@sa...> - 2009-07-09 15:39:29
|
Corrected defines for FRAMESEL1 framebuffer register. Reviewed-by: Marek Szyprowski <m.s...@sa...> Reviewed-by: Kyungmin Park <kyu...@sa...> Signed-off-by: Pawel Osciak <p.o...@sa...> diff --git a/arch/arm/plat-s3c/include/plat/regs-fb.h b/arch/arm/plat-s3c/include/plat/regs-fb.h index e9ee599..8048cae 100644 --- a/arch/arm/plat-s3c/include/plat/regs-fb.h +++ b/arch/arm/plat-s3c/include/plat/regs-fb.h @@ -291,11 +291,12 @@ #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_MASK (0x3 << 13) +#define VIDINTCON0_FRAMESEL1_SHIFT (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) |
From: Pawel O. <p.o...@sa...> - 2009-07-09 15:39:21
|
This bug caused the release function being called on the first uninitialized window, instead of starting with the last successfully initialized one. Reviewed-by: Marek Szyprowski <m.s...@sa...> Reviewed-by: Kyungmin Park <kyu...@sa...> Signed-off-by: Pawel Osciak <p.o...@sa...> diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index bb63c07..ff0b1a3 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -926,7 +926,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) ret = s3c_fb_probe_win(sfb, win, &sfb->windows[win]); if (ret < 0) { dev_err(dev, "failed to create window %d\n", win); - for (; win >= 0; win--) + for ( win = win - 1; win >= 0; win--) s3c_fb_release_win(sfb, sfb->windows[win]); goto err_ioremap; } |
From: Pawel O. <p.o...@sa...> - 2009-07-09 15:39:18
|
This is a resend as my previous attempt seems to have failed. Please accept my apologies if you are receiving this patch set for the second time. This patch series adds support for VSYNC wait ioctl (FBIO_WAITFORVSYNC) and display panning for the S3C framebuffer. It also contains minor fixes for the driver. The series contains: [PATCH 1/4] [ARM] s3c-fb: Fix an off-by-one error at window release [PATCH 2/4] [ARM] s3c-fb: Include fixes [PATCH 3/4] [ARM] s3c-fb: Add support for display panning [PATCH 4/4] [ARM] s3c-fb: Add wait for vsync support Best regards -- Pawel Osciak Linux Platform Group Samsung Poland R&D Center |