162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SH7760/SH7763 LCDC Framebuffer driver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (c) 2006-2008 MSC Vertriebsges.m.b.H., 662306a36Sopenharmony_ci * Manuel Lauss <mano@roarinelk.homelinux.net> 762306a36Sopenharmony_ci * (c) 2008 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * PLEASE HAVE A LOOK AT Documentation/fb/sh7760fb.rst! 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Thanks to Siegfried Schaefer <s.schaefer at schaefer-edv.de> 1262306a36Sopenharmony_ci * for his original source and testing! 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * sh7760_setcolreg get from drivers/video/sh_mobile_lcdcfb.c 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/completion.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2062306a36Sopenharmony_ci#include <linux/fb.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/io.h> 2362306a36Sopenharmony_ci#include <linux/kernel.h> 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <asm/sh7760fb.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct sh7760fb_par { 3162306a36Sopenharmony_ci void __iomem *base; 3262306a36Sopenharmony_ci int irq; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci struct sh7760fb_platdata *pd; /* display information */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci dma_addr_t fbdma; /* physical address */ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci int rot; /* rotation enabled? */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci u32 pseudo_palette[16]; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci struct platform_device *dev; 4362306a36Sopenharmony_ci struct resource *ioarea; 4462306a36Sopenharmony_ci struct completion vsync; /* vsync irq event */ 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic irqreturn_t sh7760fb_irq(int irq, void *data) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct completion *c = data; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci complete(c); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return IRQ_HANDLED; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* wait_for_lps - wait until power supply has reached a certain state. */ 5762306a36Sopenharmony_cistatic int wait_for_lps(struct sh7760fb_par *par, int val) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci int i = 100; 6062306a36Sopenharmony_ci while (--i && ((ioread16(par->base + LDPMMR) & 3) != val)) 6162306a36Sopenharmony_ci msleep(1); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (i <= 0) 6462306a36Sopenharmony_ci return -ETIMEDOUT; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* en/disable the LCDC */ 7062306a36Sopenharmony_cistatic int sh7760fb_blank(int blank, struct fb_info *info) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct sh7760fb_par *par = info->par; 7362306a36Sopenharmony_ci struct sh7760fb_platdata *pd = par->pd; 7462306a36Sopenharmony_ci unsigned short cntr = ioread16(par->base + LDCNTR); 7562306a36Sopenharmony_ci unsigned short intr = ioread16(par->base + LDINTR); 7662306a36Sopenharmony_ci int lps; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (blank == FB_BLANK_UNBLANK) { 7962306a36Sopenharmony_ci intr |= VINT_START; 8062306a36Sopenharmony_ci cntr = LDCNTR_DON2 | LDCNTR_DON; 8162306a36Sopenharmony_ci lps = 3; 8262306a36Sopenharmony_ci } else { 8362306a36Sopenharmony_ci intr &= ~VINT_START; 8462306a36Sopenharmony_ci cntr = LDCNTR_DON2; 8562306a36Sopenharmony_ci lps = 0; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (pd->blank) 8962306a36Sopenharmony_ci pd->blank(blank); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci iowrite16(intr, par->base + LDINTR); 9262306a36Sopenharmony_ci iowrite16(cntr, par->base + LDCNTR); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return wait_for_lps(par, lps); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int sh7760_setcolreg (u_int regno, 9862306a36Sopenharmony_ci u_int red, u_int green, u_int blue, 9962306a36Sopenharmony_ci u_int transp, struct fb_info *info) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci u32 *palette = info->pseudo_palette; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (regno >= 16) 10462306a36Sopenharmony_ci return -EINVAL; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* only FB_VISUAL_TRUECOLOR supported */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci red >>= 16 - info->var.red.length; 10962306a36Sopenharmony_ci green >>= 16 - info->var.green.length; 11062306a36Sopenharmony_ci blue >>= 16 - info->var.blue.length; 11162306a36Sopenharmony_ci transp >>= 16 - info->var.transp.length; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci palette[regno] = (red << info->var.red.offset) | 11462306a36Sopenharmony_ci (green << info->var.green.offset) | 11562306a36Sopenharmony_ci (blue << info->var.blue.offset) | 11662306a36Sopenharmony_ci (transp << info->var.transp.offset); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int sh7760fb_get_color_info(struct fb_info *info, 12262306a36Sopenharmony_ci u16 lddfr, int *bpp, int *gray) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci int lbpp, lgray; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci lgray = lbpp = 0; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci switch (lddfr & LDDFR_COLOR_MASK) { 12962306a36Sopenharmony_ci case LDDFR_1BPP_MONO: 13062306a36Sopenharmony_ci lgray = 1; 13162306a36Sopenharmony_ci lbpp = 1; 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci case LDDFR_2BPP_MONO: 13462306a36Sopenharmony_ci lgray = 1; 13562306a36Sopenharmony_ci lbpp = 2; 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci case LDDFR_4BPP_MONO: 13862306a36Sopenharmony_ci lgray = 1; 13962306a36Sopenharmony_ci fallthrough; 14062306a36Sopenharmony_ci case LDDFR_4BPP: 14162306a36Sopenharmony_ci lbpp = 4; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case LDDFR_6BPP_MONO: 14462306a36Sopenharmony_ci lgray = 1; 14562306a36Sopenharmony_ci fallthrough; 14662306a36Sopenharmony_ci case LDDFR_8BPP: 14762306a36Sopenharmony_ci lbpp = 8; 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci case LDDFR_16BPP_RGB555: 15062306a36Sopenharmony_ci case LDDFR_16BPP_RGB565: 15162306a36Sopenharmony_ci lbpp = 16; 15262306a36Sopenharmony_ci lgray = 0; 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci default: 15562306a36Sopenharmony_ci fb_dbg(info, "unsupported LDDFR bit depth.\n"); 15662306a36Sopenharmony_ci return -EINVAL; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (bpp) 16062306a36Sopenharmony_ci *bpp = lbpp; 16162306a36Sopenharmony_ci if (gray) 16262306a36Sopenharmony_ci *gray = lgray; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int sh7760fb_check_var(struct fb_var_screeninfo *var, 16862306a36Sopenharmony_ci struct fb_info *info) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct fb_fix_screeninfo *fix = &info->fix; 17162306a36Sopenharmony_ci struct sh7760fb_par *par = info->par; 17262306a36Sopenharmony_ci int ret, bpp; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* get color info from register value */ 17562306a36Sopenharmony_ci ret = sh7760fb_get_color_info(info, par->pd->lddfr, &bpp, NULL); 17662306a36Sopenharmony_ci if (ret) 17762306a36Sopenharmony_ci return ret; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci var->bits_per_pixel = bpp; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if ((var->grayscale) && (var->bits_per_pixel == 1)) 18262306a36Sopenharmony_ci fix->visual = FB_VISUAL_MONO10; 18362306a36Sopenharmony_ci else if (var->bits_per_pixel >= 15) 18462306a36Sopenharmony_ci fix->visual = FB_VISUAL_TRUECOLOR; 18562306a36Sopenharmony_ci else 18662306a36Sopenharmony_ci fix->visual = FB_VISUAL_PSEUDOCOLOR; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* TODO: add some more validation here */ 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* 19362306a36Sopenharmony_ci * sh7760fb_set_par - set videomode. 19462306a36Sopenharmony_ci * 19562306a36Sopenharmony_ci * NOTE: The rotation, grayscale and DSTN codepaths are 19662306a36Sopenharmony_ci * totally untested! 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_cistatic int sh7760fb_set_par(struct fb_info *info) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct sh7760fb_par *par = info->par; 20162306a36Sopenharmony_ci struct fb_videomode *vm = par->pd->def_mode; 20262306a36Sopenharmony_ci unsigned long sbase, dstn_off, ldsarl, stride; 20362306a36Sopenharmony_ci unsigned short hsynp, hsynw, htcn, hdcn; 20462306a36Sopenharmony_ci unsigned short vsynp, vsynw, vtln, vdln; 20562306a36Sopenharmony_ci unsigned short lddfr, ldmtr; 20662306a36Sopenharmony_ci int ret, bpp, gray; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci par->rot = par->pd->rotate; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* rotate only works with xres <= 320 */ 21162306a36Sopenharmony_ci if (par->rot && (vm->xres > 320)) { 21262306a36Sopenharmony_ci fb_dbg(info, "rotation disabled due to display size\n"); 21362306a36Sopenharmony_ci par->rot = 0; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* calculate LCDC reg vals from display parameters */ 21762306a36Sopenharmony_ci hsynp = vm->right_margin + vm->xres; 21862306a36Sopenharmony_ci hsynw = vm->hsync_len; 21962306a36Sopenharmony_ci htcn = vm->left_margin + hsynp + hsynw; 22062306a36Sopenharmony_ci hdcn = vm->xres; 22162306a36Sopenharmony_ci vsynp = vm->lower_margin + vm->yres; 22262306a36Sopenharmony_ci vsynw = vm->vsync_len; 22362306a36Sopenharmony_ci vtln = vm->upper_margin + vsynp + vsynw; 22462306a36Sopenharmony_ci vdln = vm->yres; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* get color info from register value */ 22762306a36Sopenharmony_ci ret = sh7760fb_get_color_info(info, par->pd->lddfr, &bpp, &gray); 22862306a36Sopenharmony_ci if (ret) 22962306a36Sopenharmony_ci return ret; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci fb_dbg(info, "%dx%d %dbpp %s (orientation %s)\n", hdcn, 23262306a36Sopenharmony_ci vdln, bpp, gray ? "grayscale" : "color", 23362306a36Sopenharmony_ci par->rot ? "rotated" : "normal"); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci#ifdef CONFIG_CPU_LITTLE_ENDIAN 23662306a36Sopenharmony_ci lddfr = par->pd->lddfr | (1 << 8); 23762306a36Sopenharmony_ci#else 23862306a36Sopenharmony_ci lddfr = par->pd->lddfr & ~(1 << 8); 23962306a36Sopenharmony_ci#endif 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ldmtr = par->pd->ldmtr; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (!(vm->sync & FB_SYNC_HOR_HIGH_ACT)) 24462306a36Sopenharmony_ci ldmtr |= LDMTR_CL1POL; 24562306a36Sopenharmony_ci if (!(vm->sync & FB_SYNC_VERT_HIGH_ACT)) 24662306a36Sopenharmony_ci ldmtr |= LDMTR_FLMPOL; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* shut down LCDC before changing display parameters */ 24962306a36Sopenharmony_ci sh7760fb_blank(FB_BLANK_POWERDOWN, info); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci iowrite16(par->pd->ldickr, par->base + LDICKR); /* pixclock */ 25262306a36Sopenharmony_ci iowrite16(ldmtr, par->base + LDMTR); /* polarities */ 25362306a36Sopenharmony_ci iowrite16(lddfr, par->base + LDDFR); /* color/depth */ 25462306a36Sopenharmony_ci iowrite16((par->rot ? 1 << 13 : 0), par->base + LDSMR); /* rotate */ 25562306a36Sopenharmony_ci iowrite16(par->pd->ldpmmr, par->base + LDPMMR); /* Power Management */ 25662306a36Sopenharmony_ci iowrite16(par->pd->ldpspr, par->base + LDPSPR); /* Power Supply Ctrl */ 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* display resolution */ 25962306a36Sopenharmony_ci iowrite16(((htcn >> 3) - 1) | (((hdcn >> 3) - 1) << 8), 26062306a36Sopenharmony_ci par->base + LDHCNR); 26162306a36Sopenharmony_ci iowrite16(vdln - 1, par->base + LDVDLNR); 26262306a36Sopenharmony_ci iowrite16(vtln - 1, par->base + LDVTLNR); 26362306a36Sopenharmony_ci /* h/v sync signals */ 26462306a36Sopenharmony_ci iowrite16((vsynp - 1) | ((vsynw - 1) << 12), par->base + LDVSYNR); 26562306a36Sopenharmony_ci iowrite16(((hsynp >> 3) - 1) | (((hsynw >> 3) - 1) << 12), 26662306a36Sopenharmony_ci par->base + LDHSYNR); 26762306a36Sopenharmony_ci /* AC modulation sig */ 26862306a36Sopenharmony_ci iowrite16(par->pd->ldaclnr, par->base + LDACLNR); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci stride = (par->rot) ? vtln : hdcn; 27162306a36Sopenharmony_ci if (!gray) 27262306a36Sopenharmony_ci stride *= (bpp + 7) >> 3; 27362306a36Sopenharmony_ci else { 27462306a36Sopenharmony_ci if (bpp == 1) 27562306a36Sopenharmony_ci stride >>= 3; 27662306a36Sopenharmony_ci else if (bpp == 2) 27762306a36Sopenharmony_ci stride >>= 2; 27862306a36Sopenharmony_ci else if (bpp == 4) 27962306a36Sopenharmony_ci stride >>= 1; 28062306a36Sopenharmony_ci /* 6 bpp == 8 bpp */ 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* if rotated, stride must be power of 2 */ 28462306a36Sopenharmony_ci if (par->rot) { 28562306a36Sopenharmony_ci unsigned long bit = 1 << 31; 28662306a36Sopenharmony_ci while (bit) { 28762306a36Sopenharmony_ci if (stride & bit) 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci bit >>= 1; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci if (stride & ~bit) 29262306a36Sopenharmony_ci stride = bit << 1; /* not P-o-2, round up */ 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci iowrite16(stride, par->base + LDLAOR); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* set display mem start address */ 29762306a36Sopenharmony_ci sbase = (unsigned long)par->fbdma; 29862306a36Sopenharmony_ci if (par->rot) 29962306a36Sopenharmony_ci sbase += (hdcn - 1) * stride; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci iowrite32(sbase, par->base + LDSARU); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* 30462306a36Sopenharmony_ci * for DSTN need to set address for lower half. 30562306a36Sopenharmony_ci * I (mlau) don't know which address to set it to, 30662306a36Sopenharmony_ci * so I guessed at (stride * yres/2). 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci if (((ldmtr & 0x003f) >= LDMTR_DSTN_MONO_8) && 30962306a36Sopenharmony_ci ((ldmtr & 0x003f) <= LDMTR_DSTN_COLOR_16)) { 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci fb_dbg(info, " ***** DSTN untested! *****\n"); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci dstn_off = stride; 31462306a36Sopenharmony_ci if (par->rot) 31562306a36Sopenharmony_ci dstn_off *= hdcn >> 1; 31662306a36Sopenharmony_ci else 31762306a36Sopenharmony_ci dstn_off *= vdln >> 1; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci ldsarl = sbase + dstn_off; 32062306a36Sopenharmony_ci } else 32162306a36Sopenharmony_ci ldsarl = 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci iowrite32(ldsarl, par->base + LDSARL); /* mem for lower half of DSTN */ 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci info->fix.line_length = stride; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci sh7760fb_check_var(&info->var, info); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci sh7760fb_blank(FB_BLANK_UNBLANK, info); /* panel on! */ 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci fb_dbg(info, "hdcn : %6d htcn : %6d\n", hdcn, htcn); 33262306a36Sopenharmony_ci fb_dbg(info, "hsynw : %6d hsynp : %6d\n", hsynw, hsynp); 33362306a36Sopenharmony_ci fb_dbg(info, "vdln : %6d vtln : %6d\n", vdln, vtln); 33462306a36Sopenharmony_ci fb_dbg(info, "vsynw : %6d vsynp : %6d\n", vsynw, vsynp); 33562306a36Sopenharmony_ci fb_dbg(info, "clksrc: %6d clkdiv: %6d\n", 33662306a36Sopenharmony_ci (par->pd->ldickr >> 12) & 3, par->pd->ldickr & 0x1f); 33762306a36Sopenharmony_ci fb_dbg(info, "ldpmmr: 0x%04x ldpspr: 0x%04x\n", par->pd->ldpmmr, 33862306a36Sopenharmony_ci par->pd->ldpspr); 33962306a36Sopenharmony_ci fb_dbg(info, "ldmtr : 0x%04x lddfr : 0x%04x\n", ldmtr, lddfr); 34062306a36Sopenharmony_ci fb_dbg(info, "ldlaor: %ld\n", stride); 34162306a36Sopenharmony_ci fb_dbg(info, "ldsaru: 0x%08lx ldsarl: 0x%08lx\n", sbase, ldsarl); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic const struct fb_ops sh7760fb_ops = { 34762306a36Sopenharmony_ci .owner = THIS_MODULE, 34862306a36Sopenharmony_ci FB_DEFAULT_IOMEM_OPS, 34962306a36Sopenharmony_ci .fb_blank = sh7760fb_blank, 35062306a36Sopenharmony_ci .fb_check_var = sh7760fb_check_var, 35162306a36Sopenharmony_ci .fb_setcolreg = sh7760_setcolreg, 35262306a36Sopenharmony_ci .fb_set_par = sh7760fb_set_par, 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void sh7760fb_free_mem(struct fb_info *info) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct sh7760fb_par *par = info->par; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (!info->screen_base) 36062306a36Sopenharmony_ci return; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci dma_free_coherent(info->device, info->screen_size, 36362306a36Sopenharmony_ci info->screen_base, par->fbdma); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci par->fbdma = 0; 36662306a36Sopenharmony_ci info->screen_base = NULL; 36762306a36Sopenharmony_ci info->screen_size = 0; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/* allocate the framebuffer memory. This memory must be in Area3, 37162306a36Sopenharmony_ci * (dictated by the DMA engine) and contiguous, at a 512 byte boundary. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_cistatic int sh7760fb_alloc_mem(struct fb_info *info) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct sh7760fb_par *par = info->par; 37662306a36Sopenharmony_ci void *fbmem; 37762306a36Sopenharmony_ci unsigned long vram; 37862306a36Sopenharmony_ci int ret, bpp; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (info->screen_base) 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* get color info from register value */ 38462306a36Sopenharmony_ci ret = sh7760fb_get_color_info(info, par->pd->lddfr, &bpp, NULL); 38562306a36Sopenharmony_ci if (ret) { 38662306a36Sopenharmony_ci printk(KERN_ERR "colinfo\n"); 38762306a36Sopenharmony_ci return ret; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* min VRAM: xres_min = 16, yres_min = 1, bpp = 1: 2byte -> 1 page 39162306a36Sopenharmony_ci max VRAM: xres_max = 1024, yres_max = 1024, bpp = 16: 2MB */ 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci vram = info->var.xres * info->var.yres; 39462306a36Sopenharmony_ci if (info->var.grayscale) { 39562306a36Sopenharmony_ci if (bpp == 1) 39662306a36Sopenharmony_ci vram >>= 3; 39762306a36Sopenharmony_ci else if (bpp == 2) 39862306a36Sopenharmony_ci vram >>= 2; 39962306a36Sopenharmony_ci else if (bpp == 4) 40062306a36Sopenharmony_ci vram >>= 1; 40162306a36Sopenharmony_ci } else if (bpp > 8) 40262306a36Sopenharmony_ci vram *= 2; 40362306a36Sopenharmony_ci if ((vram < 1) || (vram > 1024 * 2048)) { 40462306a36Sopenharmony_ci fb_dbg(info, "too much VRAM required. Check settings\n"); 40562306a36Sopenharmony_ci return -ENODEV; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (vram < PAGE_SIZE) 40962306a36Sopenharmony_ci vram = PAGE_SIZE; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci fbmem = dma_alloc_coherent(info->device, vram, &par->fbdma, GFP_KERNEL); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (!fbmem) 41462306a36Sopenharmony_ci return -ENOMEM; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if ((par->fbdma & SH7760FB_DMA_MASK) != SH7760FB_DMA_MASK) { 41762306a36Sopenharmony_ci sh7760fb_free_mem(info); 41862306a36Sopenharmony_ci dev_err(info->device, "kernel gave me memory at 0x%08lx, which is" 41962306a36Sopenharmony_ci "unusable for the LCDC\n", (unsigned long)par->fbdma); 42062306a36Sopenharmony_ci return -ENOMEM; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci info->screen_base = fbmem; 42462306a36Sopenharmony_ci info->screen_size = vram; 42562306a36Sopenharmony_ci info->fix.smem_start = (unsigned long)info->screen_base; 42662306a36Sopenharmony_ci info->fix.smem_len = info->screen_size; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return 0; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic int sh7760fb_probe(struct platform_device *pdev) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct fb_info *info; 43462306a36Sopenharmony_ci struct resource *res; 43562306a36Sopenharmony_ci struct sh7760fb_par *par; 43662306a36Sopenharmony_ci int ret; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 43962306a36Sopenharmony_ci if (unlikely(res == NULL)) { 44062306a36Sopenharmony_ci dev_err(&pdev->dev, "invalid resource\n"); 44162306a36Sopenharmony_ci return -EINVAL; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct sh7760fb_par), &pdev->dev); 44562306a36Sopenharmony_ci if (!info) 44662306a36Sopenharmony_ci return -ENOMEM; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci par = info->par; 44962306a36Sopenharmony_ci par->dev = pdev; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci par->pd = pdev->dev.platform_data; 45262306a36Sopenharmony_ci if (!par->pd) { 45362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "no display setup data!\n"); 45462306a36Sopenharmony_ci ret = -ENODEV; 45562306a36Sopenharmony_ci goto out_fb; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci par->ioarea = request_mem_region(res->start, 45962306a36Sopenharmony_ci resource_size(res), pdev->name); 46062306a36Sopenharmony_ci if (!par->ioarea) { 46162306a36Sopenharmony_ci dev_err(&pdev->dev, "mmio area busy\n"); 46262306a36Sopenharmony_ci ret = -EBUSY; 46362306a36Sopenharmony_ci goto out_fb; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci par->base = ioremap(res->start, resource_size(res)); 46762306a36Sopenharmony_ci if (!par->base) { 46862306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot remap\n"); 46962306a36Sopenharmony_ci ret = -ENODEV; 47062306a36Sopenharmony_ci goto out_res; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci iowrite16(0, par->base + LDINTR); /* disable vsync irq */ 47462306a36Sopenharmony_ci par->irq = platform_get_irq(pdev, 0); 47562306a36Sopenharmony_ci if (par->irq >= 0) { 47662306a36Sopenharmony_ci ret = request_irq(par->irq, sh7760fb_irq, 0, 47762306a36Sopenharmony_ci "sh7760-lcdc", &par->vsync); 47862306a36Sopenharmony_ci if (ret) { 47962306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot grab IRQ\n"); 48062306a36Sopenharmony_ci par->irq = -ENXIO; 48162306a36Sopenharmony_ci } else 48262306a36Sopenharmony_ci disable_irq_nosync(par->irq); 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci fb_videomode_to_var(&info->var, par->pd->def_mode); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci ret = sh7760fb_alloc_mem(info); 48862306a36Sopenharmony_ci if (ret) { 48962306a36Sopenharmony_ci dev_dbg(info->device, "framebuffer memory allocation failed!\n"); 49062306a36Sopenharmony_ci goto out_unmap; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* fixup color register bitpositions. These are fixed by hardware */ 49662306a36Sopenharmony_ci info->var.red.offset = 11; 49762306a36Sopenharmony_ci info->var.red.length = 5; 49862306a36Sopenharmony_ci info->var.red.msb_right = 0; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci info->var.green.offset = 5; 50162306a36Sopenharmony_ci info->var.green.length = 6; 50262306a36Sopenharmony_ci info->var.green.msb_right = 0; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci info->var.blue.offset = 0; 50562306a36Sopenharmony_ci info->var.blue.length = 5; 50662306a36Sopenharmony_ci info->var.blue.msb_right = 0; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci info->var.transp.offset = 0; 50962306a36Sopenharmony_ci info->var.transp.length = 0; 51062306a36Sopenharmony_ci info->var.transp.msb_right = 0; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci strcpy(info->fix.id, "sh7760-lcdc"); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* set the DON2 bit now, before cmap allocation, as it will randomize 51562306a36Sopenharmony_ci * palette memory. 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_ci iowrite16(LDCNTR_DON2, par->base + LDCNTR); 51862306a36Sopenharmony_ci info->fbops = &sh7760fb_ops; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci ret = fb_alloc_cmap(&info->cmap, 256, 0); 52162306a36Sopenharmony_ci if (ret) { 52262306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Unable to allocate cmap memory\n"); 52362306a36Sopenharmony_ci goto out_mem; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci ret = register_framebuffer(info); 52762306a36Sopenharmony_ci if (ret < 0) { 52862306a36Sopenharmony_ci dev_dbg(&pdev->dev, "cannot register fb!\n"); 52962306a36Sopenharmony_ci goto out_cmap; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci platform_set_drvdata(pdev, info); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci printk(KERN_INFO "%s: memory at phys 0x%08lx-0x%08lx, size %ld KiB\n", 53462306a36Sopenharmony_ci pdev->name, 53562306a36Sopenharmony_ci (unsigned long)par->fbdma, 53662306a36Sopenharmony_ci (unsigned long)(par->fbdma + info->screen_size - 1), 53762306a36Sopenharmony_ci info->screen_size >> 10); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ciout_cmap: 54262306a36Sopenharmony_ci sh7760fb_blank(FB_BLANK_POWERDOWN, info); 54362306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 54462306a36Sopenharmony_ciout_mem: 54562306a36Sopenharmony_ci sh7760fb_free_mem(info); 54662306a36Sopenharmony_ciout_unmap: 54762306a36Sopenharmony_ci if (par->irq >= 0) 54862306a36Sopenharmony_ci free_irq(par->irq, &par->vsync); 54962306a36Sopenharmony_ci iounmap(par->base); 55062306a36Sopenharmony_ciout_res: 55162306a36Sopenharmony_ci release_mem_region(res->start, resource_size(res)); 55262306a36Sopenharmony_ciout_fb: 55362306a36Sopenharmony_ci framebuffer_release(info); 55462306a36Sopenharmony_ci return ret; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic void sh7760fb_remove(struct platform_device *dev) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct fb_info *info = platform_get_drvdata(dev); 56062306a36Sopenharmony_ci struct sh7760fb_par *par = info->par; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci sh7760fb_blank(FB_BLANK_POWERDOWN, info); 56362306a36Sopenharmony_ci unregister_framebuffer(info); 56462306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 56562306a36Sopenharmony_ci sh7760fb_free_mem(info); 56662306a36Sopenharmony_ci if (par->irq >= 0) 56762306a36Sopenharmony_ci free_irq(par->irq, &par->vsync); 56862306a36Sopenharmony_ci iounmap(par->base); 56962306a36Sopenharmony_ci release_mem_region(par->ioarea->start, resource_size(par->ioarea)); 57062306a36Sopenharmony_ci framebuffer_release(info); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic struct platform_driver sh7760_lcdc_driver = { 57462306a36Sopenharmony_ci .driver = { 57562306a36Sopenharmony_ci .name = "sh7760-lcdc", 57662306a36Sopenharmony_ci }, 57762306a36Sopenharmony_ci .probe = sh7760fb_probe, 57862306a36Sopenharmony_ci .remove_new = sh7760fb_remove, 57962306a36Sopenharmony_ci}; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cimodule_platform_driver(sh7760_lcdc_driver); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ciMODULE_AUTHOR("Nobuhiro Iwamatsu, Manuel Lauss"); 58462306a36Sopenharmony_ciMODULE_DESCRIPTION("FBdev for SH7760/63 integrated LCD Controller"); 58562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 586