0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
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;
0035
0036 dma_addr_t fbdma;
0037
0038 int rot;
0039
0040 u32 pseudo_palette[16];
0041
0042 struct platform_device *dev;
0043 struct resource *ioarea;
0044 struct completion vsync;
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
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
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
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
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
0187 return 0;
0188 }
0189
0190
0191
0192
0193
0194
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
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
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
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
0247 sh7760fb_blank(FB_BLANK_POWERDOWN, info);
0248
0249 iowrite16(par->pd->ldickr, par->base + LDICKR);
0250 iowrite16(ldmtr, par->base + LDMTR);
0251 iowrite16(lddfr, par->base + LDDFR);
0252 iowrite16((par->rot ? 1 << 13 : 0), par->base + LDSMR);
0253 iowrite16(par->pd->ldpmmr, par->base + LDPMMR);
0254 iowrite16(par->pd->ldpspr, par->base + LDPSPR);
0255
0256
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
0262 iowrite16((vsynp - 1) | ((vsynw - 1) << 12), par->base + LDVSYNR);
0263 iowrite16(((hsynp >> 3) - 1) | (((hsynw >> 3) - 1) << 12),
0264 par->base + LDHSYNR);
0265
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
0279 }
0280
0281
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;
0291 }
0292 iowrite16(stride, par->base + LDLAOR);
0293
0294
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
0303
0304
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);
0322
0323 info->fix.line_length = stride;
0324
0325 sh7760fb_check_var(&info->var, info);
0326
0327 sh7760fb_blank(FB_BLANK_UNBLANK, info);
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
0371
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
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
0391
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);
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
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
0515
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");