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