linux/drivers/video/fsl-diu-fb.c
<<
>>
Prefs
   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        /* It's easier to parse the cursor data as little-endian */
 379        __le16 cursor[MAX_CURS * MAX_CURS] __aligned(32);
 380        /* Blank cursor data -- used to hide the cursor */
 381        __le16 blank_cursor[MAX_CURS * MAX_CURS] __aligned(32);
 382        uint8_t edid_data[EDID_LENGTH];
 383        bool has_edid;
 384} __aligned(32);
 385
 386/* Determine the DMA address of a member of the fsl_diu_data structure */
 387#define DMA_ADDR(p, f) ((p)->dma_addr + offsetof(struct fsl_diu_data, f))
 388
 389static struct mfb_info mfb_template[] = {
 390        {
 391                .index = PLANE0,
 392                .id = "Panel0",
 393                .registered = 0,
 394                .count = 0,
 395                .x_aoi_d = 0,
 396                .y_aoi_d = 0,
 397        },
 398        {
 399                .index = PLANE1_AOI0,
 400                .id = "Panel1 AOI0",
 401                .registered = 0,
 402                .g_alpha = 0xff,
 403                .count = 0,
 404                .x_aoi_d = 0,
 405                .y_aoi_d = 0,
 406        },
 407        {
 408                .index = PLANE1_AOI1,
 409                .id = "Panel1 AOI1",
 410                .registered = 0,
 411                .g_alpha = 0xff,
 412                .count = 0,
 413                .x_aoi_d = 0,
 414                .y_aoi_d = 480,
 415        },
 416        {
 417                .index = PLANE2_AOI0,
 418                .id = "Panel2 AOI0",
 419                .registered = 0,
 420                .g_alpha = 0xff,
 421                .count = 0,
 422                .x_aoi_d = 640,
 423                .y_aoi_d = 0,
 424        },
 425        {
 426                .index = PLANE2_AOI1,
 427                .id = "Panel2 AOI1",
 428                .registered = 0,
 429                .g_alpha = 0xff,
 430                .count = 0,
 431                .x_aoi_d = 640,
 432                .y_aoi_d = 480,
 433        },
 434};
 435
 436#ifdef DEBUG
 437static void __attribute__ ((unused)) fsl_diu_dump(struct diu __iomem *hw)
 438{
 439        mb();
 440        pr_debug("DIU: desc=%08x,%08x,%08x, gamma=%08x pallete=%08x "
 441                 "cursor=%08x curs_pos=%08x diu_mode=%08x bgnd=%08x "
 442                 "disp_size=%08x hsyn_para=%08x vsyn_para=%08x syn_pol=%08x "
 443                 "thresholds=%08x int_mask=%08x plut=%08x\n",
 444                 hw->desc[0], hw->desc[1], hw->desc[2], hw->gamma,
 445                 hw->pallete, hw->cursor, hw->curs_pos, hw->diu_mode,
 446                 hw->bgnd, hw->disp_size, hw->hsyn_para, hw->vsyn_para,
 447                 hw->syn_pol, hw->thresholds, hw->int_mask, hw->plut);
 448        rmb();
 449}
 450#endif
 451
 452/**
 453 * fsl_diu_name_to_port - convert a port name to a monitor port enum
 454 *
 455 * Takes the name of a monitor port ("dvi", "lvds", or "dlvds") and returns
 456 * the enum fsl_diu_monitor_port that corresponds to that string.
 457 *
 458 * For compatibility with older versions, a number ("0", "1", or "2") is also
 459 * supported.
 460 *
 461 * If the string is unknown, DVI is assumed.
 462 *
 463 * If the particular port is not supported by the platform, another port
 464 * (platform-specific) is chosen instead.
 465 */
 466static enum fsl_diu_monitor_port fsl_diu_name_to_port(const char *s)
 467{
 468        enum fsl_diu_monitor_port port = FSL_DIU_PORT_DVI;
 469        unsigned long val;
 470
 471        if (s) {
 472                if (!kstrtoul(s, 10, &val) && (val <= 2))
 473                        port = (enum fsl_diu_monitor_port) val;
 474                else if (strncmp(s, "lvds", 4) == 0)
 475                        port = FSL_DIU_PORT_LVDS;
 476                else if (strncmp(s, "dlvds", 5) == 0)
 477                        port = FSL_DIU_PORT_DLVDS;
 478        }
 479
 480        return diu_ops.valid_monitor_port(port);
 481}
 482
 483/*
 484 * Workaround for failed writing desc register of planes.
 485 * Needed with MPC5121 DIU rev 2.0 silicon.
 486 */
 487void wr_reg_wa(u32 *reg, u32 val)
 488{
 489        do {
 490                out_be32(reg, val);
 491        } while (in_be32(reg) != val);
 492}
 493
 494static void fsl_diu_enable_panel(struct fb_info *info)
 495{
 496        struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
 497        struct diu_ad *ad = mfbi->ad;
 498        struct fsl_diu_data *data = mfbi->parent;
 499        struct diu __iomem *hw = data->diu_reg;
 500
 501        switch (mfbi->index) {
 502        case PLANE0:
 503                wr_reg_wa(&hw->desc[0], ad->paddr);
 504                break;
 505        case PLANE1_AOI0:
 506                cmfbi = &data->mfb[2];
 507                if (hw->desc[1] != ad->paddr) { /* AOI0 closed */
 508                        if (cmfbi->count > 0)   /* AOI1 open */
 509                                ad->next_ad =
 510                                        cpu_to_le32(cmfbi->ad->paddr);
 511                        else
 512                                ad->next_ad = 0;
 513                        wr_reg_wa(&hw->desc[1], ad->paddr);
 514                }
 515                break;
 516        case PLANE2_AOI0:
 517                cmfbi = &data->mfb[4];
 518                if (hw->desc[2] != ad->paddr) { /* AOI0 closed */
 519                        if (cmfbi->count > 0)   /* AOI1 open */
 520                                ad->next_ad =
 521                                        cpu_to_le32(cmfbi->ad->paddr);
 522                        else
 523                                ad->next_ad = 0;
 524                        wr_reg_wa(&hw->desc[2], ad->paddr);
 525                }
 526                break;
 527        case PLANE1_AOI1:
 528                pmfbi = &data->mfb[1];
 529                ad->next_ad = 0;
 530                if (hw->desc[1] == data->dummy_ad.paddr)
 531                        wr_reg_wa(&hw->desc[1], ad->paddr);
 532                else                                    /* AOI0 open */
 533                        pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
 534                break;
 535        case PLANE2_AOI1:
 536                pmfbi = &data->mfb[3];
 537                ad->next_ad = 0;
 538                if (hw->desc[2] == data->dummy_ad.paddr)
 539                        wr_reg_wa(&hw->desc[2], ad->paddr);
 540                else                            /* AOI0 was open */
 541                        pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
 542                break;
 543        }
 544}
 545
 546static void fsl_diu_disable_panel(struct fb_info *info)
 547{
 548        struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
 549        struct diu_ad *ad = mfbi->ad;
 550        struct fsl_diu_data *data = mfbi->parent;
 551        struct diu __iomem *hw = data->diu_reg;
 552
 553        switch (mfbi->index) {
 554        case PLANE0:
 555                wr_reg_wa(&hw->desc[0], 0);
 556                break;
 557        case PLANE1_AOI0:
 558                cmfbi = &data->mfb[2];
 559                if (cmfbi->count > 0)   /* AOI1 is open */
 560                        wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr);
 561                                        /* move AOI1 to the first */
 562                else                    /* AOI1 was closed */
 563                        wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr);
 564                                        /* close AOI 0 */
 565                break;
 566        case PLANE2_AOI0:
 567                cmfbi = &data->mfb[4];
 568                if (cmfbi->count > 0)   /* AOI1 is open */
 569                        wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr);
 570                                        /* move AOI1 to the first */
 571                else                    /* AOI1 was closed */
 572                        wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr);
 573                                        /* close AOI 0 */
 574                break;
 575        case PLANE1_AOI1:
 576                pmfbi = &data->mfb[1];
 577                if (hw->desc[1] != ad->paddr) {
 578                                /* AOI1 is not the first in the chain */
 579                        if (pmfbi->count > 0)
 580                                        /* AOI0 is open, must be the first */
 581                                pmfbi->ad->next_ad = 0;
 582                } else                  /* AOI1 is the first in the chain */
 583                        wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr);
 584                                        /* close AOI 1 */
 585                break;
 586        case PLANE2_AOI1:
 587                pmfbi = &data->mfb[3];
 588                if (hw->desc[2] != ad->paddr) {
 589                                /* AOI1 is not the first in the chain */
 590                        if (pmfbi->count > 0)
 591                                /* AOI0 is open, must be the first */
 592                                pmfbi->ad->next_ad = 0;
 593                } else          /* AOI1 is the first in the chain */
 594                        wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr);
 595                                /* close AOI 1 */
 596                break;
 597        }
 598}
 599
 600static void enable_lcdc(struct fb_info *info)
 601{
 602        struct mfb_info *mfbi = info->par;
 603        struct fsl_diu_data *data = mfbi->parent;
 604        struct diu __iomem *hw = data->diu_reg;
 605
 606        out_be32(&hw->diu_mode, MFB_MODE1);
 607}
 608
 609static void disable_lcdc(struct fb_info *info)
 610{
 611        struct mfb_info *mfbi = info->par;
 612        struct fsl_diu_data *data = mfbi->parent;
 613        struct diu __iomem *hw = data->diu_reg;
 614
 615        out_be32(&hw->diu_mode, 0);
 616}
 617
 618static void adjust_aoi_size_position(struct fb_var_screeninfo *var,
 619                                struct fb_info *info)
 620{
 621        struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par;
 622        struct fsl_diu_data *data = mfbi->parent;
 623        int available_height, upper_aoi_bottom;
 624        enum mfb_index index = mfbi->index;
 625        int lower_aoi_is_open, upper_aoi_is_open;
 626        __u32 base_plane_width, base_plane_height, upper_aoi_height;
 627
 628        base_plane_width = data->fsl_diu_info[0].var.xres;
 629        base_plane_height = data->fsl_diu_info[0].var.yres;
 630
 631        if (mfbi->x_aoi_d < 0)
 632                mfbi->x_aoi_d = 0;
 633        if (mfbi->y_aoi_d < 0)
 634                mfbi->y_aoi_d = 0;
 635        switch (index) {
 636        case PLANE0:
 637                if (mfbi->x_aoi_d != 0)
 638                        mfbi->x_aoi_d = 0;
 639                if (mfbi->y_aoi_d != 0)
 640                        mfbi->y_aoi_d = 0;
 641                break;
 642        case PLANE1_AOI0:
 643        case PLANE2_AOI0:
 644                lower_aoi_mfbi = data->fsl_diu_info[index+1].par;
 645                lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0;
 646                if (var->xres > base_plane_width)
 647                        var->xres = base_plane_width;
 648                if ((mfbi->x_aoi_d + var->xres) > base_plane_width)
 649                        mfbi->x_aoi_d = base_plane_width - var->xres;
 650
 651                if (lower_aoi_is_open)
 652                        available_height = lower_aoi_mfbi->y_aoi_d;
 653                else
 654                        available_height = base_plane_height;
 655                if (var->yres > available_height)
 656                        var->yres = available_height;
 657                if ((mfbi->y_aoi_d + var->yres) > available_height)
 658                        mfbi->y_aoi_d = available_height - var->yres;
 659                break;
 660        case PLANE1_AOI1:
 661        case PLANE2_AOI1:
 662                upper_aoi_mfbi = data->fsl_diu_info[index-1].par;
 663                upper_aoi_height = data->fsl_diu_info[index-1].var.yres;
 664                upper_aoi_bottom = upper_aoi_mfbi->y_aoi_d + upper_aoi_height;
 665                upper_aoi_is_open = upper_aoi_mfbi->count > 0 ? 1 : 0;
 666                if (var->xres > base_plane_width)
 667                        var->xres = base_plane_width;
 668                if ((mfbi->x_aoi_d + var->xres) > base_plane_width)
 669                        mfbi->x_aoi_d = base_plane_width - var->xres;
 670                if (mfbi->y_aoi_d < 0)
 671                        mfbi->y_aoi_d = 0;
 672                if (upper_aoi_is_open) {
 673                        if (mfbi->y_aoi_d < upper_aoi_bottom)
 674                                mfbi->y_aoi_d = upper_aoi_bottom;
 675                        available_height = base_plane_height
 676                                                - upper_aoi_bottom;
 677                } else
 678                        available_height = base_plane_height;
 679                if (var->yres > available_height)
 680                        var->yres = available_height;
 681                if ((mfbi->y_aoi_d + var->yres) > base_plane_height)
 682                        mfbi->y_aoi_d = base_plane_height - var->yres;
 683                break;
 684        }
 685}
 686/*
 687 * Checks to see if the hardware supports the state requested by var passed
 688 * in. This function does not alter the hardware state! If the var passed in
 689 * is slightly off by what the hardware can support then we alter the var
 690 * PASSED in to what we can do. If the hardware doesn't support mode change
 691 * a -EINVAL will be returned by the upper layers.
 692 */
 693static int fsl_diu_check_var(struct fb_var_screeninfo *var,
 694                                struct fb_info *info)
 695{
 696        if (var->xres_virtual < var->xres)
 697                var->xres_virtual = var->xres;
 698        if (var->yres_virtual < var->yres)
 699                var->yres_virtual = var->yres;
 700
 701        if (var->xoffset < 0)
 702                var->xoffset = 0;
 703
 704        if (var->yoffset < 0)
 705                var->yoffset = 0;
 706
 707        if (var->xoffset + info->var.xres > info->var.xres_virtual)
 708                var->xoffset = info->var.xres_virtual - info->var.xres;
 709
 710        if (var->yoffset + info->var.yres > info->var.yres_virtual)
 711                var->yoffset = info->var.yres_virtual - info->var.yres;
 712
 713        if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
 714            (var->bits_per_pixel != 16))
 715                var->bits_per_pixel = default_bpp;
 716
 717        switch (var->bits_per_pixel) {
 718        case 16:
 719                var->red.length = 5;
 720                var->red.offset = 11;
 721                var->red.msb_right = 0;
 722
 723                var->green.length = 6;
 724                var->green.offset = 5;
 725                var->green.msb_right = 0;
 726
 727                var->blue.length = 5;
 728                var->blue.offset = 0;
 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 24:
 736                var->red.length = 8;
 737                var->red.offset = 0;
 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 = 16;
 746                var->blue.msb_right = 0;
 747
 748                var->transp.length = 0;
 749                var->transp.offset = 0;
 750                var->transp.msb_right = 0;
 751                break;
 752        case 32:
 753                var->red.length = 8;
 754                var->red.offset = 16;
 755                var->red.msb_right = 0;
 756
 757                var->green.length = 8;
 758                var->green.offset = 8;
 759                var->green.msb_right = 0;
 760
 761                var->blue.length = 8;
 762                var->blue.offset = 0;
 763                var->blue.msb_right = 0;
 764
 765                var->transp.length = 8;
 766                var->transp.offset = 24;
 767                var->transp.msb_right = 0;
 768
 769                break;
 770        }
 771
 772        var->height = -1;
 773        var->width = -1;
 774        var->grayscale = 0;
 775
 776        /* Copy nonstd field to/from sync for fbset usage */
 777        var->sync |= var->nonstd;
 778        var->nonstd |= var->sync;
 779
 780        adjust_aoi_size_position(var, info);
 781        return 0;
 782}
 783
 784static void set_fix(struct fb_info *info)
 785{
 786        struct fb_fix_screeninfo *fix = &info->fix;
 787        struct fb_var_screeninfo *var = &info->var;
 788        struct mfb_info *mfbi = info->par;
 789
 790        strncpy(fix->id, mfbi->id, sizeof(fix->id));
 791        fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
 792        fix->type = FB_TYPE_PACKED_PIXELS;
 793        fix->accel = FB_ACCEL_NONE;
 794        fix->visual = FB_VISUAL_TRUECOLOR;
 795        fix->xpanstep = 1;
 796        fix->ypanstep = 1;
 797}
 798
 799static void update_lcdc(struct fb_info *info)
 800{
 801        struct fb_var_screeninfo *var = &info->var;
 802        struct mfb_info *mfbi = info->par;
 803        struct fsl_diu_data *data = mfbi->parent;
 804        struct diu __iomem *hw;
 805        int i, j;
 806        u8 *gamma_table_base;
 807
 808        u32 temp;
 809
 810        hw = data->diu_reg;
 811
 812        if (diu_ops.set_monitor_port)
 813                diu_ops.set_monitor_port(data->monitor_port);
 814        gamma_table_base = data->gamma;
 815
 816        /* Prep for DIU init  - gamma table, cursor table */
 817
 818        for (i = 0; i <= 2; i++)
 819                for (j = 0; j <= 255; j++)
 820                        *gamma_table_base++ = j;
 821
 822        if (diu_ops.set_gamma_table)
 823                diu_ops.set_gamma_table(data->monitor_port, data->gamma);
 824
 825        disable_lcdc(info);
 826
 827        /* Program DIU registers */
 828
 829        out_be32(&hw->gamma, DMA_ADDR(data, gamma));
 830
 831        out_be32(&hw->bgnd, 0x007F7F7F); /* Set background to grey */
 832        out_be32(&hw->disp_size, (var->yres << 16) | var->xres);
 833
 834        /* Horizontal and vertical configuration register */
 835        temp = var->left_margin << 22 | /* BP_H */
 836               var->hsync_len << 11 |   /* PW_H */
 837               var->right_margin;       /* FP_H */
 838
 839        out_be32(&hw->hsyn_para, temp);
 840
 841        temp = var->upper_margin << 22 | /* BP_V */
 842               var->vsync_len << 11 |    /* PW_V  */
 843               var->lower_margin;        /* FP_V  */
 844
 845        out_be32(&hw->vsyn_para, temp);
 846
 847        diu_ops.set_pixel_clock(var->pixclock);
 848
 849#ifndef CONFIG_PPC_MPC512x
 850        /*
 851         * The PLUT register is defined differently on the MPC5121 than it
 852         * is on other SOCs.  Unfortunately, there's no documentation that
 853         * explains how it's supposed to be programmed, so for now, we leave
 854         * it at the default value on the MPC5121.
 855         *
 856         * For other SOCs, program it for the highest priority, which will
 857         * reduce the chance of underrun. Technically, we should scale the
 858         * priority to match the screen resolution, but doing that properly
 859         * requires delicate fine-tuning for each use-case.
 860         */
 861        out_be32(&hw->plut, 0x01F5F666);
 862#endif
 863
 864        /* Enable the DIU */
 865        enable_lcdc(info);
 866}
 867
 868static int map_video_memory(struct fb_info *info)
 869{
 870        u32 smem_len = info->fix.line_length * info->var.yres_virtual;
 871        void *p;
 872
 873        p = alloc_pages_exact(smem_len, GFP_DMA | __GFP_ZERO);
 874        if (!p) {
 875                dev_err(info->dev, "unable to allocate fb memory\n");
 876                return -ENOMEM;
 877        }
 878        mutex_lock(&info->mm_lock);
 879        info->screen_base = p;
 880        info->fix.smem_start = virt_to_phys(info->screen_base);
 881        info->fix.smem_len = smem_len;
 882        mutex_unlock(&info->mm_lock);
 883        info->screen_size = info->fix.smem_len;
 884
 885        return 0;
 886}
 887
 888static void unmap_video_memory(struct fb_info *info)
 889{
 890        void *p = info->screen_base;
 891        size_t l = info->fix.smem_len;
 892
 893        mutex_lock(&info->mm_lock);
 894        info->screen_base = NULL;
 895        info->fix.smem_start = 0;
 896        info->fix.smem_len = 0;
 897        mutex_unlock(&info->mm_lock);
 898
 899        if (p)
 900                free_pages_exact(p, l);
 901}
 902
 903/*
 904 * Using the fb_var_screeninfo in fb_info we set the aoi of this
 905 * particular framebuffer. It is a light version of fsl_diu_set_par.
 906 */
 907static int fsl_diu_set_aoi(struct fb_info *info)
 908{
 909        struct fb_var_screeninfo *var = &info->var;
 910        struct mfb_info *mfbi = info->par;
 911        struct diu_ad *ad = mfbi->ad;
 912
 913        /* AOI should not be greater than display size */
 914        ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset);
 915        ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);
 916        return 0;
 917}
 918
 919/**
 920 * fsl_diu_get_pixel_format: return the pixel format for a given color depth
 921 *
 922 * The pixel format is a 32-bit value that determine which bits in each
 923 * pixel are to be used for each color.  This is the default function used
 924 * if the platform does not define its own version.
 925 */
 926static u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel)
 927{
 928#define PF_BYTE_F               0x10000000
 929#define PF_ALPHA_C_MASK         0x0E000000
 930#define PF_ALPHA_C_SHIFT        25
 931#define PF_BLUE_C_MASK          0x01800000
 932#define PF_BLUE_C_SHIFT         23
 933#define PF_GREEN_C_MASK         0x00600000
 934#define PF_GREEN_C_SHIFT        21
 935#define PF_RED_C_MASK           0x00180000
 936#define PF_RED_C_SHIFT          19
 937#define PF_PALETTE              0x00040000
 938#define PF_PIXEL_S_MASK         0x00030000
 939#define PF_PIXEL_S_SHIFT        16
 940#define PF_COMP_3_MASK          0x0000F000
 941#define PF_COMP_3_SHIFT         12
 942#define PF_COMP_2_MASK          0x00000F00
 943#define PF_COMP_2_SHIFT         8
 944#define PF_COMP_1_MASK          0x000000F0
 945#define PF_COMP_1_SHIFT         4
 946#define PF_COMP_0_MASK          0x0000000F
 947#define PF_COMP_0_SHIFT         0
 948
 949#define MAKE_PF(alpha, red, green, blue, size, c0, c1, c2, c3) \
 950        cpu_to_le32(PF_BYTE_F | (alpha << PF_ALPHA_C_SHIFT) | \
 951        (blue << PF_BLUE_C_SHIFT) | (green << PF_GREEN_C_SHIFT) | \
 952        (red << PF_RED_C_SHIFT) | (c3 << PF_COMP_3_SHIFT) | \
 953        (c2 << PF_COMP_2_SHIFT) | (c1 << PF_COMP_1_SHIFT) | \
 954        (c0 << PF_COMP_0_SHIFT) | (size << PF_PIXEL_S_SHIFT))
 955
 956        switch (bits_per_pixel) {
 957        case 32:
 958                /* 0x88883316 */
 959                return MAKE_PF(3, 2, 1, 0, 3, 8, 8, 8, 8);
 960        case 24:
 961                /* 0x88082219 */
 962                return MAKE_PF(4, 0, 1, 2, 2, 8, 8, 8, 0);
 963        case 16:
 964                /* 0x65053118 */
 965                return MAKE_PF(4, 2, 1, 0, 1, 5, 6, 5, 0);
 966        default:
 967                pr_err("fsl-diu: unsupported color depth %u\n", bits_per_pixel);
 968                return 0;
 969        }
 970}
 971
 972/*
 973 * Copies a cursor image from user space to the proper place in driver
 974 * memory so that the hardware can display the cursor image.
 975 *
 976 * Cursor data is represented as a sequence of 'width' bits packed into bytes.
 977 * That is, the first 8 bits are in the first byte, the second 8 bits in the
 978 * second byte, and so on.  Therefore, the each row of the cursor is (width +
 979 * 7) / 8 bytes of 'data'
 980 *
 981 * The DIU only supports cursors up to 32x32 (MAX_CURS).  We reject cursors
 982 * larger than this, so we already know that 'width' <= 32.  Therefore, we can
 983 * simplify our code by using a 32-bit big-endian integer ("line") to read in
 984 * a single line of pixels, and only look at the top 'width' bits of that
 985 * integer.
 986 *
 987 * This could result in an unaligned 32-bit read.  For example, if the cursor
 988 * is 24x24, then the first three bytes of 'image' contain the pixel data for
 989 * the top line of the cursor.  We do a 32-bit read of 'image', but we look
 990 * only at the top 24 bits.  Then we increment 'image' by 3 bytes.  The next
 991 * read is unaligned.  The only problem is that we might read past the end of
 992 * 'image' by 1-3 bytes, but that should not cause any problems.
 993 */
 994static void fsl_diu_load_cursor_image(struct fb_info *info,
 995        const void *image, uint16_t bg, uint16_t fg,
 996        unsigned int width, unsigned int height)
 997{
 998        struct mfb_info *mfbi = info->par;
 999        struct fsl_diu_data *data = mfbi->parent;
1000        __le16 *cursor = data->cursor;
1001        __le16 _fg = cpu_to_le16(fg);
1002        __le16 _bg = cpu_to_le16(bg);
1003        unsigned int h, w;
1004
1005        for (h = 0; h < height; h++) {
1006                uint32_t mask = 1 << 31;
1007                uint32_t line = be32_to_cpup(image);
1008
1009                for (w = 0; w < width; w++) {
1010                        cursor[w] = (line & mask) ? _fg : _bg;
1011                        mask >>= 1;
1012                }
1013
1014                cursor += MAX_CURS;
1015                image += DIV_ROUND_UP(width, 8);
1016        }
1017}
1018
1019/*
1020 * Set a hardware cursor.  The image data for the cursor is passed via the
1021 * fb_cursor object.
1022 */
1023static int fsl_diu_cursor(struct fb_info *info, struct fb_cursor *cursor)
1024{
1025        struct mfb_info *mfbi = info->par;
1026        struct fsl_diu_data *data = mfbi->parent;
1027        struct diu __iomem *hw = data->diu_reg;
1028
1029        if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS)
1030                return -EINVAL;
1031
1032        /* The cursor size has changed */
1033        if (cursor->set & FB_CUR_SETSIZE) {
1034                /*
1035                 * The DIU cursor is a fixed size, so when we get this
1036                 * message, instead of resizing the cursor, we just clear
1037                 * all the image data, in expectation of new data.  However,
1038                 * in tests this control does not appear to be normally
1039                 * called.
1040                 */
1041                memset(data->cursor, 0, sizeof(data->cursor));
1042        }
1043
1044        /* The cursor position has changed (cursor->image.dx|dy) */
1045        if (cursor->set & FB_CUR_SETPOS) {
1046                uint32_t xx, yy;
1047
1048                yy = (cursor->image.dy - info->var.yoffset) & 0x7ff;
1049                xx = (cursor->image.dx - info->var.xoffset) & 0x7ff;
1050
1051                out_be32(&hw->curs_pos, yy << 16 | xx);
1052        }
1053
1054        /*
1055         * FB_CUR_SETIMAGE - the cursor image has changed
1056         * FB_CUR_SETCMAP  - the cursor colors has changed
1057         * FB_CUR_SETSHAPE - the cursor bitmask has changed
1058         */
1059        if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) {
1060                unsigned int image_size =
1061                        DIV_ROUND_UP(cursor->image.width, 8) * cursor->image.height;
1062                unsigned int image_words =
1063                        DIV_ROUND_UP(image_size, sizeof(uint32_t));
1064                unsigned int bg_idx = cursor->image.bg_color;
1065                unsigned int fg_idx = cursor->image.fg_color;
1066                uint8_t buffer[image_size];
1067                uint32_t *image, *source, *mask;
1068                uint16_t fg, bg;
1069                unsigned int i;
1070
1071                if (info->state != FBINFO_STATE_RUNNING)
1072                        return 0;
1073
1074                /*
1075                 * Determine the size of the cursor image data.  Normally,
1076                 * it's 8x16.
1077                 */
1078                image_size = DIV_ROUND_UP(cursor->image.width, 8) *
1079                        cursor->image.height;
1080
1081                bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) |
1082                     ((info->cmap.green[bg_idx] & 0xf8) << 2) |
1083                     ((info->cmap.blue[bg_idx] & 0xf8) >> 3) |
1084                     1 << 15;
1085
1086                fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |
1087                     ((info->cmap.green[fg_idx] & 0xf8) << 2) |
1088                     ((info->cmap.blue[fg_idx] & 0xf8) >> 3) |
1089                     1 << 15;
1090
1091                /* Use 32-bit operations on the data to improve performance */
1092                image = (uint32_t *)buffer;
1093                source = (uint32_t *)cursor->image.data;
1094                mask = (uint32_t *)cursor->mask;
1095
1096                if (cursor->rop == ROP_XOR)
1097                        for (i = 0; i < image_words; i++)
1098                                image[i] = source[i] ^ mask[i];
1099                else
1100                        for (i = 0; i < image_words; i++)
1101                                image[i] = source[i] & mask[i];
1102
1103                fsl_diu_load_cursor_image(info, image, bg, fg,
1104                        cursor->image.width, cursor->image.height);
1105        };
1106
1107        /*
1108         * Show or hide the cursor.  The cursor data is always stored in the
1109         * 'cursor' memory block, and the actual cursor position is always in
1110         * the DIU's CURS_POS register.  To hide the cursor, we redirect the
1111         * CURSOR register to a blank cursor.  The show the cursor, we
1112         * redirect the CURSOR register to the real cursor data.
1113         */
1114        if (cursor->enable)
1115                out_be32(&hw->cursor, DMA_ADDR(data, cursor));
1116        else
1117                out_be32(&hw->cursor, DMA_ADDR(data, blank_cursor));
1118
1119        return 0;
1120}
1121
1122/*
1123 * Using the fb_var_screeninfo in fb_info we set the resolution of this
1124 * particular framebuffer. This function alters the fb_fix_screeninfo stored
1125 * in fb_info. It does not alter var in fb_info since we are using that
1126 * data. This means we depend on the data in var inside fb_info to be
1127 * supported by the hardware. fsl_diu_check_var is always called before
1128 * fsl_diu_set_par to ensure this.
1129 */
1130static int fsl_diu_set_par(struct fb_info *info)
1131{
1132        unsigned long len;
1133        struct fb_var_screeninfo *var = &info->var;
1134        struct mfb_info *mfbi = info->par;
1135        struct fsl_diu_data *data = mfbi->parent;
1136        struct diu_ad *ad = mfbi->ad;
1137        struct diu __iomem *hw;
1138
1139        hw = data->diu_reg;
1140
1141        set_fix(info);
1142
1143        len = info->var.yres_virtual * info->fix.line_length;
1144        /* Alloc & dealloc each time resolution/bpp change */
1145        if (len != info->fix.smem_len) {
1146                if (info->fix.smem_start)
1147                        unmap_video_memory(info);
1148
1149                /* Memory allocation for framebuffer */
1150                if (map_video_memory(info)) {
1151                        dev_err(info->dev, "unable to allocate fb memory 1\n");
1152                        return -ENOMEM;
1153                }
1154        }
1155
1156        if (diu_ops.get_pixel_format)
1157                ad->pix_fmt = diu_ops.get_pixel_format(data->monitor_port,
1158                                                       var->bits_per_pixel);
1159        else
1160                ad->pix_fmt = fsl_diu_get_pixel_format(var->bits_per_pixel);
1161
1162        ad->addr    = cpu_to_le32(info->fix.smem_start);
1163        ad->src_size_g_alpha = cpu_to_le32((var->yres_virtual << 12) |
1164                                var->xres_virtual) | mfbi->g_alpha;
1165        /* AOI should not be greater than display size */
1166        ad->aoi_size    = cpu_to_le32((var->yres << 16) | var->xres);
1167        ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset);
1168        ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);
1169
1170        /* Disable chroma keying function */
1171        ad->ckmax_r = 0;
1172        ad->ckmax_g = 0;
1173        ad->ckmax_b = 0;
1174
1175        ad->ckmin_r = 255;
1176        ad->ckmin_g = 255;
1177        ad->ckmin_b = 255;
1178
1179        if (mfbi->index == PLANE0)
1180                update_lcdc(info);
1181        return 0;
1182}
1183
1184static inline __u32 CNVT_TOHW(__u32 val, __u32 width)
1185{
1186        return ((val << width) + 0x7FFF - val) >> 16;
1187}
1188
1189/*
1190 * Set a single color register. The values supplied have a 16 bit magnitude
1191 * which needs to be scaled in this function for the hardware. Things to take
1192 * into consideration are how many color registers, if any, are supported with
1193 * the current color visual. With truecolor mode no color palettes are
1194 * supported. Here a pseudo palette is created which we store the value in
1195 * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited
1196 * color palette.
1197 */
1198static int fsl_diu_setcolreg(unsigned int regno, unsigned int red,
1199                             unsigned int green, unsigned int blue,
1200                             unsigned int transp, struct fb_info *info)
1201{
1202        int ret = 1;
1203
1204        /*
1205         * If greyscale is true, then we convert the RGB value
1206         * to greyscale no matter what visual we are using.
1207         */
1208        if (info->var.grayscale)
1209                red = green = blue = (19595 * red + 38470 * green +
1210                                      7471 * blue) >> 16;
1211        switch (info->fix.visual) {
1212        case FB_VISUAL_TRUECOLOR:
1213                /*
1214                 * 16-bit True Colour.  We encode the RGB value
1215                 * according to the RGB bitfield information.
1216                 */
1217                if (regno < 16) {
1218                        u32 *pal = info->pseudo_palette;
1219                        u32 v;
1220
1221                        red = CNVT_TOHW(red, info->var.red.length);
1222                        green = CNVT_TOHW(green, info->var.green.length);
1223                        blue = CNVT_TOHW(blue, info->var.blue.length);
1224                        transp = CNVT_TOHW(transp, info->var.transp.length);
1225
1226                        v = (red << info->var.red.offset) |
1227                            (green << info->var.green.offset) |
1228                            (blue << info->var.blue.offset) |
1229                            (transp << info->var.transp.offset);
1230
1231                        pal[regno] = v;
1232                        ret = 0;
1233                }
1234                break;
1235        }
1236
1237        return ret;
1238}
1239
1240/*
1241 * Pan (or wrap, depending on the `vmode' field) the display using the
1242 * 'xoffset' and 'yoffset' fields of the 'var' structure. If the values
1243 * don't fit, return -EINVAL.
1244 */
1245static int fsl_diu_pan_display(struct fb_var_screeninfo *var,
1246                             struct fb_info *info)
1247{
1248        if ((info->var.xoffset == var->xoffset) &&
1249            (info->var.yoffset == var->yoffset))
1250                return 0;       /* No change, do nothing */
1251
1252        if (var->xoffset < 0 || var->yoffset < 0
1253            || var->xoffset + info->var.xres > info->var.xres_virtual
1254            || var->yoffset + info->var.yres > info->var.yres_virtual)
1255                return -EINVAL;
1256
1257        info->var.xoffset = var->xoffset;
1258        info->var.yoffset = var->yoffset;
1259
1260        if (var->vmode & FB_VMODE_YWRAP)
1261                info->var.vmode |= FB_VMODE_YWRAP;
1262        else
1263                info->var.vmode &= ~FB_VMODE_YWRAP;
1264
1265        fsl_diu_set_aoi(info);
1266
1267        return 0;
1268}
1269
1270static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
1271                       unsigned long arg)
1272{
1273        struct mfb_info *mfbi = info->par;
1274        struct diu_ad *ad = mfbi->ad;
1275        struct mfb_chroma_key ck;
1276        unsigned char global_alpha;
1277        struct aoi_display_offset aoi_d;
1278        __u32 pix_fmt;
1279        void __user *buf = (void __user *)arg;
1280
1281        if (!arg)
1282                return -EINVAL;
1283
1284        dev_dbg(info->dev, "ioctl %08x (dir=%s%s type=%u nr=%u size=%u)\n", cmd,
1285                _IOC_DIR(cmd) & _IOC_READ ? "R" : "",
1286                _IOC_DIR(cmd) & _IOC_WRITE ? "W" : "",
1287                _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd));
1288
1289        switch (cmd) {
1290        case MFB_SET_PIXFMT_OLD:
1291                dev_warn(info->dev,
1292                         "MFB_SET_PIXFMT value of 0x%08x is deprecated.\n",
1293                         MFB_SET_PIXFMT_OLD);
1294        case MFB_SET_PIXFMT:
1295                if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt)))
1296                        return -EFAULT;
1297                ad->pix_fmt = pix_fmt;
1298                break;
1299        case MFB_GET_PIXFMT_OLD:
1300                dev_warn(info->dev,
1301                         "MFB_GET_PIXFMT value of 0x%08x is deprecated.\n",
1302                         MFB_GET_PIXFMT_OLD);
1303        case MFB_GET_PIXFMT:
1304                pix_fmt = ad->pix_fmt;
1305                if (copy_to_user(buf, &pix_fmt, sizeof(pix_fmt)))
1306                        return -EFAULT;
1307                break;
1308        case MFB_SET_AOID:
1309                if (copy_from_user(&aoi_d, buf, sizeof(aoi_d)))
1310                        return -EFAULT;
1311                mfbi->x_aoi_d = aoi_d.x_aoi_d;
1312                mfbi->y_aoi_d = aoi_d.y_aoi_d;
1313                fsl_diu_check_var(&info->var, info);
1314                fsl_diu_set_aoi(info);
1315                break;
1316        case MFB_GET_AOID:
1317                aoi_d.x_aoi_d = mfbi->x_aoi_d;
1318                aoi_d.y_aoi_d = mfbi->y_aoi_d;
1319                if (copy_to_user(buf, &aoi_d, sizeof(aoi_d)))
1320                        return -EFAULT;
1321                break;
1322        case MFB_GET_ALPHA:
1323                global_alpha = mfbi->g_alpha;
1324                if (copy_to_user(buf, &global_alpha, sizeof(global_alpha)))
1325                        return -EFAULT;
1326                break;
1327        case MFB_SET_ALPHA:
1328                /* set panel information */
1329                if (copy_from_user(&global_alpha, buf, sizeof(global_alpha)))
1330                        return -EFAULT;
1331                ad->src_size_g_alpha = (ad->src_size_g_alpha & (~0xff)) |
1332                                                        (global_alpha & 0xff);
1333                mfbi->g_alpha = global_alpha;
1334                break;
1335        case MFB_SET_CHROMA_KEY:
1336                /* set panel winformation */
1337                if (copy_from_user(&ck, buf, sizeof(ck)))
1338                        return -EFAULT;
1339
1340                if (ck.enable &&
1341                   (ck.red_max < ck.red_min ||
1342                    ck.green_max < ck.green_min ||
1343                    ck.blue_max < ck.blue_min))
1344                        return -EINVAL;
1345
1346                if (!ck.enable) {
1347                        ad->ckmax_r = 0;
1348                        ad->ckmax_g = 0;
1349                        ad->ckmax_b = 0;
1350                        ad->ckmin_r = 255;
1351                        ad->ckmin_g = 255;
1352                        ad->ckmin_b = 255;
1353                } else {
1354                        ad->ckmax_r = ck.red_max;
1355                        ad->ckmax_g = ck.green_max;
1356                        ad->ckmax_b = ck.blue_max;
1357                        ad->ckmin_r = ck.red_min;
1358                        ad->ckmin_g = ck.green_min;
1359                        ad->ckmin_b = ck.blue_min;
1360                }
1361                break;
1362#ifdef CONFIG_PPC_MPC512x
1363        case MFB_SET_GAMMA: {
1364                struct fsl_diu_data *data = mfbi->parent;
1365
1366                if (copy_from_user(data->gamma, buf, sizeof(data->gamma)))
1367                        return -EFAULT;
1368                setbits32(&data->diu_reg->gamma, 0); /* Force table reload */
1369                break;
1370        }
1371        case MFB_GET_GAMMA: {
1372                struct fsl_diu_data *data = mfbi->parent;
1373
1374                if (copy_to_user(buf, data->gamma, sizeof(data->gamma)))
1375                        return -EFAULT;
1376                break;
1377        }
1378#endif
1379        default:
1380                dev_err(info->dev, "unknown ioctl command (0x%08X)\n", cmd);
1381                return -ENOIOCTLCMD;
1382        }
1383
1384        return 0;
1385}
1386
1387static inline void fsl_diu_enable_interrupts(struct fsl_diu_data *data)
1388{
1389        u32 int_mask = INT_UNDRUN; /* enable underrun detection */
1390
1391        if (IS_ENABLED(CONFIG_NOT_COHERENT_CACHE))
1392                int_mask |= INT_VSYNC; /* enable vertical sync */
1393
1394        clrbits32(&data->diu_reg->int_mask, int_mask);
1395}
1396
1397/* turn on fb if count == 1
1398 */
1399static int fsl_diu_open(struct fb_info *info, int user)
1400{
1401        struct mfb_info *mfbi = info->par;
1402        int res = 0;
1403
1404        /* free boot splash memory on first /dev/fb0 open */
1405        if ((mfbi->index == PLANE0) && diu_ops.release_bootmem)
1406                diu_ops.release_bootmem();
1407
1408        spin_lock(&diu_lock);
1409        mfbi->count++;
1410        if (mfbi->count == 1) {
1411                fsl_diu_check_var(&info->var, info);
1412                res = fsl_diu_set_par(info);
1413                if (res < 0)
1414                        mfbi->count--;
1415                else {
1416                        fsl_diu_enable_interrupts(mfbi->parent);
1417                        fsl_diu_enable_panel(info);
1418                }
1419        }
1420
1421        spin_unlock(&diu_lock);
1422        return res;
1423}
1424
1425/* turn off fb if count == 0
1426 */
1427static int fsl_diu_release(struct fb_info *info, int user)
1428{
1429        struct mfb_info *mfbi = info->par;
1430        int res = 0;
1431
1432        spin_lock(&diu_lock);
1433        mfbi->count--;
1434        if (mfbi->count == 0) {
1435                struct fsl_diu_data *data = mfbi->parent;
1436                bool disable = true;
1437                int i;
1438
1439                /* Disable interrupts only if all AOIs are closed */
1440                for (i = 0; i < NUM_AOIS; i++) {
1441                        struct mfb_info *mi = data->fsl_diu_info[i].par;
1442
1443                        if (mi->count)
1444                                disable = false;
1445                }
1446                if (disable)
1447                        out_be32(&data->diu_reg->int_mask, 0xffffffff);
1448                fsl_diu_disable_panel(info);
1449        }
1450
1451        spin_unlock(&diu_lock);
1452        return res;
1453}
1454
1455static struct fb_ops fsl_diu_ops = {
1456        .owner = THIS_MODULE,
1457        .fb_check_var = fsl_diu_check_var,
1458        .fb_set_par = fsl_diu_set_par,
1459        .fb_setcolreg = fsl_diu_setcolreg,
1460        .fb_pan_display = fsl_diu_pan_display,
1461        .fb_fillrect = cfb_fillrect,
1462        .fb_copyarea = cfb_copyarea,
1463        .fb_imageblit = cfb_imageblit,
1464        .fb_ioctl = fsl_diu_ioctl,
1465        .fb_open = fsl_diu_open,
1466        .fb_release = fsl_diu_release,
1467        .fb_cursor = fsl_diu_cursor,
1468};
1469
1470static int install_fb(struct fb_info *info)
1471{
1472        int rc;
1473        struct mfb_info *mfbi = info->par;
1474        struct fsl_diu_data *data = mfbi->parent;
1475        const char *aoi_mode, *init_aoi_mode = "320x240";
1476        struct fb_videomode *db = fsl_diu_mode_db;
1477        unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db);
1478        int has_default_mode = 1;
1479
1480        info->var.activate = FB_ACTIVATE_NOW;
1481        info->fbops = &fsl_diu_ops;
1482        info->flags = FBINFO_DEFAULT | FBINFO_VIRTFB | FBINFO_PARTIAL_PAN_OK |
1483                FBINFO_READS_FAST;
1484        info->pseudo_palette = mfbi->pseudo_palette;
1485
1486        rc = fb_alloc_cmap(&info->cmap, 16, 0);
1487        if (rc)
1488                return rc;
1489
1490        if (mfbi->index == PLANE0) {
1491                if (data->has_edid) {
1492                        /* Now build modedb from EDID */
1493                        fb_edid_to_monspecs(data->edid_data, &info->monspecs);
1494                        fb_videomode_to_modelist(info->monspecs.modedb,
1495                                                 info->monspecs.modedb_len,
1496                                                 &info->modelist);
1497                        db = info->monspecs.modedb;
1498                        dbsize = info->monspecs.modedb_len;
1499                }
1500                aoi_mode = fb_mode;
1501        } else {
1502                aoi_mode = init_aoi_mode;
1503        }
1504        rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize, NULL,
1505                          default_bpp);
1506        if (!rc) {
1507                /*
1508                 * For plane 0 we continue and look into
1509                 * driver's internal modedb.
1510                 */
1511                if ((mfbi->index == PLANE0) && data->has_edid)
1512                        has_default_mode = 0;
1513                else
1514                        return -EINVAL;
1515        }
1516
1517        if (!has_default_mode) {
1518                rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db,
1519                        ARRAY_SIZE(fsl_diu_mode_db), NULL, default_bpp);
1520                if (rc)
1521                        has_default_mode = 1;
1522        }
1523
1524        /* Still not found, use preferred mode from database if any */
1525        if (!has_default_mode && info->monspecs.modedb) {
1526                struct fb_monspecs *specs = &info->monspecs;
1527                struct fb_videomode *modedb = &specs->modedb[0];
1528
1529                /*
1530                 * Get preferred timing. If not found,
1531                 * first mode in database will be used.
1532                 */
1533                if (specs->misc & FB_MISC_1ST_DETAIL) {
1534                        int i;
1535
1536                        for (i = 0; i < specs->modedb_len; i++) {
1537                                if (specs->modedb[i].flag & FB_MODE_IS_FIRST) {
1538                                        modedb = &specs->modedb[i];
1539                                        break;
1540                                }
1541                        }
1542                }
1543
1544                info->var.bits_per_pixel = default_bpp;
1545                fb_videomode_to_var(&info->var, modedb);
1546        }
1547
1548        if (fsl_diu_check_var(&info->var, info)) {
1549                dev_err(info->dev, "fsl_diu_check_var failed\n");
1550                unmap_video_memory(info);
1551                fb_dealloc_cmap(&info->cmap);
1552                return -EINVAL;
1553        }
1554
1555        if (register_framebuffer(info) < 0) {
1556                dev_err(info->dev, "register_framebuffer failed\n");
1557                unmap_video_memory(info);
1558                fb_dealloc_cmap(&info->cmap);
1559                return -EINVAL;
1560        }
1561
1562        mfbi->registered = 1;
1563        dev_info(info->dev, "%s registered successfully\n", mfbi->id);
1564
1565        return 0;
1566}
1567
1568static void uninstall_fb(struct fb_info *info)
1569{
1570        struct mfb_info *mfbi = info->par;
1571
1572        if (!mfbi->registered)
1573                return;
1574
1575        unregister_framebuffer(info);
1576        unmap_video_memory(info);
1577        if (&info->cmap)
1578                fb_dealloc_cmap(&info->cmap);
1579
1580        mfbi->registered = 0;
1581}
1582
1583static irqreturn_t fsl_diu_isr(int irq, void *dev_id)
1584{
1585        struct diu __iomem *hw = dev_id;
1586        uint32_t status = in_be32(&hw->int_status);
1587
1588        if (status) {
1589                /* This is the workaround for underrun */
1590                if (status & INT_UNDRUN) {
1591                        out_be32(&hw->diu_mode, 0);
1592                        udelay(1);
1593                        out_be32(&hw->diu_mode, 1);
1594                }
1595#if defined(CONFIG_NOT_COHERENT_CACHE)
1596                else if (status & INT_VSYNC) {
1597                        unsigned int i;
1598
1599                        for (i = 0; i < coherence_data_size;
1600                                i += d_cache_line_size)
1601                                __asm__ __volatile__ (
1602                                        "dcbz 0, %[input]"
1603                                ::[input]"r"(&coherence_data[i]));
1604                }
1605#endif
1606                return IRQ_HANDLED;
1607        }
1608        return IRQ_NONE;
1609}
1610
1611#ifdef CONFIG_PM
1612/*
1613 * Power management hooks. Note that we won't be called from IRQ context,
1614 * unlike the blank functions above, so we may sleep.
1615 */
1616static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state)
1617{
1618        struct fsl_diu_data *data;
1619
1620        data = dev_get_drvdata(&ofdev->dev);
1621        disable_lcdc(data->fsl_diu_info);
1622
1623        return 0;
1624}
1625
1626static int fsl_diu_resume(struct platform_device *ofdev)
1627{
1628        struct fsl_diu_data *data;
1629
1630        data = dev_get_drvdata(&ofdev->dev);
1631        enable_lcdc(data->fsl_diu_info);
1632
1633        return 0;
1634}
1635
1636#else
1637#define fsl_diu_suspend NULL
1638#define fsl_diu_resume NULL
1639#endif                          /* CONFIG_PM */
1640
1641static ssize_t store_monitor(struct device *device,
1642        struct device_attribute *attr, const char *buf, size_t count)
1643{
1644        enum fsl_diu_monitor_port old_monitor_port;
1645        struct fsl_diu_data *data =
1646                container_of(attr, struct fsl_diu_data, dev_attr);
1647
1648        old_monitor_port = data->monitor_port;
1649        data->monitor_port = fsl_diu_name_to_port(buf);
1650
1651        if (old_monitor_port != data->monitor_port) {
1652                /* All AOIs need adjust pixel format
1653                 * fsl_diu_set_par only change the pixsel format here
1654                 * unlikely to fail. */
1655                unsigned int i;
1656
1657                for (i=0; i < NUM_AOIS; i++)
1658                        fsl_diu_set_par(&data->fsl_diu_info[i]);
1659        }
1660        return count;
1661}
1662
1663static ssize_t show_monitor(struct device *device,
1664        struct device_attribute *attr, char *buf)
1665{
1666        struct fsl_diu_data *data =
1667                container_of(attr, struct fsl_diu_data, dev_attr);
1668
1669        switch (data->monitor_port) {
1670        case FSL_DIU_PORT_DVI:
1671                return sprintf(buf, "DVI\n");
1672        case FSL_DIU_PORT_LVDS:
1673                return sprintf(buf, "Single-link LVDS\n");
1674        case FSL_DIU_PORT_DLVDS:
1675                return sprintf(buf, "Dual-link LVDS\n");
1676        }
1677
1678        return 0;
1679}
1680
1681static int fsl_diu_probe(struct platform_device *pdev)
1682{
1683        struct device_node *np = pdev->dev.of_node;
1684        struct mfb_info *mfbi;
1685        struct fsl_diu_data *data;
1686        dma_addr_t dma_addr; /* DMA addr of fsl_diu_data struct */
1687        const void *prop;
1688        unsigned int i;
1689        int ret;
1690
1691        data = dmam_alloc_coherent(&pdev->dev, sizeof(struct fsl_diu_data),
1692                                   &dma_addr, GFP_DMA | __GFP_ZERO);
1693        if (!data)
1694                return -ENOMEM;
1695        data->dma_addr = dma_addr;
1696
1697        /*
1698         * dma_alloc_coherent() uses a page allocator, so the address is
1699         * always page-aligned.  We need the memory to be 32-byte aligned,
1700         * so that's good.  However, if one day the allocator changes, we
1701         * need to catch that.  It's not worth the effort to handle unaligned
1702         * alloctions now because it's highly unlikely to ever be a problem.
1703         */
1704        if ((unsigned long)data & 31) {
1705                dev_err(&pdev->dev, "misaligned allocation");
1706                ret = -ENOMEM;
1707                goto error;
1708        }
1709
1710        spin_lock_init(&data->reg_lock);
1711
1712        for (i = 0; i < NUM_AOIS; i++) {
1713                struct fb_info *info = &data->fsl_diu_info[i];
1714
1715                info->device = &pdev->dev;
1716                info->par = &data->mfb[i];
1717
1718                /*
1719                 * We store the physical address of the AD in the reserved
1720                 * 'paddr' field of the AD itself.
1721                 */
1722                data->ad[i].paddr = DMA_ADDR(data, ad[i]);
1723
1724                info->fix.smem_start = 0;
1725
1726                /* Initialize the AOI data structure */
1727                mfbi = info->par;
1728                memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
1729                mfbi->parent = data;
1730                mfbi->ad = &data->ad[i];
1731        }
1732
1733        /* Get the EDID data from the device tree, if present */
1734        prop = of_get_property(np, "edid", &ret);
1735        if (prop && ret == EDID_LENGTH) {
1736                memcpy(data->edid_data, prop, EDID_LENGTH);
1737                data->has_edid = true;
1738        }
1739
1740        data->diu_reg = of_iomap(np, 0);
1741        if (!data->diu_reg) {
1742                dev_err(&pdev->dev, "cannot map DIU registers\n");
1743                ret = -EFAULT;
1744                goto error;
1745        }
1746
1747        /* Get the IRQ of the DIU */
1748        data->irq = irq_of_parse_and_map(np, 0);
1749
1750        if (!data->irq) {
1751                dev_err(&pdev->dev, "could not get DIU IRQ\n");
1752                ret = -EINVAL;
1753                goto error;
1754        }
1755        data->monitor_port = monitor_port;
1756
1757        /* Initialize the dummy Area Descriptor */
1758        data->dummy_ad.addr = cpu_to_le32(DMA_ADDR(data, dummy_aoi));
1759        data->dummy_ad.pix_fmt = 0x88882317;
1760        data->dummy_ad.src_size_g_alpha = cpu_to_le32((4 << 12) | 4);
1761        data->dummy_ad.aoi_size = cpu_to_le32((4 << 16) |  2);
1762        data->dummy_ad.offset_xyi = 0;
1763        data->dummy_ad.offset_xyd = 0;
1764        data->dummy_ad.next_ad = 0;
1765        data->dummy_ad.paddr = DMA_ADDR(data, dummy_ad);
1766
1767        /*
1768         * Let DIU continue to display splash screen if it was pre-initialized
1769         * by the bootloader; otherwise, clear the display.
1770         */
1771        if (in_be32(&data->diu_reg->diu_mode) == MFB_MODE0)
1772                out_be32(&data->diu_reg->desc[0], 0);
1773
1774        out_be32(&data->diu_reg->desc[1], data->dummy_ad.paddr);
1775        out_be32(&data->diu_reg->desc[2], data->dummy_ad.paddr);
1776
1777        /*
1778         * Older versions of U-Boot leave interrupts enabled, so disable
1779         * all of them and clear the status register.
1780         */
1781        out_be32(&data->diu_reg->int_mask, 0xffffffff);
1782        in_be32(&data->diu_reg->int_status);
1783
1784        ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb",
1785                          data->diu_reg);
1786        if (ret) {
1787                dev_err(&pdev->dev, "could not claim irq\n");
1788                goto error;
1789        }
1790
1791        for (i = 0; i < NUM_AOIS; i++) {
1792                ret = install_fb(&data->fsl_diu_info[i]);
1793                if (ret) {
1794                        dev_err(&pdev->dev, "could not register fb %d\n", i);
1795                        free_irq(data->irq, data->diu_reg);
1796                        goto error;
1797                }
1798        }
1799
1800        sysfs_attr_init(&data->dev_attr.attr);
1801        data->dev_attr.attr.name = "monitor";
1802        data->dev_attr.attr.mode = S_IRUGO|S_IWUSR;
1803        data->dev_attr.show = show_monitor;
1804        data->dev_attr.store = store_monitor;
1805        ret = device_create_file(&pdev->dev, &data->dev_attr);
1806        if (ret) {
1807                dev_err(&pdev->dev, "could not create sysfs file %s\n",
1808                        data->dev_attr.attr.name);
1809        }
1810
1811        dev_set_drvdata(&pdev->dev, data);
1812        return 0;
1813
1814error:
1815        for (i = 0; i < NUM_AOIS; i++)
1816                uninstall_fb(&data->fsl_diu_info[i]);
1817
1818        iounmap(data->diu_reg);
1819
1820        return ret;
1821}
1822
1823static int fsl_diu_remove(struct platform_device *pdev)
1824{
1825        struct fsl_diu_data *data;
1826        int i;
1827
1828        data = dev_get_drvdata(&pdev->dev);
1829        disable_lcdc(&data->fsl_diu_info[0]);
1830
1831        free_irq(data->irq, data->diu_reg);
1832
1833        for (i = 0; i < NUM_AOIS; i++)
1834                uninstall_fb(&data->fsl_diu_info[i]);
1835
1836        iounmap(data->diu_reg);
1837
1838        return 0;
1839}
1840
1841#ifndef MODULE
1842static int __init fsl_diu_setup(char *options)
1843{
1844        char *opt;
1845        unsigned long val;
1846
1847        if (!options || !*options)
1848                return 0;
1849
1850        while ((opt = strsep(&options, ",")) != NULL) {
1851                if (!*opt)
1852                        continue;
1853                if (!strncmp(opt, "monitor=", 8)) {
1854                        monitor_port = fsl_diu_name_to_port(opt + 8);
1855                } else if (!strncmp(opt, "bpp=", 4)) {
1856                        if (!kstrtoul(opt + 4, 10, &val))
1857                                default_bpp = val;
1858                } else
1859                        fb_mode = opt;
1860        }
1861
1862        return 0;
1863}
1864#endif
1865
1866static struct of_device_id fsl_diu_match[] = {
1867#ifdef CONFIG_PPC_MPC512x
1868        {
1869                .compatible = "fsl,mpc5121-diu",
1870        },
1871#endif
1872        {
1873                .compatible = "fsl,diu",
1874        },
1875        {}
1876};
1877MODULE_DEVICE_TABLE(of, fsl_diu_match);
1878
1879static struct platform_driver fsl_diu_driver = {
1880        .driver = {
1881                .name = "fsl-diu-fb",
1882                .owner = THIS_MODULE,
1883                .of_match_table = fsl_diu_match,
1884        },
1885        .probe          = fsl_diu_probe,
1886        .remove         = fsl_diu_remove,
1887        .suspend        = fsl_diu_suspend,
1888        .resume         = fsl_diu_resume,
1889};
1890
1891static int __init fsl_diu_init(void)
1892{
1893#ifdef CONFIG_NOT_COHERENT_CACHE
1894        struct device_node *np;
1895        const u32 *prop;
1896#endif
1897        int ret;
1898#ifndef MODULE
1899        char *option;
1900
1901        /*
1902         * For kernel boot options (in 'video=xxxfb:<options>' format)
1903         */
1904        if (fb_get_options("fslfb", &option))
1905                return -ENODEV;
1906        fsl_diu_setup(option);
1907#else
1908        monitor_port = fsl_diu_name_to_port(monitor_string);
1909#endif
1910        pr_info("Freescale Display Interface Unit (DIU) framebuffer driver\n");
1911
1912#ifdef CONFIG_NOT_COHERENT_CACHE
1913        np = of_find_node_by_type(NULL, "cpu");
1914        if (!np) {
1915                pr_err("fsl-diu-fb: can't find 'cpu' device node\n");
1916                return -ENODEV;
1917        }
1918
1919        prop = of_get_property(np, "d-cache-size", NULL);
1920        if (prop == NULL) {
1921                pr_err("fsl-diu-fb: missing 'd-cache-size' property' "
1922                       "in 'cpu' node\n");
1923                of_node_put(np);
1924                return -ENODEV;
1925        }
1926
1927        /*
1928         * Freescale PLRU requires 13/8 times the cache size to do a proper
1929         * displacement flush
1930         */
1931        coherence_data_size = be32_to_cpup(prop) * 13;
1932        coherence_data_size /= 8;
1933
1934        pr_debug("fsl-diu-fb: coherence data size is %zu bytes\n",
1935                 coherence_data_size);
1936
1937        prop = of_get_property(np, "d-cache-line-size", NULL);
1938        if (prop == NULL) {
1939                pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' "
1940                       "in 'cpu' node\n");
1941                of_node_put(np);
1942                return -ENODEV;
1943        }
1944        d_cache_line_size = be32_to_cpup(prop);
1945
1946        pr_debug("fsl-diu-fb: cache lines size is %u bytes\n",
1947                 d_cache_line_size);
1948
1949        of_node_put(np);
1950        coherence_data = vmalloc(coherence_data_size);
1951        if (!coherence_data) {
1952                pr_err("fsl-diu-fb: could not allocate coherence data "
1953                       "(size=%zu)\n", coherence_data_size);
1954                return -ENOMEM;
1955        }
1956
1957#endif
1958
1959        ret = platform_driver_register(&fsl_diu_driver);
1960        if (ret) {
1961                pr_err("fsl-diu-fb: failed to register platform driver\n");
1962#if defined(CONFIG_NOT_COHERENT_CACHE)
1963                vfree(coherence_data);
1964#endif
1965        }
1966        return ret;
1967}
1968
1969static void __exit fsl_diu_exit(void)
1970{
1971        platform_driver_unregister(&fsl_diu_driver);
1972#if defined(CONFIG_NOT_COHERENT_CACHE)
1973        vfree(coherence_data);
1974#endif
1975}
1976
1977module_init(fsl_diu_init);
1978module_exit(fsl_diu_exit);
1979
1980MODULE_AUTHOR("York Sun <yorksun@freescale.com>");
1981MODULE_DESCRIPTION("Freescale DIU framebuffer driver");
1982MODULE_LICENSE("GPL");
1983
1984module_param_named(mode, fb_mode, charp, 0);
1985MODULE_PARM_DESC(mode,
1986        "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
1987module_param_named(bpp, default_bpp, ulong, 0);
1988MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified in 'mode'");
1989module_param_named(monitor, monitor_string, charp, 0);
1990MODULE_PARM_DESC(monitor, "Specify the monitor port "
1991        "(\"dvi\", \"lvds\", or \"dlvds\") if supported by the platform");
1992
1993
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.