Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.28-rc2 1936 lines 46 kB view raw
1/* linux/drivers/video/sm501fb.c 2 * 3 * Copyright (c) 2006 Simtec Electronics 4 * Vincent Sanders <vince@simtec.co.uk> 5 * Ben Dooks <ben@simtec.co.uk> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 * Framebuffer driver for the Silicon Motion SM501 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/tty.h> 20#include <linux/slab.h> 21#include <linux/delay.h> 22#include <linux/fb.h> 23#include <linux/init.h> 24#include <linux/vmalloc.h> 25#include <linux/dma-mapping.h> 26#include <linux/interrupt.h> 27#include <linux/workqueue.h> 28#include <linux/wait.h> 29#include <linux/platform_device.h> 30#include <linux/clk.h> 31#include <linux/console.h> 32 33#include <asm/io.h> 34#include <asm/uaccess.h> 35#include <asm/div64.h> 36 37#ifdef CONFIG_PM 38#include <linux/pm.h> 39#endif 40 41#include <linux/sm501.h> 42#include <linux/sm501-regs.h> 43 44#define NR_PALETTE 256 45 46enum sm501_controller { 47 HEAD_CRT = 0, 48 HEAD_PANEL = 1, 49}; 50 51/* SM501 memory address. 52 * 53 * This structure is used to track memory usage within the SM501 framebuffer 54 * allocation. The sm_addr field is stored as an offset as it is often used 55 * against both the physical and mapped addresses. 56 */ 57struct sm501_mem { 58 unsigned long size; 59 unsigned long sm_addr; /* offset from base of sm501 fb. */ 60 void __iomem *k_addr; 61}; 62 63/* private data that is shared between all frambuffers* */ 64struct sm501fb_info { 65 struct device *dev; 66 struct fb_info *fb[2]; /* fb info for both heads */ 67 struct resource *fbmem_res; /* framebuffer resource */ 68 struct resource *regs_res; /* registers resource */ 69 struct sm501_platdata_fb *pdata; /* our platform data */ 70 71 unsigned long pm_crt_ctrl; /* pm: crt ctrl save */ 72 73 int irq; 74 int swap_endian; /* set to swap rgb=>bgr */ 75 void __iomem *regs; /* remapped registers */ 76 void __iomem *fbmem; /* remapped framebuffer */ 77 size_t fbmem_len; /* length of remapped region */ 78}; 79 80/* per-framebuffer private data */ 81struct sm501fb_par { 82 u32 pseudo_palette[16]; 83 84 enum sm501_controller head; 85 struct sm501_mem cursor; 86 struct sm501_mem screen; 87 struct fb_ops ops; 88 89 void *store_fb; 90 void *store_cursor; 91 void __iomem *cursor_regs; 92 struct sm501fb_info *info; 93}; 94 95/* Helper functions */ 96 97static inline int h_total(struct fb_var_screeninfo *var) 98{ 99 return var->xres + var->left_margin + 100 var->right_margin + var->hsync_len; 101} 102 103static inline int v_total(struct fb_var_screeninfo *var) 104{ 105 return var->yres + var->upper_margin + 106 var->lower_margin + var->vsync_len; 107} 108 109/* sm501fb_sync_regs() 110 * 111 * This call is mainly for PCI bus systems where we need to 112 * ensure that any writes to the bus are completed before the 113 * next phase, or after completing a function. 114*/ 115 116static inline void sm501fb_sync_regs(struct sm501fb_info *info) 117{ 118 readl(info->regs); 119} 120 121/* sm501_alloc_mem 122 * 123 * This is an attempt to lay out memory for the two framebuffers and 124 * everything else 125 * 126 * |fbmem_res->start fbmem_res->end| 127 * | | 128 * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K | 129 * |-> fb[0].fix.smem_len <-| spare |-> fb[1].fix.smem_len <-|-> cursors <-| 130 * 131 * The "spare" space is for the 2d engine data 132 * the fixed is space for the cursors (2x1Kbyte) 133 * 134 * we need to allocate memory for the 2D acceleration engine 135 * command list and the data for the engine to deal with. 136 * 137 * - all allocations must be 128bit aligned 138 * - cursors are 64x64x2 bits (1Kbyte) 139 * 140 */ 141 142#define SM501_MEMF_CURSOR (1) 143#define SM501_MEMF_PANEL (2) 144#define SM501_MEMF_CRT (4) 145#define SM501_MEMF_ACCEL (8) 146 147static int sm501_alloc_mem(struct sm501fb_info *inf, struct sm501_mem *mem, 148 unsigned int why, size_t size) 149{ 150 struct sm501fb_par *par; 151 struct fb_info *fbi; 152 unsigned int ptr; 153 unsigned int end; 154 155 switch (why) { 156 case SM501_MEMF_CURSOR: 157 ptr = inf->fbmem_len - size; 158 inf->fbmem_len = ptr; /* adjust available memory. */ 159 break; 160 161 case SM501_MEMF_PANEL: 162 ptr = inf->fbmem_len - size; 163 fbi = inf->fb[HEAD_CRT]; 164 165 /* round down, some programs such as directfb do not draw 166 * 0,0 correctly unless the start is aligned to a page start. 167 */ 168 169 if (ptr > 0) 170 ptr &= ~(PAGE_SIZE - 1); 171 172 if (fbi && ptr < fbi->fix.smem_len) 173 return -ENOMEM; 174 175 if (ptr < 0) 176 return -ENOMEM; 177 178 break; 179 180 case SM501_MEMF_CRT: 181 ptr = 0; 182 183 /* check to see if we have panel memory allocated 184 * which would put an limit on available memory. */ 185 186 fbi = inf->fb[HEAD_PANEL]; 187 if (fbi) { 188 par = fbi->par; 189 end = par->screen.k_addr ? par->screen.sm_addr : inf->fbmem_len; 190 } else 191 end = inf->fbmem_len; 192 193 if ((ptr + size) > end) 194 return -ENOMEM; 195 196 break; 197 198 case SM501_MEMF_ACCEL: 199 fbi = inf->fb[HEAD_CRT]; 200 ptr = fbi ? fbi->fix.smem_len : 0; 201 202 fbi = inf->fb[HEAD_PANEL]; 203 if (fbi) { 204 par = fbi->par; 205 end = par->screen.sm_addr; 206 } else 207 end = inf->fbmem_len; 208 209 if ((ptr + size) > end) 210 return -ENOMEM; 211 212 break; 213 214 default: 215 return -EINVAL; 216 } 217 218 mem->size = size; 219 mem->sm_addr = ptr; 220 mem->k_addr = inf->fbmem + ptr; 221 222 dev_dbg(inf->dev, "%s: result %08lx, %p - %u, %zd\n", 223 __func__, mem->sm_addr, mem->k_addr, why, size); 224 225 return 0; 226} 227 228/* sm501fb_ps_to_hz 229 * 230 * Converts a period in picoseconds to Hz. 231 * 232 * Note, we try to keep this in Hz to minimise rounding with 233 * the limited PLL settings on the SM501. 234*/ 235 236static unsigned long sm501fb_ps_to_hz(unsigned long psvalue) 237{ 238 unsigned long long numerator=1000000000000ULL; 239 240 /* 10^12 / picosecond period gives frequency in Hz */ 241 do_div(numerator, psvalue); 242 return (unsigned long)numerator; 243} 244 245/* sm501fb_hz_to_ps is identical to the oposite transform */ 246 247#define sm501fb_hz_to_ps(x) sm501fb_ps_to_hz(x) 248 249/* sm501fb_setup_gamma 250 * 251 * Programs a linear 1.0 gamma ramp in case the gamma 252 * correction is enabled without programming anything else. 253*/ 254 255static void sm501fb_setup_gamma(struct sm501fb_info *fbi, 256 unsigned long palette) 257{ 258 unsigned long value = 0; 259 int offset; 260 261 /* set gamma values */ 262 for (offset = 0; offset < 256 * 4; offset += 4) { 263 writel(value, fbi->regs + palette + offset); 264 value += 0x010101; /* Advance RGB by 1,1,1.*/ 265 } 266} 267 268/* sm501fb_check_var 269 * 270 * check common variables for both panel and crt 271*/ 272 273static int sm501fb_check_var(struct fb_var_screeninfo *var, 274 struct fb_info *info) 275{ 276 struct sm501fb_par *par = info->par; 277 struct sm501fb_info *sm = par->info; 278 unsigned long tmp; 279 280 /* check we can fit these values into the registers */ 281 282 if (var->hsync_len > 255 || var->vsync_len > 63) 283 return -EINVAL; 284 285 /* hdisplay end and hsync start */ 286 if ((var->xres + var->right_margin) > 4096) 287 return -EINVAL; 288 289 /* vdisplay end and vsync start */ 290 if ((var->yres + var->lower_margin) > 2048) 291 return -EINVAL; 292 293 /* hard limits of device */ 294 295 if (h_total(var) > 4096 || v_total(var) > 2048) 296 return -EINVAL; 297 298 /* check our line length is going to be 128 bit aligned */ 299 300 tmp = (var->xres * var->bits_per_pixel) / 8; 301 if ((tmp & 15) != 0) 302 return -EINVAL; 303 304 /* check the virtual size */ 305 306 if (var->xres_virtual > 4096 || var->yres_virtual > 2048) 307 return -EINVAL; 308 309 /* can cope with 8,16 or 32bpp */ 310 311 if (var->bits_per_pixel <= 8) 312 var->bits_per_pixel = 8; 313 else if (var->bits_per_pixel <= 16) 314 var->bits_per_pixel = 16; 315 else if (var->bits_per_pixel == 24) 316 var->bits_per_pixel = 32; 317 318 /* set r/g/b positions and validate bpp */ 319 switch(var->bits_per_pixel) { 320 case 8: 321 var->red.length = var->bits_per_pixel; 322 var->red.offset = 0; 323 var->green.length = var->bits_per_pixel; 324 var->green.offset = 0; 325 var->blue.length = var->bits_per_pixel; 326 var->blue.offset = 0; 327 var->transp.length = 0; 328 var->transp.offset = 0; 329 330 break; 331 332 case 16: 333 if (sm->pdata->flags & SM501_FBPD_SWAP_FB_ENDIAN) { 334 var->blue.offset = 11; 335 var->green.offset = 5; 336 var->red.offset = 0; 337 } else { 338 var->red.offset = 11; 339 var->green.offset = 5; 340 var->blue.offset = 0; 341 } 342 var->transp.offset = 0; 343 344 var->red.length = 5; 345 var->green.length = 6; 346 var->blue.length = 5; 347 var->transp.length = 0; 348 break; 349 350 case 32: 351 if (sm->pdata->flags & SM501_FBPD_SWAP_FB_ENDIAN) { 352 var->transp.offset = 0; 353 var->red.offset = 8; 354 var->green.offset = 16; 355 var->blue.offset = 24; 356 } else { 357 var->transp.offset = 24; 358 var->red.offset = 16; 359 var->green.offset = 8; 360 var->blue.offset = 0; 361 } 362 363 var->red.length = 8; 364 var->green.length = 8; 365 var->blue.length = 8; 366 var->transp.length = 0; 367 break; 368 369 default: 370 return -EINVAL; 371 } 372 373 return 0; 374} 375 376/* 377 * sm501fb_check_var_crt(): 378 * 379 * check the parameters for the CRT head, and either bring them 380 * back into range, or return -EINVAL. 381*/ 382 383static int sm501fb_check_var_crt(struct fb_var_screeninfo *var, 384 struct fb_info *info) 385{ 386 return sm501fb_check_var(var, info); 387} 388 389/* sm501fb_check_var_pnl(): 390 * 391 * check the parameters for the CRT head, and either bring them 392 * back into range, or return -EINVAL. 393*/ 394 395static int sm501fb_check_var_pnl(struct fb_var_screeninfo *var, 396 struct fb_info *info) 397{ 398 return sm501fb_check_var(var, info); 399} 400 401/* sm501fb_set_par_common 402 * 403 * set common registers for framebuffers 404*/ 405 406static int sm501fb_set_par_common(struct fb_info *info, 407 struct fb_var_screeninfo *var) 408{ 409 struct sm501fb_par *par = info->par; 410 struct sm501fb_info *fbi = par->info; 411 unsigned long pixclock; /* pixelclock in Hz */ 412 unsigned long sm501pixclock; /* pixelclock the 501 can achive in Hz */ 413 unsigned int mem_type; 414 unsigned int clock_type; 415 unsigned int head_addr; 416 417 dev_dbg(fbi->dev, "%s: %dx%d, bpp = %d, virtual %dx%d\n", 418 __func__, var->xres, var->yres, var->bits_per_pixel, 419 var->xres_virtual, var->yres_virtual); 420 421 switch (par->head) { 422 case HEAD_CRT: 423 mem_type = SM501_MEMF_CRT; 424 clock_type = SM501_CLOCK_V2XCLK; 425 head_addr = SM501_DC_CRT_FB_ADDR; 426 break; 427 428 case HEAD_PANEL: 429 mem_type = SM501_MEMF_PANEL; 430 clock_type = SM501_CLOCK_P2XCLK; 431 head_addr = SM501_DC_PANEL_FB_ADDR; 432 break; 433 434 default: 435 mem_type = 0; /* stop compiler warnings */ 436 head_addr = 0; 437 clock_type = 0; 438 } 439 440 switch (var->bits_per_pixel) { 441 case 8: 442 info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 443 break; 444 445 case 16: 446 info->fix.visual = FB_VISUAL_TRUECOLOR; 447 break; 448 449 case 32: 450 info->fix.visual = FB_VISUAL_TRUECOLOR; 451 break; 452 } 453 454 /* allocate fb memory within 501 */ 455 info->fix.line_length = (var->xres_virtual * var->bits_per_pixel)/8; 456 info->fix.smem_len = info->fix.line_length * var->yres_virtual; 457 458 dev_dbg(fbi->dev, "%s: line length = %u\n", __func__, 459 info->fix.line_length); 460 461 if (sm501_alloc_mem(fbi, &par->screen, mem_type, 462 info->fix.smem_len)) { 463 dev_err(fbi->dev, "no memory available\n"); 464 return -ENOMEM; 465 } 466 467 info->fix.smem_start = fbi->fbmem_res->start + par->screen.sm_addr; 468 469 info->screen_base = fbi->fbmem + par->screen.sm_addr; 470 info->screen_size = info->fix.smem_len; 471 472 /* set start of framebuffer to the screen */ 473 474 writel(par->screen.sm_addr | SM501_ADDR_FLIP, fbi->regs + head_addr); 475 476 /* program CRT clock */ 477 478 pixclock = sm501fb_ps_to_hz(var->pixclock); 479 480 sm501pixclock = sm501_set_clock(fbi->dev->parent, clock_type, 481 pixclock); 482 483 /* update fb layer with actual clock used */ 484 var->pixclock = sm501fb_hz_to_ps(sm501pixclock); 485 486 dev_dbg(fbi->dev, "%s: pixclock(ps) = %u, pixclock(Hz) = %lu, " 487 "sm501pixclock = %lu, error = %ld%%\n", 488 __func__, var->pixclock, pixclock, sm501pixclock, 489 ((pixclock - sm501pixclock)*100)/pixclock); 490 491 return 0; 492} 493 494/* sm501fb_set_par_geometry 495 * 496 * set the geometry registers for specified framebuffer. 497*/ 498 499static void sm501fb_set_par_geometry(struct fb_info *info, 500 struct fb_var_screeninfo *var) 501{ 502 struct sm501fb_par *par = info->par; 503 struct sm501fb_info *fbi = par->info; 504 void __iomem *base = fbi->regs; 505 unsigned long reg; 506 507 if (par->head == HEAD_CRT) 508 base += SM501_DC_CRT_H_TOT; 509 else 510 base += SM501_DC_PANEL_H_TOT; 511 512 /* set framebuffer width and display width */ 513 514 reg = info->fix.line_length; 515 reg |= ((var->xres * var->bits_per_pixel)/8) << 16; 516 517 writel(reg, fbi->regs + (par->head == HEAD_CRT ? 518 SM501_DC_CRT_FB_OFFSET : SM501_DC_PANEL_FB_OFFSET)); 519 520 /* program horizontal total */ 521 522 reg = (h_total(var) - 1) << 16; 523 reg |= (var->xres - 1); 524 525 writel(reg, base + SM501_OFF_DC_H_TOT); 526 527 /* program horizontal sync */ 528 529 reg = var->hsync_len << 16; 530 reg |= var->xres + var->right_margin - 1; 531 532 writel(reg, base + SM501_OFF_DC_H_SYNC); 533 534 /* program vertical total */ 535 536 reg = (v_total(var) - 1) << 16; 537 reg |= (var->yres - 1); 538 539 writel(reg, base + SM501_OFF_DC_V_TOT); 540 541 /* program vertical sync */ 542 reg = var->vsync_len << 16; 543 reg |= var->yres + var->lower_margin - 1; 544 545 writel(reg, base + SM501_OFF_DC_V_SYNC); 546} 547 548/* sm501fb_pan_crt 549 * 550 * pan the CRT display output within an virtual framebuffer 551*/ 552 553static int sm501fb_pan_crt(struct fb_var_screeninfo *var, 554 struct fb_info *info) 555{ 556 struct sm501fb_par *par = info->par; 557 struct sm501fb_info *fbi = par->info; 558 unsigned int bytes_pixel = var->bits_per_pixel / 8; 559 unsigned long reg; 560 unsigned long xoffs; 561 562 xoffs = var->xoffset * bytes_pixel; 563 564 reg = readl(fbi->regs + SM501_DC_CRT_CONTROL); 565 566 reg &= ~SM501_DC_CRT_CONTROL_PIXEL_MASK; 567 reg |= ((xoffs & 15) / bytes_pixel) << 4; 568 writel(reg, fbi->regs + SM501_DC_CRT_CONTROL); 569 570 reg = (par->screen.sm_addr + xoffs + 571 var->yoffset * info->fix.line_length); 572 writel(reg | SM501_ADDR_FLIP, fbi->regs + SM501_DC_CRT_FB_ADDR); 573 574 sm501fb_sync_regs(fbi); 575 return 0; 576} 577 578/* sm501fb_pan_pnl 579 * 580 * pan the panel display output within an virtual framebuffer 581*/ 582 583static int sm501fb_pan_pnl(struct fb_var_screeninfo *var, 584 struct fb_info *info) 585{ 586 struct sm501fb_par *par = info->par; 587 struct sm501fb_info *fbi = par->info; 588 unsigned long reg; 589 590 reg = var->xoffset | (var->xres_virtual << 16); 591 writel(reg, fbi->regs + SM501_DC_PANEL_FB_WIDTH); 592 593 reg = var->yoffset | (var->yres_virtual << 16); 594 writel(reg, fbi->regs + SM501_DC_PANEL_FB_HEIGHT); 595 596 sm501fb_sync_regs(fbi); 597 return 0; 598} 599 600/* sm501fb_set_par_crt 601 * 602 * Set the CRT video mode from the fb_info structure 603*/ 604 605static int sm501fb_set_par_crt(struct fb_info *info) 606{ 607 struct sm501fb_par *par = info->par; 608 struct sm501fb_info *fbi = par->info; 609 struct fb_var_screeninfo *var = &info->var; 610 unsigned long control; /* control register */ 611 int ret; 612 613 /* activate new configuration */ 614 615 dev_dbg(fbi->dev, "%s(%p)\n", __func__, info); 616 617 /* enable CRT DAC - note 0 is on!*/ 618 sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER); 619 620 control = readl(fbi->regs + SM501_DC_CRT_CONTROL); 621 622 control &= (SM501_DC_CRT_CONTROL_PIXEL_MASK | 623 SM501_DC_CRT_CONTROL_GAMMA | 624 SM501_DC_CRT_CONTROL_BLANK | 625 SM501_DC_CRT_CONTROL_SEL | 626 SM501_DC_CRT_CONTROL_CP | 627 SM501_DC_CRT_CONTROL_TVP); 628 629 /* set the sync polarities before we check data source */ 630 631 if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0) 632 control |= SM501_DC_CRT_CONTROL_HSP; 633 634 if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0) 635 control |= SM501_DC_CRT_CONTROL_VSP; 636 637 if ((control & SM501_DC_CRT_CONTROL_SEL) == 0) { 638 /* the head is displaying panel data... */ 639 640 sm501_alloc_mem(fbi, &par->screen, SM501_MEMF_CRT, 0); 641 goto out_update; 642 } 643 644 ret = sm501fb_set_par_common(info, var); 645 if (ret) { 646 dev_err(fbi->dev, "failed to set common parameters\n"); 647 return ret; 648 } 649 650 sm501fb_pan_crt(var, info); 651 sm501fb_set_par_geometry(info, var); 652 653 control |= SM501_FIFO_3; /* fill if >3 free slots */ 654 655 switch(var->bits_per_pixel) { 656 case 8: 657 control |= SM501_DC_CRT_CONTROL_8BPP; 658 break; 659 660 case 16: 661 control |= SM501_DC_CRT_CONTROL_16BPP; 662 sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE); 663 break; 664 665 case 32: 666 control |= SM501_DC_CRT_CONTROL_32BPP; 667 sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE); 668 break; 669 670 default: 671 BUG(); 672 } 673 674 control |= SM501_DC_CRT_CONTROL_SEL; /* CRT displays CRT data */ 675 control |= SM501_DC_CRT_CONTROL_TE; /* enable CRT timing */ 676 control |= SM501_DC_CRT_CONTROL_ENABLE; /* enable CRT plane */ 677 678 out_update: 679 dev_dbg(fbi->dev, "new control is %08lx\n", control); 680 681 writel(control, fbi->regs + SM501_DC_CRT_CONTROL); 682 sm501fb_sync_regs(fbi); 683 684 return 0; 685} 686 687static void sm501fb_panel_power(struct sm501fb_info *fbi, int to) 688{ 689 unsigned long control; 690 void __iomem *ctrl_reg = fbi->regs + SM501_DC_PANEL_CONTROL; 691 struct sm501_platdata_fbsub *pd = fbi->pdata->fb_pnl; 692 693 control = readl(ctrl_reg); 694 695 if (to && (control & SM501_DC_PANEL_CONTROL_VDD) == 0) { 696 /* enable panel power */ 697 698 control |= SM501_DC_PANEL_CONTROL_VDD; /* FPVDDEN */ 699 writel(control, ctrl_reg); 700 sm501fb_sync_regs(fbi); 701 mdelay(10); 702 703 control |= SM501_DC_PANEL_CONTROL_DATA; /* DATA */ 704 writel(control, ctrl_reg); 705 sm501fb_sync_regs(fbi); 706 mdelay(10); 707 708 /* VBIASEN */ 709 710 if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { 711 if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) 712 control &= ~SM501_DC_PANEL_CONTROL_BIAS; 713 else 714 control |= SM501_DC_PANEL_CONTROL_BIAS; 715 716 writel(control, ctrl_reg); 717 sm501fb_sync_regs(fbi); 718 mdelay(10); 719 } 720 721 if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { 722 if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) 723 control &= ~SM501_DC_PANEL_CONTROL_FPEN; 724 else 725 control |= SM501_DC_PANEL_CONTROL_FPEN; 726 727 writel(control, ctrl_reg); 728 sm501fb_sync_regs(fbi); 729 mdelay(10); 730 } 731 } else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) { 732 /* disable panel power */ 733 if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { 734 if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) 735 control |= SM501_DC_PANEL_CONTROL_FPEN; 736 else 737 control &= ~SM501_DC_PANEL_CONTROL_FPEN; 738 739 writel(control, ctrl_reg); 740 sm501fb_sync_regs(fbi); 741 mdelay(10); 742 } 743 744 if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { 745 if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) 746 control |= SM501_DC_PANEL_CONTROL_BIAS; 747 else 748 control &= ~SM501_DC_PANEL_CONTROL_BIAS; 749 750 writel(control, ctrl_reg); 751 sm501fb_sync_regs(fbi); 752 mdelay(10); 753 } 754 755 control &= ~SM501_DC_PANEL_CONTROL_DATA; 756 writel(control, ctrl_reg); 757 sm501fb_sync_regs(fbi); 758 mdelay(10); 759 760 control &= ~SM501_DC_PANEL_CONTROL_VDD; 761 writel(control, ctrl_reg); 762 sm501fb_sync_regs(fbi); 763 mdelay(10); 764 } 765 766 sm501fb_sync_regs(fbi); 767} 768 769/* sm501fb_set_par_pnl 770 * 771 * Set the panel video mode from the fb_info structure 772*/ 773 774static int sm501fb_set_par_pnl(struct fb_info *info) 775{ 776 struct sm501fb_par *par = info->par; 777 struct sm501fb_info *fbi = par->info; 778 struct fb_var_screeninfo *var = &info->var; 779 unsigned long control; 780 unsigned long reg; 781 int ret; 782 783 dev_dbg(fbi->dev, "%s(%p)\n", __func__, info); 784 785 /* activate this new configuration */ 786 787 ret = sm501fb_set_par_common(info, var); 788 if (ret) 789 return ret; 790 791 sm501fb_pan_pnl(var, info); 792 sm501fb_set_par_geometry(info, var); 793 794 /* update control register */ 795 796 control = readl(fbi->regs + SM501_DC_PANEL_CONTROL); 797 control &= (SM501_DC_PANEL_CONTROL_GAMMA | 798 SM501_DC_PANEL_CONTROL_VDD | 799 SM501_DC_PANEL_CONTROL_DATA | 800 SM501_DC_PANEL_CONTROL_BIAS | 801 SM501_DC_PANEL_CONTROL_FPEN | 802 SM501_DC_PANEL_CONTROL_CP | 803 SM501_DC_PANEL_CONTROL_CK | 804 SM501_DC_PANEL_CONTROL_HP | 805 SM501_DC_PANEL_CONTROL_VP | 806 SM501_DC_PANEL_CONTROL_HPD | 807 SM501_DC_PANEL_CONTROL_VPD); 808 809 control |= SM501_FIFO_3; /* fill if >3 free slots */ 810 811 switch(var->bits_per_pixel) { 812 case 8: 813 control |= SM501_DC_PANEL_CONTROL_8BPP; 814 break; 815 816 case 16: 817 control |= SM501_DC_PANEL_CONTROL_16BPP; 818 sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE); 819 break; 820 821 case 32: 822 control |= SM501_DC_PANEL_CONTROL_32BPP; 823 sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE); 824 break; 825 826 default: 827 BUG(); 828 } 829 830 writel(0x0, fbi->regs + SM501_DC_PANEL_PANNING_CONTROL); 831 832 /* panel plane top left and bottom right location */ 833 834 writel(0x00, fbi->regs + SM501_DC_PANEL_TL_LOC); 835 836 reg = var->xres - 1; 837 reg |= (var->yres - 1) << 16; 838 839 writel(reg, fbi->regs + SM501_DC_PANEL_BR_LOC); 840 841 /* program panel control register */ 842 843 control |= SM501_DC_PANEL_CONTROL_TE; /* enable PANEL timing */ 844 control |= SM501_DC_PANEL_CONTROL_EN; /* enable PANEL gfx plane */ 845 846 if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0) 847 control |= SM501_DC_PANEL_CONTROL_HSP; 848 849 if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0) 850 control |= SM501_DC_PANEL_CONTROL_VSP; 851 852 writel(control, fbi->regs + SM501_DC_PANEL_CONTROL); 853 sm501fb_sync_regs(fbi); 854 855 /* ensure the panel interface is not tristated at this point */ 856 857 sm501_modify_reg(fbi->dev->parent, SM501_SYSTEM_CONTROL, 858 0, SM501_SYSCTRL_PANEL_TRISTATE); 859 860 /* power the panel up */ 861 sm501fb_panel_power(fbi, 1); 862 return 0; 863} 864 865 866/* chan_to_field 867 * 868 * convert a colour value into a field position 869 * 870 * from pxafb.c 871*/ 872 873static inline unsigned int chan_to_field(unsigned int chan, 874 struct fb_bitfield *bf) 875{ 876 chan &= 0xffff; 877 chan >>= 16 - bf->length; 878 return chan << bf->offset; 879} 880 881/* sm501fb_setcolreg 882 * 883 * set the colour mapping for modes that support palettised data 884*/ 885 886static int sm501fb_setcolreg(unsigned regno, 887 unsigned red, unsigned green, unsigned blue, 888 unsigned transp, struct fb_info *info) 889{ 890 struct sm501fb_par *par = info->par; 891 struct sm501fb_info *fbi = par->info; 892 void __iomem *base = fbi->regs; 893 unsigned int val; 894 895 if (par->head == HEAD_CRT) 896 base += SM501_DC_CRT_PALETTE; 897 else 898 base += SM501_DC_PANEL_PALETTE; 899 900 switch (info->fix.visual) { 901 case FB_VISUAL_TRUECOLOR: 902 /* true-colour, use pseuo-palette */ 903 904 if (regno < 16) { 905 u32 *pal = par->pseudo_palette; 906 907 val = chan_to_field(red, &info->var.red); 908 val |= chan_to_field(green, &info->var.green); 909 val |= chan_to_field(blue, &info->var.blue); 910 911 pal[regno] = val; 912 } 913 break; 914 915 case FB_VISUAL_PSEUDOCOLOR: 916 if (regno < 256) { 917 val = (red >> 8) << 16; 918 val |= (green >> 8) << 8; 919 val |= blue >> 8; 920 921 writel(val, base + (regno * 4)); 922 } 923 924 break; 925 926 default: 927 return 1; /* unknown type */ 928 } 929 930 return 0; 931} 932 933/* sm501fb_blank_pnl 934 * 935 * Blank or un-blank the panel interface 936*/ 937 938static int sm501fb_blank_pnl(int blank_mode, struct fb_info *info) 939{ 940 struct sm501fb_par *par = info->par; 941 struct sm501fb_info *fbi = par->info; 942 943 dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info); 944 945 switch (blank_mode) { 946 case FB_BLANK_POWERDOWN: 947 sm501fb_panel_power(fbi, 0); 948 break; 949 950 case FB_BLANK_UNBLANK: 951 sm501fb_panel_power(fbi, 1); 952 break; 953 954 case FB_BLANK_NORMAL: 955 case FB_BLANK_VSYNC_SUSPEND: 956 case FB_BLANK_HSYNC_SUSPEND: 957 default: 958 return 1; 959 } 960 961 return 0; 962} 963 964/* sm501fb_blank_crt 965 * 966 * Blank or un-blank the crt interface 967*/ 968 969static int sm501fb_blank_crt(int blank_mode, struct fb_info *info) 970{ 971 struct sm501fb_par *par = info->par; 972 struct sm501fb_info *fbi = par->info; 973 unsigned long ctrl; 974 975 dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info); 976 977 ctrl = readl(fbi->regs + SM501_DC_CRT_CONTROL); 978 979 switch (blank_mode) { 980 case FB_BLANK_POWERDOWN: 981 ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE; 982 sm501_misc_control(fbi->dev->parent, SM501_MISC_DAC_POWER, 0); 983 984 case FB_BLANK_NORMAL: 985 ctrl |= SM501_DC_CRT_CONTROL_BLANK; 986 break; 987 988 case FB_BLANK_UNBLANK: 989 ctrl &= ~SM501_DC_CRT_CONTROL_BLANK; 990 ctrl |= SM501_DC_CRT_CONTROL_ENABLE; 991 sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER); 992 break; 993 994 case FB_BLANK_VSYNC_SUSPEND: 995 case FB_BLANK_HSYNC_SUSPEND: 996 default: 997 return 1; 998 999 } 1000 1001 writel(ctrl, fbi->regs + SM501_DC_CRT_CONTROL); 1002 sm501fb_sync_regs(fbi); 1003 1004 return 0; 1005} 1006 1007/* sm501fb_cursor 1008 * 1009 * set or change the hardware cursor parameters 1010*/ 1011 1012static int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor) 1013{ 1014 struct sm501fb_par *par = info->par; 1015 struct sm501fb_info *fbi = par->info; 1016 void __iomem *base = fbi->regs; 1017 unsigned long hwc_addr; 1018 unsigned long fg, bg; 1019 1020 dev_dbg(fbi->dev, "%s(%p,%p)\n", __func__, info, cursor); 1021 1022 if (par->head == HEAD_CRT) 1023 base += SM501_DC_CRT_HWC_BASE; 1024 else 1025 base += SM501_DC_PANEL_HWC_BASE; 1026 1027 /* check not being asked to exceed capabilities */ 1028 1029 if (cursor->image.width > 64) 1030 return -EINVAL; 1031 1032 if (cursor->image.height > 64) 1033 return -EINVAL; 1034 1035 if (cursor->image.depth > 1) 1036 return -EINVAL; 1037 1038 hwc_addr = readl(base + SM501_OFF_HWC_ADDR); 1039 1040 if (cursor->enable) 1041 writel(hwc_addr | SM501_HWC_EN, base + SM501_OFF_HWC_ADDR); 1042 else 1043 writel(hwc_addr & ~SM501_HWC_EN, base + SM501_OFF_HWC_ADDR); 1044 1045 /* set data */ 1046 if (cursor->set & FB_CUR_SETPOS) { 1047 unsigned int x = cursor->image.dx; 1048 unsigned int y = cursor->image.dy; 1049 1050 if (x >= 2048 || y >= 2048 ) 1051 return -EINVAL; 1052 1053 dev_dbg(fbi->dev, "set position %d,%d\n", x, y); 1054 1055 //y += cursor->image.height; 1056 1057 writel(x | (y << 16), base + SM501_OFF_HWC_LOC); 1058 } 1059 1060 if (cursor->set & FB_CUR_SETCMAP) { 1061 unsigned int bg_col = cursor->image.bg_color; 1062 unsigned int fg_col = cursor->image.fg_color; 1063 1064 dev_dbg(fbi->dev, "%s: update cmap (%08x,%08x)\n", 1065 __func__, bg_col, fg_col); 1066 1067 bg = ((info->cmap.red[bg_col] & 0xF8) << 8) | 1068 ((info->cmap.green[bg_col] & 0xFC) << 3) | 1069 ((info->cmap.blue[bg_col] & 0xF8) >> 3); 1070 1071 fg = ((info->cmap.red[fg_col] & 0xF8) << 8) | 1072 ((info->cmap.green[fg_col] & 0xFC) << 3) | 1073 ((info->cmap.blue[fg_col] & 0xF8) >> 3); 1074 1075 dev_dbg(fbi->dev, "fgcol %08lx, bgcol %08lx\n", fg, bg); 1076 1077 writel(bg, base + SM501_OFF_HWC_COLOR_1_2); 1078 writel(fg, base + SM501_OFF_HWC_COLOR_3); 1079 } 1080 1081 if (cursor->set & FB_CUR_SETSIZE || 1082 cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE)) { 1083 /* SM501 cursor is a two bpp 64x64 bitmap this routine 1084 * clears it to transparent then combines the cursor 1085 * shape plane with the colour plane to set the 1086 * cursor */ 1087 int x, y; 1088 const unsigned char *pcol = cursor->image.data; 1089 const unsigned char *pmsk = cursor->mask; 1090 void __iomem *dst = par->cursor.k_addr; 1091 unsigned char dcol = 0; 1092 unsigned char dmsk = 0; 1093 unsigned int op; 1094 1095 dev_dbg(fbi->dev, "%s: setting shape (%d,%d)\n", 1096 __func__, cursor->image.width, cursor->image.height); 1097 1098 for (op = 0; op < (64*64*2)/8; op+=4) 1099 writel(0x0, dst + op); 1100 1101 for (y = 0; y < cursor->image.height; y++) { 1102 for (x = 0; x < cursor->image.width; x++) { 1103 if ((x % 8) == 0) { 1104 dcol = *pcol++; 1105 dmsk = *pmsk++; 1106 } else { 1107 dcol >>= 1; 1108 dmsk >>= 1; 1109 } 1110 1111 if (dmsk & 1) { 1112 op = (dcol & 1) ? 1 : 3; 1113 op <<= ((x % 4) * 2); 1114 1115 op |= readb(dst + (x / 4)); 1116 writeb(op, dst + (x / 4)); 1117 } 1118 } 1119 dst += (64*2)/8; 1120 } 1121 } 1122 1123 sm501fb_sync_regs(fbi); /* ensure cursor data flushed */ 1124 return 0; 1125} 1126 1127/* sm501fb_crtsrc_show 1128 * 1129 * device attribute code to show where the crt output is sourced from 1130*/ 1131 1132static ssize_t sm501fb_crtsrc_show(struct device *dev, 1133 struct device_attribute *attr, char *buf) 1134{ 1135 struct sm501fb_info *info = dev_get_drvdata(dev); 1136 unsigned long ctrl; 1137 1138 ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); 1139 ctrl &= SM501_DC_CRT_CONTROL_SEL; 1140 1141 return snprintf(buf, PAGE_SIZE, "%s\n", ctrl ? "crt" : "panel"); 1142} 1143 1144/* sm501fb_crtsrc_show 1145 * 1146 * device attribute code to set where the crt output is sourced from 1147*/ 1148 1149static ssize_t sm501fb_crtsrc_store(struct device *dev, 1150 struct device_attribute *attr, 1151 const char *buf, size_t len) 1152{ 1153 struct sm501fb_info *info = dev_get_drvdata(dev); 1154 enum sm501_controller head; 1155 unsigned long ctrl; 1156 1157 if (len < 1) 1158 return -EINVAL; 1159 1160 if (strnicmp(buf, "crt", 3) == 0) 1161 head = HEAD_CRT; 1162 else if (strnicmp(buf, "panel", 5) == 0) 1163 head = HEAD_PANEL; 1164 else 1165 return -EINVAL; 1166 1167 dev_info(dev, "setting crt source to head %d\n", head); 1168 1169 ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); 1170 1171 if (head == HEAD_CRT) { 1172 ctrl |= SM501_DC_CRT_CONTROL_SEL; 1173 ctrl |= SM501_DC_CRT_CONTROL_ENABLE; 1174 ctrl |= SM501_DC_CRT_CONTROL_TE; 1175 } else { 1176 ctrl &= ~SM501_DC_CRT_CONTROL_SEL; 1177 ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE; 1178 ctrl &= ~SM501_DC_CRT_CONTROL_TE; 1179 } 1180 1181 writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); 1182 sm501fb_sync_regs(info); 1183 1184 return len; 1185} 1186 1187/* Prepare the device_attr for registration with sysfs later */ 1188static DEVICE_ATTR(crt_src, 0666, sm501fb_crtsrc_show, sm501fb_crtsrc_store); 1189 1190/* sm501fb_show_regs 1191 * 1192 * show the primary sm501 registers 1193*/ 1194static int sm501fb_show_regs(struct sm501fb_info *info, char *ptr, 1195 unsigned int start, unsigned int len) 1196{ 1197 void __iomem *mem = info->regs; 1198 char *buf = ptr; 1199 unsigned int reg; 1200 1201 for (reg = start; reg < (len + start); reg += 4) 1202 ptr += sprintf(ptr, "%08x = %08x\n", reg, readl(mem + reg)); 1203 1204 return ptr - buf; 1205} 1206 1207/* sm501fb_debug_show_crt 1208 * 1209 * show the crt control and cursor registers 1210*/ 1211 1212static ssize_t sm501fb_debug_show_crt(struct device *dev, 1213 struct device_attribute *attr, char *buf) 1214{ 1215 struct sm501fb_info *info = dev_get_drvdata(dev); 1216 char *ptr = buf; 1217 1218 ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_CONTROL, 0x40); 1219 ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_HWC_BASE, 0x10); 1220 1221 return ptr - buf; 1222} 1223 1224static DEVICE_ATTR(fbregs_crt, 0444, sm501fb_debug_show_crt, NULL); 1225 1226/* sm501fb_debug_show_pnl 1227 * 1228 * show the panel control and cursor registers 1229*/ 1230 1231static ssize_t sm501fb_debug_show_pnl(struct device *dev, 1232 struct device_attribute *attr, char *buf) 1233{ 1234 struct sm501fb_info *info = dev_get_drvdata(dev); 1235 char *ptr = buf; 1236 1237 ptr += sm501fb_show_regs(info, ptr, 0x0, 0x40); 1238 ptr += sm501fb_show_regs(info, ptr, SM501_DC_PANEL_HWC_BASE, 0x10); 1239 1240 return ptr - buf; 1241} 1242 1243static DEVICE_ATTR(fbregs_pnl, 0444, sm501fb_debug_show_pnl, NULL); 1244 1245/* framebuffer ops */ 1246 1247static struct fb_ops sm501fb_ops_crt = { 1248 .owner = THIS_MODULE, 1249 .fb_check_var = sm501fb_check_var_crt, 1250 .fb_set_par = sm501fb_set_par_crt, 1251 .fb_blank = sm501fb_blank_crt, 1252 .fb_setcolreg = sm501fb_setcolreg, 1253 .fb_pan_display = sm501fb_pan_crt, 1254 .fb_cursor = sm501fb_cursor, 1255 .fb_fillrect = cfb_fillrect, 1256 .fb_copyarea = cfb_copyarea, 1257 .fb_imageblit = cfb_imageblit, 1258}; 1259 1260static struct fb_ops sm501fb_ops_pnl = { 1261 .owner = THIS_MODULE, 1262 .fb_check_var = sm501fb_check_var_pnl, 1263 .fb_set_par = sm501fb_set_par_pnl, 1264 .fb_pan_display = sm501fb_pan_pnl, 1265 .fb_blank = sm501fb_blank_pnl, 1266 .fb_setcolreg = sm501fb_setcolreg, 1267 .fb_cursor = sm501fb_cursor, 1268 .fb_fillrect = cfb_fillrect, 1269 .fb_copyarea = cfb_copyarea, 1270 .fb_imageblit = cfb_imageblit, 1271}; 1272 1273/* sm501_init_cursor 1274 * 1275 * initialise hw cursor parameters 1276*/ 1277 1278static int sm501_init_cursor(struct fb_info *fbi, unsigned int reg_base) 1279{ 1280 struct sm501fb_par *par; 1281 struct sm501fb_info *info; 1282 int ret; 1283 1284 if (fbi == NULL) 1285 return 0; 1286 1287 par = fbi->par; 1288 info = par->info; 1289 1290 par->cursor_regs = info->regs + reg_base; 1291 1292 ret = sm501_alloc_mem(info, &par->cursor, SM501_MEMF_CURSOR, 1024); 1293 if (ret < 0) 1294 return ret; 1295 1296 /* initialise the colour registers */ 1297 1298 writel(par->cursor.sm_addr, par->cursor_regs + SM501_OFF_HWC_ADDR); 1299 1300 writel(0x00, par->cursor_regs + SM501_OFF_HWC_LOC); 1301 writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_1_2); 1302 writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_3); 1303 sm501fb_sync_regs(info); 1304 1305 return 0; 1306} 1307 1308/* sm501fb_info_start 1309 * 1310 * fills the par structure claiming resources and remapping etc. 1311*/ 1312 1313static int sm501fb_start(struct sm501fb_info *info, 1314 struct platform_device *pdev) 1315{ 1316 struct resource *res; 1317 struct device *dev = &pdev->dev; 1318 int k; 1319 int ret; 1320 1321 info->irq = ret = platform_get_irq(pdev, 0); 1322 if (ret < 0) { 1323 /* we currently do not use the IRQ */ 1324 dev_warn(dev, "no irq for device\n"); 1325 } 1326 1327 /* allocate, reserve and remap resources for registers */ 1328 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1329 if (res == NULL) { 1330 dev_err(dev, "no resource definition for registers\n"); 1331 ret = -ENOENT; 1332 goto err_release; 1333 } 1334 1335 info->regs_res = request_mem_region(res->start, 1336 res->end - res->start, 1337 pdev->name); 1338 1339 if (info->regs_res == NULL) { 1340 dev_err(dev, "cannot claim registers\n"); 1341 ret = -ENXIO; 1342 goto err_release; 1343 } 1344 1345 info->regs = ioremap(res->start, (res->end - res->start)+1); 1346 if (info->regs == NULL) { 1347 dev_err(dev, "cannot remap registers\n"); 1348 ret = -ENXIO; 1349 goto err_regs_res; 1350 } 1351 1352 /* allocate, reserve resources for framebuffer */ 1353 res = platform_get_resource(pdev, IORESOURCE_MEM, 2); 1354 if (res == NULL) { 1355 dev_err(dev, "no memory resource defined\n"); 1356 ret = -ENXIO; 1357 goto err_regs_map; 1358 } 1359 1360 info->fbmem_res = request_mem_region(res->start, 1361 (res->end - res->start)+1, 1362 pdev->name); 1363 if (info->fbmem_res == NULL) { 1364 dev_err(dev, "cannot claim framebuffer\n"); 1365 ret = -ENXIO; 1366 goto err_regs_map; 1367 } 1368 1369 info->fbmem = ioremap(res->start, (res->end - res->start)+1); 1370 if (info->fbmem == NULL) { 1371 dev_err(dev, "cannot remap framebuffer\n"); 1372 goto err_mem_res; 1373 } 1374 1375 info->fbmem_len = (res->end - res->start)+1; 1376 1377 /* clear framebuffer memory - avoids garbage data on unused fb */ 1378 memset(info->fbmem, 0, info->fbmem_len); 1379 1380 /* clear palette ram - undefined at power on */ 1381 for (k = 0; k < (256 * 3); k++) 1382 writel(0, info->regs + SM501_DC_PANEL_PALETTE + (k * 4)); 1383 1384 /* enable display controller */ 1385 sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1); 1386 1387 /* setup cursors */ 1388 1389 sm501_init_cursor(info->fb[HEAD_CRT], SM501_DC_CRT_HWC_ADDR); 1390 sm501_init_cursor(info->fb[HEAD_PANEL], SM501_DC_PANEL_HWC_ADDR); 1391 1392 return 0; /* everything is setup */ 1393 1394 err_mem_res: 1395 release_resource(info->fbmem_res); 1396 kfree(info->fbmem_res); 1397 1398 err_regs_map: 1399 iounmap(info->regs); 1400 1401 err_regs_res: 1402 release_resource(info->regs_res); 1403 kfree(info->regs_res); 1404 1405 err_release: 1406 return ret; 1407} 1408 1409static void sm501fb_stop(struct sm501fb_info *info) 1410{ 1411 /* disable display controller */ 1412 sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0); 1413 1414 iounmap(info->fbmem); 1415 release_resource(info->fbmem_res); 1416 kfree(info->fbmem_res); 1417 1418 iounmap(info->regs); 1419 release_resource(info->regs_res); 1420 kfree(info->regs_res); 1421} 1422 1423static int sm501fb_init_fb(struct fb_info *fb, 1424 enum sm501_controller head, 1425 const char *fbname) 1426{ 1427 struct sm501_platdata_fbsub *pd; 1428 struct sm501fb_par *par = fb->par; 1429 struct sm501fb_info *info = par->info; 1430 unsigned long ctrl; 1431 unsigned int enable; 1432 int ret; 1433 1434 switch (head) { 1435 case HEAD_CRT: 1436 pd = info->pdata->fb_crt; 1437 ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); 1438 enable = (ctrl & SM501_DC_CRT_CONTROL_ENABLE) ? 1 : 0; 1439 1440 /* ensure we set the correct source register */ 1441 if (info->pdata->fb_route != SM501_FB_CRT_PANEL) { 1442 ctrl |= SM501_DC_CRT_CONTROL_SEL; 1443 writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); 1444 } 1445 1446 break; 1447 1448 case HEAD_PANEL: 1449 pd = info->pdata->fb_pnl; 1450 ctrl = readl(info->regs + SM501_DC_PANEL_CONTROL); 1451 enable = (ctrl & SM501_DC_PANEL_CONTROL_EN) ? 1 : 0; 1452 break; 1453 1454 default: 1455 pd = NULL; /* stop compiler warnings */ 1456 ctrl = 0; 1457 enable = 0; 1458 BUG(); 1459 } 1460 1461 dev_info(info->dev, "fb %s %sabled at start\n", 1462 fbname, enable ? "en" : "dis"); 1463 1464 /* check to see if our routing allows this */ 1465 1466 if (head == HEAD_CRT && info->pdata->fb_route == SM501_FB_CRT_PANEL) { 1467 ctrl &= ~SM501_DC_CRT_CONTROL_SEL; 1468 writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); 1469 enable = 0; 1470 } 1471 1472 strlcpy(fb->fix.id, fbname, sizeof(fb->fix.id)); 1473 1474 memcpy(&par->ops, 1475 (head == HEAD_CRT) ? &sm501fb_ops_crt : &sm501fb_ops_pnl, 1476 sizeof(struct fb_ops)); 1477 1478 /* update ops dependant on what we've been passed */ 1479 1480 if ((pd->flags & SM501FB_FLAG_USE_HWCURSOR) == 0) 1481 par->ops.fb_cursor = NULL; 1482 1483 fb->fbops = &par->ops; 1484 fb->flags = FBINFO_FLAG_DEFAULT | 1485 FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; 1486 1487 /* fixed data */ 1488 1489 fb->fix.type = FB_TYPE_PACKED_PIXELS; 1490 fb->fix.type_aux = 0; 1491 fb->fix.xpanstep = 1; 1492 fb->fix.ypanstep = 1; 1493 fb->fix.ywrapstep = 0; 1494 fb->fix.accel = FB_ACCEL_NONE; 1495 1496 /* screenmode */ 1497 1498 fb->var.nonstd = 0; 1499 fb->var.activate = FB_ACTIVATE_NOW; 1500 fb->var.accel_flags = 0; 1501 fb->var.vmode = FB_VMODE_NONINTERLACED; 1502 fb->var.bits_per_pixel = 16; 1503 1504 if (enable && (pd->flags & SM501FB_FLAG_USE_INIT_MODE) && 0) { 1505 /* TODO read the mode from the current display */ 1506 1507 } else { 1508 if (pd->def_mode) { 1509 dev_info(info->dev, "using supplied mode\n"); 1510 fb_videomode_to_var(&fb->var, pd->def_mode); 1511 1512 fb->var.bits_per_pixel = pd->def_bpp ? pd->def_bpp : 8; 1513 fb->var.xres_virtual = fb->var.xres; 1514 fb->var.yres_virtual = fb->var.yres; 1515 } else { 1516 ret = fb_find_mode(&fb->var, fb, 1517 NULL, NULL, 0, NULL, 8); 1518 1519 if (ret == 0 || ret == 4) { 1520 dev_err(info->dev, 1521 "failed to get initial mode\n"); 1522 return -EINVAL; 1523 } 1524 } 1525 } 1526 1527 /* initialise and set the palette */ 1528 fb_alloc_cmap(&fb->cmap, NR_PALETTE, 0); 1529 fb_set_cmap(&fb->cmap, fb); 1530 1531 ret = (fb->fbops->fb_check_var)(&fb->var, fb); 1532 if (ret) 1533 dev_err(info->dev, "check_var() failed on initial setup?\n"); 1534 1535 /* ensure we've activated our new configuration */ 1536 (fb->fbops->fb_set_par)(fb); 1537 1538 return 0; 1539} 1540 1541/* default platform data if none is supplied (ie, PCI device) */ 1542 1543static struct sm501_platdata_fbsub sm501fb_pdata_crt = { 1544 .flags = (SM501FB_FLAG_USE_INIT_MODE | 1545 SM501FB_FLAG_USE_HWCURSOR | 1546 SM501FB_FLAG_USE_HWACCEL | 1547 SM501FB_FLAG_DISABLE_AT_EXIT), 1548 1549}; 1550 1551static struct sm501_platdata_fbsub sm501fb_pdata_pnl = { 1552 .flags = (SM501FB_FLAG_USE_INIT_MODE | 1553 SM501FB_FLAG_USE_HWCURSOR | 1554 SM501FB_FLAG_USE_HWACCEL | 1555 SM501FB_FLAG_DISABLE_AT_EXIT), 1556}; 1557 1558static struct sm501_platdata_fb sm501fb_def_pdata = { 1559 .fb_route = SM501_FB_OWN, 1560 .fb_crt = &sm501fb_pdata_crt, 1561 .fb_pnl = &sm501fb_pdata_pnl, 1562}; 1563 1564static char driver_name_crt[] = "sm501fb-crt"; 1565static char driver_name_pnl[] = "sm501fb-panel"; 1566 1567static int __devinit sm501fb_probe_one(struct sm501fb_info *info, 1568 enum sm501_controller head) 1569{ 1570 unsigned char *name = (head == HEAD_CRT) ? "crt" : "panel"; 1571 struct sm501_platdata_fbsub *pd; 1572 struct sm501fb_par *par; 1573 struct fb_info *fbi; 1574 1575 pd = (head == HEAD_CRT) ? info->pdata->fb_crt : info->pdata->fb_pnl; 1576 1577 /* Do not initialise if we've not been given any platform data */ 1578 if (pd == NULL) { 1579 dev_info(info->dev, "no data for fb %s (disabled)\n", name); 1580 return 0; 1581 } 1582 1583 fbi = framebuffer_alloc(sizeof(struct sm501fb_par), info->dev); 1584 if (fbi == NULL) { 1585 dev_err(info->dev, "cannot allocate %s framebuffer\n", name); 1586 return -ENOMEM; 1587 } 1588 1589 par = fbi->par; 1590 par->info = info; 1591 par->head = head; 1592 fbi->pseudo_palette = &par->pseudo_palette; 1593 1594 info->fb[head] = fbi; 1595 1596 return 0; 1597} 1598 1599/* Free up anything allocated by sm501fb_init_fb */ 1600 1601static void sm501_free_init_fb(struct sm501fb_info *info, 1602 enum sm501_controller head) 1603{ 1604 struct fb_info *fbi = info->fb[head]; 1605 1606 fb_dealloc_cmap(&fbi->cmap); 1607} 1608 1609static int __devinit sm501fb_start_one(struct sm501fb_info *info, 1610 enum sm501_controller head, 1611 const char *drvname) 1612{ 1613 struct fb_info *fbi = info->fb[head]; 1614 int ret; 1615 1616 if (!fbi) 1617 return 0; 1618 1619 ret = sm501fb_init_fb(info->fb[head], head, drvname); 1620 if (ret) { 1621 dev_err(info->dev, "cannot initialise fb %s\n", drvname); 1622 return ret; 1623 } 1624 1625 ret = register_framebuffer(info->fb[head]); 1626 if (ret) { 1627 dev_err(info->dev, "failed to register fb %s\n", drvname); 1628 sm501_free_init_fb(info, head); 1629 return ret; 1630 } 1631 1632 dev_info(info->dev, "fb%d: %s frame buffer\n", fbi->node, fbi->fix.id); 1633 1634 return 0; 1635} 1636 1637static int __devinit sm501fb_probe(struct platform_device *pdev) 1638{ 1639 struct sm501fb_info *info; 1640 struct device *dev = &pdev->dev; 1641 int ret; 1642 1643 /* allocate our framebuffers */ 1644 1645 info = kzalloc(sizeof(struct sm501fb_info), GFP_KERNEL); 1646 if (!info) { 1647 dev_err(dev, "failed to allocate state\n"); 1648 return -ENOMEM; 1649 } 1650 1651 info->dev = dev = &pdev->dev; 1652 platform_set_drvdata(pdev, info); 1653 1654 if (dev->parent->platform_data) { 1655 struct sm501_platdata *pd = dev->parent->platform_data; 1656 info->pdata = pd->fb; 1657 } 1658 1659 if (info->pdata == NULL) { 1660 dev_info(dev, "using default configuration data\n"); 1661 info->pdata = &sm501fb_def_pdata; 1662 } 1663 1664 /* probe for the presence of each panel */ 1665 1666 ret = sm501fb_probe_one(info, HEAD_CRT); 1667 if (ret < 0) { 1668 dev_err(dev, "failed to probe CRT\n"); 1669 goto err_alloc; 1670 } 1671 1672 ret = sm501fb_probe_one(info, HEAD_PANEL); 1673 if (ret < 0) { 1674 dev_err(dev, "failed to probe PANEL\n"); 1675 goto err_probed_crt; 1676 } 1677 1678 if (info->fb[HEAD_PANEL] == NULL && 1679 info->fb[HEAD_CRT] == NULL) { 1680 dev_err(dev, "no framebuffers found\n"); 1681 goto err_alloc; 1682 } 1683 1684 /* get the resources for both of the framebuffers */ 1685 1686 ret = sm501fb_start(info, pdev); 1687 if (ret) { 1688 dev_err(dev, "cannot initialise SM501\n"); 1689 goto err_probed_panel; 1690 } 1691 1692 ret = sm501fb_start_one(info, HEAD_CRT, driver_name_crt); 1693 if (ret) { 1694 dev_err(dev, "failed to start CRT\n"); 1695 goto err_started; 1696 } 1697 1698 ret = sm501fb_start_one(info, HEAD_PANEL, driver_name_pnl); 1699 if (ret) { 1700 dev_err(dev, "failed to start Panel\n"); 1701 goto err_started_crt; 1702 } 1703 1704 /* create device files */ 1705 1706 ret = device_create_file(dev, &dev_attr_crt_src); 1707 if (ret) 1708 goto err_started_panel; 1709 1710 ret = device_create_file(dev, &dev_attr_fbregs_pnl); 1711 if (ret) 1712 goto err_attached_crtsrc_file; 1713 1714 ret = device_create_file(dev, &dev_attr_fbregs_crt); 1715 if (ret) 1716 goto err_attached_pnlregs_file; 1717 1718 /* we registered, return ok */ 1719 return 0; 1720 1721err_attached_pnlregs_file: 1722 device_remove_file(dev, &dev_attr_fbregs_pnl); 1723 1724err_attached_crtsrc_file: 1725 device_remove_file(dev, &dev_attr_crt_src); 1726 1727err_started_panel: 1728 unregister_framebuffer(info->fb[HEAD_PANEL]); 1729 sm501_free_init_fb(info, HEAD_PANEL); 1730 1731err_started_crt: 1732 unregister_framebuffer(info->fb[HEAD_CRT]); 1733 sm501_free_init_fb(info, HEAD_CRT); 1734 1735err_started: 1736 sm501fb_stop(info); 1737 1738err_probed_panel: 1739 framebuffer_release(info->fb[HEAD_PANEL]); 1740 1741err_probed_crt: 1742 framebuffer_release(info->fb[HEAD_CRT]); 1743 1744err_alloc: 1745 kfree(info); 1746 1747 return ret; 1748} 1749 1750 1751/* 1752 * Cleanup 1753 */ 1754static int sm501fb_remove(struct platform_device *pdev) 1755{ 1756 struct sm501fb_info *info = platform_get_drvdata(pdev); 1757 struct fb_info *fbinfo_crt = info->fb[0]; 1758 struct fb_info *fbinfo_pnl = info->fb[1]; 1759 1760 device_remove_file(&pdev->dev, &dev_attr_fbregs_crt); 1761 device_remove_file(&pdev->dev, &dev_attr_fbregs_pnl); 1762 device_remove_file(&pdev->dev, &dev_attr_crt_src); 1763 1764 sm501_free_init_fb(info, HEAD_CRT); 1765 sm501_free_init_fb(info, HEAD_PANEL); 1766 1767 unregister_framebuffer(fbinfo_crt); 1768 unregister_framebuffer(fbinfo_pnl); 1769 1770 sm501fb_stop(info); 1771 kfree(info); 1772 1773 framebuffer_release(fbinfo_pnl); 1774 framebuffer_release(fbinfo_crt); 1775 1776 return 0; 1777} 1778 1779#ifdef CONFIG_PM 1780 1781static int sm501fb_suspend_fb(struct sm501fb_info *info, 1782 enum sm501_controller head) 1783{ 1784 struct fb_info *fbi = info->fb[head]; 1785 struct sm501fb_par *par = fbi->par; 1786 1787 if (par->screen.size == 0) 1788 return 0; 1789 1790 /* blank the relevant interface to ensure unit power minimised */ 1791 (par->ops.fb_blank)(FB_BLANK_POWERDOWN, fbi); 1792 1793 /* tell console/fb driver we are suspending */ 1794 1795 acquire_console_sem(); 1796 fb_set_suspend(fbi, 1); 1797 release_console_sem(); 1798 1799 /* backup copies in case chip is powered down over suspend */ 1800 1801 par->store_fb = vmalloc(par->screen.size); 1802 if (par->store_fb == NULL) { 1803 dev_err(info->dev, "no memory to store screen\n"); 1804 return -ENOMEM; 1805 } 1806 1807 par->store_cursor = vmalloc(par->cursor.size); 1808 if (par->store_cursor == NULL) { 1809 dev_err(info->dev, "no memory to store cursor\n"); 1810 goto err_nocursor; 1811 } 1812 1813 dev_dbg(info->dev, "suspending screen to %p\n", par->store_fb); 1814 dev_dbg(info->dev, "suspending cursor to %p\n", par->store_cursor); 1815 1816 memcpy_fromio(par->store_fb, par->screen.k_addr, par->screen.size); 1817 memcpy_fromio(par->store_cursor, par->cursor.k_addr, par->cursor.size); 1818 1819 return 0; 1820 1821 err_nocursor: 1822 vfree(par->store_fb); 1823 par->store_fb = NULL; 1824 1825 return -ENOMEM; 1826} 1827 1828static void sm501fb_resume_fb(struct sm501fb_info *info, 1829 enum sm501_controller head) 1830{ 1831 struct fb_info *fbi = info->fb[head]; 1832 struct sm501fb_par *par = fbi->par; 1833 1834 if (par->screen.size == 0) 1835 return; 1836 1837 /* re-activate the configuration */ 1838 1839 (par->ops.fb_set_par)(fbi); 1840 1841 /* restore the data */ 1842 1843 dev_dbg(info->dev, "restoring screen from %p\n", par->store_fb); 1844 dev_dbg(info->dev, "restoring cursor from %p\n", par->store_cursor); 1845 1846 if (par->store_fb) 1847 memcpy_toio(par->screen.k_addr, par->store_fb, 1848 par->screen.size); 1849 1850 if (par->store_cursor) 1851 memcpy_toio(par->cursor.k_addr, par->store_cursor, 1852 par->cursor.size); 1853 1854 acquire_console_sem(); 1855 fb_set_suspend(fbi, 0); 1856 release_console_sem(); 1857 1858 vfree(par->store_fb); 1859 vfree(par->store_cursor); 1860} 1861 1862 1863/* suspend and resume support */ 1864 1865static int sm501fb_suspend(struct platform_device *pdev, pm_message_t state) 1866{ 1867 struct sm501fb_info *info = platform_get_drvdata(pdev); 1868 1869 /* store crt control to resume with */ 1870 info->pm_crt_ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); 1871 1872 sm501fb_suspend_fb(info, HEAD_CRT); 1873 sm501fb_suspend_fb(info, HEAD_PANEL); 1874 1875 /* turn off the clocks, in case the device is not powered down */ 1876 sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0); 1877 1878 return 0; 1879} 1880 1881#define SM501_CRT_CTRL_SAVE (SM501_DC_CRT_CONTROL_TVP | \ 1882 SM501_DC_CRT_CONTROL_SEL) 1883 1884 1885static int sm501fb_resume(struct platform_device *pdev) 1886{ 1887 struct sm501fb_info *info = platform_get_drvdata(pdev); 1888 unsigned long crt_ctrl; 1889 1890 sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 1); 1891 1892 /* restore the items we want to be saved for crt control */ 1893 1894 crt_ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); 1895 crt_ctrl &= ~SM501_CRT_CTRL_SAVE; 1896 crt_ctrl |= info->pm_crt_ctrl & SM501_CRT_CTRL_SAVE; 1897 writel(crt_ctrl, info->regs + SM501_DC_CRT_CONTROL); 1898 1899 sm501fb_resume_fb(info, HEAD_CRT); 1900 sm501fb_resume_fb(info, HEAD_PANEL); 1901 1902 return 0; 1903} 1904 1905#else 1906#define sm501fb_suspend NULL 1907#define sm501fb_resume NULL 1908#endif 1909 1910static struct platform_driver sm501fb_driver = { 1911 .probe = sm501fb_probe, 1912 .remove = sm501fb_remove, 1913 .suspend = sm501fb_suspend, 1914 .resume = sm501fb_resume, 1915 .driver = { 1916 .name = "sm501-fb", 1917 .owner = THIS_MODULE, 1918 }, 1919}; 1920 1921static int __devinit sm501fb_init(void) 1922{ 1923 return platform_driver_register(&sm501fb_driver); 1924} 1925 1926static void __exit sm501fb_cleanup(void) 1927{ 1928 platform_driver_unregister(&sm501fb_driver); 1929} 1930 1931module_init(sm501fb_init); 1932module_exit(sm501fb_cleanup); 1933 1934MODULE_AUTHOR("Ben Dooks, Vincent Sanders"); 1935MODULE_DESCRIPTION("SM501 Framebuffer driver"); 1936MODULE_LICENSE("GPL v2");