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
  34#include <linux/of_platform.h>
  35
  36#include <sysdev/fsl_soc.h>
  37#include "fsl-diu-fb.h"
  38
  39/*
  40 * These parameters give default parameters
  41 * for video output 1024x768,
  42 * FIXME - change timing to proper amounts
  43 * hsync 31.5kHz, vsync 60Hz
  44 */
  45static struct fb_videomode __devinitdata fsl_diu_default_mode = {
  46        .refresh        = 60,
  47        .xres           = 1024,
  48        .yres           = 768,
  49        .pixclock       = 15385,
  50        .left_margin    = 160,
  51        .right_margin   = 24,
  52        .upper_margin   = 29,
  53        .lower_margin   = 3,
  54        .hsync_len      = 136,
  55        .vsync_len      = 6,
  56        .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
  57        .vmode          = FB_VMODE_NONINTERLACED
  58};
  59
  60static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
  61        {
  62                .name           = "1024x768-60",
  63                .refresh        = 60,
  64                .xres           = 1024,
  65                .yres           = 768,
  66                .pixclock       = 15385,
  67                .left_margin    = 160,
  68                .right_margin   = 24,
  69                .upper_margin   = 29,
  70                .lower_margin   = 3,
  71                .hsync_len      = 136,
  72                .vsync_len      = 6,
  73                .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
  74                .vmode          = FB_VMODE_NONINTERLACED
  75        },
  76        {
  77                .name           = "1024x768-70",
  78                .refresh        = 70,
  79                .xres           = 1024,
  80                .yres           = 768,
  81                .pixclock       = 16886,
  82                .left_margin    = 3,
  83                .right_margin   = 3,
  84                .upper_margin   = 2,
  85                .lower_margin   = 2,
  86                .hsync_len      = 40,
  87                .vsync_len      = 18,
  88                .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
  89                .vmode          = FB_VMODE_NONINTERLACED
  90        },
  91        {
  92                .name           = "1024x768-75",
  93                .refresh        = 75,
  94                .xres           = 1024,
  95                .yres           = 768,
  96                .pixclock       = 15009,
  97                .left_margin    = 3,
  98                .right_margin   = 3,
  99                .upper_margin   = 2,
 100                .lower_margin   = 2,
 101                .hsync_len      = 80,
 102                .vsync_len      = 32,
 103                .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 104                .vmode          = FB_VMODE_NONINTERLACED
 105        },
 106        {
 107                .name           = "1280x1024-60",
 108                .refresh        = 60,
 109                .xres           = 1280,
 110                .yres           = 1024,
 111                .pixclock       = 9375,
 112                .left_margin    = 38,
 113                .right_margin   = 128,
 114                .upper_margin   = 2,
 115                .lower_margin   = 7,
 116                .hsync_len      = 216,
 117                .vsync_len      = 37,
 118                .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 119                .vmode          = FB_VMODE_NONINTERLACED
 120        },
 121        {
 122                .name           = "1280x1024-70",
 123                .refresh        = 70,
 124                .xres           = 1280,
 125                .yres           = 1024,
 126                .pixclock       = 9380,
 127                .left_margin    = 6,
 128                .right_margin   = 6,
 129                .upper_margin   = 4,
 130                .lower_margin   = 4,
 131                .hsync_len      = 60,
 132                .vsync_len      = 94,
 133                .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 134                .vmode          = FB_VMODE_NONINTERLACED
 135        },
 136        {
 137                .name           = "1280x1024-75",
 138                .refresh        = 75,
 139                .xres           = 1280,
 140                .yres           = 1024,
 141                .pixclock       = 9380,
 142                .left_margin    = 6,
 143                .right_margin   = 6,
 144                .upper_margin   = 4,
 145                .lower_margin   = 4,
 146                .hsync_len      = 60,
 147                .vsync_len      = 15,
 148                .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 149                .vmode          = FB_VMODE_NONINTERLACED
 150        },
 151        {
 152                .name           = "320x240",            /* for AOI only */
 153                .refresh        = 60,
 154                .xres           = 320,
 155                .yres           = 240,
 156                .pixclock       = 15385,
 157                .left_margin    = 0,
 158                .right_margin   = 0,
 159                .upper_margin   = 0,
 160                .lower_margin   = 0,
 161                .hsync_len      = 0,
 162                .vsync_len      = 0,
 163                .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 164                .vmode          = FB_VMODE_NONINTERLACED
 165        },
 166        {
 167                .name           = "1280x480-60",
 168                .refresh        = 60,
 169                .xres           = 1280,
 170                .yres           = 480,
 171                .pixclock       = 18939,
 172                .left_margin    = 353,
 173                .right_margin   = 47,
 174                .upper_margin   = 39,
 175                .lower_margin   = 4,
 176                .hsync_len      = 8,
 177                .vsync_len      = 2,
 178                .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 179                .vmode          = FB_VMODE_NONINTERLACED
 180        },
 181};
 182
 183static char *fb_mode = "1024x768-32@60";
 184static unsigned long default_bpp = 32;
 185static int monitor_port;
 186
 187#if defined(CONFIG_NOT_COHERENT_CACHE)
 188static u8 *coherence_data;
 189static size_t coherence_data_size;
 190static unsigned int d_cache_line_size;
 191#endif
 192
 193static DEFINE_SPINLOCK(diu_lock);
 194
 195struct fsl_diu_data {
 196        struct fb_info *fsl_diu_info[FSL_AOI_NUM - 1];
 197                                /*FSL_AOI_NUM has one dummy AOI */
 198        struct device_attribute dev_attr;
 199        struct diu_ad *dummy_ad;
 200        void *dummy_aoi_virt;
 201        unsigned int irq;
 202        int fb_enabled;
 203        int monitor_port;
 204};
 205
 206struct mfb_info {
 207        int index;
 208        int type;
 209        char *id;
 210        int registered;
 211        int blank;
 212        unsigned long pseudo_palette[16];
 213        struct diu_ad *ad;
 214        int cursor_reset;
 215        unsigned char g_alpha;
 216        unsigned int count;
 217        int x_aoi_d;            /* aoi display x offset to physical screen */
 218        int y_aoi_d;            /* aoi display y offset to physical screen */
 219        struct fsl_diu_data *parent;
 220};
 221
 222
 223static struct mfb_info mfb_template[] = {
 224        {               /* AOI 0 for plane 0 */
 225        .index = 0,
 226        .type = MFB_TYPE_OUTPUT,
 227        .id = "Panel0",
 228        .registered = 0,
 229        .count = 0,
 230        .x_aoi_d = 0,
 231        .y_aoi_d = 0,
 232        },
 233        {               /* AOI 0 for plane 1 */
 234        .index = 1,
 235        .type = MFB_TYPE_OUTPUT,
 236        .id = "Panel1 AOI0",
 237        .registered = 0,
 238        .g_alpha = 0xff,
 239        .count = 0,
 240        .x_aoi_d = 0,
 241        .y_aoi_d = 0,
 242        },
 243        {               /* AOI 1 for plane 1 */
 244        .index = 2,
 245        .type = MFB_TYPE_OUTPUT,
 246        .id = "Panel1 AOI1",
 247        .registered = 0,
 248        .g_alpha = 0xff,
 249        .count = 0,
 250        .x_aoi_d = 0,
 251        .y_aoi_d = 480,
 252        },
 253        {               /* AOI 0 for plane 2 */
 254        .index = 3,
 255        .type = MFB_TYPE_OUTPUT,
 256        .id = "Panel2 AOI0",
 257        .registered = 0,
 258        .g_alpha = 0xff,
 259        .count = 0,
 260        .x_aoi_d = 640,
 261        .y_aoi_d = 0,
 262        },
 263        {               /* AOI 1 for plane 2 */
 264        .index = 4,
 265        .type = MFB_TYPE_OUTPUT,
 266        .id = "Panel2 AOI1",
 267        .registered = 0,
 268        .g_alpha = 0xff,
 269        .count = 0,
 270        .x_aoi_d = 640,
 271        .y_aoi_d = 480,
 272        },
 273};
 274
 275static struct diu_hw dr = {
 276        .mode = MFB_MODE1,
 277        .reg_lock = __SPIN_LOCK_UNLOCKED(diu_hw.reg_lock),
 278};
 279
 280static struct diu_pool pool;
 281
 282/**
 283 * fsl_diu_alloc - allocate memory for the DIU
 284 * @size: number of bytes to allocate
 285 * @param: returned physical address of memory
 286 *
 287 * This function allocates a physically-contiguous block of memory.
 288 */
 289static void *fsl_diu_alloc(size_t size, phys_addr_t *phys)
 290{
 291        void *virt;
 292
 293        pr_debug("size=%zu\n", size);
 294
 295        virt = alloc_pages_exact(size, GFP_DMA | __GFP_ZERO);
 296        if (virt) {
 297                *phys = virt_to_phys(virt);
 298                pr_debug("virt=%p phys=%llx\n", virt,
 299                        (unsigned long long)*phys);
 300        }
 301
 302        return virt;
 303}
 304
 305/**
 306 * fsl_diu_free - release DIU memory
 307 * @virt: pointer returned by fsl_diu_alloc()
 308 * @size: number of bytes allocated by fsl_diu_alloc()
 309 *
 310 * This function releases memory allocated by fsl_diu_alloc().
 311 */
 312static void fsl_diu_free(void *virt, size_t size)
 313{
 314        pr_debug("virt=%p size=%zu\n", virt, size);
 315
 316        if (virt && size)
 317                free_pages_exact(virt, size);
 318}
 319
 320static int fsl_diu_enable_panel(struct fb_info *info)
 321{
 322        struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
 323        struct diu *hw = dr.diu_reg;
 324        struct diu_ad *ad = mfbi->ad;
 325        struct fsl_diu_data *machine_data = mfbi->parent;
 326        int res = 0;
 327
 328        pr_debug("enable_panel index %d\n", mfbi->index);
 329        if (mfbi->type != MFB_TYPE_OFF) {
 330                switch (mfbi->index) {
 331                case 0:                         /* plane 0 */
 332                        if (hw->desc[0] != ad->paddr)
 333                                out_be32(&hw->desc[0], ad->paddr);
 334                        break;
 335                case 1:                         /* plane 1 AOI 0 */
 336                        cmfbi = machine_data->fsl_diu_info[2]->par;
 337                        if (hw->desc[1] != ad->paddr) { /* AOI0 closed */
 338                                if (cmfbi->count > 0)   /* AOI1 open */
 339                                        ad->next_ad =
 340                                                cpu_to_le32(cmfbi->ad->paddr);
 341                                else
 342                                        ad->next_ad = 0;
 343                                out_be32(&hw->desc[1], ad->paddr);
 344                        }
 345                        break;
 346                case 3:                         /* plane 2 AOI 0 */
 347                        cmfbi = machine_data->fsl_diu_info[4]->par;
 348                        if (hw->desc[2] != ad->paddr) { /* AOI0 closed */
 349                                if (cmfbi->count > 0)   /* AOI1 open */
 350                                        ad->next_ad =
 351                                                cpu_to_le32(cmfbi->ad->paddr);
 352                                else
 353                                        ad->next_ad = 0;
 354                                out_be32(&hw->desc[2], ad->paddr);
 355                        }
 356                        break;
 357                case 2:                         /* plane 1 AOI 1 */
 358                        pmfbi = machine_data->fsl_diu_info[1]->par;
 359                        ad->next_ad = 0;
 360                        if (hw->desc[1] == machine_data->dummy_ad->paddr)
 361                                out_be32(&hw->desc[1], ad->paddr);
 362                        else                                    /* AOI0 open */
 363                                pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
 364                        break;
 365                case 4:                         /* plane 2 AOI 1 */
 366                        pmfbi = machine_data->fsl_diu_info[3]->par;
 367                        ad->next_ad = 0;
 368                        if (hw->desc[2] == machine_data->dummy_ad->paddr)
 369                                out_be32(&hw->desc[2], ad->paddr);
 370                        else                            /* AOI0 was open */
 371                                pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
 372                        break;
 373                default:
 374                        res = -EINVAL;
 375                        break;
 376                }
 377        } else
 378                res = -EINVAL;
 379        return res;
 380}
 381
 382static int fsl_diu_disable_panel(struct fb_info *info)
 383{
 384        struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
 385        struct diu *hw = dr.diu_reg;
 386        struct diu_ad *ad = mfbi->ad;
 387        struct fsl_diu_data *machine_data = mfbi->parent;
 388        int res = 0;
 389
 390        switch (mfbi->index) {
 391        case 0:                                 /* plane 0 */
 392                if (hw->desc[0] != machine_data->dummy_ad->paddr)
 393                        out_be32(&hw->desc[0],
 394                                machine_data->dummy_ad->paddr);
 395                break;
 396        case 1:                                 /* plane 1 AOI 0 */
 397                cmfbi = machine_data->fsl_diu_info[2]->par;
 398                if (cmfbi->count > 0)   /* AOI1 is open */
 399                        out_be32(&hw->desc[1], cmfbi->ad->paddr);
 400                                        /* move AOI1 to the first */
 401                else                    /* AOI1 was closed */
 402                        out_be32(&hw->desc[1],
 403                                machine_data->dummy_ad->paddr);
 404                                        /* close AOI 0 */
 405                break;
 406        case 3:                                 /* plane 2 AOI 0 */
 407                cmfbi = machine_data->fsl_diu_info[4]->par;
 408                if (cmfbi->count > 0)   /* AOI1 is open */
 409                        out_be32(&hw->desc[2], cmfbi->ad->paddr);
 410                                        /* move AOI1 to the first */
 411                else                    /* AOI1 was closed */
 412                        out_be32(&hw->desc[2],
 413                                machine_data->dummy_ad->paddr);
 414                                        /* close AOI 0 */
 415                break;
 416        case 2:                                 /* plane 1 AOI 1 */
 417                pmfbi = machine_data->fsl_diu_info[1]->par;
 418                if (hw->desc[1] != ad->paddr) {
 419                                /* AOI1 is not the first in the chain */
 420                        if (pmfbi->count > 0)
 421                                        /* AOI0 is open, must be the first */
 422                                pmfbi->ad->next_ad = 0;
 423                } else                  /* AOI1 is the first in the chain */
 424                        out_be32(&hw->desc[1], machine_data->dummy_ad->paddr);
 425                                        /* close AOI 1 */
 426                break;
 427        case 4:                                 /* plane 2 AOI 1 */
 428                pmfbi = machine_data->fsl_diu_info[3]->par;
 429                if (hw->desc[2] != ad->paddr) {
 430                                /* AOI1 is not the first in the chain */
 431                        if (pmfbi->count > 0)
 432                                /* AOI0 is open, must be the first */
 433                                pmfbi->ad->next_ad = 0;
 434                } else          /* AOI1 is the first in the chain */
 435                        out_be32(&hw->desc[2], machine_data->dummy_ad->paddr);
 436                                /* close AOI 1 */
 437                break;
 438        default:
 439                res = -EINVAL;
 440                break;
 441        }
 442
 443        return res;
 444}
 445
 446static void enable_lcdc(struct fb_info *info)
 447{
 448        struct diu *hw = dr.diu_reg;
 449        struct mfb_info *mfbi = info->par;
 450        struct fsl_diu_data *machine_data = mfbi->parent;
 451
 452        if (!machine_data->fb_enabled) {
 453                out_be32(&hw->diu_mode, dr.mode);
 454                machine_data->fb_enabled++;
 455        }
 456}
 457
 458static void disable_lcdc(struct fb_info *info)
 459{
 460        struct diu *hw = dr.diu_reg;
 461        struct mfb_info *mfbi = info->par;
 462        struct fsl_diu_data *machine_data = mfbi->parent;
 463
 464        if (machine_data->fb_enabled) {
 465                out_be32(&hw->diu_mode, 0);
 466                machine_data->fb_enabled = 0;
 467        }
 468}
 469
 470static void adjust_aoi_size_position(struct fb_var_screeninfo *var,
 471                                struct fb_info *info)
 472{
 473        struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par;
 474        struct fsl_diu_data *machine_data = mfbi->parent;
 475        int available_height, upper_aoi_bottom, index = mfbi->index;
 476        int lower_aoi_is_open, upper_aoi_is_open;
 477        __u32 base_plane_width, base_plane_height, upper_aoi_height;
 478
 479        base_plane_width = machine_data->fsl_diu_info[0]->var.xres;
 480        base_plane_height = machine_data->fsl_diu_info[0]->var.yres;
 481
 482        if (mfbi->x_aoi_d < 0)
 483                mfbi->x_aoi_d = 0;
 484        if (mfbi->y_aoi_d < 0)
 485                mfbi->y_aoi_d = 0;
 486        switch (index) {
 487        case 0:
 488                if (mfbi->x_aoi_d != 0)
 489                        mfbi->x_aoi_d = 0;
 490                if (mfbi->y_aoi_d != 0)
 491                        mfbi->y_aoi_d = 0;
 492                break;
 493        case 1:                 /* AOI 0 */
 494        case 3:
 495                lower_aoi_mfbi = machine_data->fsl_diu_info[index+1]->par;
 496                lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0;
 497                if (var->xres > base_plane_width)
 498                        var->xres = base_plane_width;
 499                if ((mfbi->x_aoi_d + var->xres) > base_plane_width)
 500                        mfbi->x_aoi_d = base_plane_width - var->xres;
 501
 502                if (lower_aoi_is_open)
 503                        available_height = lower_aoi_mfbi->y_aoi_d;
 504                else
 505                        available_height = base_plane_height;
 506                if (var->yres > available_height)
 507                        var->yres = available_height;
 508                if ((mfbi->y_aoi_d + var->yres) > available_height)
 509                        mfbi->y_aoi_d = available_height - var->yres;
 510                break;
 511        case 2:                 /* AOI 1 */
 512        case 4:
 513                upper_aoi_mfbi = machine_data->fsl_diu_info[index-1]->par;
 514                upper_aoi_height =
 515                                machine_data->fsl_diu_info[index-1]->var.yres;
 516                upper_aoi_bottom = upper_aoi_mfbi->y_aoi_d + upper_aoi_height;
 517                upper_aoi_is_open = upper_aoi_mfbi->count > 0 ? 1 : 0;
 518                if (var->xres > base_plane_width)
 519                        var->xres = base_plane_width;
 520                if ((mfbi->x_aoi_d + var->xres) > base_plane_width)
 521                        mfbi->x_aoi_d = base_plane_width - var->xres;
 522                if (mfbi->y_aoi_d < 0)
 523                        mfbi->y_aoi_d = 0;
 524                if (upper_aoi_is_open) {
 525                        if (mfbi->y_aoi_d < upper_aoi_bottom)
 526                                mfbi->y_aoi_d = upper_aoi_bottom;
 527                        available_height = base_plane_height
 528                                                - upper_aoi_bottom;
 529                } else
 530                        available_height = base_plane_height;
 531                if (var->yres > available_height)
 532                        var->yres = available_height;
 533                if ((mfbi->y_aoi_d + var->yres) > base_plane_height)
 534                        mfbi->y_aoi_d = base_plane_height - var->yres;
 535                break;
 536        }
 537}
 538/*
 539 * Checks to see if the hardware supports the state requested by var passed
 540 * in. This function does not alter the hardware state! If the var passed in
 541 * is slightly off by what the hardware can support then we alter the var
 542 * PASSED in to what we can do. If the hardware doesn't support mode change
 543 * a -EINVAL will be returned by the upper layers.
 544 */
 545static int fsl_diu_check_var(struct fb_var_screeninfo *var,
 546                                struct fb_info *info)
 547{
 548        unsigned long htotal, vtotal;
 549
 550        pr_debug("check_var xres: %d\n", var->xres);
 551        pr_debug("check_var yres: %d\n", var->yres);
 552
 553        if (var->xres_virtual < var->xres)
 554                var->xres_virtual = var->xres;
 555        if (var->yres_virtual < var->yres)
 556                var->yres_virtual = var->yres;
 557
 558        if (var->xoffset < 0)
 559                var->xoffset = 0;
 560
 561        if (var->yoffset < 0)
 562                var->yoffset = 0;
 563
 564        if (var->xoffset + info->var.xres > info->var.xres_virtual)
 565                var->xoffset = info->var.xres_virtual - info->var.xres;
 566
 567        if (var->yoffset + info->var.yres > info->var.yres_virtual)
 568                var->yoffset = info->var.yres_virtual - info->var.yres;
 569
 570        if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
 571            (var->bits_per_pixel != 16))
 572                var->bits_per_pixel = default_bpp;
 573
 574        switch (var->bits_per_pixel) {
 575        case 16:
 576                var->red.length = 5;
 577                var->red.offset = 11;
 578                var->red.msb_right = 0;
 579
 580                var->green.length = 6;
 581                var->green.offset = 5;
 582                var->green.msb_right = 0;
 583
 584                var->blue.length = 5;
 585                var->blue.offset = 0;
 586                var->blue.msb_right = 0;
 587
 588                var->transp.length = 0;
 589                var->transp.offset = 0;
 590                var->transp.msb_right = 0;
 591                break;
 592        case 24:
 593                var->red.length = 8;
 594                var->red.offset = 0;
 595                var->red.msb_right = 0;
 596
 597                var->green.length = 8;
 598                var->green.offset = 8;
 599                var->green.msb_right = 0;
 600
 601                var->blue.length = 8;
 602                var->blue.offset = 16;
 603                var->blue.msb_right = 0;
 604
 605                var->transp.length = 0;
 606                var->transp.offset = 0;
 607                var->transp.msb_right = 0;
 608                break;
 609        case 32:
 610                var->red.length = 8;
 611                var->red.offset = 16;
 612                var->red.msb_right = 0;
 613
 614                var->green.length = 8;
 615                var->green.offset = 8;
 616                var->green.msb_right = 0;
 617
 618                var->blue.length = 8;
 619                var->blue.offset = 0;
 620                var->blue.msb_right = 0;
 621
 622                var->transp.length = 8;
 623                var->transp.offset = 24;
 624                var->transp.msb_right = 0;
 625
 626                break;
 627        }
 628        /* If the pixclock is below the minimum spec'd value then set to
 629         * refresh rate for 60Hz since this is supported by most monitors.
 630         * Refer to Documentation/fb/ for calculations.
 631         */
 632        if ((var->pixclock < MIN_PIX_CLK) || (var->pixclock > MAX_PIX_CLK)) {
 633                htotal = var->xres + var->right_margin + var->hsync_len +
 634                    var->left_margin;
 635                vtotal = var->yres + var->lower_margin + var->vsync_len +
 636                    var->upper_margin;
 637                var->pixclock = (vtotal * htotal * 6UL) / 100UL;
 638                var->pixclock = KHZ2PICOS(var->pixclock);
 639                pr_debug("pixclock set for 60Hz refresh = %u ps\n",
 640                        var->pixclock);
 641        }
 642
 643        var->height = -1;
 644        var->width = -1;
 645        var->grayscale = 0;
 646
 647        /* Copy nonstd field to/from sync for fbset usage */
 648        var->sync |= var->nonstd;
 649        var->nonstd |= var->sync;
 650
 651        adjust_aoi_size_position(var, info);
 652        return 0;
 653}
 654
 655static void set_fix(struct fb_info *info)
 656{
 657        struct fb_fix_screeninfo *fix = &info->fix;
 658        struct fb_var_screeninfo *var = &info->var;
 659        struct mfb_info *mfbi = info->par;
 660
 661        strncpy(fix->id, mfbi->id, strlen(mfbi->id));
 662        fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
 663        fix->type = FB_TYPE_PACKED_PIXELS;
 664        fix->accel = FB_ACCEL_NONE;
 665        fix->visual = FB_VISUAL_TRUECOLOR;
 666        fix->xpanstep = 1;
 667        fix->ypanstep = 1;
 668}
 669
 670static void update_lcdc(struct fb_info *info)
 671{
 672        struct fb_var_screeninfo *var = &info->var;
 673        struct mfb_info *mfbi = info->par;
 674        struct fsl_diu_data *machine_data = mfbi->parent;
 675        struct diu *hw;
 676        int i, j;
 677        char __iomem *cursor_base, *gamma_table_base;
 678
 679        u32 temp;
 680
 681        hw = dr.diu_reg;
 682
 683        if (mfbi->type == MFB_TYPE_OFF) {
 684                fsl_diu_disable_panel(info);
 685                return;
 686        }
 687
 688        diu_ops.set_monitor_port(machine_data->monitor_port);
 689        gamma_table_base = pool.gamma.vaddr;
 690        cursor_base = pool.cursor.vaddr;
 691        /* Prep for DIU init  - gamma table, cursor table */
 692
 693        for (i = 0; i <= 2; i++)
 694           for (j = 0; j <= 255; j++)
 695              *gamma_table_base++ = j;
 696
 697        diu_ops.set_gamma_table(machine_data->monitor_port, pool.gamma.vaddr);
 698
 699        pr_debug("update-lcdc: HW - %p\n Disabling DIU\n", hw);
 700        disable_lcdc(info);
 701
 702        /* Program DIU registers */
 703
 704        out_be32(&hw->gamma, pool.gamma.paddr);
 705        out_be32(&hw->cursor, pool.cursor.paddr);
 706
 707        out_be32(&hw->bgnd, 0x007F7F7F);        /* BGND */
 708        out_be32(&hw->bgnd_wb, 0);              /* BGND_WB */
 709        out_be32(&hw->disp_size, (var->yres << 16 | var->xres));
 710                                                /* DISP SIZE */
 711        pr_debug("DIU xres: %d\n", var->xres);
 712        pr_debug("DIU yres: %d\n", var->yres);
 713
 714        out_be32(&hw->wb_size, 0); /* WB SIZE */
 715        out_be32(&hw->wb_mem_addr, 0); /* WB MEM ADDR */
 716
 717        /* Horizontal and vertical configuration register */
 718        temp = var->left_margin << 22 | /* BP_H */
 719               var->hsync_len << 11 |   /* PW_H */
 720               var->right_margin;       /* FP_H */
 721
 722        out_be32(&hw->hsyn_para, temp);
 723
 724        temp = var->upper_margin << 22 | /* BP_V */
 725               var->vsync_len << 11 |    /* PW_V  */
 726               var->lower_margin;        /* FP_V  */
 727
 728        out_be32(&hw->vsyn_para, temp);
 729
 730        pr_debug("DIU right_margin - %d\n", var->right_margin);
 731        pr_debug("DIU left_margin - %d\n", var->left_margin);
 732        pr_debug("DIU hsync_len - %d\n", var->hsync_len);
 733        pr_debug("DIU upper_margin - %d\n", var->upper_margin);
 734        pr_debug("DIU lower_margin - %d\n", var->lower_margin);
 735        pr_debug("DIU vsync_len - %d\n", var->vsync_len);
 736        pr_debug("DIU HSYNC - 0x%08x\n", hw->hsyn_para);
 737        pr_debug("DIU VSYNC - 0x%08x\n", hw->vsyn_para);
 738
 739        diu_ops.set_pixel_clock(var->pixclock);
 740
 741        out_be32(&hw->syn_pol, 0);      /* SYNC SIGNALS POLARITY */
 742        out_be32(&hw->thresholds, 0x00037800); /* The Thresholds */
 743        out_be32(&hw->int_status, 0);   /* INTERRUPT STATUS */
 744        out_be32(&hw->plut, 0x01F5F666);
 745
 746        /* Enable the DIU */
 747        enable_lcdc(info);
 748}
 749
 750static int map_video_memory(struct fb_info *info)
 751{
 752        phys_addr_t phys;
 753        u32 smem_len = info->fix.line_length * info->var.yres_virtual;
 754
 755        pr_debug("info->var.xres_virtual = %d\n", info->var.xres_virtual);
 756        pr_debug("info->var.yres_virtual = %d\n", info->var.yres_virtual);
 757        pr_debug("info->fix.line_length  = %d\n", info->fix.line_length);
 758        pr_debug("MAP_VIDEO_MEMORY: smem_len = %u\n", smem_len);
 759
 760        info->screen_base = fsl_diu_alloc(smem_len, &phys);
 761        if (info->screen_base == NULL) {
 762                printk(KERN_ERR "Unable to allocate fb memory\n");
 763                return -ENOMEM;
 764        }
 765        mutex_lock(&info->mm_lock);
 766        info->fix.smem_start = (unsigned long) phys;
 767        info->fix.smem_len = smem_len;
 768        mutex_unlock(&info->mm_lock);
 769        info->screen_size = info->fix.smem_len;
 770
 771        pr_debug("Allocated fb @ paddr=0x%08lx, size=%d.\n",
 772                 info->fix.smem_start, info->fix.smem_len);
 773        pr_debug("screen base %p\n", info->screen_base);
 774
 775        return 0;
 776}
 777
 778static void unmap_video_memory(struct fb_info *info)
 779{
 780        fsl_diu_free(info->screen_base, info->fix.smem_len);
 781        mutex_lock(&info->mm_lock);
 782        info->screen_base = NULL;
 783        info->fix.smem_start = 0;
 784        info->fix.smem_len = 0;
 785        mutex_unlock(&info->mm_lock);
 786}
 787
 788/*
 789 * Using the fb_var_screeninfo in fb_info we set the aoi of this
 790 * particular framebuffer. It is a light version of fsl_diu_set_par.
 791 */
 792static int fsl_diu_set_aoi(struct fb_info *info)
 793{
 794        struct fb_var_screeninfo *var = &info->var;
 795        struct mfb_info *mfbi = info->par;
 796        struct diu_ad *ad = mfbi->ad;
 797
 798        /* AOI should not be greater than display size */
 799        ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset);
 800        ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);
 801        return 0;
 802}
 803
 804/*
 805 * Using the fb_var_screeninfo in fb_info we set the resolution of this
 806 * particular framebuffer. This function alters the fb_fix_screeninfo stored
 807 * in fb_info. It does not alter var in fb_info since we are using that
 808 * data. This means we depend on the data in var inside fb_info to be
 809 * supported by the hardware. fsl_diu_check_var is always called before
 810 * fsl_diu_set_par to ensure this.
 811 */
 812static int fsl_diu_set_par(struct fb_info *info)
 813{
 814        unsigned long len;
 815        struct fb_var_screeninfo *var = &info->var;
 816        struct mfb_info *mfbi = info->par;
 817        struct fsl_diu_data *machine_data = mfbi->parent;
 818        struct diu_ad *ad = mfbi->ad;
 819        struct diu *hw;
 820
 821        hw = dr.diu_reg;
 822
 823        set_fix(info);
 824        mfbi->cursor_reset = 1;
 825
 826        len = info->var.yres_virtual * info->fix.line_length;
 827        /* Alloc & dealloc each time resolution/bpp change */
 828        if (len != info->fix.smem_len) {
 829                if (info->fix.smem_start)
 830                        unmap_video_memory(info);
 831                pr_debug("SET PAR: smem_len = %d\n", info->fix.smem_len);
 832
 833                /* Memory allocation for framebuffer */
 834                if (map_video_memory(info)) {
 835                        printk(KERN_ERR "Unable to allocate fb memory 1\n");
 836                        return -ENOMEM;
 837                }
 838        }
 839
 840        ad->pix_fmt =
 841                diu_ops.get_pixel_format(var->bits_per_pixel,
 842                                         machine_data->monitor_port);
 843        ad->addr    = cpu_to_le32(info->fix.smem_start);
 844        ad->src_size_g_alpha = cpu_to_le32((var->yres_virtual << 12) |
 845                                var->xres_virtual) | mfbi->g_alpha;
 846        /* AOI should not be greater than display size */
 847        ad->aoi_size    = cpu_to_le32((var->yres << 16) | var->xres);
 848        ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset);
 849        ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);
 850
 851        /* Disable chroma keying function */
 852        ad->ckmax_r = 0;
 853        ad->ckmax_g = 0;
 854        ad->ckmax_b = 0;
 855
 856        ad->ckmin_r = 255;
 857        ad->ckmin_g = 255;
 858        ad->ckmin_b = 255;
 859
 860        if (mfbi->index == 0)
 861                update_lcdc(info);
 862        return 0;
 863}
 864
 865static inline __u32 CNVT_TOHW(__u32 val, __u32 width)
 866{
 867        return ((val<<width) + 0x7FFF - val)>>16;
 868}
 869
 870/*
 871 * Set a single color register. The values supplied have a 16 bit magnitude
 872 * which needs to be scaled in this function for the hardware. Things to take
 873 * into consideration are how many color registers, if any, are supported with
 874 * the current color visual. With truecolor mode no color palettes are
 875 * supported. Here a psuedo palette is created which we store the value in
 876 * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited
 877 * color palette.
 878 */
 879static int fsl_diu_setcolreg(unsigned regno, unsigned red, unsigned green,
 880                           unsigned blue, unsigned transp, struct fb_info *info)
 881{
 882        int ret = 1;
 883
 884        /*
 885         * If greyscale is true, then we convert the RGB value
 886         * to greyscale no matter what visual we are using.
 887         */
 888        if (info->var.grayscale)
 889                red = green = blue = (19595 * red + 38470 * green +
 890                                      7471 * blue) >> 16;
 891        switch (info->fix.visual) {
 892        case FB_VISUAL_TRUECOLOR:
 893                /*
 894                 * 16-bit True Colour.  We encode the RGB value
 895                 * according to the RGB bitfield information.
 896                 */
 897                if (regno < 16) {
 898                        u32 *pal = info->pseudo_palette;
 899                        u32 v;
 900
 901                        red = CNVT_TOHW(red, info->var.red.length);
 902                        green = CNVT_TOHW(green, info->var.green.length);
 903                        blue = CNVT_TOHW(blue, info->var.blue.length);
 904                        transp = CNVT_TOHW(transp, info->var.transp.length);
 905
 906                        v = (red << info->var.red.offset) |
 907                            (green << info->var.green.offset) |
 908                            (blue << info->var.blue.offset) |
 909                            (transp << info->var.transp.offset);
 910
 911                        pal[regno] = v;
 912                        ret = 0;
 913                }
 914                break;
 915        case FB_VISUAL_STATIC_PSEUDOCOLOR:
 916        case FB_VISUAL_PSEUDOCOLOR:
 917                break;
 918        }
 919
 920        return ret;
 921}
 922
 923/*
 924 * Pan (or wrap, depending on the `vmode' field) the display using the
 925 * 'xoffset' and 'yoffset' fields of the 'var' structure. If the values
 926 * don't fit, return -EINVAL.
 927 */
 928static int fsl_diu_pan_display(struct fb_var_screeninfo *var,
 929                             struct fb_info *info)
 930{
 931        if ((info->var.xoffset == var->xoffset) &&
 932            (info->var.yoffset == var->yoffset))
 933                return 0;       /* No change, do nothing */
 934
 935        if (var->xoffset < 0 || var->yoffset < 0
 936            || var->xoffset + info->var.xres > info->var.xres_virtual
 937            || var->yoffset + info->var.yres > info->var.yres_virtual)
 938                return -EINVAL;
 939
 940        info->var.xoffset = var->xoffset;
 941        info->var.yoffset = var->yoffset;
 942
 943        if (var->vmode & FB_VMODE_YWRAP)
 944                info->var.vmode |= FB_VMODE_YWRAP;
 945        else
 946                info->var.vmode &= ~FB_VMODE_YWRAP;
 947
 948        fsl_diu_set_aoi(info);
 949
 950        return 0;
 951}
 952
 953/*
 954 * Blank the screen if blank_mode != 0, else unblank. Return 0 if blanking
 955 * succeeded, != 0 if un-/blanking failed.
 956 * blank_mode == 2: suspend vsync
 957 * blank_mode == 3: suspend hsync
 958 * blank_mode == 4: powerdown
 959 */
 960static int fsl_diu_blank(int blank_mode, struct fb_info *info)
 961{
 962        struct mfb_info *mfbi = info->par;
 963
 964        mfbi->blank = blank_mode;
 965
 966        switch (blank_mode) {
 967        case FB_BLANK_VSYNC_SUSPEND:
 968        case FB_BLANK_HSYNC_SUSPEND:
 969        /* FIXME: fixes to enable_panel and enable lcdc needed */
 970        case FB_BLANK_NORMAL:
 971        /*      fsl_diu_disable_panel(info);*/
 972                break;
 973        case FB_BLANK_POWERDOWN:
 974        /*      disable_lcdc(info);     */
 975                break;
 976        case FB_BLANK_UNBLANK:
 977        /*      fsl_diu_enable_panel(info);*/
 978                break;
 979        }
 980
 981        return 0;
 982}
 983
 984static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
 985                       unsigned long arg)
 986{
 987        struct mfb_info *mfbi = info->par;
 988        struct diu_ad *ad = mfbi->ad;
 989        struct mfb_chroma_key ck;
 990        unsigned char global_alpha;
 991        struct aoi_display_offset aoi_d;
 992        __u32 pix_fmt;
 993        void __user *buf = (void __user *)arg;
 994
 995        if (!arg)
 996                return -EINVAL;
 997        switch (cmd) {
 998        case MFB_SET_PIXFMT:
 999                if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt)))
