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