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 <linux/uaccess.h> 26 #include <asm/io.h> 27 #include <asm/hd64461.h> 28 #include <cpu/dac.h> 29 30 #define WIDTH 640 31 32 static struct fb_var_screeninfo hitfb_var = { 33 .activate = FB_ACTIVATE_NOW, 34 .height = -1, 35 .width = -1, 36 .vmode = FB_VMODE_NONINTERLACED, 37 }; 38 39 static struct fb_fix_screeninfo hitfb_fix = { 40 .id = "Hitachi HD64461", 41 .type = FB_TYPE_PACKED_PIXELS, 42 .accel = FB_ACCEL_NONE, 43 }; 44 45 static volatile void __iomem *hitfb_offset_to_addr(unsigned int offset) 46 { 47 return (__force volatile void __iomem *)(uintptr_t)offset; 48 } 49 50 static u16 hitfb_readw(unsigned int offset) 51 { 52 return fb_readw(hitfb_offset_to_addr(offset)); 53 } 54 55 static void hitfb_writew(u16 value, unsigned int offset) 56 { 57 fb_writew(value, hitfb_offset_to_addr(offset)); 58 } 59 60 static inline void hitfb_accel_wait(void) 61 { 62 while (hitfb_readw(HD64461_GRCFGR) & HD64461_GRCFGR_ACCSTATUS) 63 ; 64 } 65 66 static 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 75 static 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 90 static 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 else 107 maddr = 108 (((width >> 4) + 1) * (height + 1) - 1) * 2; 109 110 hitfb_writew((1 << 5) | 1, HD64461_BBTMDR); 111 } else 112 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 140 static 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 164 static 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 172 static 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 186 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 222 static 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 246 static int hitfb_sync(struct fb_info *info) 247 { 248 hitfb_accel_wait(); 249 250 return 0; 251 } 252 253 static 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 304 static 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 329 static const struct fb_ops hitfb_ops = { 330 .owner = THIS_MODULE, 331 .fb_check_var = hitfb_check_var, 332 .fb_set_par = hitfb_set_par, 333 .fb_setcolreg = hitfb_setcolreg, 334 .fb_blank = hitfb_blank, 335 .fb_sync = hitfb_sync, 336 .fb_pan_display = hitfb_pan_display, 337 .fb_fillrect = hitfb_fillrect, 338 .fb_copyarea = hitfb_copyarea, 339 .fb_imageblit = cfb_imageblit, 340 }; 341 342 static int hitfb_probe(struct platform_device *dev) 343 { 344 unsigned short lcdclor, ldr3, ldvndr; 345 struct fb_info *info; 346 int ret; 347 348 if (fb_get_options("hitfb", NULL)) 349 return -ENODEV; 350 351 hitfb_fix.mmio_start = HD64461_IO_OFFSET(0x1000); 352 hitfb_fix.mmio_len = 0x1000; 353 hitfb_fix.smem_start = HD64461_IO_OFFSET(0x02000000); 354 hitfb_fix.smem_len = 512 * 1024; 355 356 lcdclor = hitfb_readw(HD64461_LCDCLOR); 357 ldvndr = hitfb_readw(HD64461_LDVNDR); 358 ldr3 = hitfb_readw(HD64461_LDR3); 359 360 switch (ldr3 & 15) { 361 default: 362 case 4: 363 hitfb_var.bits_per_pixel = 8; 364 hitfb_var.xres = lcdclor; 365 break; 366 case 8: 367 hitfb_var.bits_per_pixel = 16; 368 hitfb_var.xres = lcdclor / 2; 369 break; 370 } 371 hitfb_fix.line_length = lcdclor; 372 hitfb_fix.visual = (hitfb_var.bits_per_pixel == 8) ? 373 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; 374 hitfb_var.yres = ldvndr + 1; 375 hitfb_var.xres_virtual = hitfb_var.xres; 376 hitfb_var.yres_virtual = hitfb_fix.smem_len / lcdclor; 377 switch (hitfb_var.bits_per_pixel) { 378 case 8: 379 hitfb_var.red.offset = 0; 380 hitfb_var.red.length = 8; 381 hitfb_var.green.offset = 0; 382 hitfb_var.green.length = 8; 383 hitfb_var.blue.offset = 0; 384 hitfb_var.blue.length = 8; 385 hitfb_var.transp.offset = 0; 386 hitfb_var.transp.length = 0; 387 break; 388 case 16: /* RGB 565 */ 389 hitfb_var.red.offset = 11; 390 hitfb_var.red.length = 5; 391 hitfb_var.green.offset = 5; 392 hitfb_var.green.length = 6; 393 hitfb_var.blue.offset = 0; 394 hitfb_var.blue.length = 5; 395 hitfb_var.transp.offset = 0; 396 hitfb_var.transp.length = 0; 397 break; 398 } 399 400 info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev); 401 if (unlikely(!info)) 402 return -ENOMEM; 403 404 info->fbops = &hitfb_ops; 405 info->var = hitfb_var; 406 info->fix = hitfb_fix; 407 info->pseudo_palette = info->par; 408 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN | 409 FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA; 410 411 info->screen_base = (void *)hitfb_fix.smem_start; 412 413 ret = fb_alloc_cmap(&info->cmap, 256, 0); 414 if (unlikely(ret < 0)) 415 goto err_fb; 416 417 ret = register_framebuffer(info); 418 if (unlikely(ret < 0)) 419 goto err; 420 421 platform_set_drvdata(dev, info); 422 423 fb_info(info, "%s frame buffer device\n", info->fix.id); 424 425 return 0; 426 427 err: 428 fb_dealloc_cmap(&info->cmap); 429 err_fb: 430 framebuffer_release(info); 431 return ret; 432 } 433 434 static void hitfb_remove(struct platform_device *dev) 435 { 436 struct fb_info *info = platform_get_drvdata(dev); 437 438 unregister_framebuffer(info); 439 fb_dealloc_cmap(&info->cmap); 440 framebuffer_release(info); 441 } 442 443 static int hitfb_suspend(struct device *dev) 444 { 445 u16 v; 446 447 hitfb_blank(1,0); 448 v = hitfb_readw(HD64461_STBCR); 449 v |= HD64461_STBCR_SLCKE_IST; 450 hitfb_writew(v, HD64461_STBCR); 451 452 return 0; 453 } 454 455 static int hitfb_resume(struct device *dev) 456 { 457 u16 v; 458 459 v = hitfb_readw(HD64461_STBCR); 460 v &= ~HD64461_STBCR_SLCKE_OST; 461 msleep(100); 462 v = hitfb_readw(HD64461_STBCR); 463 v &= ~HD64461_STBCR_SLCKE_IST; 464 hitfb_writew(v, HD64461_STBCR); 465 hitfb_blank(0,0); 466 467 return 0; 468 } 469 470 static const struct dev_pm_ops hitfb_dev_pm_ops = { 471 .suspend = hitfb_suspend, 472 .resume = hitfb_resume, 473 }; 474 475 static struct platform_driver hitfb_driver = { 476 .probe = hitfb_probe, 477 .remove_new = hitfb_remove, 478 .driver = { 479 .name = "hitfb", 480 .pm = &hitfb_dev_pm_ops, 481 }, 482 }; 483 484 static struct platform_device hitfb_device = { 485 .name = "hitfb", 486 .id = -1, 487 }; 488 489 static int __init hitfb_init(void) 490 { 491 int ret; 492 493 ret = platform_driver_register(&hitfb_driver); 494 if (!ret) { 495 ret = platform_device_register(&hitfb_device); 496 if (ret) 497 platform_driver_unregister(&hitfb_driver); 498 } 499 return ret; 500 } 501 502 503 static void __exit hitfb_exit(void) 504 { 505 platform_device_unregister(&hitfb_device); 506 platform_driver_unregister(&hitfb_driver); 507 } 508 509 module_init(hitfb_init); 510 module_exit(hitfb_exit); 511 512 MODULE_LICENSE("GPL"); 513