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