Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * linux/drivers/video/hitfb.c -- Hitachi LCD frame buffer device
0003  *
0004  * (C) 1999 Mihai Spatar
0005  * (C) 2000 YAEGASHI Takeshi
0006  * (C) 2003, 2004 Paul Mundt
0007  * (C) 2003, 2004, 2006 Andriy Skulysh
0008  *
0009  *  This file is subject to the terms and conditions of the GNU General Public
0010  *  License. See the file COPYING in the main directory of this archive for
0011  *  more details.
0012  */
0013 
0014 #include <linux/module.h>
0015 #include <linux/kernel.h>
0016 #include <linux/errno.h>
0017 #include <linux/string.h>
0018 #include <linux/mm.h>
0019 #include <linux/delay.h>
0020 #include <linux/init.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/fb.h>
0023 
0024 #include <asm/machvec.h>
0025 #include <linux/uaccess.h>
0026 #include <asm/io.h>
0027 #include <asm/hd64461.h>
0028 #include <cpu/dac.h>
0029 
0030 #define WIDTH 640
0031 
0032 static struct fb_var_screeninfo hitfb_var = {
0033     .activate   = FB_ACTIVATE_NOW,
0034     .height     = -1,
0035     .width      = -1,
0036     .vmode      = FB_VMODE_NONINTERLACED,
0037 };
0038 
0039 static struct fb_fix_screeninfo hitfb_fix = {
0040     .id     = "Hitachi HD64461",
0041     .type       = FB_TYPE_PACKED_PIXELS,
0042     .accel      = FB_ACCEL_NONE,
0043 };
0044 
0045 static inline void hitfb_accel_wait(void)
0046 {
0047     while (fb_readw(HD64461_GRCFGR) & HD64461_GRCFGR_ACCSTATUS) ;
0048 }
0049 
0050 static inline void hitfb_accel_start(int truecolor)
0051 {
0052     if (truecolor) {
0053         fb_writew(6, HD64461_GRCFGR);
0054     } else {
0055         fb_writew(7, HD64461_GRCFGR);
0056     }
0057 }
0058 
0059 static inline void hitfb_accel_set_dest(int truecolor, u16 dx, u16 dy,
0060                     u16 width, u16 height)
0061 {
0062     u32 saddr = WIDTH * dy + dx;
0063     if (truecolor)
0064         saddr <<= 1;
0065 
0066     fb_writew(width-1, HD64461_BBTDWR);
0067     fb_writew(height-1, HD64461_BBTDHR);
0068 
0069     fb_writew(saddr & 0xffff, HD64461_BBTDSARL);
0070     fb_writew(saddr >> 16, HD64461_BBTDSARH);
0071 
0072 }
0073 
0074 static inline void hitfb_accel_bitblt(int truecolor, u16 sx, u16 sy, u16 dx,
0075                       u16 dy, u16 width, u16 height, u16 rop,
0076                       u32 mask_addr)
0077 {
0078     u32 saddr, daddr;
0079     u32 maddr = 0;
0080 
0081     height--;
0082     width--;
0083     fb_writew(rop, HD64461_BBTROPR);
0084     if ((sy < dy) || ((sy == dy) && (sx <= dx))) {
0085         saddr = WIDTH * (sy + height) + sx + width;
0086         daddr = WIDTH * (dy + height) + dx + width;
0087         if (mask_addr) {
0088             if (truecolor)
0089                 maddr = ((width >> 3) + 1) * (height + 1) - 1;
0090             else
0091                 maddr =
0092                     (((width >> 4) + 1) * (height + 1) - 1) * 2;
0093 
0094             fb_writew((1 << 5) | 1, HD64461_BBTMDR);
0095         } else
0096             fb_writew(1, HD64461_BBTMDR);
0097     } else {
0098         saddr = WIDTH * sy + sx;
0099         daddr = WIDTH * dy + dx;
0100         if (mask_addr) {
0101             fb_writew((1 << 5), HD64461_BBTMDR);
0102         } else {
0103             fb_writew(0, HD64461_BBTMDR);
0104         }
0105     }
0106     if (truecolor) {
0107         saddr <<= 1;
0108         daddr <<= 1;
0109     }
0110     fb_writew(width, HD64461_BBTDWR);
0111     fb_writew(height, HD64461_BBTDHR);
0112     fb_writew(saddr & 0xffff, HD64461_BBTSSARL);
0113     fb_writew(saddr >> 16, HD64461_BBTSSARH);
0114     fb_writew(daddr & 0xffff, HD64461_BBTDSARL);
0115     fb_writew(daddr >> 16, HD64461_BBTDSARH);
0116     if (mask_addr) {
0117         maddr += mask_addr;
0118         fb_writew(maddr & 0xffff, HD64461_BBTMARL);
0119         fb_writew(maddr >> 16, HD64461_BBTMARH);
0120     }
0121     hitfb_accel_start(truecolor);
0122 }
0123 
0124 static void hitfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
0125 {
0126     if (rect->rop != ROP_COPY)
0127         cfb_fillrect(p, rect);
0128     else {
0129         hitfb_accel_wait();
0130         fb_writew(0x00f0, HD64461_BBTROPR);
0131         fb_writew(16, HD64461_BBTMDR);
0132 
0133         if (p->var.bits_per_pixel == 16) {
0134             fb_writew(((u32 *) (p->pseudo_palette))[rect->color],
0135                   HD64461_GRSCR);
0136             hitfb_accel_set_dest(1, rect->dx, rect->dy, rect->width,
0137                          rect->height);
0138             hitfb_accel_start(1);
0139         } else {
0140             fb_writew(rect->color, HD64461_GRSCR);
0141             hitfb_accel_set_dest(0, rect->dx, rect->dy, rect->width,
0142                          rect->height);
0143             hitfb_accel_start(0);
0144         }
0145     }
0146 }
0147 
0148 static void hitfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
0149 {
0150     hitfb_accel_wait();
0151     hitfb_accel_bitblt(p->var.bits_per_pixel == 16, area->sx, area->sy,
0152                area->dx, area->dy, area->width, area->height,
0153                0x00cc, 0);
0154 }
0155 
0156 static int hitfb_pan_display(struct fb_var_screeninfo *var,
0157                  struct fb_info *info)
0158 {
0159     int xoffset = var->xoffset;
0160     int yoffset = var->yoffset;
0161 
0162     if (xoffset != 0)
0163         return -EINVAL;
0164 
0165     fb_writew((yoffset*info->fix.line_length)>>10, HD64461_LCDCBAR);
0166 
0167     return 0;
0168 }
0169 
0170 int hitfb_blank(int blank_mode, struct fb_info *info)
0171 {
0172     unsigned short v;
0173 
0174     if (blank_mode) {
0175         v = fb_readw(HD64461_LDR1);
0176         v &= ~HD64461_LDR1_DON;
0177         fb_writew(v, HD64461_LDR1);
0178 
0179         v = fb_readw(HD64461_LCDCCR);
0180         v |= HD64461_LCDCCR_MOFF;
0181         fb_writew(v, HD64461_LCDCCR);
0182 
0183         v = fb_readw(HD64461_STBCR);
0184         v |= HD64461_STBCR_SLCDST;
0185         fb_writew(v, HD64461_STBCR);
0186     } else {
0187         v = fb_readw(HD64461_STBCR);
0188         v &= ~HD64461_STBCR_SLCDST;
0189         fb_writew(v, HD64461_STBCR);
0190 
0191         v = fb_readw(HD64461_LCDCCR);
0192         v &= ~(HD64461_LCDCCR_MOFF | HD64461_LCDCCR_STREQ);
0193         fb_writew(v, HD64461_LCDCCR);
0194 
0195         do {
0196             v = fb_readw(HD64461_LCDCCR);
0197         } while(v&HD64461_LCDCCR_STBACK);
0198 
0199         v = fb_readw(HD64461_LDR1);
0200         v |= HD64461_LDR1_DON;
0201         fb_writew(v, HD64461_LDR1);
0202     }
0203     return 0;
0204 }
0205 
0206 static int hitfb_setcolreg(unsigned regno, unsigned red, unsigned green,
0207                unsigned blue, unsigned transp, struct fb_info *info)
0208 {
0209     if (regno >= 256)
0210         return 1;
0211 
0212     switch (info->var.bits_per_pixel) {
0213     case 8:
0214         fb_writew(regno << 8, HD64461_CPTWAR);
0215         fb_writew(red >> 10, HD64461_CPTWDR);
0216         fb_writew(green >> 10, HD64461_CPTWDR);
0217         fb_writew(blue >> 10, HD64461_CPTWDR);
0218         break;
0219     case 16:
0220         if (regno >= 16)
0221             return 1;
0222         ((u32 *) (info->pseudo_palette))[regno] =
0223             ((red & 0xf800)) |
0224             ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
0225         break;
0226     }
0227     return 0;
0228 }
0229 
0230 static int hitfb_sync(struct fb_info *info)
0231 {
0232     hitfb_accel_wait();
0233 
0234     return 0;
0235 }
0236 
0237 static int hitfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
0238 {
0239     int maxy;
0240 
0241     var->xres = info->var.xres;
0242     var->xres_virtual = info->var.xres;
0243     var->yres = info->var.yres;
0244 
0245     if ((var->bits_per_pixel != 8) && (var->bits_per_pixel != 16))
0246         var->bits_per_pixel = info->var.bits_per_pixel;
0247 
0248     if (var->yres_virtual < var->yres)
0249         var->yres_virtual = var->yres;
0250 
0251     maxy = info->fix.smem_len / var->xres;
0252 
0253     if (var->bits_per_pixel == 16)
0254         maxy /= 2;
0255 
0256     if (var->yres_virtual > maxy)
0257         var->yres_virtual = maxy;
0258 
0259     var->xoffset = 0;
0260     var->yoffset = 0;
0261 
0262     switch (var->bits_per_pixel) {
0263     case 8:
0264         var->red.offset = 0;
0265         var->red.length = 8;
0266         var->green.offset = 0;
0267         var->green.length = 8;
0268         var->blue.offset = 0;
0269         var->blue.length = 8;
0270         var->transp.offset = 0;
0271         var->transp.length = 0;
0272         break;
0273     case 16:        /* RGB 565 */
0274         var->red.offset = 11;
0275         var->red.length = 5;
0276         var->green.offset = 5;
0277         var->green.length = 6;
0278         var->blue.offset = 0;
0279         var->blue.length = 5;
0280         var->transp.offset = 0;
0281         var->transp.length = 0;
0282         break;
0283     }
0284 
0285     return 0;
0286 }
0287 
0288 static int hitfb_set_par(struct fb_info *info)
0289 {
0290     unsigned short ldr3;
0291 
0292     switch (info->var.bits_per_pixel) {
0293     case 8:
0294         info->fix.line_length = info->var.xres;
0295         info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
0296         info->fix.ypanstep = 16;
0297         break;
0298     case 16:
0299         info->fix.line_length = info->var.xres*2;
0300         info->fix.visual = FB_VISUAL_TRUECOLOR;
0301         info->fix.ypanstep = 8;
0302         break;
0303     }
0304 
0305     fb_writew(info->fix.line_length, HD64461_LCDCLOR);
0306     ldr3 = fb_readw(HD64461_LDR3);
0307     ldr3 &= ~15;
0308     ldr3 |= (info->var.bits_per_pixel == 8) ? 4 : 8;
0309     fb_writew(ldr3, HD64461_LDR3);
0310     return 0;
0311 }
0312 
0313 static const struct fb_ops hitfb_ops = {
0314     .owner      = THIS_MODULE,
0315     .fb_check_var   = hitfb_check_var,
0316     .fb_set_par     = hitfb_set_par,
0317     .fb_setcolreg   = hitfb_setcolreg,
0318     .fb_blank   = hitfb_blank,
0319     .fb_sync    = hitfb_sync,
0320     .fb_pan_display = hitfb_pan_display,
0321     .fb_fillrect    = hitfb_fillrect,
0322     .fb_copyarea    = hitfb_copyarea,
0323     .fb_imageblit   = cfb_imageblit,
0324 };
0325 
0326 static int hitfb_probe(struct platform_device *dev)
0327 {
0328     unsigned short lcdclor, ldr3, ldvndr;
0329     struct fb_info *info;
0330     int ret;
0331 
0332     if (fb_get_options("hitfb", NULL))
0333         return -ENODEV;
0334 
0335     hitfb_fix.mmio_start = HD64461_IO_OFFSET(0x1000);
0336     hitfb_fix.mmio_len = 0x1000;
0337     hitfb_fix.smem_start = HD64461_IO_OFFSET(0x02000000);
0338     hitfb_fix.smem_len = 512 * 1024;
0339 
0340     lcdclor = fb_readw(HD64461_LCDCLOR);
0341     ldvndr = fb_readw(HD64461_LDVNDR);
0342     ldr3 = fb_readw(HD64461_LDR3);
0343 
0344     switch (ldr3 & 15) {
0345     default:
0346     case 4:
0347         hitfb_var.bits_per_pixel = 8;
0348         hitfb_var.xres = lcdclor;
0349         break;
0350     case 8:
0351         hitfb_var.bits_per_pixel = 16;
0352         hitfb_var.xres = lcdclor / 2;
0353         break;
0354     }
0355     hitfb_fix.line_length = lcdclor;
0356     hitfb_fix.visual = (hitfb_var.bits_per_pixel == 8) ?
0357         FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
0358     hitfb_var.yres = ldvndr + 1;
0359     hitfb_var.xres_virtual = hitfb_var.xres;
0360     hitfb_var.yres_virtual = hitfb_fix.smem_len / lcdclor;
0361     switch (hitfb_var.bits_per_pixel) {
0362     case 8:
0363         hitfb_var.red.offset = 0;
0364         hitfb_var.red.length = 8;
0365         hitfb_var.green.offset = 0;
0366         hitfb_var.green.length = 8;
0367         hitfb_var.blue.offset = 0;
0368         hitfb_var.blue.length = 8;
0369         hitfb_var.transp.offset = 0;
0370         hitfb_var.transp.length = 0;
0371         break;
0372     case 16:        /* RGB 565 */
0373         hitfb_var.red.offset = 11;
0374         hitfb_var.red.length = 5;
0375         hitfb_var.green.offset = 5;
0376         hitfb_var.green.length = 6;
0377         hitfb_var.blue.offset = 0;
0378         hitfb_var.blue.length = 5;
0379         hitfb_var.transp.offset = 0;
0380         hitfb_var.transp.length = 0;
0381         break;
0382     }
0383 
0384     info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
0385     if (unlikely(!info))
0386         return -ENOMEM;
0387 
0388     info->fbops = &hitfb_ops;
0389     info->var = hitfb_var;
0390     info->fix = hitfb_fix;
0391     info->pseudo_palette = info->par;
0392     info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN |
0393         FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA;
0394 
0395     info->screen_base = (void *)hitfb_fix.smem_start;
0396 
0397     ret = fb_alloc_cmap(&info->cmap, 256, 0);
0398     if (unlikely(ret < 0))
0399         goto err_fb;
0400 
0401     ret = register_framebuffer(info);
0402     if (unlikely(ret < 0))
0403         goto err;
0404 
0405     platform_set_drvdata(dev, info);
0406 
0407     fb_info(info, "%s frame buffer device\n", info->fix.id);
0408 
0409     return 0;
0410 
0411 err:
0412     fb_dealloc_cmap(&info->cmap);
0413 err_fb:
0414     framebuffer_release(info);
0415     return ret;
0416 }
0417 
0418 static int hitfb_remove(struct platform_device *dev)
0419 {
0420     struct fb_info *info = platform_get_drvdata(dev);
0421 
0422     unregister_framebuffer(info);
0423     fb_dealloc_cmap(&info->cmap);
0424     framebuffer_release(info);
0425 
0426     return 0;
0427 }
0428 
0429 static int hitfb_suspend(struct device *dev)
0430 {
0431     u16 v;
0432 
0433     hitfb_blank(1,0);
0434     v = fb_readw(HD64461_STBCR);
0435     v |= HD64461_STBCR_SLCKE_IST;
0436     fb_writew(v, HD64461_STBCR);
0437 
0438     return 0;
0439 }
0440 
0441 static int hitfb_resume(struct device *dev)
0442 {
0443     u16 v;
0444 
0445     v = fb_readw(HD64461_STBCR);
0446     v &= ~HD64461_STBCR_SLCKE_OST;
0447     msleep(100);
0448     v = fb_readw(HD64461_STBCR);
0449     v &= ~HD64461_STBCR_SLCKE_IST;
0450     fb_writew(v, HD64461_STBCR);
0451     hitfb_blank(0,0);
0452 
0453     return 0;
0454 }
0455 
0456 static const struct dev_pm_ops hitfb_dev_pm_ops = {
0457     .suspend    = hitfb_suspend,
0458     .resume     = hitfb_resume,
0459 };
0460 
0461 static struct platform_driver hitfb_driver = {
0462     .probe      = hitfb_probe,
0463     .remove     = hitfb_remove,
0464     .driver     = {
0465         .name   = "hitfb",
0466         .pm = &hitfb_dev_pm_ops,
0467     },
0468 };
0469 
0470 static struct platform_device hitfb_device = {
0471     .name   = "hitfb",
0472     .id = -1,
0473 };
0474 
0475 static int __init hitfb_init(void)
0476 {
0477     int ret;
0478 
0479     ret = platform_driver_register(&hitfb_driver);
0480     if (!ret) {
0481         ret = platform_device_register(&hitfb_device);
0482         if (ret)
0483             platform_driver_unregister(&hitfb_driver);
0484     }
0485     return ret;
0486 }
0487 
0488 
0489 static void __exit hitfb_exit(void)
0490 {
0491     platform_device_unregister(&hitfb_device);
0492     platform_driver_unregister(&hitfb_driver);
0493 }
0494 
0495 module_init(hitfb_init);
0496 module_exit(hitfb_exit);
0497 
0498 MODULE_LICENSE("GPL");