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.4-rc6 1799 lines 46 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 __devinitdata 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 int cursor_reset; 341 unsigned char g_alpha; 342 unsigned int count; 343 int x_aoi_d; /* aoi display x offset to physical screen */ 344 int y_aoi_d; /* aoi display y offset to physical screen */ 345 struct fsl_diu_data *parent; 346 u8 *edid_data; 347}; 348 349/** 350 * struct fsl_diu_data - per-DIU data structure 351 * @dma_addr: DMA address of this structure 352 * @fsl_diu_info: fb_info objects, one per AOI 353 * @dev_attr: sysfs structure 354 * @irq: IRQ 355 * @monitor_port: the monitor port this DIU is connected to 356 * @diu_reg: pointer to the DIU hardware registers 357 * @reg_lock: spinlock for register access 358 * @dummy_aoi: video buffer for the 4x4 32-bit dummy AOI 359 * dummy_ad: DIU Area Descriptor for the dummy AOI 360 * @ad[]: Area Descriptors for each real AOI 361 * @gamma: gamma color table 362 * @cursor: hardware cursor data 363 * 364 * This data structure must be allocated with 32-byte alignment, so that the 365 * internal fields can be aligned properly. 366 */ 367struct fsl_diu_data { 368 dma_addr_t dma_addr; 369 struct fb_info fsl_diu_info[NUM_AOIS]; 370 struct mfb_info mfb[NUM_AOIS]; 371 struct device_attribute dev_attr; 372 unsigned int irq; 373 enum fsl_diu_monitor_port monitor_port; 374 struct diu __iomem *diu_reg; 375 spinlock_t reg_lock; 376 u8 dummy_aoi[4 * 4 * 4]; 377 struct diu_ad dummy_ad __aligned(8); 378 struct diu_ad ad[NUM_AOIS] __aligned(8); 379 u8 gamma[256 * 3] __aligned(32); 380 u8 cursor[MAX_CURS * MAX_CURS * 2] __aligned(32); 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/** 434 * fsl_diu_name_to_port - convert a port name to a monitor port enum 435 * 436 * Takes the name of a monitor port ("dvi", "lvds", or "dlvds") and returns 437 * the enum fsl_diu_monitor_port that corresponds to that string. 438 * 439 * For compatibility with older versions, a number ("0", "1", or "2") is also 440 * supported. 441 * 442 * If the string is unknown, DVI is assumed. 443 * 444 * If the particular port is not supported by the platform, another port 445 * (platform-specific) is chosen instead. 446 */ 447static enum fsl_diu_monitor_port fsl_diu_name_to_port(const char *s) 448{ 449 enum fsl_diu_monitor_port port = FSL_DIU_PORT_DVI; 450 unsigned long val; 451 452 if (s) { 453 if (!strict_strtoul(s, 10, &val) && (val <= 2)) 454 port = (enum fsl_diu_monitor_port) val; 455 else if (strncmp(s, "lvds", 4) == 0) 456 port = FSL_DIU_PORT_LVDS; 457 else if (strncmp(s, "dlvds", 5) == 0) 458 port = FSL_DIU_PORT_DLVDS; 459 } 460 461 return diu_ops.valid_monitor_port(port); 462} 463 464/* 465 * Workaround for failed writing desc register of planes. 466 * Needed with MPC5121 DIU rev 2.0 silicon. 467 */ 468void wr_reg_wa(u32 *reg, u32 val) 469{ 470 do { 471 out_be32(reg, val); 472 } while (in_be32(reg) != val); 473} 474 475static void fsl_diu_enable_panel(struct fb_info *info) 476{ 477 struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par; 478 struct diu_ad *ad = mfbi->ad; 479 struct fsl_diu_data *data = mfbi->parent; 480 struct diu __iomem *hw = data->diu_reg; 481 482 switch (mfbi->index) { 483 case PLANE0: 484 if (hw->desc[0] != ad->paddr) 485 wr_reg_wa(&hw->desc[0], ad->paddr); 486 break; 487 case PLANE1_AOI0: 488 cmfbi = &data->mfb[2]; 489 if (hw->desc[1] != ad->paddr) { /* AOI0 closed */ 490 if (cmfbi->count > 0) /* AOI1 open */ 491 ad->next_ad = 492 cpu_to_le32(cmfbi->ad->paddr); 493 else 494 ad->next_ad = 0; 495 wr_reg_wa(&hw->desc[1], ad->paddr); 496 } 497 break; 498 case PLANE2_AOI0: 499 cmfbi = &data->mfb[4]; 500 if (hw->desc[2] != ad->paddr) { /* AOI0 closed */ 501 if (cmfbi->count > 0) /* AOI1 open */ 502 ad->next_ad = 503 cpu_to_le32(cmfbi->ad->paddr); 504 else 505 ad->next_ad = 0; 506 wr_reg_wa(&hw->desc[2], ad->paddr); 507 } 508 break; 509 case PLANE1_AOI1: 510 pmfbi = &data->mfb[1]; 511 ad->next_ad = 0; 512 if (hw->desc[1] == data->dummy_ad.paddr) 513 wr_reg_wa(&hw->desc[1], ad->paddr); 514 else /* AOI0 open */ 515 pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); 516 break; 517 case PLANE2_AOI1: 518 pmfbi = &data->mfb[3]; 519 ad->next_ad = 0; 520 if (hw->desc[2] == data->dummy_ad.paddr) 521 wr_reg_wa(&hw->desc[2], ad->paddr); 522 else /* AOI0 was open */ 523 pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); 524 break; 525 } 526} 527 528static void fsl_diu_disable_panel(struct fb_info *info) 529{ 530 struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par; 531 struct diu_ad *ad = mfbi->ad; 532 struct fsl_diu_data *data = mfbi->parent; 533 struct diu __iomem *hw = data->diu_reg; 534 535 switch (mfbi->index) { 536 case PLANE0: 537 if (hw->desc[0] != data->dummy_ad.paddr) 538 wr_reg_wa(&hw->desc[0], data->dummy_ad.paddr); 539 break; 540 case PLANE1_AOI0: 541 cmfbi = &data->mfb[2]; 542 if (cmfbi->count > 0) /* AOI1 is open */ 543 wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr); 544 /* move AOI1 to the first */ 545 else /* AOI1 was closed */ 546 wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr); 547 /* close AOI 0 */ 548 break; 549 case PLANE2_AOI0: 550 cmfbi = &data->mfb[4]; 551 if (cmfbi->count > 0) /* AOI1 is open */ 552 wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr); 553 /* move AOI1 to the first */ 554 else /* AOI1 was closed */ 555 wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr); 556 /* close AOI 0 */ 557 break; 558 case PLANE1_AOI1: 559 pmfbi = &data->mfb[1]; 560 if (hw->desc[1] != ad->paddr) { 561 /* AOI1 is not the first in the chain */ 562 if (pmfbi->count > 0) 563 /* AOI0 is open, must be the first */ 564 pmfbi->ad->next_ad = 0; 565 } else /* AOI1 is the first in the chain */ 566 wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr); 567 /* close AOI 1 */ 568 break; 569 case PLANE2_AOI1: 570 pmfbi = &data->mfb[3]; 571 if (hw->desc[2] != ad->paddr) { 572 /* AOI1 is not the first in the chain */ 573 if (pmfbi->count > 0) 574 /* AOI0 is open, must be the first */ 575 pmfbi->ad->next_ad = 0; 576 } else /* AOI1 is the first in the chain */ 577 wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr); 578 /* close AOI 1 */ 579 break; 580 } 581} 582 583static void enable_lcdc(struct fb_info *info) 584{ 585 struct mfb_info *mfbi = info->par; 586 struct fsl_diu_data *data = mfbi->parent; 587 struct diu __iomem *hw = data->diu_reg; 588 589 out_be32(&hw->diu_mode, MFB_MODE1); 590} 591 592static void disable_lcdc(struct fb_info *info) 593{ 594 struct mfb_info *mfbi = info->par; 595 struct fsl_diu_data *data = mfbi->parent; 596 struct diu __iomem *hw = data->diu_reg; 597 598 out_be32(&hw->diu_mode, 0); 599} 600 601static void adjust_aoi_size_position(struct fb_var_screeninfo *var, 602 struct fb_info *info) 603{ 604 struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par; 605 struct fsl_diu_data *data = mfbi->parent; 606 int available_height, upper_aoi_bottom; 607 enum mfb_index index = mfbi->index; 608 int lower_aoi_is_open, upper_aoi_is_open; 609 __u32 base_plane_width, base_plane_height, upper_aoi_height; 610 611 base_plane_width = data->fsl_diu_info[0].var.xres; 612 base_plane_height = data->fsl_diu_info[0].var.yres; 613 614 if (mfbi->x_aoi_d < 0) 615 mfbi->x_aoi_d = 0; 616 if (mfbi->y_aoi_d < 0) 617 mfbi->y_aoi_d = 0; 618 switch (index) { 619 case PLANE0: 620 if (mfbi->x_aoi_d != 0) 621 mfbi->x_aoi_d = 0; 622 if (mfbi->y_aoi_d != 0) 623 mfbi->y_aoi_d = 0; 624 break; 625 case PLANE1_AOI0: 626 case PLANE2_AOI0: 627 lower_aoi_mfbi = data->fsl_diu_info[index+1].par; 628 lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0; 629 if (var->xres > base_plane_width) 630 var->xres = base_plane_width; 631 if ((mfbi->x_aoi_d + var->xres) > base_plane_width) 632 mfbi->x_aoi_d = base_plane_width - var->xres; 633 634 if (lower_aoi_is_open) 635 available_height = lower_aoi_mfbi->y_aoi_d; 636 else 637 available_height = base_plane_height; 638 if (var->yres > available_height) 639 var->yres = available_height; 640 if ((mfbi->y_aoi_d + var->yres) > available_height) 641 mfbi->y_aoi_d = available_height - var->yres; 642 break; 643 case PLANE1_AOI1: 644 case PLANE2_AOI1: 645 upper_aoi_mfbi = data->fsl_diu_info[index-1].par; 646 upper_aoi_height = data->fsl_diu_info[index-1].var.yres; 647 upper_aoi_bottom = upper_aoi_mfbi->y_aoi_d + upper_aoi_height; 648 upper_aoi_is_open = upper_aoi_mfbi->count > 0 ? 1 : 0; 649 if (var->xres > base_plane_width) 650 var->xres = base_plane_width; 651 if ((mfbi->x_aoi_d + var->xres) > base_plane_width) 652 mfbi->x_aoi_d = base_plane_width - var->xres; 653 if (mfbi->y_aoi_d < 0) 654 mfbi->y_aoi_d = 0; 655 if (upper_aoi_is_open) { 656 if (mfbi->y_aoi_d < upper_aoi_bottom) 657 mfbi->y_aoi_d = upper_aoi_bottom; 658 available_height = base_plane_height 659 - upper_aoi_bottom; 660 } else 661 available_height = base_plane_height; 662 if (var->yres > available_height) 663 var->yres = available_height; 664 if ((mfbi->y_aoi_d + var->yres) > base_plane_height) 665 mfbi->y_aoi_d = base_plane_height - var->yres; 666 break; 667 } 668} 669/* 670 * Checks to see if the hardware supports the state requested by var passed 671 * in. This function does not alter the hardware state! If the var passed in 672 * is slightly off by what the hardware can support then we alter the var 673 * PASSED in to what we can do. If the hardware doesn't support mode change 674 * a -EINVAL will be returned by the upper layers. 675 */ 676static int fsl_diu_check_var(struct fb_var_screeninfo *var, 677 struct fb_info *info) 678{ 679 if (var->xres_virtual < var->xres) 680 var->xres_virtual = var->xres; 681 if (var->yres_virtual < var->yres) 682 var->yres_virtual = var->yres; 683 684 if (var->xoffset < 0) 685 var->xoffset = 0; 686 687 if (var->yoffset < 0) 688 var->yoffset = 0; 689 690 if (var->xoffset + info->var.xres > info->var.xres_virtual) 691 var->xoffset = info->var.xres_virtual - info->var.xres; 692 693 if (var->yoffset + info->var.yres > info->var.yres_virtual) 694 var->yoffset = info->var.yres_virtual - info->var.yres; 695 696 if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && 697 (var->bits_per_pixel != 16)) 698 var->bits_per_pixel = default_bpp; 699 700 switch (var->bits_per_pixel) { 701 case 16: 702 var->red.length = 5; 703 var->red.offset = 11; 704 var->red.msb_right = 0; 705 706 var->green.length = 6; 707 var->green.offset = 5; 708 var->green.msb_right = 0; 709 710 var->blue.length = 5; 711 var->blue.offset = 0; 712 var->blue.msb_right = 0; 713 714 var->transp.length = 0; 715 var->transp.offset = 0; 716 var->transp.msb_right = 0; 717 break; 718 case 24: 719 var->red.length = 8; 720 var->red.offset = 0; 721 var->red.msb_right = 0; 722 723 var->green.length = 8; 724 var->green.offset = 8; 725 var->green.msb_right = 0; 726 727 var->blue.length = 8; 728 var->blue.offset = 16; 729 var->blue.msb_right = 0; 730 731 var->transp.length = 0; 732 var->transp.offset = 0; 733 var->transp.msb_right = 0; 734 break; 735 case 32: 736 var->red.length = 8; 737 var->red.offset = 16; 738 var->red.msb_right = 0; 739 740 var->green.length = 8; 741 var->green.offset = 8; 742 var->green.msb_right = 0; 743 744 var->blue.length = 8; 745 var->blue.offset = 0; 746 var->blue.msb_right = 0; 747 748 var->transp.length = 8; 749 var->transp.offset = 24; 750 var->transp.msb_right = 0; 751 752 break; 753 } 754 755 var->height = -1; 756 var->width = -1; 757 var->grayscale = 0; 758 759 /* Copy nonstd field to/from sync for fbset usage */ 760 var->sync |= var->nonstd; 761 var->nonstd |= var->sync; 762 763 adjust_aoi_size_position(var, info); 764 return 0; 765} 766 767static void set_fix(struct fb_info *info) 768{ 769 struct fb_fix_screeninfo *fix = &info->fix; 770 struct fb_var_screeninfo *var = &info->var; 771 struct mfb_info *mfbi = info->par; 772 773 strncpy(fix->id, mfbi->id, sizeof(fix->id)); 774 fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; 775 fix->type = FB_TYPE_PACKED_PIXELS; 776 fix->accel = FB_ACCEL_NONE; 777 fix->visual = FB_VISUAL_TRUECOLOR; 778 fix->xpanstep = 1; 779 fix->ypanstep = 1; 780} 781 782static void update_lcdc(struct fb_info *info) 783{ 784 struct fb_var_screeninfo *var = &info->var; 785 struct mfb_info *mfbi = info->par; 786 struct fsl_diu_data *data = mfbi->parent; 787 struct diu __iomem *hw; 788 int i, j; 789 u8 *gamma_table_base; 790 791 u32 temp; 792 793 hw = data->diu_reg; 794 795 diu_ops.set_monitor_port(data->monitor_port); 796 gamma_table_base = data->gamma; 797 798 /* Prep for DIU init - gamma table, cursor table */ 799 800 for (i = 0; i <= 2; i++) 801 for (j = 0; j <= 255; j++) 802 *gamma_table_base++ = j; 803 804 if (diu_ops.set_gamma_table) 805 diu_ops.set_gamma_table(data->monitor_port, data->gamma); 806 807 disable_lcdc(info); 808 809 /* Program DIU registers */ 810 811 out_be32(&hw->gamma, DMA_ADDR(data, gamma)); 812 out_be32(&hw->cursor, DMA_ADDR(data, cursor)); 813 814 out_be32(&hw->bgnd, 0x007F7F7F); /* BGND */ 815 out_be32(&hw->bgnd_wb, 0); /* BGND_WB */ 816 out_be32(&hw->disp_size, (var->yres << 16 | var->xres)); 817 /* DISP SIZE */ 818 out_be32(&hw->wb_size, 0); /* WB SIZE */ 819 out_be32(&hw->wb_mem_addr, 0); /* WB MEM ADDR */ 820 821 /* Horizontal and vertical configuration register */ 822 temp = var->left_margin << 22 | /* BP_H */ 823 var->hsync_len << 11 | /* PW_H */ 824 var->right_margin; /* FP_H */ 825 826 out_be32(&hw->hsyn_para, temp); 827 828 temp = var->upper_margin << 22 | /* BP_V */ 829 var->vsync_len << 11 | /* PW_V */ 830 var->lower_margin; /* FP_V */ 831 832 out_be32(&hw->vsyn_para, temp); 833 834 diu_ops.set_pixel_clock(var->pixclock); 835 836 out_be32(&hw->syn_pol, 0); /* SYNC SIGNALS POLARITY */ 837 out_be32(&hw->thresholds, 0x00037800); /* The Thresholds */ 838 out_be32(&hw->int_status, 0); /* INTERRUPT STATUS */ 839 out_be32(&hw->plut, 0x01F5F666); 840 841 /* Enable the DIU */ 842 enable_lcdc(info); 843} 844 845static int map_video_memory(struct fb_info *info) 846{ 847 u32 smem_len = info->fix.line_length * info->var.yres_virtual; 848 void *p; 849 850 p = alloc_pages_exact(smem_len, GFP_DMA | __GFP_ZERO); 851 if (!p) { 852 dev_err(info->dev, "unable to allocate fb memory\n"); 853 return -ENOMEM; 854 } 855 mutex_lock(&info->mm_lock); 856 info->screen_base = p; 857 info->fix.smem_start = virt_to_phys(info->screen_base); 858 info->fix.smem_len = smem_len; 859 mutex_unlock(&info->mm_lock); 860 info->screen_size = info->fix.smem_len; 861 862 return 0; 863} 864 865static void unmap_video_memory(struct fb_info *info) 866{ 867 void *p = info->screen_base; 868 size_t l = info->fix.smem_len; 869 870 mutex_lock(&info->mm_lock); 871 info->screen_base = NULL; 872 info->fix.smem_start = 0; 873 info->fix.smem_len = 0; 874 mutex_unlock(&info->mm_lock); 875 876 if (p) 877 free_pages_exact(p, l); 878} 879 880/* 881 * Using the fb_var_screeninfo in fb_info we set the aoi of this 882 * particular framebuffer. It is a light version of fsl_diu_set_par. 883 */ 884static int fsl_diu_set_aoi(struct fb_info *info) 885{ 886 struct fb_var_screeninfo *var = &info->var; 887 struct mfb_info *mfbi = info->par; 888 struct diu_ad *ad = mfbi->ad; 889 890 /* AOI should not be greater than display size */ 891 ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset); 892 ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d); 893 return 0; 894} 895 896/** 897 * fsl_diu_get_pixel_format: return the pixel format for a given color depth 898 * 899 * The pixel format is a 32-bit value that determine which bits in each 900 * pixel are to be used for each color. This is the default function used 901 * if the platform does not define its own version. 902 */ 903static u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel) 904{ 905#define PF_BYTE_F 0x10000000 906#define PF_ALPHA_C_MASK 0x0E000000 907#define PF_ALPHA_C_SHIFT 25 908#define PF_BLUE_C_MASK 0x01800000 909#define PF_BLUE_C_SHIFT 23 910#define PF_GREEN_C_MASK 0x00600000 911#define PF_GREEN_C_SHIFT 21 912#define PF_RED_C_MASK 0x00180000 913#define PF_RED_C_SHIFT 19 914#define PF_PALETTE 0x00040000 915#define PF_PIXEL_S_MASK 0x00030000 916#define PF_PIXEL_S_SHIFT 16 917#define PF_COMP_3_MASK 0x0000F000 918#define PF_COMP_3_SHIFT 12 919#define PF_COMP_2_MASK 0x00000F00 920#define PF_COMP_2_SHIFT 8 921#define PF_COMP_1_MASK 0x000000F0 922#define PF_COMP_1_SHIFT 4 923#define PF_COMP_0_MASK 0x0000000F 924#define PF_COMP_0_SHIFT 0 925 926#define MAKE_PF(alpha, red, blue, green, size, c0, c1, c2, c3) \ 927 cpu_to_le32(PF_BYTE_F | (alpha << PF_ALPHA_C_SHIFT) | \ 928 (blue << PF_BLUE_C_SHIFT) | (green << PF_GREEN_C_SHIFT) | \ 929 (red << PF_RED_C_SHIFT) | (c3 << PF_COMP_3_SHIFT) | \ 930 (c2 << PF_COMP_2_SHIFT) | (c1 << PF_COMP_1_SHIFT) | \ 931 (c0 << PF_COMP_0_SHIFT) | (size << PF_PIXEL_S_SHIFT)) 932 933 switch (bits_per_pixel) { 934 case 32: 935 /* 0x88883316 */ 936 return MAKE_PF(3, 2, 0, 1, 3, 8, 8, 8, 8); 937 case 24: 938 /* 0x88082219 */ 939 return MAKE_PF(4, 0, 1, 2, 2, 0, 8, 8, 8); 940 case 16: 941 /* 0x65053118 */ 942 return MAKE_PF(4, 2, 1, 0, 1, 5, 6, 5, 0); 943 default: 944 pr_err("fsl-diu: unsupported color depth %u\n", bits_per_pixel); 945 return 0; 946 } 947} 948 949/* 950 * Using the fb_var_screeninfo in fb_info we set the resolution of this 951 * particular framebuffer. This function alters the fb_fix_screeninfo stored 952 * in fb_info. It does not alter var in fb_info since we are using that 953 * data. This means we depend on the data in var inside fb_info to be 954 * supported by the hardware. fsl_diu_check_var is always called before 955 * fsl_diu_set_par to ensure this. 956 */ 957static int fsl_diu_set_par(struct fb_info *info) 958{ 959 unsigned long len; 960 struct fb_var_screeninfo *var = &info->var; 961 struct mfb_info *mfbi = info->par; 962 struct fsl_diu_data *data = mfbi->parent; 963 struct diu_ad *ad = mfbi->ad; 964 struct diu __iomem *hw; 965 966 hw = data->diu_reg; 967 968 set_fix(info); 969 mfbi->cursor_reset = 1; 970 971 len = info->var.yres_virtual * info->fix.line_length; 972 /* Alloc & dealloc each time resolution/bpp change */ 973 if (len != info->fix.smem_len) { 974 if (info->fix.smem_start) 975 unmap_video_memory(info); 976 977 /* Memory allocation for framebuffer */ 978 if (map_video_memory(info)) { 979 dev_err(info->dev, "unable to allocate fb memory 1\n"); 980 return -ENOMEM; 981 } 982 } 983 984 if (diu_ops.get_pixel_format) 985 ad->pix_fmt = diu_ops.get_pixel_format(data->monitor_port, 986 var->bits_per_pixel); 987 else 988 ad->pix_fmt = fsl_diu_get_pixel_format(var->bits_per_pixel); 989 990 ad->addr = cpu_to_le32(info->fix.smem_start); 991 ad->src_size_g_alpha = cpu_to_le32((var->yres_virtual << 12) | 992 var->xres_virtual) | mfbi->g_alpha; 993 /* AOI should not be greater than display size */ 994 ad->aoi_size = cpu_to_le32((var->yres << 16) | var->xres); 995 ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset); 996 ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d); 997 998 /* Disable chroma keying function */ 999 ad->ckmax_r = 0; 1000 ad->ckmax_g = 0; 1001 ad->ckmax_b = 0; 1002 1003 ad->ckmin_r = 255; 1004 ad->ckmin_g = 255; 1005 ad->ckmin_b = 255; 1006 1007 if (mfbi->index == PLANE0) 1008 update_lcdc(info); 1009 return 0; 1010} 1011 1012static inline __u32 CNVT_TOHW(__u32 val, __u32 width) 1013{ 1014 return ((val << width) + 0x7FFF - val) >> 16; 1015} 1016 1017/* 1018 * Set a single color register. The values supplied have a 16 bit magnitude 1019 * which needs to be scaled in this function for the hardware. Things to take 1020 * into consideration are how many color registers, if any, are supported with 1021 * the current color visual. With truecolor mode no color palettes are 1022 * supported. Here a pseudo palette is created which we store the value in 1023 * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited 1024 * color palette. 1025 */ 1026static int fsl_diu_setcolreg(unsigned int regno, unsigned int red, 1027 unsigned int green, unsigned int blue, 1028 unsigned int transp, struct fb_info *info) 1029{ 1030 int ret = 1; 1031 1032 /* 1033 * If greyscale is true, then we convert the RGB value 1034 * to greyscale no matter what visual we are using. 1035 */ 1036 if (info->var.grayscale) 1037 red = green = blue = (19595 * red + 38470 * green + 1038 7471 * blue) >> 16; 1039 switch (info->fix.visual) { 1040 case FB_VISUAL_TRUECOLOR: 1041 /* 1042 * 16-bit True Colour. We encode the RGB value 1043 * according to the RGB bitfield information. 1044 */ 1045 if (regno < 16) { 1046 u32 *pal = info->pseudo_palette; 1047 u32 v; 1048 1049 red = CNVT_TOHW(red, info->var.red.length); 1050 green = CNVT_TOHW(green, info->var.green.length); 1051 blue = CNVT_TOHW(blue, info->var.blue.length); 1052 transp = CNVT_TOHW(transp, info->var.transp.length); 1053 1054 v = (red << info->var.red.offset) | 1055 (green << info->var.green.offset) | 1056 (blue << info->var.blue.offset) | 1057 (transp << info->var.transp.offset); 1058 1059 pal[regno] = v; 1060 ret = 0; 1061 } 1062 break; 1063 } 1064 1065 return ret; 1066} 1067 1068/* 1069 * Pan (or wrap, depending on the `vmode' field) the display using the 1070 * 'xoffset' and 'yoffset' fields of the 'var' structure. If the values 1071 * don't fit, return -EINVAL. 1072 */ 1073static int fsl_diu_pan_display(struct fb_var_screeninfo *var, 1074 struct fb_info *info) 1075{ 1076 if ((info->var.xoffset == var->xoffset) && 1077 (info->var.yoffset == var->yoffset)) 1078 return 0; /* No change, do nothing */ 1079 1080 if (var->xoffset < 0 || var->yoffset < 0 1081 || var->xoffset + info->var.xres > info->var.xres_virtual 1082 || var->yoffset + info->var.yres > info->var.yres_virtual) 1083 return -EINVAL; 1084 1085 info->var.xoffset = var->xoffset; 1086 info->var.yoffset = var->yoffset; 1087 1088 if (var->vmode & FB_VMODE_YWRAP) 1089 info->var.vmode |= FB_VMODE_YWRAP; 1090 else 1091 info->var.vmode &= ~FB_VMODE_YWRAP; 1092 1093 fsl_diu_set_aoi(info); 1094 1095 return 0; 1096} 1097 1098static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, 1099 unsigned long arg) 1100{ 1101 struct mfb_info *mfbi = info->par; 1102 struct diu_ad *ad = mfbi->ad; 1103 struct mfb_chroma_key ck; 1104 unsigned char global_alpha; 1105 struct aoi_display_offset aoi_d; 1106 __u32 pix_fmt; 1107 void __user *buf = (void __user *)arg; 1108 1109 if (!arg) 1110 return -EINVAL; 1111 switch (cmd) { 1112 case MFB_SET_PIXFMT_OLD: 1113 dev_warn(info->dev, 1114 "MFB_SET_PIXFMT value of 0x%08x is deprecated.\n", 1115 MFB_SET_PIXFMT_OLD); 1116 case MFB_SET_PIXFMT: 1117 if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt))) 1118 return -EFAULT; 1119 ad->pix_fmt = pix_fmt; 1120 break; 1121 case MFB_GET_PIXFMT_OLD: 1122 dev_warn(info->dev, 1123 "MFB_GET_PIXFMT value of 0x%08x is deprecated.\n", 1124 MFB_GET_PIXFMT_OLD); 1125 case MFB_GET_PIXFMT: 1126 pix_fmt = ad->pix_fmt; 1127 if (copy_to_user(buf, &pix_fmt, sizeof(pix_fmt))) 1128 return -EFAULT; 1129 break; 1130 case MFB_SET_AOID: 1131 if (copy_from_user(&aoi_d, buf, sizeof(aoi_d))) 1132 return -EFAULT; 1133 mfbi->x_aoi_d = aoi_d.x_aoi_d; 1134 mfbi->y_aoi_d = aoi_d.y_aoi_d; 1135 fsl_diu_check_var(&info->var, info); 1136 fsl_diu_set_aoi(info); 1137 break; 1138 case MFB_GET_AOID: 1139 aoi_d.x_aoi_d = mfbi->x_aoi_d; 1140 aoi_d.y_aoi_d = mfbi->y_aoi_d; 1141 if (copy_to_user(buf, &aoi_d, sizeof(aoi_d))) 1142 return -EFAULT; 1143 break; 1144 case MFB_GET_ALPHA: 1145 global_alpha = mfbi->g_alpha; 1146 if (copy_to_user(buf, &global_alpha, sizeof(global_alpha))) 1147 return -EFAULT; 1148 break; 1149 case MFB_SET_ALPHA: 1150 /* set panel information */ 1151 if (copy_from_user(&global_alpha, buf, sizeof(global_alpha))) 1152 return -EFAULT; 1153 ad->src_size_g_alpha = (ad->src_size_g_alpha & (~0xff)) | 1154 (global_alpha & 0xff); 1155 mfbi->g_alpha = global_alpha; 1156 break; 1157 case MFB_SET_CHROMA_KEY: 1158 /* set panel winformation */ 1159 if (copy_from_user(&ck, buf, sizeof(ck))) 1160 return -EFAULT; 1161 1162 if (ck.enable && 1163 (ck.red_max < ck.red_min || 1164 ck.green_max < ck.green_min || 1165 ck.blue_max < ck.blue_min)) 1166 return -EINVAL; 1167 1168 if (!ck.enable) { 1169 ad->ckmax_r = 0; 1170 ad->ckmax_g = 0; 1171 ad->ckmax_b = 0; 1172 ad->ckmin_r = 255; 1173 ad->ckmin_g = 255; 1174 ad->ckmin_b = 255; 1175 } else { 1176 ad->ckmax_r = ck.red_max; 1177 ad->ckmax_g = ck.green_max; 1178 ad->ckmax_b = ck.blue_max; 1179 ad->ckmin_r = ck.red_min; 1180 ad->ckmin_g = ck.green_min; 1181 ad->ckmin_b = ck.blue_min; 1182 } 1183 break; 1184 default: 1185 dev_err(info->dev, "unknown ioctl command (0x%08X)\n", cmd); 1186 return -ENOIOCTLCMD; 1187 } 1188 1189 return 0; 1190} 1191 1192/* turn on fb if count == 1 1193 */ 1194static int fsl_diu_open(struct fb_info *info, int user) 1195{ 1196 struct mfb_info *mfbi = info->par; 1197 int res = 0; 1198 1199 /* free boot splash memory on first /dev/fb0 open */ 1200 if ((mfbi->index == PLANE0) && diu_ops.release_bootmem) 1201 diu_ops.release_bootmem(); 1202 1203 spin_lock(&diu_lock); 1204 mfbi->count++; 1205 if (mfbi->count == 1) { 1206 fsl_diu_check_var(&info->var, info); 1207 res = fsl_diu_set_par(info); 1208 if (res < 0) 1209 mfbi->count--; 1210 else 1211 fsl_diu_enable_panel(info); 1212 } 1213 1214 spin_unlock(&diu_lock); 1215 return res; 1216} 1217 1218/* turn off fb if count == 0 1219 */ 1220static int fsl_diu_release(struct fb_info *info, int user) 1221{ 1222 struct mfb_info *mfbi = info->par; 1223 int res = 0; 1224 1225 spin_lock(&diu_lock); 1226 mfbi->count--; 1227 if (mfbi->count == 0) 1228 fsl_diu_disable_panel(info); 1229 1230 spin_unlock(&diu_lock); 1231 return res; 1232} 1233 1234static struct fb_ops fsl_diu_ops = { 1235 .owner = THIS_MODULE, 1236 .fb_check_var = fsl_diu_check_var, 1237 .fb_set_par = fsl_diu_set_par, 1238 .fb_setcolreg = fsl_diu_setcolreg, 1239 .fb_pan_display = fsl_diu_pan_display, 1240 .fb_fillrect = cfb_fillrect, 1241 .fb_copyarea = cfb_copyarea, 1242 .fb_imageblit = cfb_imageblit, 1243 .fb_ioctl = fsl_diu_ioctl, 1244 .fb_open = fsl_diu_open, 1245 .fb_release = fsl_diu_release, 1246}; 1247 1248static int __devinit install_fb(struct fb_info *info) 1249{ 1250 int rc; 1251 struct mfb_info *mfbi = info->par; 1252 const char *aoi_mode, *init_aoi_mode = "320x240"; 1253 struct fb_videomode *db = fsl_diu_mode_db; 1254 unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db); 1255 int has_default_mode = 1; 1256 1257 info->var.activate = FB_ACTIVATE_NOW; 1258 info->fbops = &fsl_diu_ops; 1259 info->flags = FBINFO_DEFAULT | FBINFO_VIRTFB | FBINFO_PARTIAL_PAN_OK | 1260 FBINFO_READS_FAST; 1261 info->pseudo_palette = mfbi->pseudo_palette; 1262 1263 rc = fb_alloc_cmap(&info->cmap, 16, 0); 1264 if (rc) 1265 return rc; 1266 1267 if (mfbi->index == PLANE0) { 1268 if (mfbi->edid_data) { 1269 /* Now build modedb from EDID */ 1270 fb_edid_to_monspecs(mfbi->edid_data, &info->monspecs); 1271 fb_videomode_to_modelist(info->monspecs.modedb, 1272 info->monspecs.modedb_len, 1273 &info->modelist); 1274 db = info->monspecs.modedb; 1275 dbsize = info->monspecs.modedb_len; 1276 } 1277 aoi_mode = fb_mode; 1278 } else { 1279 aoi_mode = init_aoi_mode; 1280 } 1281 rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize, NULL, 1282 default_bpp); 1283 if (!rc) { 1284 /* 1285 * For plane 0 we continue and look into 1286 * driver's internal modedb. 1287 */ 1288 if ((mfbi->index == PLANE0) && mfbi->edid_data) 1289 has_default_mode = 0; 1290 else 1291 return -EINVAL; 1292 } 1293 1294 if (!has_default_mode) { 1295 rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db, 1296 ARRAY_SIZE(fsl_diu_mode_db), NULL, default_bpp); 1297 if (rc) 1298 has_default_mode = 1; 1299 } 1300 1301 /* Still not found, use preferred mode from database if any */ 1302 if (!has_default_mode && info->monspecs.modedb) { 1303 struct fb_monspecs *specs = &info->monspecs; 1304 struct fb_videomode *modedb = &specs->modedb[0]; 1305 1306 /* 1307 * Get preferred timing. If not found, 1308 * first mode in database will be used. 1309 */ 1310 if (specs->misc & FB_MISC_1ST_DETAIL) { 1311 int i; 1312 1313 for (i = 0; i < specs->modedb_len; i++) { 1314 if (specs->modedb[i].flag & FB_MODE_IS_FIRST) { 1315 modedb = &specs->modedb[i]; 1316 break; 1317 } 1318 } 1319 } 1320 1321 info->var.bits_per_pixel = default_bpp; 1322 fb_videomode_to_var(&info->var, modedb); 1323 } 1324 1325 if (fsl_diu_check_var(&info->var, info)) { 1326 dev_err(info->dev, "fsl_diu_check_var failed\n"); 1327 unmap_video_memory(info); 1328 fb_dealloc_cmap(&info->cmap); 1329 return -EINVAL; 1330 } 1331 1332 if (register_framebuffer(info) < 0) { 1333 dev_err(info->dev, "register_framebuffer failed\n"); 1334 unmap_video_memory(info); 1335 fb_dealloc_cmap(&info->cmap); 1336 return -EINVAL; 1337 } 1338 1339 mfbi->registered = 1; 1340 dev_info(info->dev, "%s registered successfully\n", mfbi->id); 1341 1342 return 0; 1343} 1344 1345static void uninstall_fb(struct fb_info *info) 1346{ 1347 struct mfb_info *mfbi = info->par; 1348 1349 if (!mfbi->registered) 1350 return; 1351 1352 if (mfbi->index == PLANE0) 1353 kfree(mfbi->edid_data); 1354 1355 unregister_framebuffer(info); 1356 unmap_video_memory(info); 1357 if (&info->cmap) 1358 fb_dealloc_cmap(&info->cmap); 1359 1360 mfbi->registered = 0; 1361} 1362 1363static irqreturn_t fsl_diu_isr(int irq, void *dev_id) 1364{ 1365 struct diu __iomem *hw = dev_id; 1366 unsigned int status = in_be32(&hw->int_status); 1367 1368 if (status) { 1369 /* This is the workaround for underrun */ 1370 if (status & INT_UNDRUN) { 1371 out_be32(&hw->diu_mode, 0); 1372 udelay(1); 1373 out_be32(&hw->diu_mode, 1); 1374 } 1375#if defined(CONFIG_NOT_COHERENT_CACHE) 1376 else if (status & INT_VSYNC) { 1377 unsigned int i; 1378 1379 for (i = 0; i < coherence_data_size; 1380 i += d_cache_line_size) 1381 __asm__ __volatile__ ( 1382 "dcbz 0, %[input]" 1383 ::[input]"r"(&coherence_data[i])); 1384 } 1385#endif 1386 return IRQ_HANDLED; 1387 } 1388 return IRQ_NONE; 1389} 1390 1391static int request_irq_local(struct fsl_diu_data *data) 1392{ 1393 struct diu __iomem *hw = data->diu_reg; 1394 u32 ints; 1395 int ret; 1396 1397 /* Read to clear the status */ 1398 in_be32(&hw->int_status); 1399 1400 ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb", hw); 1401 if (!ret) { 1402 ints = INT_PARERR | INT_LS_BF_VS; 1403#if !defined(CONFIG_NOT_COHERENT_CACHE) 1404 ints |= INT_VSYNC; 1405#endif 1406 1407 /* Read to clear the status */ 1408 in_be32(&hw->int_status); 1409 out_be32(&hw->int_mask, ints); 1410 } 1411 1412 return ret; 1413} 1414 1415static void free_irq_local(struct fsl_diu_data *data) 1416{ 1417 struct diu __iomem *hw = data->diu_reg; 1418 1419 /* Disable all LCDC interrupt */ 1420 out_be32(&hw->int_mask, 0x1f); 1421 1422 free_irq(data->irq, NULL); 1423} 1424 1425#ifdef CONFIG_PM 1426/* 1427 * Power management hooks. Note that we won't be called from IRQ context, 1428 * unlike the blank functions above, so we may sleep. 1429 */ 1430static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state) 1431{ 1432 struct fsl_diu_data *data; 1433 1434 data = dev_get_drvdata(&ofdev->dev); 1435 disable_lcdc(data->fsl_diu_info); 1436 1437 return 0; 1438} 1439 1440static int fsl_diu_resume(struct platform_device *ofdev) 1441{ 1442 struct fsl_diu_data *data; 1443 1444 data = dev_get_drvdata(&ofdev->dev); 1445 enable_lcdc(data->fsl_diu_info); 1446 1447 return 0; 1448} 1449 1450#else 1451#define fsl_diu_suspend NULL 1452#define fsl_diu_resume NULL 1453#endif /* CONFIG_PM */ 1454 1455static ssize_t store_monitor(struct device *device, 1456 struct device_attribute *attr, const char *buf, size_t count) 1457{ 1458 enum fsl_diu_monitor_port old_monitor_port; 1459 struct fsl_diu_data *data = 1460 container_of(attr, struct fsl_diu_data, dev_attr); 1461 1462 old_monitor_port = data->monitor_port; 1463 data->monitor_port = fsl_diu_name_to_port(buf); 1464 1465 if (old_monitor_port != data->monitor_port) { 1466 /* All AOIs need adjust pixel format 1467 * fsl_diu_set_par only change the pixsel format here 1468 * unlikely to fail. */ 1469 unsigned int i; 1470 1471 for (i=0; i < NUM_AOIS; i++) 1472 fsl_diu_set_par(&data->fsl_diu_info[i]); 1473 } 1474 return count; 1475} 1476 1477static ssize_t show_monitor(struct device *device, 1478 struct device_attribute *attr, char *buf) 1479{ 1480 struct fsl_diu_data *data = 1481 container_of(attr, struct fsl_diu_data, dev_attr); 1482 1483 switch (data->monitor_port) { 1484 case FSL_DIU_PORT_DVI: 1485 return sprintf(buf, "DVI\n"); 1486 case FSL_DIU_PORT_LVDS: 1487 return sprintf(buf, "Single-link LVDS\n"); 1488 case FSL_DIU_PORT_DLVDS: 1489 return sprintf(buf, "Dual-link LVDS\n"); 1490 } 1491 1492 return 0; 1493} 1494 1495static int __devinit fsl_diu_probe(struct platform_device *pdev) 1496{ 1497 struct device_node *np = pdev->dev.of_node; 1498 struct mfb_info *mfbi; 1499 struct fsl_diu_data *data; 1500 int diu_mode; 1501 dma_addr_t dma_addr; /* DMA addr of fsl_diu_data struct */ 1502 unsigned int i; 1503 int ret; 1504 1505 data = dma_alloc_coherent(&pdev->dev, sizeof(struct fsl_diu_data), 1506 &dma_addr, GFP_DMA | __GFP_ZERO); 1507 if (!data) 1508 return -ENOMEM; 1509 data->dma_addr = dma_addr; 1510 1511 /* 1512 * dma_alloc_coherent() uses a page allocator, so the address is 1513 * always page-aligned. We need the memory to be 32-byte aligned, 1514 * so that's good. However, if one day the allocator changes, we 1515 * need to catch that. It's not worth the effort to handle unaligned 1516 * alloctions now because it's highly unlikely to ever be a problem. 1517 */ 1518 if ((unsigned long)data & 31) { 1519 dev_err(&pdev->dev, "misaligned allocation"); 1520 ret = -ENOMEM; 1521 goto error; 1522 } 1523 1524 spin_lock_init(&data->reg_lock); 1525 1526 for (i = 0; i < NUM_AOIS; i++) { 1527 struct fb_info *info = &data->fsl_diu_info[i]; 1528 1529 info->device = &pdev->dev; 1530 info->par = &data->mfb[i]; 1531 1532 /* 1533 * We store the physical address of the AD in the reserved 1534 * 'paddr' field of the AD itself. 1535 */ 1536 data->ad[i].paddr = DMA_ADDR(data, ad[i]); 1537 1538 info->fix.smem_start = 0; 1539 1540 /* Initialize the AOI data structure */ 1541 mfbi = info->par; 1542 memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info)); 1543 mfbi->parent = data; 1544 mfbi->ad = &data->ad[i]; 1545 1546 if (mfbi->index == PLANE0) { 1547 const u8 *prop; 1548 int len; 1549 1550 /* Get EDID */ 1551 prop = of_get_property(np, "edid", &len); 1552 if (prop && len == EDID_LENGTH) 1553 mfbi->edid_data = kmemdup(prop, EDID_LENGTH, 1554 GFP_KERNEL); 1555 } 1556 } 1557 1558 data->diu_reg = of_iomap(np, 0); 1559 if (!data->diu_reg) { 1560 dev_err(&pdev->dev, "cannot map DIU registers\n"); 1561 ret = -EFAULT; 1562 goto error; 1563 } 1564 1565 diu_mode = in_be32(&data->diu_reg->diu_mode); 1566 if (diu_mode == MFB_MODE0) 1567 out_be32(&data->diu_reg->diu_mode, 0); /* disable DIU */ 1568 1569 /* Get the IRQ of the DIU */ 1570 data->irq = irq_of_parse_and_map(np, 0); 1571 1572 if (!data->irq) { 1573 dev_err(&pdev->dev, "could not get DIU IRQ\n"); 1574 ret = -EINVAL; 1575 goto error; 1576 } 1577 data->monitor_port = monitor_port; 1578 1579 /* Initialize the dummy Area Descriptor */ 1580 data->dummy_ad.addr = cpu_to_le32(DMA_ADDR(data, dummy_aoi)); 1581 data->dummy_ad.pix_fmt = 0x88882317; 1582 data->dummy_ad.src_size_g_alpha = cpu_to_le32((4 << 12) | 4); 1583 data->dummy_ad.aoi_size = cpu_to_le32((4 << 16) | 2); 1584 data->dummy_ad.offset_xyi = 0; 1585 data->dummy_ad.offset_xyd = 0; 1586 data->dummy_ad.next_ad = 0; 1587 data->dummy_ad.paddr = DMA_ADDR(data, dummy_ad); 1588 1589 /* 1590 * Let DIU display splash screen if it was pre-initialized 1591 * by the bootloader, set dummy area descriptor otherwise. 1592 */ 1593 if (diu_mode == MFB_MODE0) 1594 out_be32(&data->diu_reg->desc[0], data->dummy_ad.paddr); 1595 1596 out_be32(&data->diu_reg->desc[1], data->dummy_ad.paddr); 1597 out_be32(&data->diu_reg->desc[2], data->dummy_ad.paddr); 1598 1599 for (i = 0; i < NUM_AOIS; i++) { 1600 ret = install_fb(&data->fsl_diu_info[i]); 1601 if (ret) { 1602 dev_err(&pdev->dev, "could not register fb %d\n", i); 1603 goto error; 1604 } 1605 } 1606 1607 if (request_irq_local(data)) { 1608 dev_err(&pdev->dev, "could not claim irq\n"); 1609 goto error; 1610 } 1611 1612 sysfs_attr_init(&data->dev_attr.attr); 1613 data->dev_attr.attr.name = "monitor"; 1614 data->dev_attr.attr.mode = S_IRUGO|S_IWUSR; 1615 data->dev_attr.show = show_monitor; 1616 data->dev_attr.store = store_monitor; 1617 ret = device_create_file(&pdev->dev, &data->dev_attr); 1618 if (ret) { 1619 dev_err(&pdev->dev, "could not create sysfs file %s\n", 1620 data->dev_attr.attr.name); 1621 } 1622 1623 dev_set_drvdata(&pdev->dev, data); 1624 return 0; 1625 1626error: 1627 for (i = 0; i < NUM_AOIS; i++) 1628 uninstall_fb(&data->fsl_diu_info[i]); 1629 1630 iounmap(data->diu_reg); 1631 1632 dma_free_coherent(&pdev->dev, sizeof(struct fsl_diu_data), data, 1633 data->dma_addr); 1634 1635 return ret; 1636} 1637 1638static int fsl_diu_remove(struct platform_device *pdev) 1639{ 1640 struct fsl_diu_data *data; 1641 int i; 1642 1643 data = dev_get_drvdata(&pdev->dev); 1644 disable_lcdc(&data->fsl_diu_info[0]); 1645 free_irq_local(data); 1646 1647 for (i = 0; i < NUM_AOIS; i++) 1648 uninstall_fb(&data->fsl_diu_info[i]); 1649 1650 iounmap(data->diu_reg); 1651 1652 dma_free_coherent(&pdev->dev, sizeof(struct fsl_diu_data), data, 1653 data->dma_addr); 1654 1655 return 0; 1656} 1657 1658#ifndef MODULE 1659static int __init fsl_diu_setup(char *options) 1660{ 1661 char *opt; 1662 unsigned long val; 1663 1664 if (!options || !*options) 1665 return 0; 1666 1667 while ((opt = strsep(&options, ",")) != NULL) { 1668 if (!*opt) 1669 continue; 1670 if (!strncmp(opt, "monitor=", 8)) { 1671 monitor_port = fsl_diu_name_to_port(opt + 8); 1672 } else if (!strncmp(opt, "bpp=", 4)) { 1673 if (!strict_strtoul(opt + 4, 10, &val)) 1674 default_bpp = val; 1675 } else 1676 fb_mode = opt; 1677 } 1678 1679 return 0; 1680} 1681#endif 1682 1683static struct of_device_id fsl_diu_match[] = { 1684#ifdef CONFIG_PPC_MPC512x 1685 { 1686 .compatible = "fsl,mpc5121-diu", 1687 }, 1688#endif 1689 { 1690 .compatible = "fsl,diu", 1691 }, 1692 {} 1693}; 1694MODULE_DEVICE_TABLE(of, fsl_diu_match); 1695 1696static struct platform_driver fsl_diu_driver = { 1697 .driver = { 1698 .name = "fsl-diu-fb", 1699 .owner = THIS_MODULE, 1700 .of_match_table = fsl_diu_match, 1701 }, 1702 .probe = fsl_diu_probe, 1703 .remove = fsl_diu_remove, 1704 .suspend = fsl_diu_suspend, 1705 .resume = fsl_diu_resume, 1706}; 1707 1708static int __init fsl_diu_init(void) 1709{ 1710#ifdef CONFIG_NOT_COHERENT_CACHE 1711 struct device_node *np; 1712 const u32 *prop; 1713#endif 1714 int ret; 1715#ifndef MODULE 1716 char *option; 1717 1718 /* 1719 * For kernel boot options (in 'video=xxxfb:<options>' format) 1720 */ 1721 if (fb_get_options("fslfb", &option)) 1722 return -ENODEV; 1723 fsl_diu_setup(option); 1724#else 1725 monitor_port = fsl_diu_name_to_port(monitor_string); 1726#endif 1727 pr_info("Freescale Display Interface Unit (DIU) framebuffer driver\n"); 1728 1729#ifdef CONFIG_NOT_COHERENT_CACHE 1730 np = of_find_node_by_type(NULL, "cpu"); 1731 if (!np) { 1732 pr_err("fsl-diu-fb: can't find 'cpu' device node\n"); 1733 return -ENODEV; 1734 } 1735 1736 prop = of_get_property(np, "d-cache-size", NULL); 1737 if (prop == NULL) { 1738 pr_err("fsl-diu-fb: missing 'd-cache-size' property' " 1739 "in 'cpu' node\n"); 1740 of_node_put(np); 1741 return -ENODEV; 1742 } 1743 1744 /* 1745 * Freescale PLRU requires 13/8 times the cache size to do a proper 1746 * displacement flush 1747 */ 1748 coherence_data_size = be32_to_cpup(prop) * 13; 1749 coherence_data_size /= 8; 1750 1751 prop = of_get_property(np, "d-cache-line-size", NULL); 1752 if (prop == NULL) { 1753 pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' " 1754 "in 'cpu' node\n"); 1755 of_node_put(np); 1756 return -ENODEV; 1757 } 1758 d_cache_line_size = be32_to_cpup(prop); 1759 1760 of_node_put(np); 1761 coherence_data = vmalloc(coherence_data_size); 1762 if (!coherence_data) 1763 return -ENOMEM; 1764#endif 1765 1766 ret = platform_driver_register(&fsl_diu_driver); 1767 if (ret) { 1768 pr_err("fsl-diu-fb: failed to register platform driver\n"); 1769#if defined(CONFIG_NOT_COHERENT_CACHE) 1770 vfree(coherence_data); 1771#endif 1772 } 1773 return ret; 1774} 1775 1776static void __exit fsl_diu_exit(void) 1777{ 1778 platform_driver_unregister(&fsl_diu_driver); 1779#if defined(CONFIG_NOT_COHERENT_CACHE) 1780 vfree(coherence_data); 1781#endif 1782} 1783 1784module_init(fsl_diu_init); 1785module_exit(fsl_diu_exit); 1786 1787MODULE_AUTHOR("York Sun <yorksun@freescale.com>"); 1788MODULE_DESCRIPTION("Freescale DIU framebuffer driver"); 1789MODULE_LICENSE("GPL"); 1790 1791module_param_named(mode, fb_mode, charp, 0); 1792MODULE_PARM_DESC(mode, 1793 "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" "); 1794module_param_named(bpp, default_bpp, ulong, 0); 1795MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified in 'mode'"); 1796module_param_named(monitor, monitor_string, charp, 0); 1797MODULE_PARM_DESC(monitor, "Specify the monitor port " 1798 "(\"dvi\", \"lvds\", or \"dlvds\") if supported by the platform"); 1799