515 lines · c
1/*2 * linux/drivers/video/hitfb.c -- Hitachi LCD frame buffer device3 *4 * (C) 1999 Mihai Spatar5 * (C) 2000 YAEGASHI Takeshi6 * (C) 2003, 2004 Paul Mundt7 * (C) 2003, 2004, 2006 Andriy Skulysh8 *9 * This file is subject to the terms and conditions of the GNU General Public10 * License. See the file COPYING in the main directory of this archive for11 * more details.12 */13 14#include <linux/module.h>15#include <linux/kernel.h>16#include <linux/errno.h>17#include <linux/string.h>18#include <linux/mm.h>19#include <linux/delay.h>20#include <linux/init.h>21#include <linux/platform_device.h>22#include <linux/fb.h>23 24#include <asm/machvec.h>25#include <linux/uaccess.h>26#include <asm/io.h>27#include <asm/hd64461.h>28#include <cpu/dac.h>29 30#define WIDTH 64031 32static struct fb_var_screeninfo hitfb_var = {33 .activate = FB_ACTIVATE_NOW,34 .height = -1,35 .width = -1,36 .vmode = FB_VMODE_NONINTERLACED,37};38 39static struct fb_fix_screeninfo hitfb_fix = {40 .id = "Hitachi HD64461",41 .type = FB_TYPE_PACKED_PIXELS,42 .accel = FB_ACCEL_NONE,43};44 45static volatile void __iomem *hitfb_offset_to_addr(unsigned int offset)46{47 return (__force volatile void __iomem *)(uintptr_t)offset;48}49 50static u16 hitfb_readw(unsigned int offset)51{52 return fb_readw(hitfb_offset_to_addr(offset));53}54 55static void hitfb_writew(u16 value, unsigned int offset)56{57 fb_writew(value, hitfb_offset_to_addr(offset));58}59 60static inline void hitfb_accel_wait(void)61{62 while (hitfb_readw(HD64461_GRCFGR) & HD64461_GRCFGR_ACCSTATUS)63 ;64}65 66static inline void hitfb_accel_start(int truecolor)67{68 if (truecolor) {69 hitfb_writew(6, HD64461_GRCFGR);70 } else {71 hitfb_writew(7, HD64461_GRCFGR);72 }73}74 75static inline void hitfb_accel_set_dest(int truecolor, u16 dx, u16 dy,76 u16 width, u16 height)77{78 u32 saddr = WIDTH * dy + dx;79 if (truecolor)80 saddr <<= 1;81 82 hitfb_writew(width-1, HD64461_BBTDWR);83 hitfb_writew(height-1, HD64461_BBTDHR);84 85 hitfb_writew(saddr & 0xffff, HD64461_BBTDSARL);86 hitfb_writew(saddr >> 16, HD64461_BBTDSARH);87 88}89 90static inline void hitfb_accel_bitblt(int truecolor, u16 sx, u16 sy, u16 dx,91 u16 dy, u16 width, u16 height, u16 rop,92 u32 mask_addr)93{94 u32 saddr, daddr;95 u32 maddr = 0;96 97 height--;98 width--;99 hitfb_writew(rop, HD64461_BBTROPR);100 if ((sy < dy) || ((sy == dy) && (sx <= dx))) {101 saddr = WIDTH * (sy + height) + sx + width;102 daddr = WIDTH * (dy + height) + dx + width;103 if (mask_addr) {104 if (truecolor)105 maddr = ((width >> 3) + 1) * (height + 1) - 1;106 else107 maddr =108 (((width >> 4) + 1) * (height + 1) - 1) * 2;109 110 hitfb_writew((1 << 5) | 1, HD64461_BBTMDR);111 } else112 hitfb_writew(1, HD64461_BBTMDR);113 } else {114 saddr = WIDTH * sy + sx;115 daddr = WIDTH * dy + dx;116 if (mask_addr) {117 hitfb_writew((1 << 5), HD64461_BBTMDR);118 } else {119 hitfb_writew(0, HD64461_BBTMDR);120 }121 }122 if (truecolor) {123 saddr <<= 1;124 daddr <<= 1;125 }126 hitfb_writew(width, HD64461_BBTDWR);127 hitfb_writew(height, HD64461_BBTDHR);128 hitfb_writew(saddr & 0xffff, HD64461_BBTSSARL);129 hitfb_writew(saddr >> 16, HD64461_BBTSSARH);130 hitfb_writew(daddr & 0xffff, HD64461_BBTDSARL);131 hitfb_writew(daddr >> 16, HD64461_BBTDSARH);132 if (mask_addr) {133 maddr += mask_addr;134 hitfb_writew(maddr & 0xffff, HD64461_BBTMARL);135 hitfb_writew(maddr >> 16, HD64461_BBTMARH);136 }137 hitfb_accel_start(truecolor);138}139 140static void hitfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)141{142 if (rect->rop != ROP_COPY)143 cfb_fillrect(p, rect);144 else {145 hitfb_accel_wait();146 hitfb_writew(0x00f0, HD64461_BBTROPR);147 hitfb_writew(16, HD64461_BBTMDR);148 149 if (p->var.bits_per_pixel == 16) {150 hitfb_writew(((u32 *) (p->pseudo_palette))[rect->color],151 HD64461_GRSCR);152 hitfb_accel_set_dest(1, rect->dx, rect->dy, rect->width,153 rect->height);154 hitfb_accel_start(1);155 } else {156 hitfb_writew(rect->color, HD64461_GRSCR);157 hitfb_accel_set_dest(0, rect->dx, rect->dy, rect->width,158 rect->height);159 hitfb_accel_start(0);160 }161 }162}163 164static void hitfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)165{166 hitfb_accel_wait();167 hitfb_accel_bitblt(p->var.bits_per_pixel == 16, area->sx, area->sy,168 area->dx, area->dy, area->width, area->height,169 0x00cc, 0);170}171 172static int hitfb_pan_display(struct fb_var_screeninfo *var,173 struct fb_info *info)174{175 int xoffset = var->xoffset;176 int yoffset = var->yoffset;177 178 if (xoffset != 0)179 return -EINVAL;180 181 hitfb_writew((yoffset*info->fix.line_length)>>10, HD64461_LCDCBAR);182 183 return 0;184}185 186static int hitfb_blank(int blank_mode, struct fb_info *info)187{188 unsigned short v;189 190 if (blank_mode) {191 v = hitfb_readw(HD64461_LDR1);192 v &= ~HD64461_LDR1_DON;193 hitfb_writew(v, HD64461_LDR1);194 195 v = hitfb_readw(HD64461_LCDCCR);196 v |= HD64461_LCDCCR_MOFF;197 hitfb_writew(v, HD64461_LCDCCR);198 199 v = hitfb_readw(HD64461_STBCR);200 v |= HD64461_STBCR_SLCDST;201 hitfb_writew(v, HD64461_STBCR);202 } else {203 v = hitfb_readw(HD64461_STBCR);204 v &= ~HD64461_STBCR_SLCDST;205 hitfb_writew(v, HD64461_STBCR);206 207 v = hitfb_readw(HD64461_LCDCCR);208 v &= ~(HD64461_LCDCCR_MOFF | HD64461_LCDCCR_STREQ);209 hitfb_writew(v, HD64461_LCDCCR);210 211 do {212 v = hitfb_readw(HD64461_LCDCCR);213 } while(v&HD64461_LCDCCR_STBACK);214 215 v = hitfb_readw(HD64461_LDR1);216 v |= HD64461_LDR1_DON;217 hitfb_writew(v, HD64461_LDR1);218 }219 return 0;220}221 222static int hitfb_setcolreg(unsigned regno, unsigned red, unsigned green,223 unsigned blue, unsigned transp, struct fb_info *info)224{225 if (regno >= 256)226 return 1;227 228 switch (info->var.bits_per_pixel) {229 case 8:230 hitfb_writew(regno << 8, HD64461_CPTWAR);231 hitfb_writew(red >> 10, HD64461_CPTWDR);232 hitfb_writew(green >> 10, HD64461_CPTWDR);233 hitfb_writew(blue >> 10, HD64461_CPTWDR);234 break;235 case 16:236 if (regno >= 16)237 return 1;238 ((u32 *) (info->pseudo_palette))[regno] =239 ((red & 0xf800)) |240 ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);241 break;242 }243 return 0;244}245 246static int hitfb_sync(struct fb_info *info)247{248 hitfb_accel_wait();249 250 return 0;251}252 253static int hitfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)254{255 int maxy;256 257 var->xres = info->var.xres;258 var->xres_virtual = info->var.xres;259 var->yres = info->var.yres;260 261 if ((var->bits_per_pixel != 8) && (var->bits_per_pixel != 16))262 var->bits_per_pixel = info->var.bits_per_pixel;263 264 if (var->yres_virtual < var->yres)265 var->yres_virtual = var->yres;266 267 maxy = info->fix.smem_len / var->xres;268 269 if (var->bits_per_pixel == 16)270 maxy /= 2;271 272 if (var->yres_virtual > maxy)273 var->yres_virtual = maxy;274 275 var->xoffset = 0;276 var->yoffset = 0;277 278 switch (var->bits_per_pixel) {279 case 8:280 var->red.offset = 0;281 var->red.length = 8;282 var->green.offset = 0;283 var->green.length = 8;284 var->blue.offset = 0;285 var->blue.length = 8;286 var->transp.offset = 0;287 var->transp.length = 0;288 break;289 case 16: /* RGB 565 */290 var->red.offset = 11;291 var->red.length = 5;292 var->green.offset = 5;293 var->green.length = 6;294 var->blue.offset = 0;295 var->blue.length = 5;296 var->transp.offset = 0;297 var->transp.length = 0;298 break;299 }300 301 return 0;302}303 304static int hitfb_set_par(struct fb_info *info)305{306 unsigned short ldr3;307 308 switch (info->var.bits_per_pixel) {309 case 8:310 info->fix.line_length = info->var.xres;311 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;312 info->fix.ypanstep = 16;313 break;314 case 16:315 info->fix.line_length = info->var.xres*2;316 info->fix.visual = FB_VISUAL_TRUECOLOR;317 info->fix.ypanstep = 8;318 break;319 }320 321 hitfb_writew(info->fix.line_length, HD64461_LCDCLOR);322 ldr3 = hitfb_readw(HD64461_LDR3);323 ldr3 &= ~15;324 ldr3 |= (info->var.bits_per_pixel == 8) ? 4 : 8;325 hitfb_writew(ldr3, HD64461_LDR3);326 return 0;327}328 329static const struct fb_ops hitfb_ops = {330 .owner = THIS_MODULE,331 __FB_DEFAULT_IOMEM_OPS_RDWR,332 .fb_check_var = hitfb_check_var,333 .fb_set_par = hitfb_set_par,334 .fb_setcolreg = hitfb_setcolreg,335 .fb_blank = hitfb_blank,336 .fb_sync = hitfb_sync,337 .fb_pan_display = hitfb_pan_display,338 .fb_fillrect = hitfb_fillrect,339 .fb_copyarea = hitfb_copyarea,340 .fb_imageblit = cfb_imageblit,341 __FB_DEFAULT_IOMEM_OPS_MMAP,342};343 344static int hitfb_probe(struct platform_device *dev)345{346 unsigned short lcdclor, ldr3, ldvndr;347 struct fb_info *info;348 int ret;349 350 if (fb_get_options("hitfb", NULL))351 return -ENODEV;352 353 hitfb_fix.mmio_start = HD64461_IO_OFFSET(0x1000);354 hitfb_fix.mmio_len = 0x1000;355 hitfb_fix.smem_start = HD64461_IO_OFFSET(0x02000000);356 hitfb_fix.smem_len = 512 * 1024;357 358 lcdclor = hitfb_readw(HD64461_LCDCLOR);359 ldvndr = hitfb_readw(HD64461_LDVNDR);360 ldr3 = hitfb_readw(HD64461_LDR3);361 362 switch (ldr3 & 15) {363 default:364 case 4:365 hitfb_var.bits_per_pixel = 8;366 hitfb_var.xres = lcdclor;367 break;368 case 8:369 hitfb_var.bits_per_pixel = 16;370 hitfb_var.xres = lcdclor / 2;371 break;372 }373 hitfb_fix.line_length = lcdclor;374 hitfb_fix.visual = (hitfb_var.bits_per_pixel == 8) ?375 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;376 hitfb_var.yres = ldvndr + 1;377 hitfb_var.xres_virtual = hitfb_var.xres;378 hitfb_var.yres_virtual = hitfb_fix.smem_len / lcdclor;379 switch (hitfb_var.bits_per_pixel) {380 case 8:381 hitfb_var.red.offset = 0;382 hitfb_var.red.length = 8;383 hitfb_var.green.offset = 0;384 hitfb_var.green.length = 8;385 hitfb_var.blue.offset = 0;386 hitfb_var.blue.length = 8;387 hitfb_var.transp.offset = 0;388 hitfb_var.transp.length = 0;389 break;390 case 16: /* RGB 565 */391 hitfb_var.red.offset = 11;392 hitfb_var.red.length = 5;393 hitfb_var.green.offset = 5;394 hitfb_var.green.length = 6;395 hitfb_var.blue.offset = 0;396 hitfb_var.blue.length = 5;397 hitfb_var.transp.offset = 0;398 hitfb_var.transp.length = 0;399 break;400 }401 402 info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);403 if (unlikely(!info))404 return -ENOMEM;405 406 info->fbops = &hitfb_ops;407 info->var = hitfb_var;408 info->fix = hitfb_fix;409 info->pseudo_palette = info->par;410 info->flags = FBINFO_HWACCEL_YPAN |411 FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA;412 413 info->screen_base = (char __iomem *)(uintptr_t)hitfb_fix.smem_start;414 415 ret = fb_alloc_cmap(&info->cmap, 256, 0);416 if (unlikely(ret < 0))417 goto err_fb;418 419 ret = register_framebuffer(info);420 if (unlikely(ret < 0))421 goto err;422 423 platform_set_drvdata(dev, info);424 425 fb_info(info, "%s frame buffer device\n", info->fix.id);426 427 return 0;428 429err:430 fb_dealloc_cmap(&info->cmap);431err_fb:432 framebuffer_release(info);433 return ret;434}435 436static void hitfb_remove(struct platform_device *dev)437{438 struct fb_info *info = platform_get_drvdata(dev);439 440 unregister_framebuffer(info);441 fb_dealloc_cmap(&info->cmap);442 framebuffer_release(info);443}444 445static int hitfb_suspend(struct device *dev)446{447 u16 v;448 449 hitfb_blank(1, NULL);450 v = hitfb_readw(HD64461_STBCR);451 v |= HD64461_STBCR_SLCKE_IST;452 hitfb_writew(v, HD64461_STBCR);453 454 return 0;455}456 457static int hitfb_resume(struct device *dev)458{459 u16 v;460 461 v = hitfb_readw(HD64461_STBCR);462 v &= ~HD64461_STBCR_SLCKE_OST;463 msleep(100);464 v = hitfb_readw(HD64461_STBCR);465 v &= ~HD64461_STBCR_SLCKE_IST;466 hitfb_writew(v, HD64461_STBCR);467 hitfb_blank(0, NULL);468 469 return 0;470}471 472static const struct dev_pm_ops hitfb_dev_pm_ops = {473 .suspend = hitfb_suspend,474 .resume = hitfb_resume,475};476 477static struct platform_driver hitfb_driver = {478 .probe = hitfb_probe,479 .remove = hitfb_remove,480 .driver = {481 .name = "hitfb",482 .pm = &hitfb_dev_pm_ops,483 },484};485 486static struct platform_device hitfb_device = {487 .name = "hitfb",488 .id = -1,489};490 491static int __init hitfb_init(void)492{493 int ret;494 495 ret = platform_driver_register(&hitfb_driver);496 if (!ret) {497 ret = platform_device_register(&hitfb_device);498 if (ret)499 platform_driver_unregister(&hitfb_driver);500 }501 return ret;502}503 504 505static void __exit hitfb_exit(void)506{507 platform_device_unregister(&hitfb_device);508 platform_driver_unregister(&hitfb_driver);509}510 511module_init(hitfb_init);512module_exit(hitfb_exit);513 514MODULE_LICENSE("GPL");515