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

Configure Feed

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

at v3.8-rc4 1831 lines 47 kB view raw
1/* 2 * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. 3 * 4 * Freescale DIU Frame Buffer device driver 5 * 6 * Authors: Hongjun Chen <hong-jun.chen@freescale.com> 7 * Paul Widmer <paul.widmer@freescale.com> 8 * Srikanth Srinivasan <srikanth.srinivasan@freescale.com> 9 * York Sun <yorksun@freescale.com> 10 * 11 * Based on imxfb.c Copyright (C) 2004 S.Hauer, Pengutronix 12 * 13 * This program is free software; you can redistribute it and/or modify it 14 * under the terms of the GNU General Public License as published by the 15 * Free Software Foundation; either version 2 of the License, or (at your 16 * option) any later version. 17 * 18 */ 19 20#include <linux/module.h> 21#include <linux/kernel.h> 22#include <linux/errno.h> 23#include <linux/string.h> 24#include <linux/slab.h> 25#include <linux/fb.h> 26#include <linux/init.h> 27#include <linux/dma-mapping.h> 28#include <linux/platform_device.h> 29#include <linux/interrupt.h> 30#include <linux/clk.h> 31#include <linux/uaccess.h> 32#include <linux/vmalloc.h> 33#include <linux/spinlock.h> 34 35#include <sysdev/fsl_soc.h> 36#include <linux/fsl-diu-fb.h> 37#include "edid.h" 38 39#define NUM_AOIS 5 /* 1 for plane 0, 2 for planes 1 & 2 each */ 40 41/* HW cursor parameters */ 42#define MAX_CURS 32 43 44/* INT_STATUS/INT_MASK field descriptions */ 45#define INT_VSYNC 0x01 /* Vsync interrupt */ 46#define INT_VSYNC_WB 0x02 /* Vsync interrupt for write back operation */ 47#define INT_UNDRUN 0x04 /* Under run exception interrupt */ 48#define INT_PARERR 0x08 /* Display parameters error interrupt */ 49#define INT_LS_BF_VS 0x10 /* Lines before vsync. interrupt */ 50 51/* 52 * List of supported video modes 53 * 54 * The first entry is the default video mode. The remain entries are in 55 * order if increasing resolution and frequency. The 320x240-60 mode is 56 * the initial AOI for the second and third planes. 57 */ 58static struct fb_videomode fsl_diu_mode_db[] = { 59 { 60 .refresh = 60, 61 .xres = 1024, 62 .yres = 768, 63 .pixclock = 15385, 64 .left_margin = 160, 65 .right_margin = 24, 66 .upper_margin = 29, 67 .lower_margin = 3, 68 .hsync_len = 136, 69 .vsync_len = 6, 70 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 71 .vmode = FB_VMODE_NONINTERLACED 72 }, 73 { 74 .refresh = 60, 75 .xres = 320, 76 .yres = 240, 77 .pixclock = 79440, 78 .left_margin = 16, 79 .right_margin = 16, 80 .upper_margin = 16, 81 .lower_margin = 5, 82 .hsync_len = 48, 83 .vsync_len = 1, 84 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 85 .vmode = FB_VMODE_NONINTERLACED 86 }, 87 { 88 .refresh = 60, 89 .xres = 640, 90 .yres = 480, 91 .pixclock = 39722, 92 .left_margin = 48, 93 .right_margin = 16, 94 .upper_margin = 33, 95 .lower_margin = 10, 96 .hsync_len = 96, 97 .vsync_len = 2, 98 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 99 .vmode = FB_VMODE_NONINTERLACED 100 }, 101 { 102 .refresh = 72, 103 .xres = 640, 104 .yres = 480, 105 .pixclock = 32052, 106 .left_margin = 128, 107 .right_margin = 24, 108 .upper_margin = 28, 109 .lower_margin = 9, 110 .hsync_len = 40, 111 .vsync_len = 3, 112 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 113 .vmode = FB_VMODE_NONINTERLACED 114 }, 115 { 116 .refresh = 75, 117 .xres = 640, 118 .yres = 480, 119 .pixclock = 31747, 120 .left_margin = 120, 121 .right_margin = 16, 122 .upper_margin = 16, 123 .lower_margin = 1, 124 .hsync_len = 64, 125 .vsync_len = 3, 126 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 127 .vmode = FB_VMODE_NONINTERLACED 128 }, 129 { 130 .refresh = 90, 131 .xres = 640, 132 .yres = 480, 133 .pixclock = 25057, 134 .left_margin = 120, 135 .right_margin = 32, 136 .upper_margin = 14, 137 .lower_margin = 25, 138 .hsync_len = 40, 139 .vsync_len = 14, 140 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 141 .vmode = FB_VMODE_NONINTERLACED 142 }, 143 { 144 .refresh = 100, 145 .xres = 640, 146 .yres = 480, 147 .pixclock = 22272, 148 .left_margin = 48, 149 .right_margin = 32, 150 .upper_margin = 17, 151 .lower_margin = 22, 152 .hsync_len = 128, 153 .vsync_len = 12, 154 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 155 .vmode = FB_VMODE_NONINTERLACED 156 }, 157 { 158 .refresh = 60, 159 .xres = 800, 160 .yres = 480, 161 .pixclock = 33805, 162 .left_margin = 96, 163 .right_margin = 24, 164 .upper_margin = 10, 165 .lower_margin = 3, 166 .hsync_len = 72, 167 .vsync_len = 7, 168 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 169 .vmode = FB_VMODE_NONINTERLACED 170 }, 171 { 172 .refresh = 60, 173 .xres = 800, 174 .yres = 600, 175 .pixclock = 25000, 176 .left_margin = 88, 177 .right_margin = 40, 178 .upper_margin = 23, 179 .lower_margin = 1, 180 .hsync_len = 128, 181 .vsync_len = 4, 182 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 183 .vmode = FB_VMODE_NONINTERLACED 184 }, 185 { 186 .refresh = 60, 187 .xres = 854, 188 .yres = 480, 189 .pixclock = 31518, 190 .left_margin = 104, 191 .right_margin = 16, 192 .upper_margin = 13, 193 .lower_margin = 1, 194 .hsync_len = 88, 195 .vsync_len = 3, 196 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 197 .vmode = FB_VMODE_NONINTERLACED 198 }, 199 { 200 .refresh = 70, 201 .xres = 1024, 202 .yres = 768, 203 .pixclock = 16886, 204 .left_margin = 3, 205 .right_margin = 3, 206 .upper_margin = 2, 207 .lower_margin = 2, 208 .hsync_len = 40, 209 .vsync_len = 18, 210 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 211 .vmode = FB_VMODE_NONINTERLACED 212 }, 213 { 214 .refresh = 75, 215 .xres = 1024, 216 .yres = 768, 217 .pixclock = 15009, 218 .left_margin = 3, 219 .right_margin = 3, 220 .upper_margin = 2, 221 .lower_margin = 2, 222 .hsync_len = 80, 223 .vsync_len = 32, 224 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 225 .vmode = FB_VMODE_NONINTERLACED 226 }, 227 { 228 .refresh = 60, 229 .xres = 1280, 230 .yres = 480, 231 .pixclock = 18939, 232 .left_margin = 353, 233 .right_margin = 47, 234 .upper_margin = 39, 235 .lower_margin = 4, 236 .hsync_len = 8, 237 .vsync_len = 2, 238 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 239 .vmode = FB_VMODE_NONINTERLACED 240 }, 241 { 242 .refresh = 60, 243 .xres = 1280, 244 .yres = 720, 245 .pixclock = 13426, 246 .left_margin = 192, 247 .right_margin = 64, 248 .upper_margin = 22, 249 .lower_margin = 1, 250 .hsync_len = 136, 251 .vsync_len = 3, 252 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 253 .vmode = FB_VMODE_NONINTERLACED 254 }, 255 { 256 .refresh = 60, 257 .xres = 1280, 258 .yres = 1024, 259 .pixclock = 9375, 260 .left_margin = 38, 261 .right_margin = 128, 262 .upper_margin = 2, 263 .lower_margin = 7, 264 .hsync_len = 216, 265 .vsync_len = 37, 266 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 267 .vmode = FB_VMODE_NONINTERLACED 268 }, 269 { 270 .refresh = 70, 271 .xres = 1280, 272 .yres = 1024, 273 .pixclock = 9380, 274 .left_margin = 6, 275 .right_margin = 6, 276 .upper_margin = 4, 277 .lower_margin = 4, 278 .hsync_len = 60, 279 .vsync_len = 94, 280 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 281 .vmode = FB_VMODE_NONINTERLACED 282 }, 283 { 284 .refresh = 75, 285 .xres = 1280, 286 .yres = 1024, 287 .pixclock = 9380, 288 .left_margin = 6, 289 .right_margin = 6, 290 .upper_margin = 4, 291 .lower_margin = 4, 292 .hsync_len = 60, 293 .vsync_len = 15, 294 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 295 .vmode = FB_VMODE_NONINTERLACED 296 }, 297 { 298 .refresh = 60, 299 .xres = 1920, 300 .yres = 1080, 301 .pixclock = 5787, 302 .left_margin = 328, 303 .right_margin = 120, 304 .upper_margin = 34, 305 .lower_margin = 1, 306 .hsync_len = 208, 307 .vsync_len = 3, 308 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 309 .vmode = FB_VMODE_NONINTERLACED 310 }, 311}; 312 313static char *fb_mode; 314static unsigned long default_bpp = 32; 315static enum fsl_diu_monitor_port monitor_port; 316static char *monitor_string; 317 318#if defined(CONFIG_NOT_COHERENT_CACHE) 319static u8 *coherence_data; 320static size_t coherence_data_size; 321static unsigned int d_cache_line_size; 322#endif 323 324static DEFINE_SPINLOCK(diu_lock); 325 326enum mfb_index { 327 PLANE0 = 0, /* Plane 0, only one AOI that fills the screen */ 328 PLANE1_AOI0, /* Plane 1, first AOI */ 329 PLANE1_AOI1, /* Plane 1, second AOI */ 330 PLANE2_AOI0, /* Plane 2, first AOI */ 331 PLANE2_AOI1, /* Plane 2, second AOI */ 332}; 333 334struct mfb_info { 335 enum mfb_index index; 336 char *id; 337 int registered; 338 unsigned long pseudo_palette[16]; 339 struct diu_ad *ad; 340 unsigned char g_alpha; 341 unsigned int count; 342 int x_aoi_d; /* aoi display x offset to physical screen */ 343 int y_aoi_d; /* aoi display y offset to physical screen */ 344 struct fsl_diu_data *parent; 345}; 346 347/** 348 * struct fsl_diu_data - per-DIU data structure 349 * @dma_addr: DMA address of this structure 350 * @fsl_diu_info: fb_info objects, one per AOI 351 * @dev_attr: sysfs structure 352 * @irq: IRQ 353 * @monitor_port: the monitor port this DIU is connected to 354 * @diu_reg: pointer to the DIU hardware registers 355 * @reg_lock: spinlock for register access 356 * @dummy_aoi: video buffer for the 4x4 32-bit dummy AOI 357 * dummy_ad: DIU Area Descriptor for the dummy AOI 358 * @ad[]: Area Descriptors for each real AOI 359 * @gamma: gamma color table 360 * @cursor: hardware cursor data 361 * 362 * This data structure must be allocated with 32-byte alignment, so that the 363 * internal fields can be aligned properly. 364 */ 365struct fsl_diu_data { 366 dma_addr_t dma_addr; 367 struct fb_info fsl_diu_info[NUM_AOIS]; 368 struct mfb_info mfb[NUM_AOIS]; 369 struct device_attribute dev_attr; 370 unsigned int irq; 371 enum fsl_diu_monitor_port monitor_port; 372 struct diu __iomem *diu_reg; 373 spinlock_t reg_lock; 374 u8 dummy_aoi[4 * 4 * 4]; 375 struct diu_ad dummy_ad __aligned(8); 376 struct diu_ad ad[NUM_AOIS] __aligned(8); 377 u8 gamma[256 * 3] __aligned(32); 378 u8 cursor[MAX_CURS * MAX_CURS * 2] __aligned(32); 379 uint8_t edid_data[EDID_LENGTH]; 380 bool has_edid; 381} __aligned(32); 382 383/* Determine the DMA address of a member of the fsl_diu_data structure */ 384#define DMA_ADDR(p, f) ((p)->dma_addr + offsetof(struct fsl_diu_data, f)) 385 386static struct mfb_info mfb_template[] = { 387 { 388 .index = PLANE0, 389 .id = "Panel0", 390 .registered = 0, 391 .count = 0, 392 .x_aoi_d = 0, 393 .y_aoi_d = 0, 394 }, 395 { 396 .index = PLANE1_AOI0, 397 .id = "Panel1 AOI0", 398 .registered = 0, 399 .g_alpha = 0xff, 400 .count = 0, 401 .x_aoi_d = 0, 402 .y_aoi_d = 0, 403 }, 404 { 405 .index = PLANE1_AOI1, 406 .id = "Panel1 AOI1", 407 .registered = 0, 408 .g_alpha = 0xff, 409 .count = 0, 410 .x_aoi_d = 0, 411 .y_aoi_d = 480, 412 }, 413 { 414 .index = PLANE2_AOI0, 415 .id = "Panel2 AOI0", 416 .registered = 0, 417 .g_alpha = 0xff, 418 .count = 0, 419 .x_aoi_d = 640, 420 .y_aoi_d = 0, 421 }, 422 { 423 .index = PLANE2_AOI1, 424 .id = "Panel2 AOI1", 425 .registered = 0, 426 .g_alpha = 0xff, 427 .count = 0, 428 .x_aoi_d = 640, 429 .y_aoi_d = 480, 430 }, 431}; 432 433#ifdef DEBUG 434static void __attribute__ ((unused)) fsl_diu_dump(struct diu __iomem *hw) 435{ 436 mb(); 437 pr_debug("DIU: desc=%08x,%08x,%08x, gamma=%08x pallete=%08x " 438 "cursor=%08x curs_pos=%08x diu_mode=%08x bgnd=%08x " 439 "disp_size=%08x hsyn_para=%08x vsyn_para=%08x syn_pol=%08x " 440 "thresholds=%08x int_mask=%08x plut=%08x\n", 441 hw->desc[0], hw->desc[1], hw->desc[2], hw->gamma, 442 hw->pallete, hw->cursor, hw->curs_pos, hw->diu_mode, 443 hw->bgnd, hw->disp_size, hw->hsyn_para, hw->vsyn_para, 444 hw->syn_pol, hw->thresholds, hw->int_mask, hw->plut); 445 rmb(); 446} 447#endif 448 449/** 450 * fsl_diu_name_to_port - convert a port name to a monitor port enum 451 * 452 * Takes the name of a monitor port ("dvi", "lvds", or "dlvds") and returns 453 * the enum fsl_diu_monitor_port that corresponds to that string. 454 * 455 * For compatibility with older versions, a number ("0", "1", or "2") is also 456 * supported. 457 * 458 * If the string is unknown, DVI is assumed. 459 * 460 * If the particular port is not supported by the platform, another port 461 * (platform-specific) is chosen instead. 462 */ 463static enum fsl_diu_monitor_port fsl_diu_name_to_port(const char *s) 464{ 465 enum fsl_diu_monitor_port port = FSL_DIU_PORT_DVI; 466 unsigned long val; 467 468 if (s) { 469 if (!strict_strtoul(s, 10, &val) && (val <= 2)) 470 port = (enum fsl_diu_monitor_port) val; 471 else if (strncmp(s, "lvds", 4) == 0) 472 port = FSL_DIU_PORT_LVDS; 473 else if (strncmp(s, "dlvds", 5) == 0) 474 port = FSL_DIU_PORT_DLVDS; 475 } 476 477 return diu_ops.valid_monitor_port(port); 478} 479 480/* 481 * Workaround for failed writing desc register of planes. 482 * Needed with MPC5121 DIU rev 2.0 silicon. 483 */ 484void wr_reg_wa(u32 *reg, u32 val) 485{ 486 do { 487 out_be32(reg, val); 488 } while (in_be32(reg) != val); 489} 490 491static void fsl_diu_enable_panel(struct fb_info *info) 492{ 493 struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par; 494 struct diu_ad *ad = mfbi->ad; 495 struct fsl_diu_data *data = mfbi->parent; 496 struct diu __iomem *hw = data->diu_reg; 497 498 switch (mfbi->index) { 499 case PLANE0: 500 wr_reg_wa(&hw->desc[0], ad->paddr); 501 break; 502 case PLANE1_AOI0: 503 cmfbi = &data->mfb[2]; 504 if (hw->desc[1] != ad->paddr) { /* AOI0 closed */ 505 if (cmfbi->count > 0) /* AOI1 open */ 506 ad->next_ad = 507 cpu_to_le32(cmfbi->ad->paddr); 508 else 509 ad->next_ad = 0; 510 wr_reg_wa(&hw->desc[1], ad->paddr); 511 } 512 break; 513 case PLANE2_AOI0: 514 cmfbi = &data->mfb[4]; 515 if (hw->desc[2] != ad->paddr) { /* AOI0 closed */ 516 if (cmfbi->count > 0) /* AOI1 open */ 517 ad->next_ad = 518 cpu_to_le32(cmfbi->ad->paddr); 519 else 520 ad->next_ad = 0; 521 wr_reg_wa(&hw->desc[2], ad->paddr); 522 } 523 break; 524 case PLANE1_AOI1: 525 pmfbi = &data->mfb[1]; 526 ad->next_ad = 0; 527 if (hw->desc[1] == data->dummy_ad.paddr) 528 wr_reg_wa(&hw->desc[1], ad->paddr); 529 else /* AOI0 open */ 530 pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); 531 break; 532 case PLANE2_AOI1: 533 pmfbi = &data->mfb[3]; 534 ad->next_ad = 0; 535 if (hw->desc[2] == data->dummy_ad.paddr) 536 wr_reg_wa(&hw->desc[2], ad->paddr); 537 else /* AOI0 was open */ 538 pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); 539 break; 540 } 541} 542 543static void fsl_diu_disable_panel(struct fb_info *info) 544{ 545 struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par; 546 struct diu_ad *ad = mfbi->ad; 547 struct fsl_diu_data *data = mfbi->parent; 548 struct diu __iomem *hw = data->diu_reg; 549 550 switch (mfbi->index) { 551 case PLANE0: 552 wr_reg_wa(&hw->desc[0], 0); 553 break; 554 case PLANE1_AOI0: 555 cmfbi = &data->mfb[2]; 556 if (cmfbi->count > 0) /* AOI1 is open */ 557 wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr); 558 /* move AOI1 to the first */ 559 else /* AOI1 was closed */ 560 wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr); 561 /* close AOI 0 */ 562 break; 563 case PLANE2_AOI0: 564 cmfbi = &data->mfb[4]; 565 if (cmfbi->count > 0) /* AOI1 is open */ 566 wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr); 567 /* move AOI1 to the first */ 568 else /* AOI1 was closed */ 569 wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr); 570 /* close AOI 0 */ 571 break; 572 case PLANE1_AOI1: 573 pmfbi = &data->mfb[1]; 574 if (hw->desc[1] != ad->paddr) { 575 /* AOI1 is not the first in the chain */ 576 if (pmfbi->count > 0) 577 /* AOI0 is open, must be the first */ 578 pmfbi->ad->next_ad = 0; 579 } else /* AOI1 is the first in the chain */ 580 wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr); 581 /* close AOI 1 */ 582 break; 583 case PLANE2_AOI1: 584 pmfbi = &data->mfb[3]; 585 if (hw->desc[2] != ad->paddr) { 586 /* AOI1 is not the first in the chain */ 587 if (pmfbi->count > 0) 588 /* AOI0 is open, must be the first */ 589 pmfbi->ad->next_ad = 0; 590 } else /* AOI1 is the first in the chain */ 591 wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr); 592 /* close AOI 1 */ 593 break; 594 } 595} 596 597static void enable_lcdc(struct fb_info *info) 598{ 599 struct mfb_info *mfbi = info->par; 600 struct fsl_diu_data *data = mfbi->parent; 601 struct diu __iomem *hw = data->diu_reg; 602 603 out_be32(&hw->diu_mode, MFB_MODE1); 604} 605 606static void disable_lcdc(struct fb_info *info) 607{ 608 struct mfb_info *mfbi = info->par; 609 struct fsl_diu_data *data = mfbi->parent; 610 struct diu __iomem *hw = data->diu_reg; 611 612 out_be32(&hw->diu_mode, 0); 613} 614 615static void adjust_aoi_size_position(struct fb_var_screeninfo *var, 616 struct fb_info *info) 617{ 618 struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par; 619 struct fsl_diu_data *data = mfbi->parent; 620 int available_height, upper_aoi_bottom; 621 enum mfb_index index = mfbi->index; 622 int lower_aoi_is_open, upper_aoi_is_open; 623 __u32 base_plane_width, base_plane_height, upper_aoi_height; 624 625 base_plane_width = data->fsl_diu_info[0].var.xres; 626 base_plane_height = data->fsl_diu_info[0].var.yres; 627 628 if (mfbi->x_aoi_d < 0) 629 mfbi->x_aoi_d = 0; 630 if (mfbi->y_aoi_d < 0) 631 mfbi->y_aoi_d = 0; 632 switch (index) { 633 case PLANE0: 634 if (mfbi->x_aoi_d != 0) 635 mfbi->x_aoi_d = 0; 636 if (mfbi->y_aoi_d != 0) 637 mfbi->y_aoi_d = 0; 638 break; 639 case PLANE1_AOI0: 640 case PLANE2_AOI0: 641 lower_aoi_mfbi = data->fsl_diu_info[index+1].par; 642 lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0; 643 if (var->xres > base_plane_width) 644 var->xres = base_plane_width; 645 if ((mfbi->x_aoi_d + var->xres) > base_plane_width) 646 mfbi->x_aoi_d = base_plane_width - var->xres; 647 648 if (lower_aoi_is_open) 649 available_height = lower_aoi_mfbi->y_aoi_d; 650 else 651 available_height = base_plane_height; 652 if (var->yres > available_height) 653 var->yres = available_height; 654 if ((mfbi->y_aoi_d + var->yres) > available_height) 655 mfbi->y_aoi_d = available_height - var->yres; 656 break; 657 case PLANE1_AOI1: 658 case PLANE2_AOI1: 659 upper_aoi_mfbi = data->fsl_diu_info[index-1].par; 660 upper_aoi_height = data->fsl_diu_info[index-1].var.yres; 661 upper_aoi_bottom = upper_aoi_mfbi->y_aoi_d + upper_aoi_height; 662 upper_aoi_is_open = upper_aoi_mfbi->count > 0 ? 1 : 0; 663 if (var->xres > base_plane_width) 664 var->xres = base_plane_width; 665 if ((mfbi->x_aoi_d + var->xres) > base_plane_width) 666 mfbi->x_aoi_d = base_plane_width - var->xres; 667 if (mfbi->y_aoi_d < 0) 668 mfbi->y_aoi_d = 0; 669 if (upper_aoi_is_open) { 670 if (mfbi->y_aoi_d < upper_aoi_bottom) 671 mfbi->y_aoi_d = upper_aoi_bottom; 672 available_height = base_plane_height 673 - upper_aoi_bottom; 674 } else 675 available_height = base_plane_height; 676 if (var->yres > available_height) 677 var->yres = available_height; 678 if ((mfbi->y_aoi_d + var->yres) > base_plane_height) 679 mfbi->y_aoi_d = base_plane_height - var->yres; 680 break; 681 } 682} 683/* 684 * Checks to see if the hardware supports the state requested by var passed 685 * in. This function does not alter the hardware state! If the var passed in 686 * is slightly off by what the hardware can support then we alter the var 687 * PASSED in to what we can do. If the hardware doesn't support mode change 688 * a -EINVAL will be returned by the upper layers. 689 */ 690static int fsl_diu_check_var(struct fb_var_screeninfo *var, 691 struct fb_info *info) 692{ 693 if (var->xres_virtual < var->xres) 694 var->xres_virtual = var->xres; 695 if (var->yres_virtual < var->yres) 696 var->yres_virtual = var->yres; 697 698 if (var->xoffset < 0) 699 var->xoffset = 0; 700 701 if (var->yoffset < 0) 702 var->yoffset = 0; 703 704 if (var->xoffset + info->var.xres > info->var.xres_virtual) 705 var->xoffset = info->var.xres_virtual - info->var.xres; 706 707 if (var->yoffset + info->var.yres > info->var.yres_virtual) 708 var->yoffset = info->var.yres_virtual - info->var.yres; 709 710 if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && 711 (var->bits_per_pixel != 16)) 712 var->bits_per_pixel = default_bpp; 713 714 switch (var->bits_per_pixel) { 715 case 16: 716 var->red.length = 5; 717 var->red.offset = 11; 718 var->red.msb_right = 0; 719 720 var->green.length = 6; 721 var->green.offset = 5; 722 var->green.msb_right = 0; 723 724 var->blue.length = 5; 725 var->blue.offset = 0; 726 var->blue.msb_right = 0; 727 728 var->transp.length = 0; 729 var->transp.offset = 0; 730 var->transp.msb_right = 0; 731 break; 732 case 24: 733 var->red.length = 8; 734 var->red.offset = 0; 735 var->red.msb_right = 0; 736 737 var->green.length = 8; 738 var->green.offset = 8; 739 var->green.msb_right = 0; 740 741 var->blue.length = 8; 742 var->blue.offset = 16; 743 var->blue.msb_right = 0; 744 745 var->transp.length = 0; 746 var->transp.offset = 0; 747 var->transp.msb_right = 0; 748 break; 749 case 32: 750 var->red.length = 8; 751 var->red.offset = 16; 752 var->red.msb_right = 0; 753 754 var->green.length = 8; 755 var->green.offset = 8; 756 var->green.msb_right = 0; 757 758 var->blue.length = 8; 759 var->blue.offset = 0; 760 var->blue.msb_right = 0; 761 762 var->transp.length = 8; 763 var->transp.offset = 24; 764 var->transp.msb_right = 0; 765 766 break; 767 } 768 769 var->height = -1; 770 var->width = -1; 771 var->grayscale = 0; 772 773 /* Copy nonstd field to/from sync for fbset usage */ 774 var->sync |= var->nonstd; 775 var->nonstd |= var->sync; 776 777 adjust_aoi_size_position(var, info); 778 return 0; 779} 780 781static void set_fix(struct fb_info *info) 782{ 783 struct fb_fix_screeninfo *fix = &info->fix; 784 struct fb_var_screeninfo *var = &info->var; 785 struct mfb_info *mfbi = info->par; 786 787 strncpy(fix->id, mfbi->id, sizeof(fix->id)); 788 fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; 789 fix->type = FB_TYPE_PACKED_PIXELS; 790 fix->accel = FB_ACCEL_NONE; 791 fix->visual = FB_VISUAL_TRUECOLOR; 792 fix->xpanstep = 1; 793 fix->ypanstep = 1; 794} 795 796static void update_lcdc(struct fb_info *info) 797{ 798 struct fb_var_screeninfo *var = &info->var; 799 struct mfb_info *mfbi = info->par; 800 struct fsl_diu_data *data = mfbi->parent; 801 struct diu __iomem *hw; 802 int i, j; 803 u8 *gamma_table_base; 804 805 u32 temp; 806 807 hw = data->diu_reg; 808 809 if (diu_ops.set_monitor_port) 810 diu_ops.set_monitor_port(data->monitor_port); 811 gamma_table_base = data->gamma; 812 813 /* Prep for DIU init - gamma table, cursor table */ 814 815 for (i = 0; i <= 2; i++) 816 for (j = 0; j <= 255; j++) 817 *gamma_table_base++ = j; 818 819 if (diu_ops.set_gamma_table) 820 diu_ops.set_gamma_table(data->monitor_port, data->gamma); 821 822 disable_lcdc(info); 823 824 /* Program DIU registers */ 825 826 out_be32(&hw->gamma, DMA_ADDR(data, gamma)); 827 out_be32(&hw->cursor, DMA_ADDR(data, cursor)); 828 829 out_be32(&hw->bgnd, 0x007F7F7F); /* Set background to grey */ 830 out_be32(&hw->disp_size, (var->yres << 16) | var->xres); 831 832 /* Horizontal and vertical configuration register */ 833 temp = var->left_margin << 22 | /* BP_H */ 834 var->hsync_len << 11 | /* PW_H */ 835 var->right_margin; /* FP_H */ 836 837 out_be32(&hw->hsyn_para, temp); 838 839 temp = var->upper_margin << 22 | /* BP_V */ 840 var->vsync_len << 11 | /* PW_V */ 841 var->lower_margin; /* FP_V */ 842 843 out_be32(&hw->vsyn_para, temp); 844 845 diu_ops.set_pixel_clock(var->pixclock); 846 847#ifndef CONFIG_PPC_MPC512x 848 /* 849 * The PLUT register is defined differently on the MPC5121 than it 850 * is on other SOCs. Unfortunately, there's no documentation that 851 * explains how it's supposed to be programmed, so for now, we leave 852 * it at the default value on the MPC5121. 853 * 854 * For other SOCs, program it for the highest priority, which will 855 * reduce the chance of underrun. Technically, we should scale the 856 * priority to match the screen resolution, but doing that properly 857 * requires delicate fine-tuning for each use-case. 858 */ 859 out_be32(&hw->plut, 0x01F5F666); 860#endif 861 862 /* Enable the DIU */ 863 enable_lcdc(info); 864} 865 866static int map_video_memory(struct fb_info *info) 867{ 868 u32 smem_len = info->fix.line_length * info->var.yres_virtual; 869 void *p; 870 871 p = alloc_pages_exact(smem_len, GFP_DMA | __GFP_ZERO); 872 if (!p) { 873 dev_err(info->dev, "unable to allocate fb memory\n"); 874 return -ENOMEM; 875 } 876 mutex_lock(&info->mm_lock); 877 info->screen_base = p; 878 info->fix.smem_start = virt_to_phys(info->screen_base); 879 info->fix.smem_len = smem_len; 880 mutex_unlock(&info->mm_lock); 881 info->screen_size = info->fix.smem_len; 882 883 return 0; 884} 885 886static void unmap_video_memory(struct fb_info *info) 887{ 888 void *p = info->screen_base; 889 size_t l = info->fix.smem_len; 890 891 mutex_lock(&info->mm_lock); 892 info->screen_base = NULL; 893 info->fix.smem_start = 0; 894 info->fix.smem_len = 0; 895 mutex_unlock(&info->mm_lock); 896 897 if (p) 898 free_pages_exact(p, l); 899} 900 901/* 902 * Using the fb_var_screeninfo in fb_info we set the aoi of this 903 * particular framebuffer. It is a light version of fsl_diu_set_par. 904 */ 905static int fsl_diu_set_aoi(struct fb_info *info) 906{ 907 struct fb_var_screeninfo *var = &info->var; 908 struct mfb_info *mfbi = info->par; 909 struct diu_ad *ad = mfbi->ad; 910 911 /* AOI should not be greater than display size */ 912 ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset); 913 ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d); 914 return 0; 915} 916 917/** 918 * fsl_diu_get_pixel_format: return the pixel format for a given color depth 919 * 920 * The pixel format is a 32-bit value that determine which bits in each 921 * pixel are to be used for each color. This is the default function used 922 * if the platform does not define its own version. 923 */ 924static u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel) 925{ 926#define PF_BYTE_F 0x10000000 927#define PF_ALPHA_C_MASK 0x0E000000 928#define PF_ALPHA_C_SHIFT 25 929#define PF_BLUE_C_MASK 0x01800000 930#define PF_BLUE_C_SHIFT 23 931#define PF_GREEN_C_MASK 0x00600000 932#define PF_GREEN_C_SHIFT 21 933#define PF_RED_C_MASK 0x00180000 934#define PF_RED_C_SHIFT 19 935#define PF_PALETTE 0x00040000 936#define PF_PIXEL_S_MASK 0x00030000 937#define PF_PIXEL_S_SHIFT 16 938#define PF_COMP_3_MASK 0x0000F000 939#define PF_COMP_3_SHIFT 12 940#define PF_COMP_2_MASK 0x00000F00 941#define PF_COMP_2_SHIFT 8 942#define PF_COMP_1_MASK 0x000000F0 943#define PF_COMP_1_SHIFT 4 944#define PF_COMP_0_MASK 0x0000000F 945#define PF_COMP_0_SHIFT 0 946 947#define MAKE_PF(alpha, red, blue, green, size, c0, c1, c2, c3) \ 948 cpu_to_le32(PF_BYTE_F | (alpha << PF_ALPHA_C_SHIFT) | \ 949 (blue << PF_BLUE_C_SHIFT) | (green << PF_GREEN_C_SHIFT) | \ 950 (red << PF_RED_C_SHIFT) | (c3 << PF_COMP_3_SHIFT) | \ 951 (c2 << PF_COMP_2_SHIFT) | (c1 << PF_COMP_1_SHIFT) | \ 952 (c0 << PF_COMP_0_SHIFT) | (size << PF_PIXEL_S_SHIFT)) 953 954 switch (bits_per_pixel) { 955 case 32: 956 /* 0x88883316 */ 957 return MAKE_PF(3, 2, 0, 1, 3, 8, 8, 8, 8); 958 case 24: 959 /* 0x88082219 */ 960 return MAKE_PF(4, 0, 1, 2, 2, 0, 8, 8, 8); 961 case 16: 962 /* 0x65053118 */ 963 return MAKE_PF(4, 2, 1, 0, 1, 5, 6, 5, 0); 964 default: 965 pr_err("fsl-diu: unsupported color depth %u\n", bits_per_pixel); 966 return 0; 967 } 968} 969 970/* 971 * Using the fb_var_screeninfo in fb_info we set the resolution of this 972 * particular framebuffer. This function alters the fb_fix_screeninfo stored 973 * in fb_info. It does not alter var in fb_info since we are using that 974 * data. This means we depend on the data in var inside fb_info to be 975 * supported by the hardware. fsl_diu_check_var is always called before 976 * fsl_diu_set_par to ensure this. 977 */ 978static int fsl_diu_set_par(struct fb_info *info) 979{ 980 unsigned long len; 981 struct fb_var_screeninfo *var = &info->var; 982 struct mfb_info *mfbi = info->par; 983 struct fsl_diu_data *data = mfbi->parent; 984 struct diu_ad *ad = mfbi->ad; 985 struct diu __iomem *hw; 986 987 hw = data->diu_reg; 988 989 set_fix(info); 990 991 len = info->var.yres_virtual * info->fix.line_length; 992 /* Alloc & dealloc each time resolution/bpp change */ 993 if (len != info->fix.smem_len) { 994 if (info->fix.smem_start) 995 unmap_video_memory(info); 996 997 /* Memory allocation for framebuffer */ 998 if (map_video_memory(info)) { 999 dev_err(info->dev, "unable to allocate fb memory 1\n"); 1000 return -ENOMEM; 1001 } 1002 } 1003 1004 if (diu_ops.get_pixel_format) 1005 ad->pix_fmt = diu_ops.get_pixel_format(data->monitor_port, 1006 var->bits_per_pixel); 1007 else 1008 ad->pix_fmt = fsl_diu_get_pixel_format(var->bits_per_pixel); 1009 1010 ad->addr = cpu_to_le32(info->fix.smem_start); 1011 ad->src_size_g_alpha = cpu_to_le32((var->yres_virtual << 12) | 1012 var->xres_virtual) | mfbi->g_alpha; 1013 /* AOI should not be greater than display size */ 1014 ad->aoi_size = cpu_to_le32((var->yres << 16) | var->xres); 1015 ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset); 1016 ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d); 1017 1018 /* Disable chroma keying function */ 1019 ad->ckmax_r = 0; 1020 ad->ckmax_g = 0; 1021 ad->ckmax_b = 0; 1022 1023 ad->ckmin_r = 255; 1024 ad->ckmin_g = 255; 1025 ad->ckmin_b = 255; 1026 1027 if (mfbi->index == PLANE0) 1028 update_lcdc(info); 1029 return 0; 1030} 1031 1032static inline __u32 CNVT_TOHW(__u32 val, __u32 width) 1033{ 1034 return ((val << width) + 0x7FFF - val) >> 16; 1035} 1036 1037/* 1038 * Set a single color register. The values supplied have a 16 bit magnitude 1039 * which needs to be scaled in this function for the hardware. Things to take 1040 * into consideration are how many color registers, if any, are supported with 1041 * the current color visual. With truecolor mode no color palettes are 1042 * supported. Here a pseudo palette is created which we store the value in 1043 * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited 1044 * color palette. 1045 */ 1046static int fsl_diu_setcolreg(unsigned int regno, unsigned int red, 1047 unsigned int green, unsigned int blue, 1048 unsigned int transp, struct fb_info *info) 1049{ 1050 int ret = 1; 1051 1052 /* 1053 * If greyscale is true, then we convert the RGB value 1054 * to greyscale no matter what visual we are using. 1055 */ 1056 if (info->var.grayscale) 1057 red = green = blue = (19595 * red + 38470 * green + 1058 7471 * blue) >> 16; 1059 switch (info->fix.visual) { 1060 case FB_VISUAL_TRUECOLOR: 1061 /* 1062 * 16-bit True Colour. We encode the RGB value 1063 * according to the RGB bitfield information. 1064 */ 1065 if (regno < 16) { 1066 u32 *pal = info->pseudo_palette; 1067 u32 v; 1068 1069 red = CNVT_TOHW(red, info->var.red.length); 1070 green = CNVT_TOHW(green, info->var.green.length); 1071 blue = CNVT_TOHW(blue, info->var.blue.length); 1072 transp = CNVT_TOHW(transp, info->var.transp.length); 1073 1074 v = (red << info->var.red.offset) | 1075 (green << info->var.green.offset) | 1076 (blue << info->var.blue.offset) | 1077 (transp << info->var.transp.offset); 1078 1079 pal[regno] = v; 1080 ret = 0; 1081 } 1082 break; 1083 } 1084 1085 return ret; 1086} 1087 1088/* 1089 * Pan (or wrap, depending on the `vmode' field) the display using the 1090 * 'xoffset' and 'yoffset' fields of the 'var' structure. If the values 1091 * don't fit, return -EINVAL. 1092 */ 1093static int fsl_diu_pan_display(struct fb_var_screeninfo *var, 1094 struct fb_info *info) 1095{ 1096 if ((info->var.xoffset == var->xoffset) && 1097 (info->var.yoffset == var->yoffset)) 1098 return 0; /* No change, do nothing */ 1099 1100 if (var->xoffset < 0 || var->yoffset < 0 1101 || var->xoffset + info->var.xres > info->var.xres_virtual 1102 || var->yoffset + info->var.yres > info->var.yres_virtual) 1103 return -EINVAL; 1104 1105 info->var.xoffset = var->xoffset; 1106 info->var.yoffset = var->yoffset; 1107 1108 if (var->vmode & FB_VMODE_YWRAP) 1109 info->var.vmode |= FB_VMODE_YWRAP; 1110 else 1111 info->var.vmode &= ~FB_VMODE_YWRAP; 1112 1113 fsl_diu_set_aoi(info); 1114 1115 return 0; 1116} 1117 1118static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, 1119 unsigned long arg) 1120{ 1121 struct mfb_info *mfbi = info->par; 1122 struct diu_ad *ad = mfbi->ad; 1123 struct mfb_chroma_key ck; 1124 unsigned char global_alpha; 1125 struct aoi_display_offset aoi_d; 1126 __u32 pix_fmt; 1127 void __user *buf = (void __user *)arg; 1128 1129 if (!arg) 1130 return -EINVAL; 1131 1132 dev_dbg(info->dev, "ioctl %08x (dir=%s%s type=%u nr=%u size=%u)\n", cmd, 1133 _IOC_DIR(cmd) & _IOC_READ ? "R" : "", 1134 _IOC_DIR(cmd) & _IOC_WRITE ? "W" : "", 1135 _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd)); 1136 1137 switch (cmd) { 1138 case MFB_SET_PIXFMT_OLD: 1139 dev_warn(info->dev, 1140 "MFB_SET_PIXFMT value of 0x%08x is deprecated.\n", 1141 MFB_SET_PIXFMT_OLD); 1142 case MFB_SET_PIXFMT: 1143 if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt))) 1144 return -EFAULT; 1145 ad->pix_fmt = pix_fmt; 1146 break; 1147 case MFB_GET_PIXFMT_OLD: 1148 dev_warn(info->dev, 1149 "MFB_GET_PIXFMT value of 0x%08x is deprecated.\n", 1150 MFB_GET_PIXFMT_OLD); 1151 case MFB_GET_PIXFMT: 1152 pix_fmt = ad->pix_fmt; 1153 if (copy_to_user(buf, &pix_fmt, sizeof(pix_fmt))) 1154 return -EFAULT; 1155 break; 1156 case MFB_SET_AOID: 1157 if (copy_from_user(&aoi_d, buf, sizeof(aoi_d))) 1158 return -EFAULT; 1159 mfbi->x_aoi_d = aoi_d.x_aoi_d; 1160 mfbi->y_aoi_d = aoi_d.y_aoi_d; 1161 fsl_diu_check_var(&info->var, info); 1162 fsl_diu_set_aoi(info); 1163 break; 1164 case MFB_GET_AOID: 1165 aoi_d.x_aoi_d = mfbi->x_aoi_d; 1166 aoi_d.y_aoi_d = mfbi->y_aoi_d; 1167 if (copy_to_user(buf, &aoi_d, sizeof(aoi_d))) 1168 return -EFAULT; 1169 break; 1170 case MFB_GET_ALPHA: 1171 global_alpha = mfbi->g_alpha; 1172 if (copy_to_user(buf, &global_alpha, sizeof(global_alpha))) 1173 return -EFAULT; 1174 break; 1175 case MFB_SET_ALPHA: 1176 /* set panel information */ 1177 if (copy_from_user(&global_alpha, buf, sizeof(global_alpha))) 1178 return -EFAULT; 1179 ad->src_size_g_alpha = (ad->src_size_g_alpha & (~0xff)) | 1180 (global_alpha & 0xff); 1181 mfbi->g_alpha = global_alpha; 1182 break; 1183 case MFB_SET_CHROMA_KEY: 1184 /* set panel winformation */ 1185 if (copy_from_user(&ck, buf, sizeof(ck))) 1186 return -EFAULT; 1187 1188 if (ck.enable && 1189 (ck.red_max < ck.red_min || 1190 ck.green_max < ck.green_min || 1191 ck.blue_max < ck.blue_min)) 1192 return -EINVAL; 1193 1194 if (!ck.enable) { 1195 ad->ckmax_r = 0; 1196 ad->ckmax_g = 0; 1197 ad->ckmax_b = 0; 1198 ad->ckmin_r = 255; 1199 ad->ckmin_g = 255; 1200 ad->ckmin_b = 255; 1201 } else { 1202 ad->ckmax_r = ck.red_max; 1203 ad->ckmax_g = ck.green_max; 1204 ad->ckmax_b = ck.blue_max; 1205 ad->ckmin_r = ck.red_min; 1206 ad->ckmin_g = ck.green_min; 1207 ad->ckmin_b = ck.blue_min; 1208 } 1209 break; 1210#ifdef CONFIG_PPC_MPC512x 1211 case MFB_SET_GAMMA: { 1212 struct fsl_diu_data *data = mfbi->parent; 1213 1214 if (copy_from_user(data->gamma, buf, sizeof(data->gamma))) 1215 return -EFAULT; 1216 setbits32(&data->diu_reg->gamma, 0); /* Force table reload */ 1217 break; 1218 } 1219 case MFB_GET_GAMMA: { 1220 struct fsl_diu_data *data = mfbi->parent; 1221 1222 if (copy_to_user(buf, data->gamma, sizeof(data->gamma))) 1223 return -EFAULT; 1224 break; 1225 } 1226#endif 1227 default: 1228 dev_err(info->dev, "unknown ioctl command (0x%08X)\n", cmd); 1229 return -ENOIOCTLCMD; 1230 } 1231 1232 return 0; 1233} 1234 1235/* turn on fb if count == 1 1236 */ 1237static int fsl_diu_open(struct fb_info *info, int user) 1238{ 1239 struct mfb_info *mfbi = info->par; 1240 int res = 0; 1241 1242 /* free boot splash memory on first /dev/fb0 open */ 1243 if ((mfbi->index == PLANE0) && diu_ops.release_bootmem) 1244 diu_ops.release_bootmem(); 1245 1246 spin_lock(&diu_lock); 1247 mfbi->count++; 1248 if (mfbi->count == 1) { 1249 fsl_diu_check_var(&info->var, info); 1250 res = fsl_diu_set_par(info); 1251 if (res < 0) 1252 mfbi->count--; 1253 else { 1254 struct fsl_diu_data *data = mfbi->parent; 1255 1256#ifdef CONFIG_NOT_COHERENT_CACHE 1257 /* 1258 * Enable underrun detection and vertical sync 1259 * interrupts. 1260 */ 1261 clrbits32(&data->diu_reg->int_mask, 1262 INT_UNDRUN | INT_VSYNC); 1263#else 1264 /* Enable underrun detection */ 1265 clrbits32(&data->diu_reg->int_mask, INT_UNDRUN); 1266#endif 1267 fsl_diu_enable_panel(info); 1268 } 1269 } 1270 1271 spin_unlock(&diu_lock); 1272 return res; 1273} 1274 1275/* turn off fb if count == 0 1276 */ 1277static int fsl_diu_release(struct fb_info *info, int user) 1278{ 1279 struct mfb_info *mfbi = info->par; 1280 int res = 0; 1281 1282 spin_lock(&diu_lock); 1283 mfbi->count--; 1284 if (mfbi->count == 0) { 1285 struct fsl_diu_data *data = mfbi->parent; 1286 1287 /* Disable interrupts */ 1288 out_be32(&data->diu_reg->int_mask, 0xffffffff); 1289 fsl_diu_disable_panel(info); 1290 } 1291 1292 spin_unlock(&diu_lock); 1293 return res; 1294} 1295 1296static struct fb_ops fsl_diu_ops = { 1297 .owner = THIS_MODULE, 1298 .fb_check_var = fsl_diu_check_var, 1299 .fb_set_par = fsl_diu_set_par, 1300 .fb_setcolreg = fsl_diu_setcolreg, 1301 .fb_pan_display = fsl_diu_pan_display, 1302 .fb_fillrect = cfb_fillrect, 1303 .fb_copyarea = cfb_copyarea, 1304 .fb_imageblit = cfb_imageblit, 1305 .fb_ioctl = fsl_diu_ioctl, 1306 .fb_open = fsl_diu_open, 1307 .fb_release = fsl_diu_release, 1308}; 1309 1310static int install_fb(struct fb_info *info) 1311{ 1312 int rc; 1313 struct mfb_info *mfbi = info->par; 1314 struct fsl_diu_data *data = mfbi->parent; 1315 const char *aoi_mode, *init_aoi_mode = "320x240"; 1316 struct fb_videomode *db = fsl_diu_mode_db; 1317 unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db); 1318 int has_default_mode = 1; 1319 1320 info->var.activate = FB_ACTIVATE_NOW; 1321 info->fbops = &fsl_diu_ops; 1322 info->flags = FBINFO_DEFAULT | FBINFO_VIRTFB | FBINFO_PARTIAL_PAN_OK | 1323 FBINFO_READS_FAST; 1324 info->pseudo_palette = mfbi->pseudo_palette; 1325 1326 rc = fb_alloc_cmap(&info->cmap, 16, 0); 1327 if (rc) 1328 return rc; 1329 1330 if (mfbi->index == PLANE0) { 1331 if (data->has_edid) { 1332 /* Now build modedb from EDID */ 1333 fb_edid_to_monspecs(data->edid_data, &info->monspecs); 1334 fb_videomode_to_modelist(info->monspecs.modedb, 1335 info->monspecs.modedb_len, 1336 &info->modelist); 1337 db = info->monspecs.modedb; 1338 dbsize = info->monspecs.modedb_len; 1339 } 1340 aoi_mode = fb_mode; 1341 } else { 1342 aoi_mode = init_aoi_mode; 1343 } 1344 rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize, NULL, 1345 default_bpp); 1346 if (!rc) { 1347 /* 1348 * For plane 0 we continue and look into 1349 * driver's internal modedb. 1350 */ 1351 if ((mfbi->index == PLANE0) && data->has_edid) 1352 has_default_mode = 0; 1353 else 1354 return -EINVAL; 1355 } 1356 1357 if (!has_default_mode) { 1358 rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db, 1359 ARRAY_SIZE(fsl_diu_mode_db), NULL, default_bpp); 1360 if (rc) 1361 has_default_mode = 1; 1362 } 1363 1364 /* Still not found, use preferred mode from database if any */ 1365 if (!has_default_mode && info->monspecs.modedb) { 1366 struct fb_monspecs *specs = &info->monspecs; 1367 struct fb_videomode *modedb = &specs->modedb[0]; 1368 1369 /* 1370 * Get preferred timing. If not found, 1371 * first mode in database will be used. 1372 */ 1373 if (specs->misc & FB_MISC_1ST_DETAIL) { 1374 int i; 1375 1376 for (i = 0; i < specs->modedb_len; i++) { 1377 if (specs->modedb[i].flag & FB_MODE_IS_FIRST) { 1378 modedb = &specs->modedb[i]; 1379 break; 1380 } 1381 } 1382 } 1383 1384 info->var.bits_per_pixel = default_bpp; 1385 fb_videomode_to_var(&info->var, modedb); 1386 } 1387 1388 if (fsl_diu_check_var(&info->var, info)) { 1389 dev_err(info->dev, "fsl_diu_check_var failed\n"); 1390 unmap_video_memory(info); 1391 fb_dealloc_cmap(&info->cmap); 1392 return -EINVAL; 1393 } 1394 1395 if (register_framebuffer(info) < 0) { 1396 dev_err(info->dev, "register_framebuffer failed\n"); 1397 unmap_video_memory(info); 1398 fb_dealloc_cmap(&info->cmap); 1399 return -EINVAL; 1400 } 1401 1402 mfbi->registered = 1; 1403 dev_info(info->dev, "%s registered successfully\n", mfbi->id); 1404 1405 return 0; 1406} 1407 1408static void uninstall_fb(struct fb_info *info) 1409{ 1410 struct mfb_info *mfbi = info->par; 1411 1412 if (!mfbi->registered) 1413 return; 1414 1415 unregister_framebuffer(info); 1416 unmap_video_memory(info); 1417 if (&info->cmap) 1418 fb_dealloc_cmap(&info->cmap); 1419 1420 mfbi->registered = 0; 1421} 1422 1423static irqreturn_t fsl_diu_isr(int irq, void *dev_id) 1424{ 1425 struct diu __iomem *hw = dev_id; 1426 uint32_t status = in_be32(&hw->int_status); 1427 1428 if (status) { 1429 /* This is the workaround for underrun */ 1430 if (status & INT_UNDRUN) { 1431 out_be32(&hw->diu_mode, 0); 1432 udelay(1); 1433 out_be32(&hw->diu_mode, 1); 1434 } 1435#if defined(CONFIG_NOT_COHERENT_CACHE) 1436 else if (status & INT_VSYNC) { 1437 unsigned int i; 1438 1439 for (i = 0; i < coherence_data_size; 1440 i += d_cache_line_size) 1441 __asm__ __volatile__ ( 1442 "dcbz 0, %[input]" 1443 ::[input]"r"(&coherence_data[i])); 1444 } 1445#endif 1446 return IRQ_HANDLED; 1447 } 1448 return IRQ_NONE; 1449} 1450 1451#ifdef CONFIG_PM 1452/* 1453 * Power management hooks. Note that we won't be called from IRQ context, 1454 * unlike the blank functions above, so we may sleep. 1455 */ 1456static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state) 1457{ 1458 struct fsl_diu_data *data; 1459 1460 data = dev_get_drvdata(&ofdev->dev); 1461 disable_lcdc(data->fsl_diu_info); 1462 1463 return 0; 1464} 1465 1466static int fsl_diu_resume(struct platform_device *ofdev) 1467{ 1468 struct fsl_diu_data *data; 1469 1470 data = dev_get_drvdata(&ofdev->dev); 1471 enable_lcdc(data->fsl_diu_info); 1472 1473 return 0; 1474} 1475 1476#else 1477#define fsl_diu_suspend NULL 1478#define fsl_diu_resume NULL 1479#endif /* CONFIG_PM */ 1480 1481static ssize_t store_monitor(struct device *device, 1482 struct device_attribute *attr, const char *buf, size_t count) 1483{ 1484 enum fsl_diu_monitor_port old_monitor_port; 1485 struct fsl_diu_data *data = 1486 container_of(attr, struct fsl_diu_data, dev_attr); 1487 1488 old_monitor_port = data->monitor_port; 1489 data->monitor_port = fsl_diu_name_to_port(buf); 1490 1491 if (old_monitor_port != data->monitor_port) { 1492 /* All AOIs need adjust pixel format 1493 * fsl_diu_set_par only change the pixsel format here 1494 * unlikely to fail. */ 1495 unsigned int i; 1496 1497 for (i=0; i < NUM_AOIS; i++) 1498 fsl_diu_set_par(&data->fsl_diu_info[i]); 1499 } 1500 return count; 1501} 1502 1503static ssize_t show_monitor(struct device *device, 1504 struct device_attribute *attr, char *buf) 1505{ 1506 struct fsl_diu_data *data = 1507 container_of(attr, struct fsl_diu_data, dev_attr); 1508 1509 switch (data->monitor_port) { 1510 case FSL_DIU_PORT_DVI: 1511 return sprintf(buf, "DVI\n"); 1512 case FSL_DIU_PORT_LVDS: 1513 return sprintf(buf, "Single-link LVDS\n"); 1514 case FSL_DIU_PORT_DLVDS: 1515 return sprintf(buf, "Dual-link LVDS\n"); 1516 } 1517 1518 return 0; 1519} 1520 1521static int fsl_diu_probe(struct platform_device *pdev) 1522{ 1523 struct device_node *np = pdev->dev.of_node; 1524 struct mfb_info *mfbi; 1525 struct fsl_diu_data *data; 1526 dma_addr_t dma_addr; /* DMA addr of fsl_diu_data struct */ 1527 const void *prop; 1528 unsigned int i; 1529 int ret; 1530 1531 data = dmam_alloc_coherent(&pdev->dev, sizeof(struct fsl_diu_data), 1532 &dma_addr, GFP_DMA | __GFP_ZERO); 1533 if (!data) 1534 return -ENOMEM; 1535 data->dma_addr = dma_addr; 1536 1537 /* 1538 * dma_alloc_coherent() uses a page allocator, so the address is 1539 * always page-aligned. We need the memory to be 32-byte aligned, 1540 * so that's good. However, if one day the allocator changes, we 1541 * need to catch that. It's not worth the effort to handle unaligned 1542 * alloctions now because it's highly unlikely to ever be a problem. 1543 */ 1544 if ((unsigned long)data & 31) { 1545 dev_err(&pdev->dev, "misaligned allocation"); 1546 ret = -ENOMEM; 1547 goto error; 1548 } 1549 1550 spin_lock_init(&data->reg_lock); 1551 1552 for (i = 0; i < NUM_AOIS; i++) { 1553 struct fb_info *info = &data->fsl_diu_info[i]; 1554 1555 info->device = &pdev->dev; 1556 info->par = &data->mfb[i]; 1557 1558 /* 1559 * We store the physical address of the AD in the reserved 1560 * 'paddr' field of the AD itself. 1561 */ 1562 data->ad[i].paddr = DMA_ADDR(data, ad[i]); 1563 1564 info->fix.smem_start = 0; 1565 1566 /* Initialize the AOI data structure */ 1567 mfbi = info->par; 1568 memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info)); 1569 mfbi->parent = data; 1570 mfbi->ad = &data->ad[i]; 1571 } 1572 1573 /* Get the EDID data from the device tree, if present */ 1574 prop = of_get_property(np, "edid", &ret); 1575 if (prop && ret == EDID_LENGTH) { 1576 memcpy(data->edid_data, prop, EDID_LENGTH); 1577 data->has_edid = true; 1578 } 1579 1580 data->diu_reg = of_iomap(np, 0); 1581 if (!data->diu_reg) { 1582 dev_err(&pdev->dev, "cannot map DIU registers\n"); 1583 ret = -EFAULT; 1584 goto error; 1585 } 1586 1587 /* Get the IRQ of the DIU */ 1588 data->irq = irq_of_parse_and_map(np, 0); 1589 1590 if (!data->irq) { 1591 dev_err(&pdev->dev, "could not get DIU IRQ\n"); 1592 ret = -EINVAL; 1593 goto error; 1594 } 1595 data->monitor_port = monitor_port; 1596 1597 /* Initialize the dummy Area Descriptor */ 1598 data->dummy_ad.addr = cpu_to_le32(DMA_ADDR(data, dummy_aoi)); 1599 data->dummy_ad.pix_fmt = 0x88882317; 1600 data->dummy_ad.src_size_g_alpha = cpu_to_le32((4 << 12) | 4); 1601 data->dummy_ad.aoi_size = cpu_to_le32((4 << 16) | 2); 1602 data->dummy_ad.offset_xyi = 0; 1603 data->dummy_ad.offset_xyd = 0; 1604 data->dummy_ad.next_ad = 0; 1605 data->dummy_ad.paddr = DMA_ADDR(data, dummy_ad); 1606 1607 /* 1608 * Let DIU continue to display splash screen if it was pre-initialized 1609 * by the bootloader; otherwise, clear the display. 1610 */ 1611 if (in_be32(&data->diu_reg->diu_mode) == MFB_MODE0) 1612 out_be32(&data->diu_reg->desc[0], 0); 1613 1614 out_be32(&data->diu_reg->desc[1], data->dummy_ad.paddr); 1615 out_be32(&data->diu_reg->desc[2], data->dummy_ad.paddr); 1616 1617 for (i = 0; i < NUM_AOIS; i++) { 1618 ret = install_fb(&data->fsl_diu_info[i]); 1619 if (ret) { 1620 dev_err(&pdev->dev, "could not register fb %d\n", i); 1621 goto error; 1622 } 1623 } 1624 1625 /* 1626 * Older versions of U-Boot leave interrupts enabled, so disable 1627 * all of them and clear the status register. 1628 */ 1629 out_be32(&data->diu_reg->int_mask, 0xffffffff); 1630 in_be32(&data->diu_reg->int_status); 1631 1632 ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb", 1633 &data->diu_reg); 1634 if (ret) { 1635 dev_err(&pdev->dev, "could not claim irq\n"); 1636 goto error; 1637 } 1638 1639 sysfs_attr_init(&data->dev_attr.attr); 1640 data->dev_attr.attr.name = "monitor"; 1641 data->dev_attr.attr.mode = S_IRUGO|S_IWUSR; 1642 data->dev_attr.show = show_monitor; 1643 data->dev_attr.store = store_monitor; 1644 ret = device_create_file(&pdev->dev, &data->dev_attr); 1645 if (ret) { 1646 dev_err(&pdev->dev, "could not create sysfs file %s\n", 1647 data->dev_attr.attr.name); 1648 } 1649 1650 dev_set_drvdata(&pdev->dev, data); 1651 return 0; 1652 1653error: 1654 for (i = 0; i < NUM_AOIS; i++) 1655 uninstall_fb(&data->fsl_diu_info[i]); 1656 1657 iounmap(data->diu_reg); 1658 1659 return ret; 1660} 1661 1662static int fsl_diu_remove(struct platform_device *pdev) 1663{ 1664 struct fsl_diu_data *data; 1665 int i; 1666 1667 data = dev_get_drvdata(&pdev->dev); 1668 disable_lcdc(&data->fsl_diu_info[0]); 1669 1670 free_irq(data->irq, &data->diu_reg); 1671 1672 for (i = 0; i < NUM_AOIS; i++) 1673 uninstall_fb(&data->fsl_diu_info[i]); 1674 1675 iounmap(data->diu_reg); 1676 1677 return 0; 1678} 1679 1680#ifndef MODULE 1681static int __init fsl_diu_setup(char *options) 1682{ 1683 char *opt; 1684 unsigned long val; 1685 1686 if (!options || !*options) 1687 return 0; 1688 1689 while ((opt = strsep(&options, ",")) != NULL) { 1690 if (!*opt) 1691 continue; 1692 if (!strncmp(opt, "monitor=", 8)) { 1693 monitor_port = fsl_diu_name_to_port(opt + 8); 1694 } else if (!strncmp(opt, "bpp=", 4)) { 1695 if (!strict_strtoul(opt + 4, 10, &val)) 1696 default_bpp = val; 1697 } else 1698 fb_mode = opt; 1699 } 1700 1701 return 0; 1702} 1703#endif 1704 1705static struct of_device_id fsl_diu_match[] = { 1706#ifdef CONFIG_PPC_MPC512x 1707 { 1708 .compatible = "fsl,mpc5121-diu", 1709 }, 1710#endif 1711 { 1712 .compatible = "fsl,diu", 1713 }, 1714 {} 1715}; 1716MODULE_DEVICE_TABLE(of, fsl_diu_match); 1717 1718static struct platform_driver fsl_diu_driver = { 1719 .driver = { 1720 .name = "fsl-diu-fb", 1721 .owner = THIS_MODULE, 1722 .of_match_table = fsl_diu_match, 1723 }, 1724 .probe = fsl_diu_probe, 1725 .remove = fsl_diu_remove, 1726 .suspend = fsl_diu_suspend, 1727 .resume = fsl_diu_resume, 1728}; 1729 1730static int __init fsl_diu_init(void) 1731{ 1732#ifdef CONFIG_NOT_COHERENT_CACHE 1733 struct device_node *np; 1734 const u32 *prop; 1735#endif 1736 int ret; 1737#ifndef MODULE 1738 char *option; 1739 1740 /* 1741 * For kernel boot options (in 'video=xxxfb:<options>' format) 1742 */ 1743 if (fb_get_options("fslfb", &option)) 1744 return -ENODEV; 1745 fsl_diu_setup(option); 1746#else 1747 monitor_port = fsl_diu_name_to_port(monitor_string); 1748#endif 1749 pr_info("Freescale Display Interface Unit (DIU) framebuffer driver\n"); 1750 1751#ifdef CONFIG_NOT_COHERENT_CACHE 1752 np = of_find_node_by_type(NULL, "cpu"); 1753 if (!np) { 1754 pr_err("fsl-diu-fb: can't find 'cpu' device node\n"); 1755 return -ENODEV; 1756 } 1757 1758 prop = of_get_property(np, "d-cache-size", NULL); 1759 if (prop == NULL) { 1760 pr_err("fsl-diu-fb: missing 'd-cache-size' property' " 1761 "in 'cpu' node\n"); 1762 of_node_put(np); 1763 return -ENODEV; 1764 } 1765 1766 /* 1767 * Freescale PLRU requires 13/8 times the cache size to do a proper 1768 * displacement flush 1769 */ 1770 coherence_data_size = be32_to_cpup(prop) * 13; 1771 coherence_data_size /= 8; 1772 1773 pr_debug("fsl-diu-fb: coherence data size is %zu bytes\n", 1774 coherence_data_size); 1775 1776 prop = of_get_property(np, "d-cache-line-size", NULL); 1777 if (prop == NULL) { 1778 pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' " 1779 "in 'cpu' node\n"); 1780 of_node_put(np); 1781 return -ENODEV; 1782 } 1783 d_cache_line_size = be32_to_cpup(prop); 1784 1785 pr_debug("fsl-diu-fb: cache lines size is %u bytes\n", 1786 d_cache_line_size); 1787 1788 of_node_put(np); 1789 coherence_data = vmalloc(coherence_data_size); 1790 if (!coherence_data) { 1791 pr_err("fsl-diu-fb: could not allocate coherence data " 1792 "(size=%zu)\n", coherence_data_size); 1793 return -ENOMEM; 1794 } 1795 1796#endif 1797 1798 ret = platform_driver_register(&fsl_diu_driver); 1799 if (ret) { 1800 pr_err("fsl-diu-fb: failed to register platform driver\n"); 1801#if defined(CONFIG_NOT_COHERENT_CACHE) 1802 vfree(coherence_data); 1803#endif 1804 } 1805 return ret; 1806} 1807 1808static void __exit fsl_diu_exit(void) 1809{ 1810 platform_driver_unregister(&fsl_diu_driver); 1811#if defined(CONFIG_NOT_COHERENT_CACHE) 1812 vfree(coherence_data); 1813#endif 1814} 1815 1816module_init(fsl_diu_init); 1817module_exit(fsl_diu_exit); 1818 1819MODULE_AUTHOR("York Sun <yorksun@freescale.com>"); 1820MODULE_DESCRIPTION("Freescale DIU framebuffer driver"); 1821MODULE_LICENSE("GPL"); 1822 1823module_param_named(mode, fb_mode, charp, 0); 1824MODULE_PARM_DESC(mode, 1825 "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" "); 1826module_param_named(bpp, default_bpp, ulong, 0); 1827MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified in 'mode'"); 1828module_param_named(monitor, monitor_string, charp, 0); 1829MODULE_PARM_DESC(monitor, "Specify the monitor port " 1830 "(\"dvi\", \"lvds\", or \"dlvds\") if supported by the platform"); 1831