From mboxrd@z Thu Jan 1 00:00:00 1970 From: Nobuhiro Iwamatsu Subject: [PATCH 1/4] video: sh7760fb: SH7760/SH7763 LCDC framebuffer driver part 1/3 Date: Wed, 25 Jun 2008 16:38:25 +0900 Message-ID: <4861F5F1.3030003@renesas.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: Received: from sc8-sf-mx2-b.sourceforge.net ([10.3.1.92] helo=mail.sourceforge.net) by sc8-sf-list1-new.sourceforge.net with esmtp (Exim 4.43) id 1KBPaR-0001D3-JB for Linux-fbdev-devel@lists.sourceforge.net; Wed, 25 Jun 2008 00:38:39 -0700 Received: from mail.renesas.com ([202.234.163.13] helo=mail01.idc.renesas.com) by mail.sourceforge.net with esmtp (Exim 4.44) id 1KBPaQ-0002Q7-Mk for Linux-fbdev-devel@lists.sourceforge.net; Wed, 25 Jun 2008 00:38:39 -0700 List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-fbdev-devel-bounces@lists.sourceforge.net Errors-To: linux-fbdev-devel-bounces@lists.sourceforge.net To: Linux-fbdev-devel@lists.sourceforge.net Cc: Paul Mundt , Linux-sh Main source code of Driver for the LCDC interface on the SH7760 and SH7763 SoCs. Signed-off-by: Manuel Lauss Signed-off-by: Nobuhiro Iwamatsu --- drivers/video/sh7760fb.c | 717 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 717 insertions(+), 0 deletions(-) create mode 100644 drivers/video/sh7760fb.c diff --git a/drivers/video/sh7760fb.c b/drivers/video/sh7760fb.c new file mode 100644 index 0000000..57b1dfb --- /dev/null +++ b/drivers/video/sh7760fb.c @@ -0,0 +1,717 @@ +/* + * SH7760/63 LCDC Framebuffer driver. + * + * (c) 2006-2008 MSC Vertriebsges.m.b.H., Manuel Lauss. + * (c) 2008 Nobuhiro Iwamatsu + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + * PLEASE HAVE A LOOK AT Documentation/fb/sh7760fb.txt! + * + * Thanks to Siegfried Schaefer + * for his original source and testing! + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define fb_msg(args...) \ + do { printk(KERN_ALERT "sh7760fb: " args); } while (0) +#if 0 +#define dbg(args...) fb_msg(args) +#else +#define dbg(fmt, args...) do { } while (0) +#endif + +/* palette */ +#define LDPR(x) (((x) << 2)) + +/* registers and bits */ +#define LDICKR 0x400 +#define LDMTR 0x402 +/* see sh7760fb.h for LDMTR bits */ +#define LDDFR 0x404 +#define LDDFR_PABD (1 << 8) +#define LDDFR_COLOR_MASK 0x7F +#define LDSMR 0x406 +#define LDSMR_ROT (1 << 13) +#define LDSARU 0x408 +#define LDSARL 0x40c +#define LDLAOR 0x410 +#define LDPALCR 0x412 +#define LDHCNR 0x414 +#define LDHSYNR 0x416 +#define LDVDLNR 0x418 +#define LDVTLNR 0x41a +#define LDVSYNR 0x41c +#define LDACLNR 0x41e +#define LDINTR 0x420 +#define LDPMMR 0x424 +#define LDPSPR 0x426 +#define LDCNTR 0x428 +#define LDCNTR_DON (1 << 0) +#define LDCNTR_DON2 (1 << 4) +#ifdef CONFIG_CPU_SUBTYPE_SH7763 +# define LDLIRNR 0x440 +#endif + +/* + * some bits of the colormap registers should be written as zero. + * create a mask for that. + */ +#define SH7760FB_PALETTE_MASK 0x00f8fcf8 + +/* The LCDC dma engine always sets bits 27-26 to 1: this is Area3 */ +#define SH7760FB_DMA_MASK 0x0C000000 + +struct sh7760fb_par { + void __iomem *base; + struct sh7760fb_platdata *pd; /* display information */ + + /* framebuffer memory information */ + void *fbmem; /* alloced framebuffer */ + dma_addr_t fbdma; /* physical address */ + unsigned long vram; /* size, in bytes */ + unsigned long stride; + + int rot; /* rotation enabled? */ + int disp_set; /* parameters already set? */ + u32 pseudo_palette[16]; + struct platform_device *dev; + struct resource *ioarea; +}; + +static inline void OUT16(struct sh7760fb_par *par, int reg, unsigned short v) +{ + iowrite16(v, par->base + reg); +} + +static inline unsigned short IN16(struct sh7760fb_par *par, int reg) +{ + return ioread16(par->base + reg); +} + +static inline void OUT32(struct sh7760fb_par *par, int reg, unsigned long v) +{ + iowrite32(v, par->base + reg); +} + +static inline unsigned long IN32(struct sh7760fb_par *par, int reg) +{ + return ioread32(par->base + reg); +} + +static void sh7760fb_wait_vsync(struct fb_info *info) +{ + struct sh7760fb_par *par = info->par; + + if (par->pd->novsync) + return; +/* + while (IN16(par, LDINTR) & 1) + OUT16(par, LDINTR, 0x1100); +*/ +} + + +/* + * wait_for_lps - wait until power supply has reached a certain state. + * @val: bitmask to wait for. + */ +static void wait_for_lps(struct sh7760fb_par *par, int val) +{ + int i = 100; + while (--i && ((IN16(par, LDPMMR) & 3) != val)) + msleep(1); +} + +/* en/disable the LCDC */ +static int sh7760fb_blank(int blank, struct fb_info *info) +{ + struct sh7760fb_par *par = info->par; + struct sh7760fb_platdata *pd = par->pd; + unsigned short cntr = IN16(par, LDCNTR); + int lps; + + if (blank == FB_BLANK_UNBLANK) { + cntr = LDCNTR_DON2 | LDCNTR_DON; + lps = 3; + if (pd->blank) + pd->blank(blank); + } else { + if (pd->blank) + pd->blank(blank); + cntr = LDCNTR_DON2; + lps = 0; + } + + OUT16(par, LDCNTR, cntr); + + wait_for_lps(par, lps); + + return 0; +} + +/* set color registers */ +static int sh7760fb_setcmap(struct fb_cmap *cmap, struct fb_info *info) +{ + struct sh7760fb_par *par = info->par; + u32 s = cmap->start; + u32 l = cmap->len; + u16 *r = cmap->red; + u16 *g = cmap->green; + u16 *b = cmap->blue; + u32 col, tmo; + int ret; + + ret = 0; + + /* wait for vsync */ + sh7760fb_wait_vsync(info); + + /* request palette access */ + OUT16(par, LDPALCR, 1); + + /* poll for access grant */ + tmo = 100; + while (!(IN16(par, LDPALCR) & (1 << 4)) && (--tmo)) + msleep(0); + + if (!tmo) { + ret = 1; + pr_debug("sh7760fb: no palette access!\n"); + goto out; + } + + while (l && (s < 256)) { + col = ((*r) & 0xff) << 16; + col |= ((*g) & 0xff) << 8; + col |= ((*b) & 0xff); + col &= SH7760FB_PALETTE_MASK; + OUT32(par, LDPR(s), col); + if (s < 16) + ((u32 *) (info->pseudo_palette))[s] = s; + + s++; + l--; + r++; + g++; + b++; + } +out: + OUT16(par, LDPALCR, 0); + return ret; +} + +static void encode_fix(struct fb_fix_screeninfo *fix, struct fb_info *info) +{ + struct sh7760fb_par *par = info->par; + struct fb_var_screeninfo *var = &info->var; + + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + strcpy(fix->id, "sh7760fb"); + + fix->smem_start = (unsigned long)par->fbmem; + fix->smem_len = par->vram; + + if ((var->grayscale) && (var->bits_per_pixel == 1)) + fix->visual = FB_VISUAL_MONO10; + else if (var->bits_per_pixel >= 15) + fix->visual = FB_VISUAL_TRUECOLOR; + else + fix->visual = FB_VISUAL_PSEUDOCOLOR; + + fix->line_length = par->stride; +} + +/** + * sh7760fb_set_par - set videomode. + * @info: ptr to fb_info structure with mode info. + * @return: success. + * + * Set up video mode as described in info->var. + * NOTE: The rotation, grayscale and DSTN codepaths are + * totally untested! + */ +static int sh7760fb_set_par(struct fb_info *info) +{ + struct fb_var_screeninfo *var = &info->var; + struct sh7760fb_par *par = info->par; + struct fb_videomode *vm = par->pd->def_mode; + unsigned long sbase, dstn_off, ldsarl, stride; + unsigned short hsynp, hsynw, htcn, hdcn; + unsigned short vsynp, vsynw, vtln, vdln; + unsigned short lddfr, ldmtr; + int bpp, gray; + + if (par->disp_set) + return 0; + + par->rot = par->pd->rotate; + + /* rotate only works with xres <= 320 */ + if (par->rot && (vm->xres > 320)) { + fb_msg("rotation disabled due to display size\n"); + par->rot = 0; + } + + /* calculate LCDC reg vals from display parameters */ + hsynp = vm->right_margin + vm->xres; + hsynw = vm->hsync_len; + htcn = vm->left_margin + hsynp + hsynw; + hdcn = vm->xres; + vsynp = vm->lower_margin + vm->yres; + vsynw = vm->vsync_len; + vtln = vm->upper_margin + vsynp + vsynw; + vdln = vm->yres; + + bpp = gray = 0; + switch (par->pd->lddfr & LDDFR_COLOR_MASK) { + case LDDFR_1BPP_MONO: + gray = 1; + bpp = 1; + break; + case LDDFR_2BPP_MONO: + gray = 1; + bpp = 2; + break; + case LDDFR_4BPP_MONO: + gray = 1; + case LDDFR_4BPP: + bpp = 4; + break; + case LDDFR_6BPP_MONO: + gray = 1; + case LDDFR_8BPP: + bpp = 8; + break; + case LDDFR_16BPP_RGB555: + case LDDFR_16BPP_RGB565: + bpp = 16; + break; + } + + var->bits_per_pixel = bpp; + + fb_msg("%dx%d %dbpp %s (orientation %s)\n", hdcn, vdln, bpp, + gray ? "grayscale" : "color", par->rot ? "rotated" : "normal"); + +#ifdef CONFIG_CPU_LITTLE_ENDIAN + lddfr = par->pd->lddfr | (1 << 8); +#else + lddfr = par->pd->lddfr & ~(1 << 8); +#endif + + ldmtr = par->pd->ldmtr; + + if (!(vm->sync & FB_SYNC_HOR_HIGH_ACT)) + ldmtr |= LDMTR_CL1POL; + if (!(vm->sync & FB_SYNC_VERT_HIGH_ACT)) + ldmtr |= LDMTR_FLMPOL; + + /* shut down LCDC before changing display parameters */ + sh7760fb_blank(FB_BLANK_POWERDOWN, info); + + OUT16(par, LDICKR, par->pd->ldickr); /* pixclock */ + OUT16(par, LDMTR, ldmtr); /* polarities */ + OUT16(par, LDDFR, lddfr); /* color/depth */ + OUT16(par, LDSMR, (par->rot ? 1 << 13 : 0)); /* rotate */ + OUT16(par, LDPMMR, par->pd->ldpmmr); /* Power Management */ + OUT16(par, LDPSPR, par->pd->ldpspr); /* Power Supply Ctrl */ + + /* display resolution */ + OUT16(par, LDHCNR, ((htcn >> 3) - 1) | (((hdcn >> 3) - 1) << 8)); + OUT16(par, LDVDLNR, vdln - 1); + OUT16(par, LDVTLNR, vtln - 1); + /* h/v sync signals */ + OUT16(par, LDVSYNR, (vsynp - 1) | ((vsynw - 1) << 12)); + OUT16(par, LDHSYNR, ((hsynp >> 3) - 1) | (((hsynw >> 3) - 1) << 12)); + OUT16(par, LDACLNR, par->pd->ldaclnr); /* AC modulation sig */ + + stride = (par->rot) ? vtln : hdcn; + if (!gray) + stride *= (bpp + 7) >> 3; + else { + if (bpp == 1) + stride >>= 3; + else if (bpp == 2) + stride >>= 2; + else if (bpp == 4) + stride >>= 1; + /* 6 bpp == 8 bpp */ + } + + /* if rotated, stride must be power of 2 */ + if (par->rot) { + unsigned long bit = 1 << 31; + while (bit) { + if (stride & bit) + break; + bit >>= 1; + } + if (stride & ~bit) + stride = bit << 1; /* not P-o-2, round up */ + } + OUT16(par, LDLAOR, stride); + par->stride = stride; + /* set display mem start address */ + sbase = (unsigned long)par->fbdma; + if (par->rot) + sbase += (hdcn - 1) * stride; + + OUT32(par, LDSARU, sbase); + + /* + * for DSTN need to set address for lower half. + * I (mlau) don't know which address to set it to, + * so I guessed at (stride * yres/2). + */ + if (((ldmtr & 0x003f) >= LDMTR_DSTN_MONO_8) && + ((ldmtr & 0x003f) <= LDMTR_DSTN_COLOR_16)) { + + pr_debug(" ***** DSTN untested! *****\n"); + + dstn_off = stride; + if (par->rot) + dstn_off *= hdcn >> 1; + else + dstn_off *= vdln >> 1; + + ldsarl = sbase + dstn_off; + } else + ldsarl = 0; + + OUT32(par, LDSARL, ldsarl); /* mem for lower half of DSTN */ + + encode_fix(&info->fix, info); + + sh7760fb_blank(FB_BLANK_UNBLANK, info); /* panel on! */ + + par->disp_set = 1; + + dbg("hdcn : %6d htcn : %6d\n", hdcn, htcn); + dbg("hsynw : %6d hsynp : %6d\n", hsynw, hsynp); + dbg("vdln : %6d vtln : %6d\n", vdln, vtln); + dbg("vsynw : %6d vsynp : %6d\n", vsynw, vsynp); + dbg("clksrc: %6d clkdiv: %6d\n", (par->pd->ldickr >> 12) & 3, + par->pd->ldickr & 0x1f); + dbg("ldpmmr: 0x%04x ldpspr: 0x%04x\n", par->pd->ldpmmr, + par->pd->ldpspr); + dbg("ldmtr : 0x%04x lddfr : 0x%04x\n", ldmtr, lddfr); + dbg("ldlaor: %6d\n", stride); + dbg("ldsaru: 0x%08lx ldsarl: 0x%08lx\n", sbase, ldsarl); + + return 0; +} + +static int sh7760fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct sh7760fb_par *par = info->par; + + /* validate LDDFR bit depth setting */ + switch (par->pd->lddfr & LDDFR_COLOR_MASK) { + case LDDFR_1BPP_MONO: + case LDDFR_2BPP_MONO: + case LDDFR_4BPP_MONO: + case LDDFR_4BPP: + case LDDFR_6BPP_MONO: + case LDDFR_8BPP: + case LDDFR_16BPP_RGB555: + case LDDFR_16BPP_RGB565: + break; + default: + fb_msg("unsupported LDDFR bit depth.\n"); + return -EINVAL; + } + + /* TODO: add some more validation here */ + return 0; +} + +static struct fb_ops sh7760fb_ops = { + .owner = THIS_MODULE, + .fb_blank = sh7760fb_blank, + .fb_check_var = sh7760fb_check_var, + .fb_setcmap = sh7760fb_setcmap, + .fb_set_par = sh7760fb_set_par, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +/* free framebuffer memory */ +static void sh7760fb_free_mem(struct fb_info *info) +{ + struct sh7760fb_par *par = info->par; + + if (!par->fbmem) + return; + + dma_free_coherent(info->dev, par->vram, par->fbmem, par->fbdma); + + par->fbmem = NULL; + par->fbdma = 0; + info->screen_base = NULL; + info->screen_size = 0; +} + +/* allocate the framebuffer memory. This memory must be in Area3, + * (dictated by the DMA engine) and contiguous, at a 512 byte boundary. + */ +static int sh7760fb_alloc_mem(struct fb_info *info) +{ + struct sh7760fb_par *par = info->par; + unsigned long vram; + int bpp; + + if (info->screen_base) + return 0; + + bpp = 1; + switch (par->pd->lddfr & LDDFR_COLOR_MASK) { + case LDDFR_1BPP_MONO: + bpp = 1; + break; + case LDDFR_2BPP_MONO: + bpp = 2; + break; + case LDDFR_4BPP_MONO: + case LDDFR_4BPP: + bpp = 4; + break; + case LDDFR_6BPP_MONO: + case LDDFR_8BPP: + bpp = 8; + break; + case LDDFR_16BPP_RGB555: + case LDDFR_16BPP_RGB565: + bpp = 16; + break; + default: + fb_msg("unsupported LDDFR bit depth.\n"); + return -ENOMEM; + } + + /* min VRAM: xres_min = 16, yres_min = 1, bpp = 1: 2byte -> 1 page + max VRAM: xres_max = 1024, yres_max = 1024, bpp = 16: 2MB */ + + vram = info->var.xres * info->var.yres; + if (info->var.grayscale) { + if (bpp == 1) + vram >>= 3; + else if (bpp == 2) + vram >>= 2; + else if (bpp == 4) + vram >>= 1; + } else if (bpp > 8) + vram *= 2; + if ((vram < 1) || (vram > 1024 * 2048)) { + fb_msg("too much VRAM required. Check settings\n"); + return -ENODEV; + } + + if (vram < PAGE_SIZE) + vram = PAGE_SIZE; + + par->vram = vram; + par->fbmem = + dma_alloc_coherent(info->dev, vram, &par->fbdma, GFP_KERNEL); + + if (!par->fbmem) + return -ENOMEM; + + if ((par->fbdma & SH7760FB_DMA_MASK) != SH7760FB_DMA_MASK) { + sh7760fb_free_mem(info); + fb_msg("kernel gave me memory at 0x%08lx, which is" + "unusable for the LCDC\n", (unsigned long)par->fbdma); + return -ENOMEM; + } + + info->screen_base = par->fbmem; + info->screen_size = par->vram; + + return 0; +} + +/* register the framebuffer device */ +static int __devinit sh7760fb_probe(struct platform_device *pdev) +{ + struct fb_info *info; + struct resource *res; + struct sh7760fb_par *par; + int ret; + + /* Get base addr */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(res == NULL)) { + dev_err(&pdev->dev, "invalid resource\n"); + return -EINVAL; + } + + info = framebuffer_alloc(sizeof(struct sh7760fb_par), &pdev->dev); + if (!info) + return -ENOMEM; + + par = info->par; + par->dev = pdev; + + info->pseudo_palette = par->pseudo_palette; + par->pd = pdev->dev.platform_data; + if (!par->pd) { + fb_msg("no display setup data!\n"); + ret = -ENODEV; + goto out_fb; + } + + fb_videomode_to_var(&info->var, par->pd->def_mode); + + /* fixup color register bitpositions. These are fixed by hardware */ + info->var.red.offset = 11; + info->var.red.length = 5; + info->var.red.msb_right = 0; + + info->var.green.offset = 5; + info->var.green.length = 6; + info->var.green.msb_right = 0; + + info->var.blue.offset = 0; + info->var.blue.length = 5; + info->var.blue.msb_right = 0; + + info->var.transp.offset = 0; + info->var.transp.length = 0; + info->var.transp.msb_right = 0; + + par->ioarea = request_mem_region(res->start, + (res->end - res->start), pdev->name); + if (!par->ioarea) { + dev_err(&pdev->dev, "mmio area busy\n"); + ret = -EBUSY; + goto out_fb; + } + + par->base = ioremap_nocache(res->start, (res->end - res->start)); + if (!par->base) { + dev_err(&pdev->dev, "cannot remap\n"); + ret = -ENODEV; + goto out_res; + } + + ret = sh7760fb_alloc_mem(info); + if (ret) { + fb_msg("framebuffer memory allocation failed!\n"); + goto out_unmap; + } + + /* set the DON2 bit now; this will randomize palette memory. + * do it now so the palette does not get destroyed when blanking + */ + OUT16(par, LDCNTR_DON2, LDCNTR); + info->fbops = &sh7760fb_ops; + fb_alloc_cmap(&info->cmap, 256, 0); + + /* activate, get info->fix */ + sh7760fb_set_par(info); + + ret = register_framebuffer(info); + if (ret < 0) { + /* stop LCDC before freeing ram */ + sh7760fb_blank(FB_BLANK_POWERDOWN, info); + fb_msg("cannot register fb!\n"); + goto out_mem; + } + platform_set_drvdata(pdev, info); + + fb_msg("memory at phys 0x%08lx-0x%08lx, size %ld KiB\n", + (unsigned long)par->fbdma, + (unsigned long)(par->fbdma + par->vram - 1), par->vram >> 10); + + return 0; + +out_mem: + sh7760fb_free_mem(info); +out_unmap: + iounmap(par->base); +out_res: + release_resource(par->ioarea); + kfree(par->ioarea); +out_fb: + framebuffer_release(info); + return ret; +} + +static int __devexit sh7760fb_remove(struct platform_device *dev) +{ + struct fb_info *info = platform_get_drvdata(dev); + struct sh7760fb_par *par = info->par; + + sh7760fb_blank(FB_BLANK_POWERDOWN, info); + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + sh7760fb_free_mem(info); + iounmap(par->base); + release_resource(par->ioarea); + kfree(par->ioarea); + framebuffer_release(info); + platform_set_drvdata(dev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int sh7760fb_suspend(struct platform_device *dev, u32 level) +{ + dbg("sh7760fb_suspend()\n"); + return 0; +} + +static int sh7760fb_resume(struct platform_device *dev, u32 level) +{ + dbg("sh7760fb_resume()\n"); + return 0; +} +#else +#define sh7760fb_suspend NULL +#define sh7760fb_resume NULL +#endif + +static struct platform_driver sh7760_lcdc_driver = { + .driver = { + .name = "sh7760-lcdc", + .owner = THIS_MODULE, + }, + .probe = sh7760fb_probe, + .remove = __devexit_p(sh7760fb_remove), + .suspend = sh7760fb_suspend, + .resume = sh7760fb_resume, +}; + +static int __init sh7760fb_init(void) +{ + return platform_driver_register(&sh7760_lcdc_driver); +} + +static void __exit sh7760fb_exit(void) +{ + platform_driver_unregister(&sh7760_lcdc_driver); +} + +module_init(sh7760fb_init); +module_exit(sh7760fb_exit); + +MODULE_AUTHOR("Manuel Lauss "); +MODULE_DESCRIPTION("FBdev for SH7760/63 integrated LCD Controller"); +MODULE_LICENSE("GPL"); -- 1.5.5.1 ------------------------------------------------------------------------- Check out the new SourceForge.net Marketplace. It's the best place to buy or sell services for just about anything Open Source. http://sourceforge.net/services/buy/index.php