Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * SH7760/SH7763 LCDC Framebuffer driver.
0004  *
0005  * (c) 2006-2008 MSC Vertriebsges.m.b.H.,
0006  *             Manuel Lauss <mano@roarinelk.homelinux.net>
0007  * (c) 2008 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
0008  *
0009  * PLEASE HAVE A LOOK AT Documentation/fb/sh7760fb.rst!
0010  *
0011  * Thanks to Siegfried Schaefer <s.schaefer at schaefer-edv.de>
0012  *     for his original source and testing!
0013  *
0014  * sh7760_setcolreg get from drivers/video/sh_mobile_lcdcfb.c
0015  */
0016 
0017 #include <linux/completion.h>
0018 #include <linux/delay.h>
0019 #include <linux/dma-mapping.h>
0020 #include <linux/fb.h>
0021 #include <linux/interrupt.h>
0022 #include <linux/io.h>
0023 #include <linux/kernel.h>
0024 #include <linux/module.h>
0025 #include <linux/platform_device.h>
0026 #include <linux/slab.h>
0027 
0028 #include <asm/sh7760fb.h>
0029 
0030 struct sh7760fb_par {
0031     void __iomem *base;
0032     int irq;
0033 
0034     struct sh7760fb_platdata *pd;   /* display information */
0035 
0036     dma_addr_t fbdma;   /* physical address */
0037 
0038     int rot;        /* rotation enabled? */
0039 
0040     u32 pseudo_palette[16];
0041 
0042     struct platform_device *dev;
0043     struct resource *ioarea;
0044     struct completion vsync;    /* vsync irq event */
0045 };
0046 
0047 static irqreturn_t sh7760fb_irq(int irq, void *data)
0048 {
0049     struct completion *c = data;
0050 
0051     complete(c);
0052 
0053     return IRQ_HANDLED;
0054 }
0055 
0056 /* wait_for_lps - wait until power supply has reached a certain state. */
0057 static int wait_for_lps(struct sh7760fb_par *par, int val)
0058 {
0059     int i = 100;
0060     while (--i && ((ioread16(par->base + LDPMMR) & 3) != val))
0061         msleep(1);
0062 
0063     if (i <= 0)
0064         return -ETIMEDOUT;
0065 
0066     return 0;
0067 }
0068 
0069 /* en/disable the LCDC */
0070 static int sh7760fb_blank(int blank, struct fb_info *info)
0071 {
0072     struct sh7760fb_par *par = info->par;
0073     struct sh7760fb_platdata *pd = par->pd;
0074     unsigned short cntr = ioread16(par->base + LDCNTR);
0075     unsigned short intr = ioread16(par->base + LDINTR);
0076     int lps;
0077 
0078     if (blank == FB_BLANK_UNBLANK) {
0079         intr |= VINT_START;
0080         cntr = LDCNTR_DON2 | LDCNTR_DON;
0081         lps = 3;
0082     } else {
0083         intr &= ~VINT_START;
0084         cntr = LDCNTR_DON2;
0085         lps = 0;
0086     }
0087 
0088     if (pd->blank)
0089         pd->blank(blank);
0090 
0091     iowrite16(intr, par->base + LDINTR);
0092     iowrite16(cntr, par->base + LDCNTR);
0093 
0094     return wait_for_lps(par, lps);
0095 }
0096 
0097 static int sh7760_setcolreg (u_int regno,
0098     u_int red, u_int green, u_int blue,
0099     u_int transp, struct fb_info *info)
0100 {
0101     u32 *palette = info->pseudo_palette;
0102 
0103     if (regno >= 16)
0104         return -EINVAL;
0105 
0106     /* only FB_VISUAL_TRUECOLOR supported */
0107 
0108     red >>= 16 - info->var.red.length;
0109     green >>= 16 - info->var.green.length;
0110     blue >>= 16 - info->var.blue.length;
0111     transp >>= 16 - info->var.transp.length;
0112 
0113     palette[regno] = (red << info->var.red.offset) |
0114         (green << info->var.green.offset) |
0115         (blue << info->var.blue.offset) |
0116         (transp << info->var.transp.offset);
0117 
0118     return 0;
0119 }
0120 
0121 static int sh7760fb_get_color_info(struct device *dev,
0122                    u16 lddfr, int *bpp, int *gray)
0123 {
0124     int lbpp, lgray;
0125 
0126     lgray = lbpp = 0;
0127 
0128     switch (lddfr & LDDFR_COLOR_MASK) {
0129     case LDDFR_1BPP_MONO:
0130         lgray = 1;
0131         lbpp = 1;
0132         break;
0133     case LDDFR_2BPP_MONO:
0134         lgray = 1;
0135         lbpp = 2;
0136         break;
0137     case LDDFR_4BPP_MONO:
0138         lgray = 1;
0139     case LDDFR_4BPP:
0140         lbpp = 4;
0141         break;
0142     case LDDFR_6BPP_MONO:
0143         lgray = 1;
0144     case LDDFR_8BPP:
0145         lbpp = 8;
0146         break;
0147     case LDDFR_16BPP_RGB555:
0148     case LDDFR_16BPP_RGB565:
0149         lbpp = 16;
0150         lgray = 0;
0151         break;
0152     default:
0153         dev_dbg(dev, "unsupported LDDFR bit depth.\n");
0154         return -EINVAL;
0155     }
0156 
0157     if (bpp)
0158         *bpp = lbpp;
0159     if (gray)
0160         *gray = lgray;
0161 
0162     return 0;
0163 }
0164 
0165 static int sh7760fb_check_var(struct fb_var_screeninfo *var,
0166                   struct fb_info *info)
0167 {
0168     struct fb_fix_screeninfo *fix = &info->fix;
0169     struct sh7760fb_par *par = info->par;
0170     int ret, bpp;
0171 
0172     /* get color info from register value */
0173     ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL);
0174     if (ret)
0175         return ret;
0176 
0177     var->bits_per_pixel = bpp;
0178 
0179     if ((var->grayscale) && (var->bits_per_pixel == 1))
0180         fix->visual = FB_VISUAL_MONO10;
0181     else if (var->bits_per_pixel >= 15)
0182         fix->visual = FB_VISUAL_TRUECOLOR;
0183     else
0184         fix->visual = FB_VISUAL_PSEUDOCOLOR;
0185 
0186     /* TODO: add some more validation here */
0187     return 0;
0188 }
0189 
0190 /*
0191  * sh7760fb_set_par - set videomode.
0192  *
0193  * NOTE: The rotation, grayscale and DSTN codepaths are
0194  *     totally untested!
0195  */
0196 static int sh7760fb_set_par(struct fb_info *info)
0197 {
0198     struct sh7760fb_par *par = info->par;
0199     struct fb_videomode *vm = par->pd->def_mode;
0200     unsigned long sbase, dstn_off, ldsarl, stride;
0201     unsigned short hsynp, hsynw, htcn, hdcn;
0202     unsigned short vsynp, vsynw, vtln, vdln;
0203     unsigned short lddfr, ldmtr;
0204     int ret, bpp, gray;
0205 
0206     par->rot = par->pd->rotate;
0207 
0208     /* rotate only works with xres <= 320 */
0209     if (par->rot && (vm->xres > 320)) {
0210         dev_dbg(info->dev, "rotation disabled due to display size\n");
0211         par->rot = 0;
0212     }
0213 
0214     /* calculate LCDC reg vals from display parameters */
0215     hsynp = vm->right_margin + vm->xres;
0216     hsynw = vm->hsync_len;
0217     htcn = vm->left_margin + hsynp + hsynw;
0218     hdcn = vm->xres;
0219     vsynp = vm->lower_margin + vm->yres;
0220     vsynw = vm->vsync_len;
0221     vtln = vm->upper_margin + vsynp + vsynw;
0222     vdln = vm->yres;
0223 
0224     /* get color info from register value */
0225     ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, &gray);
0226     if (ret)
0227         return ret;
0228 
0229     dev_dbg(info->dev, "%dx%d %dbpp %s (orientation %s)\n", hdcn,
0230         vdln, bpp, gray ? "grayscale" : "color",
0231         par->rot ? "rotated" : "normal");
0232 
0233 #ifdef CONFIG_CPU_LITTLE_ENDIAN
0234     lddfr = par->pd->lddfr | (1 << 8);
0235 #else
0236     lddfr = par->pd->lddfr & ~(1 << 8);
0237 #endif
0238 
0239     ldmtr = par->pd->ldmtr;
0240 
0241     if (!(vm->sync & FB_SYNC_HOR_HIGH_ACT))
0242         ldmtr |= LDMTR_CL1POL;
0243     if (!(vm->sync & FB_SYNC_VERT_HIGH_ACT))
0244         ldmtr |= LDMTR_FLMPOL;
0245 
0246     /* shut down LCDC before changing display parameters */
0247     sh7760fb_blank(FB_BLANK_POWERDOWN, info);
0248 
0249     iowrite16(par->pd->ldickr, par->base + LDICKR); /* pixclock */
0250     iowrite16(ldmtr, par->base + LDMTR);    /* polarities */
0251     iowrite16(lddfr, par->base + LDDFR);    /* color/depth */
0252     iowrite16((par->rot ? 1 << 13 : 0), par->base + LDSMR); /* rotate */
0253     iowrite16(par->pd->ldpmmr, par->base + LDPMMR); /* Power Management */
0254     iowrite16(par->pd->ldpspr, par->base + LDPSPR); /* Power Supply Ctrl */
0255 
0256     /* display resolution */
0257     iowrite16(((htcn >> 3) - 1) | (((hdcn >> 3) - 1) << 8),
0258           par->base + LDHCNR);
0259     iowrite16(vdln - 1, par->base + LDVDLNR);
0260     iowrite16(vtln - 1, par->base + LDVTLNR);
0261     /* h/v sync signals */
0262     iowrite16((vsynp - 1) | ((vsynw - 1) << 12), par->base + LDVSYNR);
0263     iowrite16(((hsynp >> 3) - 1) | (((hsynw >> 3) - 1) << 12),
0264           par->base + LDHSYNR);
0265     /* AC modulation sig */
0266     iowrite16(par->pd->ldaclnr, par->base + LDACLNR);
0267 
0268     stride = (par->rot) ? vtln : hdcn;
0269     if (!gray)
0270         stride *= (bpp + 7) >> 3;
0271     else {
0272         if (bpp == 1)
0273             stride >>= 3;
0274         else if (bpp == 2)
0275             stride >>= 2;
0276         else if (bpp == 4)
0277             stride >>= 1;
0278         /* 6 bpp == 8 bpp */
0279     }
0280 
0281     /* if rotated, stride must be power of 2 */
0282     if (par->rot) {
0283         unsigned long bit = 1 << 31;
0284         while (bit) {
0285             if (stride & bit)
0286                 break;
0287             bit >>= 1;
0288         }
0289         if (stride & ~bit)
0290             stride = bit << 1;  /* not P-o-2, round up */
0291     }
0292     iowrite16(stride, par->base + LDLAOR);
0293 
0294     /* set display mem start address */
0295     sbase = (unsigned long)par->fbdma;
0296     if (par->rot)
0297         sbase += (hdcn - 1) * stride;
0298 
0299     iowrite32(sbase, par->base + LDSARU);
0300 
0301     /*
0302      * for DSTN need to set address for lower half.
0303      * I (mlau) don't know which address to set it to,
0304      * so I guessed at (stride * yres/2).
0305      */
0306     if (((ldmtr & 0x003f) >= LDMTR_DSTN_MONO_8) &&
0307         ((ldmtr & 0x003f) <= LDMTR_DSTN_COLOR_16)) {
0308 
0309         dev_dbg(info->dev, " ***** DSTN untested! *****\n");
0310 
0311         dstn_off = stride;
0312         if (par->rot)
0313             dstn_off *= hdcn >> 1;
0314         else
0315             dstn_off *= vdln >> 1;
0316 
0317         ldsarl = sbase + dstn_off;
0318     } else
0319         ldsarl = 0;
0320 
0321     iowrite32(ldsarl, par->base + LDSARL);  /* mem for lower half of DSTN */
0322 
0323     info->fix.line_length = stride;
0324 
0325     sh7760fb_check_var(&info->var, info);
0326 
0327     sh7760fb_blank(FB_BLANK_UNBLANK, info); /* panel on! */
0328 
0329     dev_dbg(info->dev, "hdcn  : %6d htcn  : %6d\n", hdcn, htcn);
0330     dev_dbg(info->dev, "hsynw : %6d hsynp : %6d\n", hsynw, hsynp);
0331     dev_dbg(info->dev, "vdln  : %6d vtln  : %6d\n", vdln, vtln);
0332     dev_dbg(info->dev, "vsynw : %6d vsynp : %6d\n", vsynw, vsynp);
0333     dev_dbg(info->dev, "clksrc: %6d clkdiv: %6d\n",
0334         (par->pd->ldickr >> 12) & 3, par->pd->ldickr & 0x1f);
0335     dev_dbg(info->dev, "ldpmmr: 0x%04x ldpspr: 0x%04x\n", par->pd->ldpmmr,
0336         par->pd->ldpspr);
0337     dev_dbg(info->dev, "ldmtr : 0x%04x lddfr : 0x%04x\n", ldmtr, lddfr);
0338     dev_dbg(info->dev, "ldlaor: %ld\n", stride);
0339     dev_dbg(info->dev, "ldsaru: 0x%08lx ldsarl: 0x%08lx\n", sbase, ldsarl);
0340 
0341     return 0;
0342 }
0343 
0344 static const struct fb_ops sh7760fb_ops = {
0345     .owner = THIS_MODULE,
0346     .fb_blank = sh7760fb_blank,
0347     .fb_check_var = sh7760fb_check_var,
0348     .fb_setcolreg = sh7760_setcolreg,
0349     .fb_set_par = sh7760fb_set_par,
0350     .fb_fillrect = cfb_fillrect,
0351     .fb_copyarea = cfb_copyarea,
0352     .fb_imageblit = cfb_imageblit,
0353 };
0354 
0355 static void sh7760fb_free_mem(struct fb_info *info)
0356 {
0357     struct sh7760fb_par *par = info->par;
0358 
0359     if (!info->screen_base)
0360         return;
0361 
0362     dma_free_coherent(info->dev, info->screen_size,
0363               info->screen_base, par->fbdma);
0364 
0365     par->fbdma = 0;
0366     info->screen_base = NULL;
0367     info->screen_size = 0;
0368 }
0369 
0370 /* allocate the framebuffer memory. This memory must be in Area3,
0371  * (dictated by the DMA engine) and contiguous, at a 512 byte boundary.
0372  */
0373 static int sh7760fb_alloc_mem(struct fb_info *info)
0374 {
0375     struct sh7760fb_par *par = info->par;
0376     void *fbmem;
0377     unsigned long vram;
0378     int ret, bpp;
0379 
0380     if (info->screen_base)
0381         return 0;
0382 
0383     /* get color info from register value */
0384     ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL);
0385     if (ret) {
0386         printk(KERN_ERR "colinfo\n");
0387         return ret;
0388     }
0389 
0390     /* min VRAM: xres_min = 16, yres_min = 1, bpp = 1: 2byte -> 1 page
0391        max VRAM: xres_max = 1024, yres_max = 1024, bpp = 16: 2MB */
0392 
0393     vram = info->var.xres * info->var.yres;
0394     if (info->var.grayscale) {
0395         if (bpp == 1)
0396             vram >>= 3;
0397         else if (bpp == 2)
0398             vram >>= 2;
0399         else if (bpp == 4)
0400             vram >>= 1;
0401     } else if (bpp > 8)
0402         vram *= 2;
0403     if ((vram < 1) || (vram > 1024 * 2048)) {
0404         dev_dbg(info->dev, "too much VRAM required. Check settings\n");
0405         return -ENODEV;
0406     }
0407 
0408     if (vram < PAGE_SIZE)
0409         vram = PAGE_SIZE;
0410 
0411     fbmem = dma_alloc_coherent(info->dev, vram, &par->fbdma, GFP_KERNEL);
0412 
0413     if (!fbmem)
0414         return -ENOMEM;
0415 
0416     if ((par->fbdma & SH7760FB_DMA_MASK) != SH7760FB_DMA_MASK) {
0417         sh7760fb_free_mem(info);
0418         dev_err(info->dev, "kernel gave me memory at 0x%08lx, which is"
0419             "unusable for the LCDC\n", (unsigned long)par->fbdma);
0420         return -ENOMEM;
0421     }
0422 
0423     info->screen_base = fbmem;
0424     info->screen_size = vram;
0425     info->fix.smem_start = (unsigned long)info->screen_base;
0426     info->fix.smem_len = info->screen_size;
0427 
0428     return 0;
0429 }
0430 
0431 static int sh7760fb_probe(struct platform_device *pdev)
0432 {
0433     struct fb_info *info;
0434     struct resource *res;
0435     struct sh7760fb_par *par;
0436     int ret;
0437 
0438     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0439     if (unlikely(res == NULL)) {
0440         dev_err(&pdev->dev, "invalid resource\n");
0441         return -EINVAL;
0442     }
0443 
0444     info = framebuffer_alloc(sizeof(struct sh7760fb_par), &pdev->dev);
0445     if (!info)
0446         return -ENOMEM;
0447 
0448     par = info->par;
0449     par->dev = pdev;
0450 
0451     par->pd = pdev->dev.platform_data;
0452     if (!par->pd) {
0453         dev_dbg(info->dev, "no display setup data!\n");
0454         ret = -ENODEV;
0455         goto out_fb;
0456     }
0457 
0458     par->ioarea = request_mem_region(res->start,
0459                      resource_size(res), pdev->name);
0460     if (!par->ioarea) {
0461         dev_err(&pdev->dev, "mmio area busy\n");
0462         ret = -EBUSY;
0463         goto out_fb;
0464     }
0465 
0466     par->base = ioremap(res->start, resource_size(res));
0467     if (!par->base) {
0468         dev_err(&pdev->dev, "cannot remap\n");
0469         ret = -ENODEV;
0470         goto out_res;
0471     }
0472 
0473     iowrite16(0, par->base + LDINTR);   /* disable vsync irq */
0474     par->irq = platform_get_irq(pdev, 0);
0475     if (par->irq >= 0) {
0476         ret = request_irq(par->irq, sh7760fb_irq, 0,
0477                   "sh7760-lcdc", &par->vsync);
0478         if (ret) {
0479             dev_err(&pdev->dev, "cannot grab IRQ\n");
0480             par->irq = -ENXIO;
0481         } else
0482             disable_irq_nosync(par->irq);
0483     }
0484 
0485     fb_videomode_to_var(&info->var, par->pd->def_mode);
0486 
0487     ret = sh7760fb_alloc_mem(info);
0488     if (ret) {
0489         dev_dbg(info->dev, "framebuffer memory allocation failed!\n");
0490         goto out_unmap;
0491     }
0492 
0493     info->pseudo_palette = par->pseudo_palette;
0494 
0495     /* fixup color register bitpositions. These are fixed by hardware */
0496     info->var.red.offset = 11;
0497     info->var.red.length = 5;
0498     info->var.red.msb_right = 0;
0499 
0500     info->var.green.offset = 5;
0501     info->var.green.length = 6;
0502     info->var.green.msb_right = 0;
0503 
0504     info->var.blue.offset = 0;
0505     info->var.blue.length = 5;
0506     info->var.blue.msb_right = 0;
0507 
0508     info->var.transp.offset = 0;
0509     info->var.transp.length = 0;
0510     info->var.transp.msb_right = 0;
0511 
0512     strcpy(info->fix.id, "sh7760-lcdc");
0513 
0514     /* set the DON2 bit now, before cmap allocation, as it will randomize
0515      * palette memory.
0516      */
0517     iowrite16(LDCNTR_DON2, par->base + LDCNTR);
0518     info->fbops = &sh7760fb_ops;
0519 
0520     ret = fb_alloc_cmap(&info->cmap, 256, 0);
0521     if (ret) {
0522         dev_dbg(info->dev, "Unable to allocate cmap memory\n");
0523         goto out_mem;
0524     }
0525 
0526     ret = register_framebuffer(info);
0527     if (ret < 0) {
0528         dev_dbg(info->dev, "cannot register fb!\n");
0529         goto out_cmap;
0530     }
0531     platform_set_drvdata(pdev, info);
0532 
0533     printk(KERN_INFO "%s: memory at phys 0x%08lx-0x%08lx, size %ld KiB\n",
0534            pdev->name,
0535            (unsigned long)par->fbdma,
0536            (unsigned long)(par->fbdma + info->screen_size - 1),
0537            info->screen_size >> 10);
0538 
0539     return 0;
0540 
0541 out_cmap:
0542     sh7760fb_blank(FB_BLANK_POWERDOWN, info);
0543     fb_dealloc_cmap(&info->cmap);
0544 out_mem:
0545     sh7760fb_free_mem(info);
0546 out_unmap:
0547     if (par->irq >= 0)
0548         free_irq(par->irq, &par->vsync);
0549     iounmap(par->base);
0550 out_res:
0551     release_mem_region(res->start, resource_size(res));
0552 out_fb:
0553     framebuffer_release(info);
0554     return ret;
0555 }
0556 
0557 static int sh7760fb_remove(struct platform_device *dev)
0558 {
0559     struct fb_info *info = platform_get_drvdata(dev);
0560     struct sh7760fb_par *par = info->par;
0561 
0562     sh7760fb_blank(FB_BLANK_POWERDOWN, info);
0563     unregister_framebuffer(info);
0564     fb_dealloc_cmap(&info->cmap);
0565     sh7760fb_free_mem(info);
0566     if (par->irq >= 0)
0567         free_irq(par->irq, &par->vsync);
0568     iounmap(par->base);
0569     release_mem_region(par->ioarea->start, resource_size(par->ioarea));
0570     framebuffer_release(info);
0571 
0572     return 0;
0573 }
0574 
0575 static struct platform_driver sh7760_lcdc_driver = {
0576     .driver = {
0577            .name = "sh7760-lcdc",
0578            },
0579     .probe = sh7760fb_probe,
0580     .remove = sh7760fb_remove,
0581 };
0582 
0583 module_platform_driver(sh7760_lcdc_driver);
0584 
0585 MODULE_AUTHOR("Nobuhiro Iwamatsu, Manuel Lauss");
0586 MODULE_DESCRIPTION("FBdev for SH7760/63 integrated LCD Controller");
0587 MODULE_LICENSE("GPL v2");