18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SH7760/SH7763 LCDC Framebuffer driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) 2006-2008 MSC Vertriebsges.m.b.H., 68c2ecf20Sopenharmony_ci * Manuel Lauss <mano@roarinelk.homelinux.net> 78c2ecf20Sopenharmony_ci * (c) 2008 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * PLEASE HAVE A LOOK AT Documentation/fb/sh7760fb.rst! 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Thanks to Siegfried Schaefer <s.schaefer at schaefer-edv.de> 128c2ecf20Sopenharmony_ci * for his original source and testing! 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * sh7760_setcolreg get from drivers/video/sh_mobile_lcdcfb.c 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/completion.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 208c2ecf20Sopenharmony_ci#include <linux/fb.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/io.h> 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <asm/sh7760fb.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct sh7760fb_par { 318c2ecf20Sopenharmony_ci void __iomem *base; 328c2ecf20Sopenharmony_ci int irq; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci struct sh7760fb_platdata *pd; /* display information */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci dma_addr_t fbdma; /* physical address */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci int rot; /* rotation enabled? */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci u32 pseudo_palette[16]; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci struct platform_device *dev; 438c2ecf20Sopenharmony_ci struct resource *ioarea; 448c2ecf20Sopenharmony_ci struct completion vsync; /* vsync irq event */ 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic irqreturn_t sh7760fb_irq(int irq, void *data) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct completion *c = data; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci complete(c); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return IRQ_HANDLED; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* wait_for_lps - wait until power supply has reached a certain state. */ 578c2ecf20Sopenharmony_cistatic int wait_for_lps(struct sh7760fb_par *par, int val) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci int i = 100; 608c2ecf20Sopenharmony_ci while (--i && ((ioread16(par->base + LDPMMR) & 3) != val)) 618c2ecf20Sopenharmony_ci msleep(1); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (i <= 0) 648c2ecf20Sopenharmony_ci return -ETIMEDOUT; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* en/disable the LCDC */ 708c2ecf20Sopenharmony_cistatic int sh7760fb_blank(int blank, struct fb_info *info) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct sh7760fb_par *par = info->par; 738c2ecf20Sopenharmony_ci struct sh7760fb_platdata *pd = par->pd; 748c2ecf20Sopenharmony_ci unsigned short cntr = ioread16(par->base + LDCNTR); 758c2ecf20Sopenharmony_ci unsigned short intr = ioread16(par->base + LDINTR); 768c2ecf20Sopenharmony_ci int lps; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (blank == FB_BLANK_UNBLANK) { 798c2ecf20Sopenharmony_ci intr |= VINT_START; 808c2ecf20Sopenharmony_ci cntr = LDCNTR_DON2 | LDCNTR_DON; 818c2ecf20Sopenharmony_ci lps = 3; 828c2ecf20Sopenharmony_ci } else { 838c2ecf20Sopenharmony_ci intr &= ~VINT_START; 848c2ecf20Sopenharmony_ci cntr = LDCNTR_DON2; 858c2ecf20Sopenharmony_ci lps = 0; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (pd->blank) 898c2ecf20Sopenharmony_ci pd->blank(blank); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci iowrite16(intr, par->base + LDINTR); 928c2ecf20Sopenharmony_ci iowrite16(cntr, par->base + LDCNTR); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return wait_for_lps(par, lps); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int sh7760_setcolreg (u_int regno, 988c2ecf20Sopenharmony_ci u_int red, u_int green, u_int blue, 998c2ecf20Sopenharmony_ci u_int transp, struct fb_info *info) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci u32 *palette = info->pseudo_palette; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (regno >= 16) 1048c2ecf20Sopenharmony_ci return -EINVAL; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* only FB_VISUAL_TRUECOLOR supported */ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci red >>= 16 - info->var.red.length; 1098c2ecf20Sopenharmony_ci green >>= 16 - info->var.green.length; 1108c2ecf20Sopenharmony_ci blue >>= 16 - info->var.blue.length; 1118c2ecf20Sopenharmony_ci transp >>= 16 - info->var.transp.length; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci palette[regno] = (red << info->var.red.offset) | 1148c2ecf20Sopenharmony_ci (green << info->var.green.offset) | 1158c2ecf20Sopenharmony_ci (blue << info->var.blue.offset) | 1168c2ecf20Sopenharmony_ci (transp << info->var.transp.offset); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int sh7760fb_get_color_info(struct device *dev, 1228c2ecf20Sopenharmony_ci u16 lddfr, int *bpp, int *gray) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci int lbpp, lgray; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci lgray = lbpp = 0; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci switch (lddfr & LDDFR_COLOR_MASK) { 1298c2ecf20Sopenharmony_ci case LDDFR_1BPP_MONO: 1308c2ecf20Sopenharmony_ci lgray = 1; 1318c2ecf20Sopenharmony_ci lbpp = 1; 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci case LDDFR_2BPP_MONO: 1348c2ecf20Sopenharmony_ci lgray = 1; 1358c2ecf20Sopenharmony_ci lbpp = 2; 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci case LDDFR_4BPP_MONO: 1388c2ecf20Sopenharmony_ci lgray = 1; 1398c2ecf20Sopenharmony_ci case LDDFR_4BPP: 1408c2ecf20Sopenharmony_ci lbpp = 4; 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci case LDDFR_6BPP_MONO: 1438c2ecf20Sopenharmony_ci lgray = 1; 1448c2ecf20Sopenharmony_ci case LDDFR_8BPP: 1458c2ecf20Sopenharmony_ci lbpp = 8; 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci case LDDFR_16BPP_RGB555: 1488c2ecf20Sopenharmony_ci case LDDFR_16BPP_RGB565: 1498c2ecf20Sopenharmony_ci lbpp = 16; 1508c2ecf20Sopenharmony_ci lgray = 0; 1518c2ecf20Sopenharmony_ci break; 1528c2ecf20Sopenharmony_ci default: 1538c2ecf20Sopenharmony_ci dev_dbg(dev, "unsupported LDDFR bit depth.\n"); 1548c2ecf20Sopenharmony_ci return -EINVAL; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (bpp) 1588c2ecf20Sopenharmony_ci *bpp = lbpp; 1598c2ecf20Sopenharmony_ci if (gray) 1608c2ecf20Sopenharmony_ci *gray = lgray; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int sh7760fb_check_var(struct fb_var_screeninfo *var, 1668c2ecf20Sopenharmony_ci struct fb_info *info) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct fb_fix_screeninfo *fix = &info->fix; 1698c2ecf20Sopenharmony_ci struct sh7760fb_par *par = info->par; 1708c2ecf20Sopenharmony_ci int ret, bpp; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* get color info from register value */ 1738c2ecf20Sopenharmony_ci ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL); 1748c2ecf20Sopenharmony_ci if (ret) 1758c2ecf20Sopenharmony_ci return ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci var->bits_per_pixel = bpp; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if ((var->grayscale) && (var->bits_per_pixel == 1)) 1808c2ecf20Sopenharmony_ci fix->visual = FB_VISUAL_MONO10; 1818c2ecf20Sopenharmony_ci else if (var->bits_per_pixel >= 15) 1828c2ecf20Sopenharmony_ci fix->visual = FB_VISUAL_TRUECOLOR; 1838c2ecf20Sopenharmony_ci else 1848c2ecf20Sopenharmony_ci fix->visual = FB_VISUAL_PSEUDOCOLOR; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* TODO: add some more validation here */ 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/* 1918c2ecf20Sopenharmony_ci * sh7760fb_set_par - set videomode. 1928c2ecf20Sopenharmony_ci * 1938c2ecf20Sopenharmony_ci * NOTE: The rotation, grayscale and DSTN codepaths are 1948c2ecf20Sopenharmony_ci * totally untested! 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_cistatic int sh7760fb_set_par(struct fb_info *info) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct sh7760fb_par *par = info->par; 1998c2ecf20Sopenharmony_ci struct fb_videomode *vm = par->pd->def_mode; 2008c2ecf20Sopenharmony_ci unsigned long sbase, dstn_off, ldsarl, stride; 2018c2ecf20Sopenharmony_ci unsigned short hsynp, hsynw, htcn, hdcn; 2028c2ecf20Sopenharmony_ci unsigned short vsynp, vsynw, vtln, vdln; 2038c2ecf20Sopenharmony_ci unsigned short lddfr, ldmtr; 2048c2ecf20Sopenharmony_ci int ret, bpp, gray; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci par->rot = par->pd->rotate; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* rotate only works with xres <= 320 */ 2098c2ecf20Sopenharmony_ci if (par->rot && (vm->xres > 320)) { 2108c2ecf20Sopenharmony_ci dev_dbg(info->dev, "rotation disabled due to display size\n"); 2118c2ecf20Sopenharmony_ci par->rot = 0; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* calculate LCDC reg vals from display parameters */ 2158c2ecf20Sopenharmony_ci hsynp = vm->right_margin + vm->xres; 2168c2ecf20Sopenharmony_ci hsynw = vm->hsync_len; 2178c2ecf20Sopenharmony_ci htcn = vm->left_margin + hsynp + hsynw; 2188c2ecf20Sopenharmony_ci hdcn = vm->xres; 2198c2ecf20Sopenharmony_ci vsynp = vm->lower_margin + vm->yres; 2208c2ecf20Sopenharmony_ci vsynw = vm->vsync_len; 2218c2ecf20Sopenharmony_ci vtln = vm->upper_margin + vsynp + vsynw; 2228c2ecf20Sopenharmony_ci vdln = vm->yres; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* get color info from register value */ 2258c2ecf20Sopenharmony_ci ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, &gray); 2268c2ecf20Sopenharmony_ci if (ret) 2278c2ecf20Sopenharmony_ci return ret; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci dev_dbg(info->dev, "%dx%d %dbpp %s (orientation %s)\n", hdcn, 2308c2ecf20Sopenharmony_ci vdln, bpp, gray ? "grayscale" : "color", 2318c2ecf20Sopenharmony_ci par->rot ? "rotated" : "normal"); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_LITTLE_ENDIAN 2348c2ecf20Sopenharmony_ci lddfr = par->pd->lddfr | (1 << 8); 2358c2ecf20Sopenharmony_ci#else 2368c2ecf20Sopenharmony_ci lddfr = par->pd->lddfr & ~(1 << 8); 2378c2ecf20Sopenharmony_ci#endif 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci ldmtr = par->pd->ldmtr; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (!(vm->sync & FB_SYNC_HOR_HIGH_ACT)) 2428c2ecf20Sopenharmony_ci ldmtr |= LDMTR_CL1POL; 2438c2ecf20Sopenharmony_ci if (!(vm->sync & FB_SYNC_VERT_HIGH_ACT)) 2448c2ecf20Sopenharmony_ci ldmtr |= LDMTR_FLMPOL; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* shut down LCDC before changing display parameters */ 2478c2ecf20Sopenharmony_ci sh7760fb_blank(FB_BLANK_POWERDOWN, info); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci iowrite16(par->pd->ldickr, par->base + LDICKR); /* pixclock */ 2508c2ecf20Sopenharmony_ci iowrite16(ldmtr, par->base + LDMTR); /* polarities */ 2518c2ecf20Sopenharmony_ci iowrite16(lddfr, par->base + LDDFR); /* color/depth */ 2528c2ecf20Sopenharmony_ci iowrite16((par->rot ? 1 << 13 : 0), par->base + LDSMR); /* rotate */ 2538c2ecf20Sopenharmony_ci iowrite16(par->pd->ldpmmr, par->base + LDPMMR); /* Power Management */ 2548c2ecf20Sopenharmony_ci iowrite16(par->pd->ldpspr, par->base + LDPSPR); /* Power Supply Ctrl */ 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* display resolution */ 2578c2ecf20Sopenharmony_ci iowrite16(((htcn >> 3) - 1) | (((hdcn >> 3) - 1) << 8), 2588c2ecf20Sopenharmony_ci par->base + LDHCNR); 2598c2ecf20Sopenharmony_ci iowrite16(vdln - 1, par->base + LDVDLNR); 2608c2ecf20Sopenharmony_ci iowrite16(vtln - 1, par->base + LDVTLNR); 2618c2ecf20Sopenharmony_ci /* h/v sync signals */ 2628c2ecf20Sopenharmony_ci iowrite16((vsynp - 1) | ((vsynw - 1) << 12), par->base + LDVSYNR); 2638c2ecf20Sopenharmony_ci iowrite16(((hsynp >> 3) - 1) | (((hsynw >> 3) - 1) << 12), 2648c2ecf20Sopenharmony_ci par->base + LDHSYNR); 2658c2ecf20Sopenharmony_ci /* AC modulation sig */ 2668c2ecf20Sopenharmony_ci iowrite16(par->pd->ldaclnr, par->base + LDACLNR); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci stride = (par->rot) ? vtln : hdcn; 2698c2ecf20Sopenharmony_ci if (!gray) 2708c2ecf20Sopenharmony_ci stride *= (bpp + 7) >> 3; 2718c2ecf20Sopenharmony_ci else { 2728c2ecf20Sopenharmony_ci if (bpp == 1) 2738c2ecf20Sopenharmony_ci stride >>= 3; 2748c2ecf20Sopenharmony_ci else if (bpp == 2) 2758c2ecf20Sopenharmony_ci stride >>= 2; 2768c2ecf20Sopenharmony_ci else if (bpp == 4) 2778c2ecf20Sopenharmony_ci stride >>= 1; 2788c2ecf20Sopenharmony_ci /* 6 bpp == 8 bpp */ 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* if rotated, stride must be power of 2 */ 2828c2ecf20Sopenharmony_ci if (par->rot) { 2838c2ecf20Sopenharmony_ci unsigned long bit = 1 << 31; 2848c2ecf20Sopenharmony_ci while (bit) { 2858c2ecf20Sopenharmony_ci if (stride & bit) 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci bit >>= 1; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci if (stride & ~bit) 2908c2ecf20Sopenharmony_ci stride = bit << 1; /* not P-o-2, round up */ 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci iowrite16(stride, par->base + LDLAOR); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* set display mem start address */ 2958c2ecf20Sopenharmony_ci sbase = (unsigned long)par->fbdma; 2968c2ecf20Sopenharmony_ci if (par->rot) 2978c2ecf20Sopenharmony_ci sbase += (hdcn - 1) * stride; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci iowrite32(sbase, par->base + LDSARU); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* 3028c2ecf20Sopenharmony_ci * for DSTN need to set address for lower half. 3038c2ecf20Sopenharmony_ci * I (mlau) don't know which address to set it to, 3048c2ecf20Sopenharmony_ci * so I guessed at (stride * yres/2). 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_ci if (((ldmtr & 0x003f) >= LDMTR_DSTN_MONO_8) && 3078c2ecf20Sopenharmony_ci ((ldmtr & 0x003f) <= LDMTR_DSTN_COLOR_16)) { 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci dev_dbg(info->dev, " ***** DSTN untested! *****\n"); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci dstn_off = stride; 3128c2ecf20Sopenharmony_ci if (par->rot) 3138c2ecf20Sopenharmony_ci dstn_off *= hdcn >> 1; 3148c2ecf20Sopenharmony_ci else 3158c2ecf20Sopenharmony_ci dstn_off *= vdln >> 1; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ldsarl = sbase + dstn_off; 3188c2ecf20Sopenharmony_ci } else 3198c2ecf20Sopenharmony_ci ldsarl = 0; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci iowrite32(ldsarl, par->base + LDSARL); /* mem for lower half of DSTN */ 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci info->fix.line_length = stride; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci sh7760fb_check_var(&info->var, info); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci sh7760fb_blank(FB_BLANK_UNBLANK, info); /* panel on! */ 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci dev_dbg(info->dev, "hdcn : %6d htcn : %6d\n", hdcn, htcn); 3308c2ecf20Sopenharmony_ci dev_dbg(info->dev, "hsynw : %6d hsynp : %6d\n", hsynw, hsynp); 3318c2ecf20Sopenharmony_ci dev_dbg(info->dev, "vdln : %6d vtln : %6d\n", vdln, vtln); 3328c2ecf20Sopenharmony_ci dev_dbg(info->dev, "vsynw : %6d vsynp : %6d\n", vsynw, vsynp); 3338c2ecf20Sopenharmony_ci dev_dbg(info->dev, "clksrc: %6d clkdiv: %6d\n", 3348c2ecf20Sopenharmony_ci (par->pd->ldickr >> 12) & 3, par->pd->ldickr & 0x1f); 3358c2ecf20Sopenharmony_ci dev_dbg(info->dev, "ldpmmr: 0x%04x ldpspr: 0x%04x\n", par->pd->ldpmmr, 3368c2ecf20Sopenharmony_ci par->pd->ldpspr); 3378c2ecf20Sopenharmony_ci dev_dbg(info->dev, "ldmtr : 0x%04x lddfr : 0x%04x\n", ldmtr, lddfr); 3388c2ecf20Sopenharmony_ci dev_dbg(info->dev, "ldlaor: %ld\n", stride); 3398c2ecf20Sopenharmony_ci dev_dbg(info->dev, "ldsaru: 0x%08lx ldsarl: 0x%08lx\n", sbase, ldsarl); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic const struct fb_ops sh7760fb_ops = { 3458c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3468c2ecf20Sopenharmony_ci .fb_blank = sh7760fb_blank, 3478c2ecf20Sopenharmony_ci .fb_check_var = sh7760fb_check_var, 3488c2ecf20Sopenharmony_ci .fb_setcolreg = sh7760_setcolreg, 3498c2ecf20Sopenharmony_ci .fb_set_par = sh7760fb_set_par, 3508c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 3518c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 3528c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 3538c2ecf20Sopenharmony_ci}; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic void sh7760fb_free_mem(struct fb_info *info) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct sh7760fb_par *par = info->par; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (!info->screen_base) 3608c2ecf20Sopenharmony_ci return; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci dma_free_coherent(info->dev, info->screen_size, 3638c2ecf20Sopenharmony_ci info->screen_base, par->fbdma); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci par->fbdma = 0; 3668c2ecf20Sopenharmony_ci info->screen_base = NULL; 3678c2ecf20Sopenharmony_ci info->screen_size = 0; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci/* allocate the framebuffer memory. This memory must be in Area3, 3718c2ecf20Sopenharmony_ci * (dictated by the DMA engine) and contiguous, at a 512 byte boundary. 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_cistatic int sh7760fb_alloc_mem(struct fb_info *info) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct sh7760fb_par *par = info->par; 3768c2ecf20Sopenharmony_ci void *fbmem; 3778c2ecf20Sopenharmony_ci unsigned long vram; 3788c2ecf20Sopenharmony_ci int ret, bpp; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (info->screen_base) 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* get color info from register value */ 3848c2ecf20Sopenharmony_ci ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL); 3858c2ecf20Sopenharmony_ci if (ret) { 3868c2ecf20Sopenharmony_ci printk(KERN_ERR "colinfo\n"); 3878c2ecf20Sopenharmony_ci return ret; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* min VRAM: xres_min = 16, yres_min = 1, bpp = 1: 2byte -> 1 page 3918c2ecf20Sopenharmony_ci max VRAM: xres_max = 1024, yres_max = 1024, bpp = 16: 2MB */ 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci vram = info->var.xres * info->var.yres; 3948c2ecf20Sopenharmony_ci if (info->var.grayscale) { 3958c2ecf20Sopenharmony_ci if (bpp == 1) 3968c2ecf20Sopenharmony_ci vram >>= 3; 3978c2ecf20Sopenharmony_ci else if (bpp == 2) 3988c2ecf20Sopenharmony_ci vram >>= 2; 3998c2ecf20Sopenharmony_ci else if (bpp == 4) 4008c2ecf20Sopenharmony_ci vram >>= 1; 4018c2ecf20Sopenharmony_ci } else if (bpp > 8) 4028c2ecf20Sopenharmony_ci vram *= 2; 4038c2ecf20Sopenharmony_ci if ((vram < 1) || (vram > 1024 * 2048)) { 4048c2ecf20Sopenharmony_ci dev_dbg(info->dev, "too much VRAM required. Check settings\n"); 4058c2ecf20Sopenharmony_ci return -ENODEV; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (vram < PAGE_SIZE) 4098c2ecf20Sopenharmony_ci vram = PAGE_SIZE; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci fbmem = dma_alloc_coherent(info->dev, vram, &par->fbdma, GFP_KERNEL); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (!fbmem) 4148c2ecf20Sopenharmony_ci return -ENOMEM; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if ((par->fbdma & SH7760FB_DMA_MASK) != SH7760FB_DMA_MASK) { 4178c2ecf20Sopenharmony_ci sh7760fb_free_mem(info); 4188c2ecf20Sopenharmony_ci dev_err(info->dev, "kernel gave me memory at 0x%08lx, which is" 4198c2ecf20Sopenharmony_ci "unusable for the LCDC\n", (unsigned long)par->fbdma); 4208c2ecf20Sopenharmony_ci return -ENOMEM; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci info->screen_base = fbmem; 4248c2ecf20Sopenharmony_ci info->screen_size = vram; 4258c2ecf20Sopenharmony_ci info->fix.smem_start = (unsigned long)info->screen_base; 4268c2ecf20Sopenharmony_ci info->fix.smem_len = info->screen_size; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int sh7760fb_probe(struct platform_device *pdev) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct fb_info *info; 4348c2ecf20Sopenharmony_ci struct resource *res; 4358c2ecf20Sopenharmony_ci struct sh7760fb_par *par; 4368c2ecf20Sopenharmony_ci int ret; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4398c2ecf20Sopenharmony_ci if (unlikely(res == NULL)) { 4408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "invalid resource\n"); 4418c2ecf20Sopenharmony_ci return -EINVAL; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct sh7760fb_par), &pdev->dev); 4458c2ecf20Sopenharmony_ci if (!info) 4468c2ecf20Sopenharmony_ci return -ENOMEM; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci par = info->par; 4498c2ecf20Sopenharmony_ci par->dev = pdev; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci par->pd = pdev->dev.platform_data; 4528c2ecf20Sopenharmony_ci if (!par->pd) { 4538c2ecf20Sopenharmony_ci dev_dbg(info->dev, "no display setup data!\n"); 4548c2ecf20Sopenharmony_ci ret = -ENODEV; 4558c2ecf20Sopenharmony_ci goto out_fb; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci par->ioarea = request_mem_region(res->start, 4598c2ecf20Sopenharmony_ci resource_size(res), pdev->name); 4608c2ecf20Sopenharmony_ci if (!par->ioarea) { 4618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "mmio area busy\n"); 4628c2ecf20Sopenharmony_ci ret = -EBUSY; 4638c2ecf20Sopenharmony_ci goto out_fb; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci par->base = ioremap(res->start, resource_size(res)); 4678c2ecf20Sopenharmony_ci if (!par->base) { 4688c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot remap\n"); 4698c2ecf20Sopenharmony_ci ret = -ENODEV; 4708c2ecf20Sopenharmony_ci goto out_res; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci iowrite16(0, par->base + LDINTR); /* disable vsync irq */ 4748c2ecf20Sopenharmony_ci par->irq = platform_get_irq(pdev, 0); 4758c2ecf20Sopenharmony_ci if (par->irq >= 0) { 4768c2ecf20Sopenharmony_ci ret = request_irq(par->irq, sh7760fb_irq, 0, 4778c2ecf20Sopenharmony_ci "sh7760-lcdc", &par->vsync); 4788c2ecf20Sopenharmony_ci if (ret) { 4798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot grab IRQ\n"); 4808c2ecf20Sopenharmony_ci par->irq = -ENXIO; 4818c2ecf20Sopenharmony_ci } else 4828c2ecf20Sopenharmony_ci disable_irq_nosync(par->irq); 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci fb_videomode_to_var(&info->var, par->pd->def_mode); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci ret = sh7760fb_alloc_mem(info); 4888c2ecf20Sopenharmony_ci if (ret) { 4898c2ecf20Sopenharmony_ci dev_dbg(info->dev, "framebuffer memory allocation failed!\n"); 4908c2ecf20Sopenharmony_ci goto out_unmap; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* fixup color register bitpositions. These are fixed by hardware */ 4968c2ecf20Sopenharmony_ci info->var.red.offset = 11; 4978c2ecf20Sopenharmony_ci info->var.red.length = 5; 4988c2ecf20Sopenharmony_ci info->var.red.msb_right = 0; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci info->var.green.offset = 5; 5018c2ecf20Sopenharmony_ci info->var.green.length = 6; 5028c2ecf20Sopenharmony_ci info->var.green.msb_right = 0; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci info->var.blue.offset = 0; 5058c2ecf20Sopenharmony_ci info->var.blue.length = 5; 5068c2ecf20Sopenharmony_ci info->var.blue.msb_right = 0; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci info->var.transp.offset = 0; 5098c2ecf20Sopenharmony_ci info->var.transp.length = 0; 5108c2ecf20Sopenharmony_ci info->var.transp.msb_right = 0; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci strcpy(info->fix.id, "sh7760-lcdc"); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* set the DON2 bit now, before cmap allocation, as it will randomize 5158c2ecf20Sopenharmony_ci * palette memory. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ci iowrite16(LDCNTR_DON2, par->base + LDCNTR); 5188c2ecf20Sopenharmony_ci info->fbops = &sh7760fb_ops; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci ret = fb_alloc_cmap(&info->cmap, 256, 0); 5218c2ecf20Sopenharmony_ci if (ret) { 5228c2ecf20Sopenharmony_ci dev_dbg(info->dev, "Unable to allocate cmap memory\n"); 5238c2ecf20Sopenharmony_ci goto out_mem; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci ret = register_framebuffer(info); 5278c2ecf20Sopenharmony_ci if (ret < 0) { 5288c2ecf20Sopenharmony_ci dev_dbg(info->dev, "cannot register fb!\n"); 5298c2ecf20Sopenharmony_ci goto out_cmap; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: memory at phys 0x%08lx-0x%08lx, size %ld KiB\n", 5348c2ecf20Sopenharmony_ci pdev->name, 5358c2ecf20Sopenharmony_ci (unsigned long)par->fbdma, 5368c2ecf20Sopenharmony_ci (unsigned long)(par->fbdma + info->screen_size - 1), 5378c2ecf20Sopenharmony_ci info->screen_size >> 10); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return 0; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ciout_cmap: 5428c2ecf20Sopenharmony_ci sh7760fb_blank(FB_BLANK_POWERDOWN, info); 5438c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 5448c2ecf20Sopenharmony_ciout_mem: 5458c2ecf20Sopenharmony_ci sh7760fb_free_mem(info); 5468c2ecf20Sopenharmony_ciout_unmap: 5478c2ecf20Sopenharmony_ci if (par->irq >= 0) 5488c2ecf20Sopenharmony_ci free_irq(par->irq, &par->vsync); 5498c2ecf20Sopenharmony_ci iounmap(par->base); 5508c2ecf20Sopenharmony_ciout_res: 5518c2ecf20Sopenharmony_ci release_mem_region(res->start, resource_size(res)); 5528c2ecf20Sopenharmony_ciout_fb: 5538c2ecf20Sopenharmony_ci framebuffer_release(info); 5548c2ecf20Sopenharmony_ci return ret; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic int sh7760fb_remove(struct platform_device *dev) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct fb_info *info = platform_get_drvdata(dev); 5608c2ecf20Sopenharmony_ci struct sh7760fb_par *par = info->par; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci sh7760fb_blank(FB_BLANK_POWERDOWN, info); 5638c2ecf20Sopenharmony_ci unregister_framebuffer(info); 5648c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 5658c2ecf20Sopenharmony_ci sh7760fb_free_mem(info); 5668c2ecf20Sopenharmony_ci if (par->irq >= 0) 5678c2ecf20Sopenharmony_ci free_irq(par->irq, &par->vsync); 5688c2ecf20Sopenharmony_ci iounmap(par->base); 5698c2ecf20Sopenharmony_ci release_mem_region(par->ioarea->start, resource_size(par->ioarea)); 5708c2ecf20Sopenharmony_ci framebuffer_release(info); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci return 0; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic struct platform_driver sh7760_lcdc_driver = { 5768c2ecf20Sopenharmony_ci .driver = { 5778c2ecf20Sopenharmony_ci .name = "sh7760-lcdc", 5788c2ecf20Sopenharmony_ci }, 5798c2ecf20Sopenharmony_ci .probe = sh7760fb_probe, 5808c2ecf20Sopenharmony_ci .remove = sh7760fb_remove, 5818c2ecf20Sopenharmony_ci}; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cimodule_platform_driver(sh7760_lcdc_driver); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nobuhiro Iwamatsu, Manuel Lauss"); 5868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FBdev for SH7760/63 integrated LCD Controller"); 5878c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 588