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