1000                        return -EFAULT;
1001                ad->pix_fmt = pix_fmt;
1002                pr_debug("Set pixel format to 0x%08x\n", ad->pix_fmt);
1003                break;
1004        case MFB_GET_PIXFMT:
1005                pix_fmt = ad->pix_fmt;
1006                if (copy_to_user(buf, &pix_fmt, sizeof(pix_fmt)))
1007                        return -EFAULT;
1008                pr_debug("get pixel format 0x%08x\n", ad->pix_fmt);
1009                break;
1010        case MFB_SET_AOID:
1011                if (copy_from_user(&aoi_d, buf, sizeof(aoi_d)))
1012                        return -EFAULT;
1013                mfbi->x_aoi_d = aoi_d.x_aoi_d;
1014                mfbi->y_aoi_d = aoi_d.y_aoi_d;
1015                pr_debug("set AOI display offset of index %d to (%d,%d)\n",
1016                                 mfbi->index, aoi_d.x_aoi_d, aoi_d.y_aoi_d);
1017                fsl_diu_check_var(&info->var, info);
1018                fsl_diu_set_aoi(info);
1019                break;
1020        case MFB_GET_AOID:
1021                aoi_d.x_aoi_d = mfbi->x_aoi_d;
1022                aoi_d.y_aoi_d = mfbi->y_aoi_d;
1023                if (copy_to_user(buf, &aoi_d, sizeof(aoi_d)))
1024                        return -EFAULT;
1025                pr_debug("get AOI display offset of index %d (%d,%d)\n",
1026                                mfbi->index, aoi_d.x_aoi_d, aoi_d.y_aoi_d);
1027                break;
1028        case MFB_GET_ALPHA:
1029                global_alpha = mfbi->g_alpha;
1030                if (copy_to_user(buf, &global_alpha, sizeof(global_alpha)))
1031                        return -EFAULT;
1032                pr_debug("get global alpha of index %d\n", mfbi->index);
1033                break;
1034        case MFB_SET_ALPHA:
1035                /* set panel information */
1036                if (copy_from_user(&global_alpha, buf, sizeof(global_alpha)))
1037                        return -EFAULT;
1038                ad->src_size_g_alpha = (ad->src_size_g_alpha & (~0xff)) |
1039                                                        (global_alpha & 0xff);
1040                mfbi->g_alpha = global_alpha;
1041                pr_debug("set global alpha for index %d\n", mfbi->index);
1042                break;
1043        case MFB_SET_CHROMA_KEY:
1044                /* set panel winformation */
1045                if (copy_from_user(&ck, buf, sizeof(ck)))
1046                        return -EFAULT;
1047
1048                if (ck.enable &&
1049                   (ck.red_max < ck.red_min ||
1050                    ck.green_max < ck.green_min ||
1051                    ck.blue_max < ck.blue_min))
1052                        return -EINVAL;
1053
1054                if (!ck.enable) {
1055                        ad->ckmax_r = 0;
1056                        ad->ckmax_g = 0;
1057                        ad->ckmax_b = 0;
1058                        ad->ckmin_r = 255;
1059                        ad->ckmin_g = 255;
1060                        ad->ckmin_b = 255;
1061                } else {
1062                        ad->ckmax_r = ck.red_max;
1063                        ad->ckmax_g = ck.green_max;
1064                        ad->ckmax_b = ck.blue_max;
1065                        ad->ckmin_r = ck.red_min;
1066                        ad->ckmin_g = ck.green_min;
1067                        ad->ckmin_b = ck.blue_min;
1068                }
1069                pr_debug("set chroma key\n");
1070                break;
1071        case FBIOGET_GWINFO:
1072                if (mfbi->type == MFB_TYPE_OFF)
1073                        return -ENODEV;
1074                /* get graphic window information */
1075                if (copy_to_user(buf, ad, sizeof(*ad)))
1076                        return -EFAULT;
1077                break;
1078        case FBIOGET_HWCINFO:
1079                pr_debug("FBIOGET_HWCINFO:0x%08x\n", FBIOGET_HWCINFO);
1080                break;
1081        case FBIOPUT_MODEINFO:
1082                pr_debug("FBIOPUT_MODEINFO:0x%08x\n", FBIOPUT_MODEINFO);
1083                break;
1084        case FBIOGET_DISPINFO:
1085                pr_debug("FBIOGET_DISPINFO:0x%08x\n", FBIOGET_DISPINFO);
1086                break;
1087
1088        default:
1089                printk(KERN_ERR "Unknown ioctl command (0x%08X)\n", cmd);
1090                return -ENOIOCTLCMD;
1091        }
1092
1093        return 0;
1094}
1095
1096/* turn on fb if count == 1
1097 */
1098static int fsl_diu_open(struct fb_info *info, int user)
1099{
1100        struct mfb_info *mfbi = info->par;
1101        int res = 0;
1102
1103        spin_lock(&diu_lock);
1104        mfbi->count++;
1105        if (mfbi->count == 1) {
1106                pr_debug("open plane index %d\n", mfbi->index);
1107                fsl_diu_check_var(&info->var, info);
1108                res = fsl_diu_set_par(info);
1109                if (res < 0)
1110                        mfbi->count--;
1111                else {
1112                        res = fsl_diu_enable_panel(info);
1113                        if (res < 0)
1114                                mfbi->count--;
1115                }
1116        }
1117
1118        spin_unlock(&diu_lock);
1119        return res;
1120}
1121
1122/* turn off fb if count == 0
1123 */
1124static int fsl_diu_release(struct fb_info *info, int user)
1125{
1126        struct mfb_info *mfbi = info->par;
1127        int res = 0;
1128
1129        spin_lock(&diu_lock);
1130        mfbi->count--;
1131        if (mfbi->count == 0) {
1132                pr_debug("release plane index %d\n", mfbi->index);
1133                res = fsl_diu_disable_panel(info);
1134                if (res < 0)
1135                        mfbi->count++;
1136        }
1137        spin_unlock(&diu_lock);
1138        return res;
1139}
1140
1141static struct fb_ops fsl_diu_ops = {
1142        .owner = THIS_MODULE,
1143        .fb_check_var = fsl_diu_check_var,
1144        .fb_set_par = fsl_diu_set_par,
1145        .fb_setcolreg = fsl_diu_setcolreg,
1146        .fb_blank = fsl_diu_blank,
1147        .fb_pan_display = fsl_diu_pan_display,
1148        .fb_fillrect = cfb_fillrect,
1149        .fb_copyarea = cfb_copyarea,
1150        .fb_imageblit = cfb_imageblit,
1151        .fb_ioctl = fsl_diu_ioctl,
1152        .fb_open = fsl_diu_open,
1153        .fb_release = fsl_diu_release,
1154};
1155
1156static int init_fbinfo(struct fb_info *info)
1157{
1158        struct mfb_info *mfbi = info->par;
1159
1160        info->device = NULL;
1161        info->var.activate = FB_ACTIVATE_NOW;
1162        info->fbops = &fsl_diu_ops;
1163        info->flags = FBINFO_FLAG_DEFAULT;
1164        info->pseudo_palette = &mfbi->pseudo_palette;
1165
1166        /* Allocate colormap */
1167        fb_alloc_cmap(&info->cmap, 16, 0);
1168        return 0;
1169}
1170
1171static int __devinit install_fb(struct fb_info *info)
1172{
1173        int rc;
1174        struct mfb_info *mfbi = info->par;
1175        const char *aoi_mode, *init_aoi_mode = "320x240";
1176
1177        if (init_fbinfo(info))
1178                return -EINVAL;
1179
1180        if (mfbi->index == 0)   /* plane 0 */
1181                aoi_mode = fb_mode;
1182        else
1183                aoi_mode = init_aoi_mode;
1184        pr_debug("mode used = %s\n", aoi_mode);
1185        rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db,
1186             ARRAY_SIZE(fsl_diu_mode_db), &fsl_diu_default_mode, default_bpp);
1187
1188        switch (rc) {
1189        case 1:
1190                pr_debug("using mode specified in @mode\n");
1191                break;
1192        case 2:
1193                pr_debug("using mode specified in @mode "
1194                        "with ignored refresh rate\n");
1195                break;
1196        case 3:
1197                pr_debug("using mode default mode\n");
1198                break;
1199        case 4:
1200                pr_debug("using mode from list\n");
1201                break;
1202        default:
1203                pr_debug("rc = %d\n", rc);
1204                pr_debug("failed to find mode\n");
1205                return -EINVAL;
1206                break;
1207        }
1208
1209        pr_debug("xres_virtual %d\n", info->var.xres_virtual);
1210        pr_debug("bits_per_pixel %d\n", info->var.bits_per_pixel);
1211
1212        pr_debug("info->var.yres_virtual = %d\n", info->var.yres_virtual);
1213        pr_debug("info->fix.line_length = %d\n", info->fix.line_length);
1214
1215        if (mfbi->type == MFB_TYPE_OFF)
1216                mfbi->blank = FB_BLANK_NORMAL;
1217        else
1218                mfbi->blank = FB_BLANK_UNBLANK;
1219
1220        if (fsl_diu_check_var(&info->var, info)) {
1221                printk(KERN_ERR "fb_check_var failed");
1222                fb_dealloc_cmap(&info->cmap);
1223                return -EINVAL;
1224        }
1225
1226        if (register_framebuffer(info) < 0) {
1227                printk(KERN_ERR "register_framebuffer failed");
1228                unmap_video_memory(info);
1229                fb_dealloc_cmap(&info->cmap);
1230                return -EINVAL;
1231        }
1232
1233        mfbi->registered = 1;
1234        printk(KERN_INFO "fb%d: %s fb device registered successfully.\n",
1235                 info->node, info->fix.id);
1236
1237        return 0;
1238}
1239
1240static void uninstall_fb(struct fb_info *info)
1241{
1242        struct mfb_info *mfbi = info->par;
1243
1244        if (!mfbi->registered)
1245                return;
1246
1247        unregister_framebuffer(info);
1248        unmap_video_memory(info);
1249        if (&info->cmap)
1250                fb_dealloc_cmap(&info->cmap);
1251
1252        mfbi->registered = 0;
1253}
1254
1255static irqreturn_t fsl_diu_isr(int irq, void *dev_id)
1256{
1257        struct diu *hw = dr.diu_reg;
1258        unsigned int status = in_be32(&hw->int_status);
1259
1260        if (status) {
1261                /* This is the workaround for underrun */
1262                if (status & INT_UNDRUN) {
1263                        out_be32(&hw->diu_mode, 0);
1264                        pr_debug("Err: DIU occurs underrun!\n");
1265                        udelay(1);
1266                        out_be32(&hw->diu_mode, 1);
1267                }
1268#if defined(CONFIG_NOT_COHERENT_CACHE)
1269                else if (status & INT_VSYNC) {
1270                        unsigned int i;
1271                        for (i = 0; i < coherence_data_size;
1272                                i += d_cache_line_size)
1273                                __asm__ __volatile__ (
1274                                        "dcbz 0, %[input]"
1275                                ::[input]"r"(&coherence_data[i]));
1276                }
1277#endif
1278                return IRQ_HANDLED;
1279        }
1280        return IRQ_NONE;
1281}
1282
1283static int request_irq_local(int irq)
1284{
1285        unsigned long status, ints;
1286        struct diu *hw;
1287        int ret;
1288
1289        hw = dr.diu_reg;
1290
1291        /* Read to clear the status */
1292        status = in_be32(&hw->int_status);
1293
1294        ret = request_irq(irq, fsl_diu_isr, 0, "diu", NULL);
1295        if (ret)
1296                pr_info("Request diu IRQ failed.\n");
1297        else {
1298                ints = INT_PARERR | INT_LS_BF_VS;
1299#if !defined(CONFIG_NOT_COHERENT_CACHE)
1300                ints |= INT_VSYNC;
1301#endif
1302                if (dr.mode == MFB_MODE2 || dr.mode == MFB_MODE3)
1303                        ints |= INT_VSYNC_WB;
1304
1305                /* Read to clear the status */
1306                status = in_be32(&hw->int_status);
1307                out_be32(&hw->int_mask, ints);
1308        }
1309        return ret;
1310}
1311
1312static void free_irq_local(int irq)
1313{
1314        struct diu *hw = dr.diu_reg;
1315
1316        /* Disable all LCDC interrupt */
1317        out_be32(&hw->int_mask, 0x1f);
1318
1319        free_irq(irq, NULL);
1320}
1321
1322#ifdef CONFIG_PM
1323/*
1324 * Power management hooks. Note that we won't be called from IRQ context,
1325 * unlike the blank functions above, so we may sleep.
1326 */
1327static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state)
1328{
1329        struct fsl_diu_data *machine_data;
1330
1331        machine_data = dev_get_drvdata(&ofdev->dev);
1332        disable_lcdc(machine_data->fsl_diu_info[0]);
1333
1334        return 0;
1335}
1336
1337static int fsl_diu_resume(struct of_device *ofdev)
1338{
1339        struct fsl_diu_data *machine_data;
1340
1341        machine_data = dev_get_drvdata(&ofdev->dev);
1342        enable_lcdc(machine_data->fsl_diu_info[0]);
1343
1344        return 0;
1345}
1346
1347#else
1348#define fsl_diu_suspend NULL
1349#define fsl_diu_resume NULL
1350#endif                          /* CONFIG_PM */
1351
1352/* Align to 64-bit(8-byte), 32-byte, etc. */
1353static int allocate_buf(struct device *dev, struct diu_addr *buf, u32 size,
1354                        u32 bytes_align)
1355{
1356        u32 offset, ssize;
1357        u32 mask;
1358        dma_addr_t paddr = 0;
1359
1360        ssize = size + bytes_align;
1361        buf->vaddr = dma_alloc_coherent(dev, ssize, &paddr, GFP_DMA |
1362                                                             __GFP_ZERO);
1363        if (!buf->vaddr)
1364                return -ENOMEM;
1365
1366        buf->paddr = (__u32) paddr;
1367
1368        mask = bytes_align - 1;
1369        offset = (u32)buf->paddr & mask;
1370        if (offset) {
1371                buf->offset = bytes_align - offset;
1372                buf->paddr = (u32)buf->paddr + offset;
1373        } else
1374                buf->offset = 0;
1375        return 0;
1376}
1377
1378static void free_buf(struct device *dev, struct diu_addr *buf, u32 size,
1379                     u32 bytes_align)
1380{
1381        dma_free_coherent(dev, size + bytes_align,
1382                                buf->vaddr, (buf->paddr - buf->offset));
1383        return;
1384}
1385
1386static ssize_t store_monitor(struct device *device,
1387        struct device_attribute *attr, const char *buf, size_t count)
1388{
1389        int old_monitor_port;
1390        unsigned long val;
1391        struct fsl_diu_data *machine_data =
1392                container_of(attr, struct fsl_diu_data, dev_attr);
1393
1394        if (strict_strtoul(buf, 10, &val))
1395                return 0;
1396
1397        old_monitor_port = machine_data->monitor_port;
1398        machine_data->monitor_port = diu_ops.set_sysfs_monitor_port(val);
1399
1400        if (old_monitor_port != machine_data->monitor_port) {
1401                /* All AOIs need adjust pixel format
1402                 * fsl_diu_set_par only change the pixsel format here
1403                 * unlikely to fail. */
1404                fsl_diu_set_par(machine_data->fsl_diu_info[0]);
1405                fsl_diu_set_par(machine_data->fsl_diu_info[1]);
1406                fsl_diu_set_par(machine_data->fsl_diu_info[2]);
1407                fsl_diu_set_par(machine_data->fsl_diu_info[3]);
1408                fsl_diu_set_par(machine_data->fsl_diu_info[4]);
1409        }
1410        return count;
1411}
1412
1413static ssize_t show_monitor(struct device *device,
1414        struct device_attribute *attr, char *buf)
1415{
1416        struct fsl_diu_data *machine_data =
1417                container_of(attr, struct fsl_diu_data, dev_attr);
1418        return diu_ops.show_monitor_port(machine_data->monitor_port, buf);
1419}
1420
1421static int __devinit fsl_diu_probe(struct of_device *ofdev,
1422        const struct of_device_id *match)
1423{
1424        struct device_node *np = ofdev->node;
1425        struct mfb_info *mfbi;
1426        phys_addr_t dummy_ad_addr;
1427        int ret, i, error = 0;
1428        struct resource res;
1429        struct fsl_diu_data *machine_data;
1430
1431        machine_data = kzalloc(sizeof(struct fsl_diu_data), GFP_KERNEL);
1432        if (!machine_data)
1433                return -ENOMEM;
1434
1435        for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
1436                machine_data->fsl_diu_info[i] =
1437                        framebuffer_alloc(sizeof(struct mfb_info), &ofdev->dev);
1438                if (!machine_data->fsl_diu_info[i]) {
1439                        dev_err(&ofdev->dev, "cannot allocate memory\n");
1440                        ret = -ENOMEM;
1441                        goto error2;
1442                }
1443                mfbi = machine_data->fsl_diu_info[i]->par;
1444                memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
1445                mfbi->parent = machine_data;
1446        }
1447
1448        ret = of_address_to_resource(np, 0, &res);
1449        if (ret) {
1450                dev_err(&ofdev->dev, "could not obtain DIU address\n");
1451                goto error;
1452        }
1453        if (!res.start) {
1454                dev_err(&ofdev->dev, "invalid DIU address\n");
1455                goto error;
1456        }
1457        dev_dbg(&ofdev->dev, "%s, res.start: 0x%08x\n", __func__, res.start);
1458
1459        dr.diu_reg = ioremap(res.start, sizeof(struct diu));
1460        if (!dr.diu_reg) {
1461                dev_err(&ofdev->dev, "Err: can't map DIU registers!\n");
1462                ret = -EFAULT;
1463                goto error2;
1464        }
1465
1466        out_be32(&dr.diu_reg->diu_mode, 0);             /* disable DIU anyway*/
1467
1468        /* Get the IRQ of the DIU */
1469        machine_data->irq = irq_of_parse_and_map(np, 0);
1470
1471        if (!machine_data->irq) {
1472                dev_err(&ofdev->dev, "could not get DIU IRQ\n");
1473                ret = -EINVAL;
1474                goto error;
1475        }
1476        machine_data->monitor_port = monitor_port;
1477
1478        /* Area descriptor memory pool aligns to 64-bit boundary */
1479        if (allocate_buf(&ofdev->dev, &pool.ad,
1480                         sizeof(struct diu_ad) * FSL_AOI_NUM, 8))
1481                return -ENOMEM;
1482
1483        /* Get memory for Gamma Table  - 32-byte aligned memory */
1484        if (allocate_buf(&ofdev->dev, &pool.gamma, 768, 32)) {
1485                ret = -ENOMEM;
1486                goto error;
1487        }
1488
1489        /* For performance, cursor bitmap buffer aligns to 32-byte boundary */
1490        if (allocate_buf(&ofdev->dev, &pool.cursor, MAX_CURS * MAX_CURS * 2,
1491                         32)) {
1492                ret = -ENOMEM;
1493                goto error;
1494        }
1495
1496        i = ARRAY_SIZE(machine_data->fsl_diu_info);
1497        machine_data->dummy_ad = (struct diu_ad *)
1498                        ((u32)pool.ad.vaddr + pool.ad.offset) + i;
1499        machine_data->dummy_ad->paddr = pool.ad.paddr +
1500                        i * sizeof(struct diu_ad);
1501        machine_data->dummy_aoi_virt = fsl_diu_alloc(64, &dummy_ad_addr);
1502        if (!machine_data->dummy_aoi_virt) {
1503                ret = -ENOMEM;
1504                goto error;
1505        }
1506        machine_data->dummy_ad->addr = cpu_to_le32(dummy_ad_addr);
1507        machine_data->dummy_ad->pix_fmt = 0x88882317;
1508        machine_data->dummy_ad->src_size_g_alpha = cpu_to_le32((4 << 12) | 4);
1509        machine_data->dummy_ad->aoi_size = cpu_to_le32((4 << 16) |  2);
1510        machine_data->dummy_ad->offset_xyi = 0;
1511        machine_data->dummy_ad->offset_xyd = 0;
1512        machine_data->dummy_ad->next_ad = 0;
1513
1514        out_be32(&dr.diu_reg->desc[0], machine_data->dummy_ad->paddr);
1515        out_be32(&dr.diu_reg->desc[1], machine_data->dummy_ad->paddr);
1516        out_be32(&dr.diu_reg->desc[2], machine_data->dummy_ad->paddr);
1517
1518        for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
1519                machine_data->fsl_diu_info[i]->fix.smem_start = 0;
1520                mfbi = machine_data->fsl_diu_info[i]->par;
1521                mfbi->ad = (struct diu_ad *)((u32)pool.ad.vaddr
1522                                        + pool.ad.offset) + i;
1523                mfbi->ad->paddr = pool.ad.paddr + i * sizeof(struct diu_ad);
1524                ret = install_fb(machine_data->fsl_diu_info[i]);
1525                if (ret) {
1526                        dev_err(&ofdev->dev,
1527                                "Failed to register framebuffer %d\n",
1528                                i);
1529                        goto error;
1530                }
1531        }
1532
1533        if (request_irq_local(machine_data->irq)) {
1534                dev_err(machine_data->fsl_diu_info[0]->dev,
1535                        "could not request irq for diu.");
1536                goto error;
1537        }
1538
1539        machine_data->dev_attr.attr.name = "monitor";
1540        machine_data->dev_attr.attr.mode = S_IRUGO|S_IWUSR;
1541        machine_data->dev_attr.show = show_monitor;
1542        machine_data->dev_attr.store = store_monitor;
1543        error = device_create_file(machine_data->fsl_diu_info[0]->dev,
1544                                  &machine_data->dev_attr);
1545        if (error) {
1546                dev_err(machine_data->fsl_diu_info[0]->dev,
1547                        "could not create sysfs %s file\n",
1548                        machine_data->dev_attr.attr.name);
1549        }
1550
1551        dev_set_drvdata(&ofdev->dev, machine_data);
1552        return 0;
1553
1554error:
1555        for (i = ARRAY_SIZE(machine_data->fsl_diu_info);
1556                i > 0; i--)
1557                uninstall_fb(machine_data->fsl_diu_info[i - 1]);
1558        if (pool.ad.vaddr)
1559                free_buf(&ofdev->dev, &pool.ad,
1560                         sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
1561        if (pool.gamma.vaddr)
1562                free_buf(&ofdev->dev, &pool.gamma, 768, 32);
1563        if (pool.cursor.vaddr)
1564                free_buf(&ofdev->dev, &pool.cursor, MAX_CURS * MAX_CURS * 2,
1565                         32);
1566        if (machine_data->dummy_aoi_virt)
1567                fsl_diu_free(machine_data->dummy_aoi_virt, 64);
1568        iounmap(dr.diu_reg);
1569
1570error2:
1571        for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
1572                if (machine_data->fsl_diu_info[i])
1573                        framebuffer_release(machine_data->fsl_diu_info[i]);
1574        kfree(machine_data);
1575
1576        return ret;
1577}
1578
1579
1580static int fsl_diu_remove(struct of_device *ofdev)
1581{
1582        struct fsl_diu_data *machine_data;
1583        int i;
1584
1585        machine_data = dev_get_drvdata(&ofdev->dev);
1586        disable_lcdc(machine_data->fsl_diu_info[0]);
1587        free_irq_local(machine_data->irq);
1588        for (i = ARRAY_SIZE(machine_data->fsl_diu_info); i > 0; i--)
1589                uninstall_fb(machine_data->fsl_diu_info[i - 1]);
1590        if (pool.ad.vaddr)
1591                free_buf(&ofdev->dev, &pool.ad,
1592                         sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
1593        if (pool.gamma.vaddr)
1594                free_buf(&ofdev->dev, &pool.gamma, 768, 32);
1595        if (pool.cursor.vaddr)
1596                free_buf(&ofdev->dev, &pool.cursor, MAX_CURS * MAX_CURS * 2,
1597                         32);
1598        if (machine_data->dummy_aoi_virt)
1599                fsl_diu_free(machine_data->dummy_aoi_virt, 64);
1600        iounmap(dr.diu_reg);
1601        for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
1602                if (machine_data->fsl_diu_info[i])
1603                        framebuffer_release(machine_data->fsl_diu_info[i]);
1604        kfree(machine_data);
1605
1606        return 0;
1607}
1608
1609#ifndef MODULE
1610static int __init fsl_diu_setup(char *options)
1611{
1612        char *opt;
1613        unsigned long val;
1614
1615        if (!options || !*options)
1616                return 0;
1617
1618        while ((opt = strsep(&options, ",")) != NULL) {
1619                if (!*opt)
1620                        continue;
1621                if (!strncmp(opt, "monitor=", 8)) {
1622                        if (!strict_strtoul(opt + 8, 10, &val) && (val <= 2))
1623                                monitor_port = val;
1624                } else if (!strncmp(opt, "bpp=", 4)) {
1625                        if (!strict_strtoul(opt + 4, 10, &val))
1626                                default_bpp = val;
1627                } else
1628                        fb_mode = opt;
1629        }
1630
1631        return 0;
1632}
1633#endif
1634
1635static struct of_device_id fsl_diu_match[] = {
1636        {
1637                .compatible = "fsl,diu",
1638        },
1639        {}
1640};
1641MODULE_DEVICE_TABLE(of, fsl_diu_match);
1642
1643static struct of_platform_driver fsl_diu_driver = {
1644        .owner          = THIS_MODULE,
1645        .name           = "fsl_diu",
1646        .match_table    = fsl_diu_match,
1647        .probe          = fsl_diu_probe,
1648        .remove         = fsl_diu_remove,
1649        .suspend        = fsl_diu_suspend,
1650        .resume         = fsl_diu_resume,
1651};
1652
1653static int __init fsl_diu_init(void)
1654{
1655#ifdef CONFIG_NOT_COHERENT_CACHE
1656        struct device_node *np;
1657        const u32 *prop;
1658#endif
1659        int ret;
1660#ifndef MODULE
1661        char *option;
1662
1663        /*
1664         * For kernel boot options (in 'video=xxxfb:<options>' format)
1665         */
1666        if (fb_get_options("fslfb", &option))
1667                return -ENODEV;
1668        fsl_diu_setup(option);
1669#endif
1670        printk(KERN_INFO "Freescale DIU driver\n");
1671
1672#ifdef CONFIG_NOT_COHERENT_CACHE
1673        np = of_find_node_by_type(NULL, "cpu");
1674        if (!np) {
1675                printk(KERN_ERR "Err: can't find device node 'cpu'\n");
1676                return -ENODEV;
1677        }
1678
1679        prop = of_get_property(np, "d-cache-size", NULL);
1680        if (prop == NULL) {
1681                of_node_put(np);
1682                return -ENODEV;
1683        }
1684
1685        /* Freescale PLRU requires 13/8 times the cache size to do a proper
1686           displacement flush
1687         */
1688        coherence_data_size = *prop * 13;
1689        coherence_data_size /= 8;
1690
1691        prop = of_get_property(np, "d-cache-line-size", NULL);
1692        if (prop == NULL) {
1693                of_node_put(np);
1694                return -ENODEV;
1695        }
1696        d_cache_line_size = *prop;
1697
1698        of_node_put(np);
1699        coherence_data = vmalloc(coherence_data_size);
1700        if (!coherence_data)
1701                return -ENOMEM;
1702#endif
1703        ret = of_register_platform_driver(&fsl_diu_driver);
1704        if (ret) {
1705                printk(KERN_ERR
1706                        "fsl-diu: failed to register platform driver\n");
1707#if defined(CONFIG_NOT_COHERENT_CACHE)
1708                vfree(coherence_data);
1709#endif
1710                iounmap(dr.diu_reg);
1711        }
1712        return ret;
1713}
1714
1715static void __exit fsl_diu_exit(void)
1716{
1717        of_unregister_platform_driver(&fsl_diu_driver);
1718#if defined(CONFIG_NOT_COHERENT_CACHE)
1719        vfree(coherence_data);
1720#endif
1721}
1722
1723module_init(fsl_diu_init);
1724module_exit(fsl_diu_exit);
1725
1726MODULE_AUTHOR("York Sun <yorksun@freescale.com>");
1727MODULE_DESCRIPTION("Freescale DIU framebuffer driver");
1728MODULE_LICENSE("GPL");
1729
1730module_param_named(mode, fb_mode, charp, 0);
1731MODULE_PARM_DESC(mode,
1732        "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
1733module_param_named(bpp, default_bpp, ulong, 0);
1734MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified mode");
1735module_param_named(monitor, monitor_port, int, 0);
1736MODULE_PARM_DESC(monitor,
1737        "Specify the monitor port (0, 1 or 2) if supported by the platform");
1738
1739