]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
MXC IPUv3 fb: remove bogus type casts
authorLothar Waßmann <LW@KARO-electronics.de>
Fri, 13 Jun 2014 13:36:30 +0000 (15:36 +0200)
committerLothar Waßmann <LW@KARO-electronics.de>
Tue, 17 Jun 2014 07:32:59 +0000 (09:32 +0200)
Conflicts:
drivers/video/mxc/mxc_ipuv3_fb.c

drivers/video/mxc/mxc_ipuv3_fb.c [new file with mode: 0644]

diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c
new file mode 100644 (file)
index 0000000..1c15d4a
--- /dev/null
@@ -0,0 +1,2577 @@
+/*
+ * Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb.c
+ *
+ * @brief MXC Frame buffer driver for SDC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/fsl_devices.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/ipu.h>
+#include <linux/ipu-v3.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mxcfb.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+
+#include "mxc_dispdrv.h"
+
+/*
+ * Driver name
+ */
+#define MXCFB_NAME      "mxc_sdc_fb"
+
+/* Display port number */
+#define MXCFB_PORT_NUM 2
+/*!
+ * Structure containing the MXC specific framebuffer information.
+ */
+struct mxcfb_info {
+       int default_bpp;
+       int cur_blank;
+       int next_blank;
+       ipu_channel_t ipu_ch;
+       int ipu_id;
+       int ipu_di;
+       u32 ipu_di_pix_fmt;
+       bool ipu_int_clk;
+       bool overlay;
+       bool alpha_chan_en;
+       bool late_init;
+       bool first_set_par;
+       dma_addr_t alpha_phy_addr0;
+       dma_addr_t alpha_phy_addr1;
+       void *alpha_virt_addr0;
+       void *alpha_virt_addr1;
+       uint32_t alpha_mem_len;
+       uint32_t ipu_ch_irq;
+       uint32_t ipu_ch_nf_irq;
+       uint32_t ipu_alp_ch_irq;
+       uint32_t cur_ipu_buf;
+       uint32_t cur_ipu_alpha_buf;
+
+       u32 pseudo_palette[16];
+
+       bool mode_found;
+       struct completion flip_complete;
+       struct completion alpha_flip_complete;
+       struct completion vsync_complete;
+
+       void *ipu;
+       struct fb_info *ovfbi;
+
+       struct mxc_dispdrv_handle *dispdrv;
+
+       struct fb_var_screeninfo cur_var;
+};
+
+struct mxcfb_pfmt {
+       u32 fb_pix_fmt;
+       int bpp;
+       struct fb_bitfield red;
+       struct fb_bitfield green;
+       struct fb_bitfield blue;
+       struct fb_bitfield transp;
+};
+
+static const struct mxcfb_pfmt mxcfb_pfmts[] = {
+       /*     pixel         bpp    red         green        blue      transp */
+       {IPU_PIX_FMT_RGB565, 16, {11, 5, 0}, { 5, 6, 0}, { 0, 5, 0}, { 0, 0, 0} },
+       {IPU_PIX_FMT_RGB24,  24, { 0, 8, 0}, { 8, 8, 0}, {16, 8, 0}, { 0, 0, 0} },
+       {IPU_PIX_FMT_BGR24,  24, {16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, { 0, 0, 0} },
+       {IPU_PIX_FMT_RGB32,  32, { 0, 8, 0}, { 8, 8, 0}, {16, 8, 0}, {24, 8, 0} },
+       {IPU_PIX_FMT_BGR32,  32, {16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, {24, 8, 0} },
+       {IPU_PIX_FMT_ABGR32, 32, {24, 8, 0}, {16, 8, 0}, { 8, 8, 0}, { 0, 8, 0} },
+};
+
+struct mxcfb_alloc_list {
+       struct list_head list;
+       dma_addr_t phy_addr;
+       void *cpu_addr;
+       u32 size;
+};
+
+enum {
+       BOTH_ON,
+       SRC_ON,
+       TGT_ON,
+       BOTH_OFF
+};
+
+static bool g_dp_in_use[2];
+LIST_HEAD(fb_alloc_list);
+
+/* Return default standard(RGB) pixel format */
+static uint32_t bpp_to_pixfmt(int bpp)
+{
+       uint32_t pixfmt = 0;
+
+       switch (bpp) {
+       case 24:
+               pixfmt = IPU_PIX_FMT_BGR24;
+               break;
+       case 32:
+               pixfmt = IPU_PIX_FMT_BGR32;
+               break;
+       case 16:
+               pixfmt = IPU_PIX_FMT_RGB565;
+               break;
+       }
+       return pixfmt;
+}
+
+static inline int bitfield_is_equal(struct fb_bitfield f1,
+                                   struct fb_bitfield f2)
+{
+       return !memcmp(&f1, &f2, sizeof(f1));
+}
+
+static int pixfmt_to_var(uint32_t pixfmt, struct fb_var_screeninfo *var)
+{
+       int i, ret = -1;
+
+       for (i = 0; i < ARRAY_SIZE(mxcfb_pfmts); i++) {
+               if (pixfmt == mxcfb_pfmts[i].fb_pix_fmt) {
+                       var->red    = mxcfb_pfmts[i].red;
+                       var->green  = mxcfb_pfmts[i].green;
+                       var->blue   = mxcfb_pfmts[i].blue;
+                       var->transp = mxcfb_pfmts[i].transp;
+                       var->bits_per_pixel = mxcfb_pfmts[i].bpp;
+                       ret = 0;
+                       break;
+               }
+       }
+       return ret;
+}
+
+static int bpp_to_var(int bpp, struct fb_var_screeninfo *var)
+{
+       uint32_t pixfmt = 0;
+
+       pixfmt = bpp_to_pixfmt(bpp);
+       if (pixfmt)
+               return pixfmt_to_var(pixfmt, var);
+       else
+               return -1;
+}
+
+static int check_var_pixfmt(struct fb_var_screeninfo *var)
+{
+       int i, ret = -1;
+
+       for (i = 0; i < ARRAY_SIZE(mxcfb_pfmts); i++) {
+               if (bitfield_is_equal(var->red, mxcfb_pfmts[i].red) &&
+                   bitfield_is_equal(var->green, mxcfb_pfmts[i].green) &&
+                   bitfield_is_equal(var->blue, mxcfb_pfmts[i].blue) &&
+                   bitfield_is_equal(var->transp, mxcfb_pfmts[i].transp) &&
+                   var->bits_per_pixel == mxcfb_pfmts[i].bpp) {
+                       ret = 0;
+                       break;
+               }
+       }
+       return ret;
+}
+
+static uint32_t fbi_to_pixfmt(struct fb_info *fbi)
+{
+       int i;
+       uint32_t pixfmt = 0;
+
+       if (fbi->var.nonstd)
+               return fbi->var.nonstd;
+
+       for (i = 0; i < ARRAY_SIZE(mxcfb_pfmts); i++) {
+               if (bitfield_is_equal(fbi->var.red, mxcfb_pfmts[i].red) &&
+                   bitfield_is_equal(fbi->var.green, mxcfb_pfmts[i].green) &&
+                   bitfield_is_equal(fbi->var.blue, mxcfb_pfmts[i].blue) &&
+                   bitfield_is_equal(fbi->var.transp, mxcfb_pfmts[i].transp)) {
+                       pixfmt = mxcfb_pfmts[i].fb_pix_fmt;
+                       break;
+               }
+       }
+
+       if (pixfmt == 0)
+               dev_err(fbi->device, "cannot get pixel format\n");
+
+       return pixfmt;
+}
+
+static struct fb_info *found_registered_fb(ipu_channel_t ipu_ch, int ipu_id)
+{
+       int i;
+       struct mxcfb_info *mxc_fbi;
+       struct fb_info *fbi = NULL;
+
+       for (i = 0; i < num_registered_fb; i++) {
+               mxc_fbi = registered_fb[i]->par;
+
+               if ((mxc_fbi->ipu_ch == ipu_ch) &&
+                       (mxc_fbi->ipu_id == ipu_id)) {
+                       fbi = registered_fb[i];
+                       break;
+               }
+       }
+       return fbi;
+}
+
+static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id);
+static irqreturn_t mxcfb_nf_irq_handler(int irq, void *dev_id);
+static int mxcfb_blank(int blank, struct fb_info *info);
+static int mxcfb_map_video_memory(struct fb_info *fbi);
+static int mxcfb_unmap_video_memory(struct fb_info *fbi);
+
+/*
+ * Set fixed framebuffer parameters based on variable settings.
+ *
+ * @param       info     framebuffer information pointer
+ */
+static int mxcfb_set_fix(struct fb_info *info)
+{
+       struct fb_fix_screeninfo *fix = &info->fix;
+       struct fb_var_screeninfo *var = &info->var;
+
+       fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+       fix->type = FB_TYPE_PACKED_PIXELS;
+       fix->accel = FB_ACCEL_NONE;
+       fix->visual = FB_VISUAL_TRUECOLOR;
+       fix->xpanstep = 1;
+       fix->ywrapstep = 1;
+       fix->ypanstep = 1;
+
+       return 0;
+}
+
+static int _setup_disp_channel1(struct fb_info *fbi)
+{
+       ipu_channel_params_t params;
+       struct mxcfb_info *mxc_fbi = fbi->par;
+
+       memset(&params, 0, sizeof(params));
+
+       if (mxc_fbi->ipu_ch == MEM_DC_SYNC) {
+               params.mem_dc_sync.di = mxc_fbi->ipu_di;
+               if (fbi->var.vmode & FB_VMODE_INTERLACED)
+                       params.mem_dc_sync.interlaced = true;
+               params.mem_dc_sync.out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+               params.mem_dc_sync.in_pixel_fmt = fbi_to_pixfmt(fbi);
+       } else {
+               params.mem_dp_bg_sync.di = mxc_fbi->ipu_di;
+               if (fbi->var.vmode & FB_VMODE_INTERLACED)
+                       params.mem_dp_bg_sync.interlaced = true;
+               params.mem_dp_bg_sync.out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+               params.mem_dp_bg_sync.in_pixel_fmt = fbi_to_pixfmt(fbi);
+               if (mxc_fbi->alpha_chan_en)
+                       params.mem_dp_bg_sync.alpha_chan_en = true;
+       }
+       ipu_init_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch, &params);
+
+       return 0;
+}
+
+static int _setup_disp_channel2(struct fb_info *fbi)
+{
+       int retval = 0;
+       struct mxcfb_info *mxc_fbi = fbi->par;
+       int fb_stride;
+       unsigned long base;
+       unsigned int fr_xoff, fr_yoff, fr_w, fr_h;
+
+       switch (fbi_to_pixfmt(fbi)) {
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_NV12:
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YVU422P:
+       case IPU_PIX_FMT_YUV420P:
+       case IPU_PIX_FMT_YUV444P:
+               fb_stride = fbi->var.xres_virtual;
+               break;
+       default:
+               fb_stride = fbi->fix.line_length;
+       }
+
+       base = fbi->fix.smem_start;
+       fr_xoff = fbi->var.xoffset;
+       fr_w = fbi->var.xres_virtual;
+       if (!(fbi->var.vmode & FB_VMODE_YWRAP)) {
+               dev_dbg(fbi->device, "Y wrap disabled\n");
+               fr_yoff = fbi->var.yoffset % fbi->var.yres;
+               fr_h = fbi->var.yres;
+               base += fbi->fix.line_length * fbi->var.yres *
+                       (fbi->var.yoffset / fbi->var.yres);
+       } else {
+               dev_dbg(fbi->device, "Y wrap enabled\n");
+               fr_yoff = fbi->var.yoffset;
+               fr_h = fbi->var.yres_virtual;
+       }
+       base += fr_yoff * fb_stride + fr_xoff;
+
+       mxc_fbi->cur_ipu_buf = 2;
+       init_completion(&mxc_fbi->flip_complete);
+       /*
+        * We don't need to wait for vsync at the first time
+        * we do pan display after fb is initialized, as IPU will
+        * switch to the newly selected buffer automatically,
+        * so we call complete() for both mxc_fbi->flip_complete
+        * and mxc_fbi->alpha_flip_complete.
+        */
+       complete(&mxc_fbi->flip_complete);
+       if (mxc_fbi->alpha_chan_en) {
+               mxc_fbi->cur_ipu_alpha_buf = 1;
+               init_completion(&mxc_fbi->alpha_flip_complete);
+               complete(&mxc_fbi->alpha_flip_complete);
+       }
+
+       retval = ipu_init_channel_buffer(mxc_fbi->ipu,
+                                        mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+                                        fbi_to_pixfmt(fbi),
+                                        fbi->var.xres, fbi->var.yres,
+                                        fb_stride,
+                                        fbi->var.rotate,
+                                        base,
+                                        base,
+                                        fbi->var.accel_flags &
+                                               FB_ACCEL_DOUBLE_FLAG ? 0 : base,
+                                        0, 0);
+       if (retval) {
+               dev_err(fbi->device,
+                       "ipu_init_channel_buffer error %d\n", retval);
+               return retval;
+       }
+
+       /* update u/v offset */
+       ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                       IPU_INPUT_BUFFER,
+                       fbi_to_pixfmt(fbi),
+                       fr_w,
+                       fr_h,
+                       fr_w,
+                       0, 0,
+                       fr_yoff,
+                       fr_xoff);
+
+       if (mxc_fbi->alpha_chan_en) {
+               retval = ipu_init_channel_buffer(mxc_fbi->ipu,
+                                                mxc_fbi->ipu_ch,
+                                                IPU_ALPHA_IN_BUFFER,
+                                                IPU_PIX_FMT_GENERIC,
+                                                fbi->var.xres, fbi->var.yres,
+                                                fbi->var.xres,
+                                                fbi->var.rotate,
+                                                mxc_fbi->alpha_phy_addr1,
+                                                mxc_fbi->alpha_phy_addr0,
+                                                0,
+                                                0, 0);
+               if (retval) {
+                       dev_err(fbi->device,
+                               "ipu_init_channel_buffer error %d\n", retval);
+                       return retval;
+               }
+       }
+
+       return retval;
+}
+
+static bool mxcfb_need_to_set_par(struct fb_info *fbi)
+{
+       struct mxcfb_info *mxc_fbi = fbi->par;
+
+       if ((fbi->var.activate & FB_ACTIVATE_FORCE) &&
+           (fbi->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
+               return true;
+
+       /*
+        * Ignore xoffset and yoffset update,
+        * because pan display handles this case.
+        */
+       mxc_fbi->cur_var.xoffset = fbi->var.xoffset;
+       mxc_fbi->cur_var.yoffset = fbi->var.yoffset;
+
+       return !!memcmp(&mxc_fbi->cur_var, &fbi->var,
+                       sizeof(struct fb_var_screeninfo));
+}
+
+/*
+ * Set framebuffer parameters and change the operating mode.
+ *
+ * @param       info     framebuffer information pointer
+ */
+static int mxcfb_set_par(struct fb_info *fbi)
+{
+       int retval = 0;
+       u32 mem_len, alpha_mem_len;
+       ipu_di_signal_cfg_t sig_cfg;
+       struct mxcfb_info *mxc_fbi = fbi->par;
+
+       int16_t ov_pos_x = 0, ov_pos_y = 0;
+       int ov_pos_ret = 0;
+       struct mxcfb_info *mxc_fbi_fg = NULL;
+       bool ovfbi_enable = false;
+
+       if (ipu_ch_param_bad_alpha_pos(fbi_to_pixfmt(fbi)) &&
+           mxc_fbi->alpha_chan_en) {
+               dev_err(fbi->device, "Bad pixel format for "
+                               "graphics plane fb\n");
+               return -EINVAL;
+       }
+
+       if (mxc_fbi->ovfbi)
+               mxc_fbi_fg = mxc_fbi->ovfbi->par;
+
+       if (mxc_fbi->ovfbi && mxc_fbi_fg)
+               if (mxc_fbi_fg->next_blank == FB_BLANK_UNBLANK)
+                       ovfbi_enable = true;
+
+       if (!mxcfb_need_to_set_par(fbi))
+               return 0;
+
+       dev_dbg(fbi->device, "Reconfiguring framebuffer\n");
+
+       if (fbi->var.xres == 0 || fbi->var.yres == 0)
+               return 0;
+
+       if (ovfbi_enable) {
+               ov_pos_ret = ipu_disp_get_window_pos(
+                                               mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch,
+                                               &ov_pos_x, &ov_pos_y);
+               if (ov_pos_ret < 0)
+                       dev_err(fbi->device, "Get overlay pos failed, dispdrv:%s.\n",
+                                       mxc_fbi->dispdrv->drv->name);
+
+               ipu_clear_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_irq);
+               ipu_disable_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_irq);
+               ipu_clear_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_nf_irq);
+               ipu_disable_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_nf_irq);
+               ipu_disable_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch, true);
+               ipu_uninit_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch);
+       }
+
+       ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+       ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+       ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq);
+       ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq);
+       ipu_disable_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch, true);
+       ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch);
+
+       /*
+        * Disable IPU hsp clock if it is enabled for an
+        * additional time in ipu common driver.
+        */
+       if (mxc_fbi->first_set_par && mxc_fbi->late_init)
+               ipu_disable_hsp_clk(mxc_fbi->ipu);
+
+       mxcfb_set_fix(fbi);
+
+       mem_len = fbi->var.yres_virtual * fbi->fix.line_length;
+       if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) {
+               if (fbi->fix.smem_start)
+                       mxcfb_unmap_video_memory(fbi);
+
+               if (mxcfb_map_video_memory(fbi) < 0)
+                       return -ENOMEM;
+       }
+
+       if (mxc_fbi->first_set_par) {
+               /*
+                * Clear the screen in case uboot fb pixel format is not
+                * the same to kernel fb pixel format.
+                */
+               if (mxc_fbi->late_init)
+                       memset(fbi->screen_base, 0, fbi->fix.smem_len);
+
+               mxc_fbi->first_set_par = false;
+       }
+
+       if (mxc_fbi->alpha_chan_en) {
+               alpha_mem_len = fbi->var.xres * fbi->var.yres;
+               if ((!mxc_fbi->alpha_phy_addr0 && !mxc_fbi->alpha_phy_addr1) ||
+                   (alpha_mem_len > mxc_fbi->alpha_mem_len)) {
+                       if (mxc_fbi->alpha_phy_addr0)
+                               dma_free_coherent(fbi->device,
+                                                 mxc_fbi->alpha_mem_len,
+                                                 mxc_fbi->alpha_virt_addr0,
+                                                 mxc_fbi->alpha_phy_addr0);
+                       if (mxc_fbi->alpha_phy_addr1)
+                               dma_free_coherent(fbi->device,
+                                                 mxc_fbi->alpha_mem_len,
+                                                 mxc_fbi->alpha_virt_addr1,
+                                                 mxc_fbi->alpha_phy_addr1);
+
+                       mxc_fbi->alpha_virt_addr0 =
+                                       dma_alloc_coherent(fbi->device,
+                                                 alpha_mem_len,
+                                                 &mxc_fbi->alpha_phy_addr0,
+                                                 GFP_DMA | GFP_KERNEL);
+
+                       mxc_fbi->alpha_virt_addr1 =
+                                       dma_alloc_coherent(fbi->device,
+                                                 alpha_mem_len,
+                                                 &mxc_fbi->alpha_phy_addr1,
+                                                 GFP_DMA | GFP_KERNEL);
+                       if (mxc_fbi->alpha_virt_addr0 == NULL ||
+                           mxc_fbi->alpha_virt_addr1 == NULL) {
+                               dev_err(fbi->device, "mxcfb: dma alloc for"
+                                       " alpha buffer failed.\n");
+                               if (mxc_fbi->alpha_virt_addr0)
+                                       dma_free_coherent(fbi->device,
+                                                 mxc_fbi->alpha_mem_len,
+                                                 mxc_fbi->alpha_virt_addr0,
+                                                 mxc_fbi->alpha_phy_addr0);
+                               if (mxc_fbi->alpha_virt_addr1)
+                                       dma_free_coherent(fbi->device,
+                                                 mxc_fbi->alpha_mem_len,
+                                                 mxc_fbi->alpha_virt_addr1,
+                                                 mxc_fbi->alpha_phy_addr1);
+                               return -ENOMEM;
+                       }
+                       mxc_fbi->alpha_mem_len = alpha_mem_len;
+               }
+       }
+
+       if (mxc_fbi->next_blank != FB_BLANK_UNBLANK)
+               return retval;
+
+       if (mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->setup) {
+               retval = mxc_fbi->dispdrv->drv->setup(mxc_fbi->dispdrv, fbi);
+               if (retval < 0) {
+                       dev_err(fbi->device, "setup error, dispdrv:%s.\n",
+                                       mxc_fbi->dispdrv->drv->name);
+                       return -EINVAL;
+               }
+       }
+
+       _setup_disp_channel1(fbi);
+       if (ovfbi_enable)
+               _setup_disp_channel1(mxc_fbi->ovfbi);
+
+       if (!mxc_fbi->overlay) {
+               uint32_t out_pixel_fmt;
+
+               memset(&sig_cfg, 0, sizeof(sig_cfg));
+               if (fbi->var.vmode & FB_VMODE_INTERLACED)
+                       sig_cfg.interlaced = true;
+               out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+               if (fbi->var.vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
+                       sig_cfg.odd_field_first = true;
+               if (mxc_fbi->ipu_int_clk)
+                       sig_cfg.int_clk = true;
+               if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT)
+                       sig_cfg.Hsync_pol = true;
+               if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)
+                       sig_cfg.Vsync_pol = true;
+               if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL))
+                       sig_cfg.clk_pol = true;
+               if (fbi->var.sync & FB_SYNC_DATA_INVERT)
+                       sig_cfg.data_pol = true;
+               if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT))
+                       sig_cfg.enable_pol = true;
+               if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN)
+                       sig_cfg.clkidle_en = true;
+
+               dev_dbg(fbi->device, "pixclock = %ul Hz\n",
+                       (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL));
+
+               if (ipu_init_sync_panel(mxc_fbi->ipu, mxc_fbi->ipu_di,
+                                       (PICOS2KHZ(fbi->var.pixclock)) * 1000UL,
+                                       fbi->var.xres, fbi->var.yres,
+                                       out_pixel_fmt,
+                                       fbi->var.left_margin,
+                                       fbi->var.hsync_len,
+                                       fbi->var.right_margin,
+                                       fbi->var.upper_margin,
+                                       fbi->var.vsync_len,
+                                       fbi->var.lower_margin,
+                                       0, sig_cfg) != 0) {
+                       dev_err(fbi->device,
+                               "mxcfb: Error initializing panel.\n");
+                       return -EINVAL;
+               }
+
+               fbi->mode =
+                   (struct fb_videomode *)fb_match_mode(&fbi->var,
+                                                        &fbi->modelist);
+
+               ipu_disp_set_window_pos(mxc_fbi->ipu, mxc_fbi->ipu_ch, 0, 0);
+       }
+
+       retval = _setup_disp_channel2(fbi);
+       if (retval) {
+               ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch);
+               return retval;
+       }
+
+       if (ovfbi_enable) {
+               if (ov_pos_ret >= 0)
+                       ipu_disp_set_window_pos(
+                                       mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch,
+                                       ov_pos_x, ov_pos_y);
+               retval = _setup_disp_channel2(mxc_fbi->ovfbi);
+               if (retval) {
+                       ipu_uninit_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch);
+                       ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch);
+                       return retval;
+               }
+       }
+
+       ipu_enable_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch);
+       if (ovfbi_enable)
+               ipu_enable_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch);
+
+       if (mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->enable) {
+               retval = mxc_fbi->dispdrv->drv->enable(mxc_fbi->dispdrv);
+               if (retval < 0) {
+                       dev_err(fbi->device, "enable error, dispdrv:%s.\n",
+                                       mxc_fbi->dispdrv->drv->name);
+                       return -EINVAL;
+               }
+       }
+
+       mxc_fbi->cur_var = fbi->var;
+
+       return retval;
+}
+
+static int _swap_channels(struct fb_info *fbi_from,
+                         struct fb_info *fbi_to, bool both_on)
+{
+       int retval, tmp;
+       ipu_channel_t old_ch;
+       struct fb_info *ovfbi;
+       struct mxcfb_info *mxc_fbi_from = fbi_from->par;
+       struct mxcfb_info *mxc_fbi_to = fbi_to->par;
+
+       if (both_on) {
+               ipu_disable_channel(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch, true);
+               ipu_uninit_channel(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch);
+       }
+
+       /* switch the mxc fbi parameters */
+       old_ch = mxc_fbi_from->ipu_ch;
+       mxc_fbi_from->ipu_ch = mxc_fbi_to->ipu_ch;
+       mxc_fbi_to->ipu_ch = old_ch;
+       tmp = mxc_fbi_from->ipu_ch_irq;
+       mxc_fbi_from->ipu_ch_irq = mxc_fbi_to->ipu_ch_irq;
+       mxc_fbi_to->ipu_ch_irq = tmp;
+       tmp = mxc_fbi_from->ipu_ch_nf_irq;
+       mxc_fbi_from->ipu_ch_nf_irq = mxc_fbi_to->ipu_ch_nf_irq;
+       mxc_fbi_to->ipu_ch_nf_irq = tmp;
+       ovfbi = mxc_fbi_from->ovfbi;
+       mxc_fbi_from->ovfbi = mxc_fbi_to->ovfbi;
+       mxc_fbi_to->ovfbi = ovfbi;
+
+       _setup_disp_channel1(fbi_from);
+       retval = _setup_disp_channel2(fbi_from);
+       if (retval)
+               return retval;
+
+       /* switch between dp and dc, disable old idmac, enable new idmac */
+       retval = ipu_swap_channel(mxc_fbi_from->ipu, old_ch, mxc_fbi_from->ipu_ch);
+       ipu_uninit_channel(mxc_fbi_from->ipu, old_ch);
+
+       if (both_on) {
+               _setup_disp_channel1(fbi_to);
+               retval = _setup_disp_channel2(fbi_to);
+               if (retval)
+                       return retval;
+               ipu_enable_channel(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch);
+       }
+
+       return retval;
+}
+
+static int swap_channels(struct fb_info *fbi_from)
+{
+       int i;
+       int swap_mode;
+       ipu_channel_t ch_to;
+       struct mxcfb_info *mxc_fbi_from = fbi_from->par;
+       struct fb_info *fbi_to = NULL;
+       struct mxcfb_info *mxc_fbi_to;
+
+       /* what's the target channel? */
+       if (mxc_fbi_from->ipu_ch == MEM_BG_SYNC)
+               ch_to = MEM_DC_SYNC;
+       else
+               ch_to = MEM_BG_SYNC;
+
+       fbi_to = found_registered_fb(ch_to, mxc_fbi_from->ipu_id);
+       if (!fbi_to)
+               return -1;
+       mxc_fbi_to = fbi_to->par;
+
+       ipu_clear_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_irq);
+       ipu_clear_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq);
+       ipu_free_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_irq, fbi_from);
+       ipu_free_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq, fbi_to);
+       ipu_clear_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq);
+       ipu_clear_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq);
+       ipu_free_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq, fbi_from);
+       ipu_free_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq, fbi_to);
+
+       if (mxc_fbi_from->cur_blank == FB_BLANK_UNBLANK) {
+               if (mxc_fbi_to->cur_blank == FB_BLANK_UNBLANK)
+                       swap_mode = BOTH_ON;
+               else
+                       swap_mode = SRC_ON;
+       } else {
+               if (mxc_fbi_to->cur_blank == FB_BLANK_UNBLANK)
+                       swap_mode = TGT_ON;
+               else
+                       swap_mode = BOTH_OFF;
+       }
+
+       switch (swap_mode) {
+       case BOTH_ON:
+               /* disable target->switch src->enable target */
+               _swap_channels(fbi_from, fbi_to, true);
+               break;
+       case SRC_ON:
+               /* just switch src */
+               _swap_channels(fbi_from, fbi_to, false);
+               break;
+       case TGT_ON:
+               /* just switch target */
+               _swap_channels(fbi_to, fbi_from, false);
+               break;
+       case BOTH_OFF:
+               /* switch directly, no more need to do */
+               mxc_fbi_to->ipu_ch = mxc_fbi_from->ipu_ch;
+               mxc_fbi_from->ipu_ch = ch_to;
+               i = mxc_fbi_from->ipu_ch_irq;
+               mxc_fbi_from->ipu_ch_irq = mxc_fbi_to->ipu_ch_irq;
+               mxc_fbi_to->ipu_ch_irq = i;
+               i = mxc_fbi_from->ipu_ch_nf_irq;
+               mxc_fbi_from->ipu_ch_nf_irq = mxc_fbi_to->ipu_ch_nf_irq;
+               mxc_fbi_to->ipu_ch_nf_irq = i;
+               break;
+       default:
+               break;
+       }
+
+       if (ipu_request_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_irq,
+               mxcfb_irq_handler, IPU_IRQF_ONESHOT,
+               MXCFB_NAME, fbi_from) != 0) {
+               dev_err(fbi_from->device, "Error registering irq %d\n",
+                       mxc_fbi_from->ipu_ch_irq);
+               return -EBUSY;
+       }
+       ipu_disable_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_irq);
+       if (ipu_request_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq,
+               mxcfb_irq_handler, IPU_IRQF_ONESHOT,
+               MXCFB_NAME, fbi_to) != 0) {
+               dev_err(fbi_to->device, "Error registering irq %d\n",
+                       mxc_fbi_to->ipu_ch_irq);
+               return -EBUSY;
+       }
+       ipu_disable_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq);
+       if (ipu_request_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq,
+               mxcfb_nf_irq_handler, IPU_IRQF_ONESHOT,
+               MXCFB_NAME, fbi_from) != 0) {
+               dev_err(fbi_from->device, "Error registering irq %d\n",
+                       mxc_fbi_from->ipu_ch_nf_irq);
+               return -EBUSY;
+       }
+       ipu_disable_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq);
+       if (ipu_request_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq,
+               mxcfb_nf_irq_handler, IPU_IRQF_ONESHOT,
+               MXCFB_NAME, fbi_to) != 0) {
+               dev_err(fbi_to->device, "Error registering irq %d\n",
+                       mxc_fbi_to->ipu_ch_nf_irq);
+               return -EBUSY;
+       }
+       ipu_disable_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq);
+
+       return 0;
+}
+
+/*
+ * Check framebuffer variable parameters and adjust to valid values.
+ *
+ * @param       var      framebuffer variable parameters
+ *
+ * @param       info     framebuffer information pointer
+ */
+static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       u32 vtotal;
+       u32 htotal;
+       struct mxcfb_info *mxc_fbi = info->par;
+
+
+       if (var->xres == 0 || var->yres == 0)
+               return 0;
+
+       /* fg should not bigger than bg */
+       if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+               struct fb_info *fbi_tmp;
+               int bg_xres = 0, bg_yres = 0;
+               int16_t pos_x, pos_y;
+
+               bg_xres = var->xres;
+               bg_yres = var->yres;
+
+               fbi_tmp = found_registered_fb(MEM_BG_SYNC, mxc_fbi->ipu_id);
+               if (fbi_tmp) {
+                       bg_xres = fbi_tmp->var.xres;
+                       bg_yres = fbi_tmp->var.yres;
+               }
+
+               ipu_disp_get_window_pos(mxc_fbi->ipu, mxc_fbi->ipu_ch, &pos_x, &pos_y);
+
+               if ((var->xres + pos_x) > bg_xres)
+                       var->xres = bg_xres - pos_x;
+               if ((var->yres + pos_y) > bg_yres)
+                       var->yres = bg_yres - pos_y;
+       }
+
+       if (var->rotate > IPU_ROTATE_VERT_FLIP)
+               var->rotate = IPU_ROTATE_NONE;
+
+       if (var->xres_virtual < var->xres)
+               var->xres_virtual = var->xres;
+
+       if (var->yres_virtual < var->yres)
+               var->yres_virtual = var->yres * 3;
+
+       if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
+           (var->bits_per_pixel != 16) && (var->bits_per_pixel != 12) &&
+           (var->bits_per_pixel != 8))
+               var->bits_per_pixel = 16;
+
+       if (check_var_pixfmt(var))
+               /* Fall back to default */
+               bpp_to_var(var->bits_per_pixel, var);
+
+       if (var->pixclock < 1000) {
+               htotal = var->xres + var->right_margin + var->hsync_len +
+                   var->left_margin;
+               vtotal = var->yres + var->lower_margin + var->vsync_len +
+                   var->upper_margin;
+               var->pixclock = (vtotal * htotal * 6UL) / 100UL;
+               var->pixclock = KHZ2PICOS(var->pixclock);
+               dev_dbg(info->device,
+                       "pixclock set for 60Hz refresh = %u ps\n",
+                       var->pixclock);
+       }
+
+       var->height = -1;
+       var->width = -1;
+       var->grayscale = 0;
+
+       return 0;
+}
+
+static inline u_int _chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+       chan &= 0xffff;
+       chan >>= 16 - bf->length;
+       return chan << bf->offset;
+}
+
+static int mxcfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                          u_int trans, struct fb_info *fbi)
+{
+       unsigned int val;
+       int ret = 1;
+
+       /*
+        * If greyscale is true, then we convert the RGB value
+        * to greyscale no matter what visual we are using.
+        */
+       if (fbi->var.grayscale)
+               red = green = blue = (19595 * red + 38470 * green +
+                                     7471 * blue) >> 16;
+       switch (fbi->fix.visual) {
+       case FB_VISUAL_TRUECOLOR:
+               /*
+                * 16-bit True Colour.  We encode the RGB value
+                * according to the RGB bitfield information.
+                */
+               if (regno < 16) {
+                       u32 *pal = fbi->pseudo_palette;
+
+                       val = _chan_to_field(red, &fbi->var.red);
+                       val |= _chan_to_field(green, &fbi->var.green);
+                       val |= _chan_to_field(blue, &fbi->var.blue);
+
+                       pal[regno] = val;
+                       ret = 0;
+               }
+               break;
+
+       case FB_VISUAL_STATIC_PSEUDOCOLOR:
+       case FB_VISUAL_PSEUDOCOLOR:
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * Function to handle custom ioctls for MXC framebuffer.
+ *
+ * @param       inode   inode struct
+ *
+ * @param       file    file struct
+ *
+ * @param       cmd     Ioctl command to handle
+ *
+ * @param       arg     User pointer to command arguments
+ *
+ * @param       fbi     framebuffer information pointer
+ */
+static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
+{
+       int retval = 0;
+       int __user *argp = (void __user *)arg;
+       struct mxcfb_info *mxc_fbi = fbi->par;
+
+       switch (cmd) {
+       case MXCFB_SET_GBL_ALPHA:
+               {
+                       struct mxcfb_gbl_alpha ga;
+
+                       if (copy_from_user(&ga, argp, sizeof(ga))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       if (ipu_disp_set_global_alpha(mxc_fbi->ipu,
+                                                     mxc_fbi->ipu_ch,
+                                                     (bool)ga.enable,
+                                                     ga.alpha)) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       if (ga.enable)
+                               mxc_fbi->alpha_chan_en = false;
+
+                       if (ga.enable)
+                               dev_dbg(fbi->device,
+                                       "Set global alpha of %s to %d\n",
+                                       fbi->fix.id, ga.alpha);
+                       break;
+               }
+       case MXCFB_SET_LOC_ALPHA:
+               {
+                       struct mxcfb_loc_alpha la;
+                       bool bad_pixfmt =
+                               ipu_ch_param_bad_alpha_pos(fbi_to_pixfmt(fbi));
+
+                       if (copy_from_user(&la, argp, sizeof(la))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       if (la.enable && !la.alpha_in_pixel) {
+                               struct fb_info *fbi_tmp;
+                               ipu_channel_t ipu_ch;
+
+                               if (bad_pixfmt) {
+                                       dev_err(fbi->device, "Bad pixel format "
+                                               "for graphics plane fb\n");
+                                       retval = -EINVAL;
+                                       break;
+                               }
+
+                               mxc_fbi->alpha_chan_en = true;
+
+                               if (mxc_fbi->ipu_ch == MEM_FG_SYNC)
+                                       ipu_ch = MEM_BG_SYNC;
+                               else if (mxc_fbi->ipu_ch == MEM_BG_SYNC)
+                                       ipu_ch = MEM_FG_SYNC;
+                               else {
+                                       retval = -EINVAL;
+                                       break;
+                               }
+
+                               fbi_tmp = found_registered_fb(ipu_ch, mxc_fbi->ipu_id);
+                               if (fbi_tmp)
+                                       ((struct mxcfb_info *)(fbi_tmp->par))->alpha_chan_en = false;
+                       } else
+                               mxc_fbi->alpha_chan_en = false;
+
+                       if (ipu_disp_set_global_alpha(mxc_fbi->ipu,
+                                                     mxc_fbi->ipu_ch,
+                                                     !(bool)la.enable, 0)) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       fbi->var.activate = (fbi->var.activate & ~FB_ACTIVATE_MASK) |
+                                               FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
+                       mxcfb_set_par(fbi);
+
+                       la.alpha_phy_addr0 = mxc_fbi->alpha_phy_addr0;
+                       la.alpha_phy_addr1 = mxc_fbi->alpha_phy_addr1;
+                       if (copy_to_user(argp, &la, sizeof(la))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       if (la.enable)
+                               dev_dbg(fbi->device,
+                                       "Enable DP local alpha for %s\n",
+                                       fbi->fix.id);
+                       break;
+               }
+       case MXCFB_SET_LOC_ALP_BUF:
+               {
+                       unsigned long base;
+                       uint32_t ipu_alp_ch_irq;
+
+                       if (!(((mxc_fbi->ipu_ch == MEM_FG_SYNC) ||
+                            (mxc_fbi->ipu_ch == MEM_BG_SYNC)) &&
+                            (mxc_fbi->alpha_chan_en))) {
+                               dev_err(fbi->device,
+                                       "Should use background or overlay "
+                                       "framebuffer to set the alpha buffer "
+                                       "number\n");
+                               return -EINVAL;
+                       }
+
+                       if (get_user(base, argp))
+                               return -EFAULT;
+
+                       if (base != mxc_fbi->alpha_phy_addr0 &&
+                           base != mxc_fbi->alpha_phy_addr1) {
+                               dev_err(fbi->device,
+                                       "Wrong alpha buffer physical address "
+                                       "%lu\n", base);
+                               return -EINVAL;
+                       }
+
+                       if (mxc_fbi->ipu_ch == MEM_FG_SYNC)
+                               ipu_alp_ch_irq = IPU_IRQ_FG_ALPHA_SYNC_EOF;
+                       else
+                               ipu_alp_ch_irq = IPU_IRQ_BG_ALPHA_SYNC_EOF;
+
+                       retval = wait_for_completion_timeout(
+                               &mxc_fbi->alpha_flip_complete, HZ/2);
+                       if (retval == 0) {
+                               dev_err(fbi->device, "timeout when waiting for alpha flip irq\n");
+                               retval = -ETIMEDOUT;
+                               break;
+                       }
+
+                       mxc_fbi->cur_ipu_alpha_buf =
+                                               !mxc_fbi->cur_ipu_alpha_buf;
+                       if (ipu_update_channel_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                                                     IPU_ALPHA_IN_BUFFER,
+                                                     mxc_fbi->
+                                                       cur_ipu_alpha_buf,
+                                                     base) == 0) {
+                               ipu_select_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                                                 IPU_ALPHA_IN_BUFFER,
+                                                 mxc_fbi->cur_ipu_alpha_buf);
+                               ipu_clear_irq(mxc_fbi->ipu, ipu_alp_ch_irq);
+                               ipu_enable_irq(mxc_fbi->ipu, ipu_alp_ch_irq);
+                       } else {
+                               dev_err(fbi->device,
+                                       "Error updating %s SDC alpha buf %d "
+                                       "to address=0x%08lX\n",
+                                       fbi->fix.id,
+                                       mxc_fbi->cur_ipu_alpha_buf, base);
+                       }
+                       break;
+               }
+       case MXCFB_SET_CLR_KEY:
+               {
+                       struct mxcfb_color_key key;
+                       if (copy_from_user(&key, argp, sizeof(key))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+                       retval = ipu_disp_set_color_key(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                                                       key.enable,
+                                                       key.color_key);
+                       dev_dbg(fbi->device, "Set color key to 0x%08X\n",
+                               key.color_key);
+                       break;
+               }
+       case MXCFB_SET_GAMMA:
+               {
+                       struct mxcfb_gamma gamma;
+                       if (copy_from_user(&gamma, argp, sizeof(gamma))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+                       retval = ipu_disp_set_gamma_correction(mxc_fbi->ipu,
+                                                       mxc_fbi->ipu_ch,
+                                                       gamma.enable,
+                                                       gamma.constk,
+                                                       gamma.slopek);
+                       break;
+               }
+       case MXCFB_WAIT_FOR_VSYNC:
+               {
+                       if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+                               /* BG should poweron */
+                               struct mxcfb_info *bg_mxcfbi = NULL;
+                               struct fb_info *fbi_tmp;
+
+                               fbi_tmp = found_registered_fb(MEM_BG_SYNC, mxc_fbi->ipu_id);
+                               if (fbi_tmp)
+                                       bg_mxcfbi = ((struct mxcfb_info *)(fbi_tmp->par));
+
+                               if (!bg_mxcfbi) {
+                                       retval = -EINVAL;
+                                       break;
+                               }
+                               if (bg_mxcfbi->cur_blank != FB_BLANK_UNBLANK) {
+                                       retval = -EINVAL;
+                                       break;
+                               }
+                       }
+                       if (mxc_fbi->cur_blank != FB_BLANK_UNBLANK) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       init_completion(&mxc_fbi->vsync_complete);
+                       ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq);
+                       ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq);
+                       retval = wait_for_completion_interruptible_timeout(
+                               &mxc_fbi->vsync_complete, 1 * HZ);
+                       if (retval == 0) {
+                               dev_err(fbi->device,
+                                       "MXCFB_WAIT_FOR_VSYNC: timeout %d\n",
+                                       retval);
+                               retval = -ETIME;
+                       } else if (retval > 0) {
+                               retval = 0;
+                       }
+                       break;
+               }
+       case FBIO_ALLOC:
+               {
+                       int size;
+                       struct mxcfb_alloc_list *mem;
+
+                       mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+                       if (mem == NULL)
+                               return -ENOMEM;
+
+                       if (get_user(size, argp))
+                               return -EFAULT;
+
+                       mem->size = PAGE_ALIGN(size);
+
+                       mem->cpu_addr = dma_alloc_coherent(fbi->device, size,
+                                                          &mem->phy_addr,
+                                                          GFP_KERNEL);
+                       if (mem->cpu_addr == NULL) {
+                               kfree(mem);
+                               return -ENOMEM;
+                       }
+
+                       list_add(&mem->list, &fb_alloc_list);
+
+                       dev_dbg(fbi->device, "allocated %d bytes @ 0x%08X\n",
+                               mem->size, mem->phy_addr);
+
+                       if (put_user(mem->phy_addr, argp))
+                               return -EFAULT;
+
+                       break;
+               }
+       case FBIO_FREE:
+               {
+                       unsigned long offset;
+                       struct mxcfb_alloc_list *mem;
+
+                       if (get_user(offset, argp))
+                               return -EFAULT;
+
+                       retval = -EINVAL;
+                       list_for_each_entry(mem, &fb_alloc_list, list) {
+                               if (mem->phy_addr == offset) {
+                                       list_del(&mem->list);
+                                       dma_free_coherent(fbi->device,
+                                                         mem->size,
+                                                         mem->cpu_addr,
+                                                         mem->phy_addr);
+                                       kfree(mem);
+                                       retval = 0;
+                                       break;
+                               }
+                       }
+
+                       break;
+               }
+       case MXCFB_SET_OVERLAY_POS:
+               {
+                       struct mxcfb_pos pos;
+                       struct fb_info *bg_fbi = NULL;
+                       struct mxcfb_info *bg_mxcfbi = NULL;
+
+                       if (mxc_fbi->ipu_ch != MEM_FG_SYNC) {
+                               dev_err(fbi->device, "Should use the overlay "
+                                       "framebuffer to set the position of "
+                                       "the overlay window\n");
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       if (copy_from_user(&pos, argp, sizeof(pos))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       bg_fbi = found_registered_fb(MEM_BG_SYNC, mxc_fbi->ipu_id);
+                       if (bg_fbi)
+                               bg_mxcfbi = ((struct mxcfb_info *)(bg_fbi->par));
+
+                       if (bg_fbi == NULL) {
+                               dev_err(fbi->device, "Cannot find the "
+                                       "background framebuffer\n");
+                               retval = -ENOENT;
+                               break;
+                       }
+
+                       /* if fb is unblank, check if the pos fit the display */
+                       if (mxc_fbi->cur_blank == FB_BLANK_UNBLANK) {
+                               if (fbi->var.xres + pos.x > bg_fbi->var.xres) {
+                                       if (bg_fbi->var.xres < fbi->var.xres)
+                                               pos.x = 0;
+                                       else
+                                               pos.x = bg_fbi->var.xres - fbi->var.xres;
+                               }
+                               if (fbi->var.yres + pos.y > bg_fbi->var.yres) {
+                                       if (bg_fbi->var.yres < fbi->var.yres)
+                                               pos.y = 0;
+                                       else
+                                               pos.y = bg_fbi->var.yres - fbi->var.yres;
+                               }
+                       }
+
+                       retval = ipu_disp_set_window_pos(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                                                        pos.x, pos.y);
+
+                       if (copy_to_user(argp, &pos, sizeof(pos))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+                       break;
+               }
+       case MXCFB_GET_FB_IPU_CHAN:
+               {
+                       struct mxcfb_info *mxc_fbi =
+                               (struct mxcfb_info *)fbi->par;
+
+                       if (put_user(mxc_fbi->ipu_ch, argp))
+                               return -EFAULT;
+                       break;
+               }
+       case MXCFB_GET_DIFMT:
+               {
+                       struct mxcfb_info *mxc_fbi =
+                               (struct mxcfb_info *)fbi->par;
+
+                       if (put_user(mxc_fbi->ipu_di_pix_fmt, argp))
+                               return -EFAULT;
+                       break;
+               }
+       case MXCFB_GET_FB_IPU_DI:
+               {
+                       struct mxcfb_info *mxc_fbi =
+                               (struct mxcfb_info *)fbi->par;
+
+                       if (put_user(mxc_fbi->ipu_di, argp))
+                               return -EFAULT;
+                       break;
+               }
+       case MXCFB_GET_FB_BLANK:
+               {
+                       struct mxcfb_info *mxc_fbi =
+                               (struct mxcfb_info *)fbi->par;
+
+                       if (put_user(mxc_fbi->cur_blank, argp))
+                               return -EFAULT;
+                       break;
+               }
+       case MXCFB_SET_DIFMT:
+               {
+                       struct mxcfb_info *mxc_fbi =
+                               (struct mxcfb_info *)fbi->par;
+
+                       if (get_user(mxc_fbi->ipu_di_pix_fmt, argp))
+                               return -EFAULT;
+
+                       break;
+               }
+       case MXCFB_CSC_UPDATE:
+               {
+                       struct mxcfb_csc_matrix csc;
+
+                       if (copy_from_user(&csc, argp, sizeof(csc)))
+                               return -EFAULT;
+
+                       if ((mxc_fbi->ipu_ch != MEM_FG_SYNC) &&
+                               (mxc_fbi->ipu_ch != MEM_BG_SYNC) &&
+                               (mxc_fbi->ipu_ch != MEM_BG_ASYNC0))
+                               return -EFAULT;
+                       ipu_set_csc_coefficients(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                                               csc.param);
+               }
+       default:
+               retval = -EINVAL;
+       }
+       return retval;
+}
+
+/*
+ * mxcfb_blank():
+ *      Blank the display.
+ */
+static int mxcfb_blank(int blank, struct fb_info *info)
+{
+       struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+       int ret = 0;
+
+       dev_dbg(info->device, "blank = %d\n", blank);
+
+       if (mxc_fbi->cur_blank == blank)
+               return 0;
+
+       mxc_fbi->next_blank = blank;
+
+       switch (blank) {
+       case FB_BLANK_POWERDOWN:
+       case FB_BLANK_VSYNC_SUSPEND:
+       case FB_BLANK_HSYNC_SUSPEND:
+       case FB_BLANK_NORMAL:
+               if (mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->disable)
+                       mxc_fbi->dispdrv->drv->disable(mxc_fbi->dispdrv);
+               ipu_disable_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch, true);
+               if (mxc_fbi->ipu_di >= 0)
+                       ipu_uninit_sync_panel(mxc_fbi->ipu, mxc_fbi->ipu_di);
+               ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch);
+               break;
+       case FB_BLANK_UNBLANK:
+               info->var.activate = (info->var.activate & ~FB_ACTIVATE_MASK) |
+                               FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
+               ret = mxcfb_set_par(info);
+               break;
+       }
+       if (!ret)
+               mxc_fbi->cur_blank = blank;
+       return ret;
+}
+
+/*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ *
+ * @param               var     Variable screen buffer information
+ * @param               info    Framebuffer information pointer
+ */
+static int
+mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par,
+                         *mxc_graphic_fbi = NULL;
+       u_int y_bottom;
+       unsigned int fr_xoff, fr_yoff, fr_w, fr_h;
+       unsigned long base, active_alpha_phy_addr = 0;
+       bool loc_alpha_en = false;
+       int fb_stride;
+       int i;
+       int ret;
+
+       /* no pan display during fb blank */
+       if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+               struct mxcfb_info *bg_mxcfbi = NULL;
+               struct fb_info *fbi_tmp;
+
+               fbi_tmp = found_registered_fb(MEM_BG_SYNC, mxc_fbi->ipu_id);
+               if (fbi_tmp)
+                       bg_mxcfbi = ((struct mxcfb_info *)(fbi_tmp->par));
+               if (!bg_mxcfbi)
+                       return -EINVAL;
+               if (bg_mxcfbi->cur_blank != FB_BLANK_UNBLANK)
+                       return -EINVAL;
+       }
+       if (mxc_fbi->cur_blank != FB_BLANK_UNBLANK)
+               return -EINVAL;
+
+       y_bottom = var->yoffset;
+
+       if (y_bottom > info->var.yres_virtual)
+               return -EINVAL;
+
+       switch (fbi_to_pixfmt(info)) {
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_NV12:
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YVU422P:
+       case IPU_PIX_FMT_YUV420P:
+       case IPU_PIX_FMT_YUV444P:
+               fb_stride = info->var.xres_virtual;
+               break;
+       default:
+               fb_stride = info->fix.line_length;
+       }
+
+       base = info->fix.smem_start;
+       fr_xoff = var->xoffset;
+       fr_w = info->var.xres_virtual;
+       if (!(var->vmode & FB_VMODE_YWRAP)) {
+               dev_dbg(info->device, "Y wrap disabled\n");
+               fr_yoff = var->yoffset % info->var.yres;
+               fr_h = info->var.yres;
+               base += info->fix.line_length * info->var.yres *
+                       (var->yoffset / info->var.yres);
+       } else {
+               dev_dbg(info->device, "Y wrap enabled\n");
+               fr_yoff = var->yoffset;
+               fr_h = info->var.yres_virtual;
+       }
+       base += fr_yoff * fb_stride + fr_xoff;
+
+       /* Check if DP local alpha is enabled and find the graphic fb */
+       if (mxc_fbi->ipu_ch == MEM_BG_SYNC || mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+               for (i = 0; i < num_registered_fb; i++) {
+                       char bg_id[] = "DISP3 BG";
+                       char fg_id[] = "DISP3 FG";
+                       char *idstr = registered_fb[i]->fix.id;
+                       bg_id[4] += mxc_fbi->ipu_id;
+                       fg_id[4] += mxc_fbi->ipu_id;
+                       if ((strcmp(idstr, bg_id) == 0 ||
+                            strcmp(idstr, fg_id) == 0) &&
+                           ((struct mxcfb_info *)
+                             (registered_fb[i]->par))->alpha_chan_en) {
+                               loc_alpha_en = true;
+                               mxc_graphic_fbi = (struct mxcfb_info *)
+                                               (registered_fb[i]->par);
+                               active_alpha_phy_addr =
+                                       mxc_fbi->cur_ipu_alpha_buf ?
+                                       mxc_graphic_fbi->alpha_phy_addr1 :
+                                       mxc_graphic_fbi->alpha_phy_addr0;
+                               dev_dbg(info->device, "Updating SDC alpha "
+                                       "buf %d address=0x%08lX\n",
+                                       !mxc_fbi->cur_ipu_alpha_buf,
+                                       active_alpha_phy_addr);
+                               break;
+                       }
+               }
+       }
+
+       ret = wait_for_completion_timeout(&mxc_fbi->flip_complete, HZ/2);
+       if (ret == 0) {
+               dev_err(info->device, "timeout when waiting for flip irq\n");
+               return -ETIMEDOUT;
+       }
+
+       ++mxc_fbi->cur_ipu_buf;
+       mxc_fbi->cur_ipu_buf %= 3;
+       mxc_fbi->cur_ipu_alpha_buf = !mxc_fbi->cur_ipu_alpha_buf;
+
+       dev_dbg(info->device, "Updating SDC %s buf %d address=0x%08lX\n",
+               info->fix.id, mxc_fbi->cur_ipu_buf, base);
+
+       if (ipu_update_channel_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+                                     mxc_fbi->cur_ipu_buf, base) == 0) {
+               /* Update the DP local alpha buffer only for graphic plane */
+               if (loc_alpha_en && mxc_graphic_fbi == mxc_fbi &&
+                   ipu_update_channel_buffer(mxc_graphic_fbi->ipu, mxc_graphic_fbi->ipu_ch,
+                                             IPU_ALPHA_IN_BUFFER,
+                                             mxc_fbi->cur_ipu_alpha_buf,
+                                             active_alpha_phy_addr) == 0) {
+                       ipu_select_buffer(mxc_graphic_fbi->ipu, mxc_graphic_fbi->ipu_ch,
+                                         IPU_ALPHA_IN_BUFFER,
+                                         mxc_fbi->cur_ipu_alpha_buf);
+               }
+
+               /* update u/v offset */
+               ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                               IPU_INPUT_BUFFER,
+                               fbi_to_pixfmt(info),
+                               fr_w,
+                               fr_h,
+                               fr_w,
+                               0, 0,
+                               fr_yoff,
+                               fr_xoff);
+
+               ipu_select_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+                                 mxc_fbi->cur_ipu_buf);
+               ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+               ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+       } else {
+               dev_err(info->device,
+                       "Error updating SDC buf %d to address=0x%08lX, "
+                       "current buf %d, buf0 ready %d, buf1 ready %d, "
+                       "buf2 ready %d\n", mxc_fbi->cur_ipu_buf, base,
+                       ipu_get_cur_buffer_idx(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                                              IPU_INPUT_BUFFER),
+                       ipu_check_buffer_ready(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                                              IPU_INPUT_BUFFER, 0),
+                       ipu_check_buffer_ready(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                                              IPU_INPUT_BUFFER, 1),
+                       ipu_check_buffer_ready(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                                              IPU_INPUT_BUFFER, 2));
+               ++mxc_fbi->cur_ipu_buf;
+               mxc_fbi->cur_ipu_buf %= 3;
+               ++mxc_fbi->cur_ipu_buf;
+               mxc_fbi->cur_ipu_buf %= 3;
+               mxc_fbi->cur_ipu_alpha_buf = !mxc_fbi->cur_ipu_alpha_buf;
+               ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+               ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+               return -EBUSY;
+       }
+
+       dev_dbg(info->device, "Update complete\n");
+
+       info->var.yoffset = var->yoffset;
+
+       return 0;
+}
+
+/*
+ * Function to handle custom mmap for MXC framebuffer.
+ *
+ * @param       fbi     framebuffer information pointer
+ *
+ * @param       vma     Pointer to vm_area_struct
+ */
+static int mxcfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
+{
+       bool found = false;
+       u32 len;
+       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+       struct mxcfb_alloc_list *mem;
+       struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+       if (offset < fbi->fix.smem_len) {
+               /* mapping framebuffer memory */
+               len = fbi->fix.smem_len - offset;
+               vma->vm_pgoff = (fbi->fix.smem_start + offset) >> PAGE_SHIFT;
+       } else if ((vma->vm_pgoff ==
+                       (mxc_fbi->alpha_phy_addr0 >> PAGE_SHIFT)) ||
+                  (vma->vm_pgoff ==
+                       (mxc_fbi->alpha_phy_addr1 >> PAGE_SHIFT))) {
+               len = mxc_fbi->alpha_mem_len;
+       } else {
+               list_for_each_entry(mem, &fb_alloc_list, list) {
+                       if (offset == mem->phy_addr) {
+                               found = true;
+                               len = mem->size;
+                               break;
+                       }
+               }
+               if (!found)
+                       return -EINVAL;
+       }
+
+       len = PAGE_ALIGN(len);
+       if (vma->vm_end - vma->vm_start > len)
+               return -EINVAL;
+
+       /* make buffers bufferable */
+       vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+       vma->vm_flags |= VM_IO;
+
+       if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+                           vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
+               dev_dbg(fbi->device, "mmap remap_pfn_range failed\n");
+               return -ENOBUFS;
+       }
+
+       return 0;
+}
+
+/*!
+ * This structure contains the pointers to the control functions that are
+ * invoked by the core framebuffer driver to perform operations like
+ * blitting, rectangle filling, copy regions and cursor definition.
+ */
+static struct fb_ops mxcfb_ops = {
+       .owner = THIS_MODULE,
+       .fb_set_par = mxcfb_set_par,
+       .fb_check_var = mxcfb_check_var,
+       .fb_setcolreg = mxcfb_setcolreg,
+       .fb_pan_display = mxcfb_pan_display,
+       .fb_ioctl = mxcfb_ioctl,
+       .fb_mmap = mxcfb_mmap,
+       .fb_fillrect = cfb_fillrect,
+       .fb_copyarea = cfb_copyarea,
+       .fb_imageblit = cfb_imageblit,
+       .fb_blank = mxcfb_blank,
+};
+
+static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id)
+{
+       struct fb_info *fbi = dev_id;
+       struct mxcfb_info *mxc_fbi = fbi->par;
+
+       complete(&mxc_fbi->flip_complete);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t mxcfb_nf_irq_handler(int irq, void *dev_id)
+{
+       struct fb_info *fbi = dev_id;
+       struct mxcfb_info *mxc_fbi = fbi->par;
+
+       complete(&mxc_fbi->vsync_complete);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t mxcfb_alpha_irq_handler(int irq, void *dev_id)
+{
+       struct fb_info *fbi = dev_id;
+       struct mxcfb_info *mxc_fbi = fbi->par;
+
+       complete(&mxc_fbi->alpha_flip_complete);
+       return IRQ_HANDLED;
+}
+
+/*
+ * Suspends the framebuffer and blanks the screen. Power management support
+ */
+static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct fb_info *fbi = platform_get_drvdata(pdev);
+       struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+       int saved_blank;
+#ifdef CONFIG_FB_MXC_LOW_PWR_DISPLAY
+       void *fbmem;
+#endif
+
+       if (mxc_fbi->ovfbi) {
+               struct mxcfb_info *mxc_fbi_fg =
+                       (struct mxcfb_info *)mxc_fbi->ovfbi->par;
+
+               console_lock();
+               fb_set_suspend(mxc_fbi->ovfbi, 1);
+               saved_blank = mxc_fbi_fg->cur_blank;
+               mxcfb_blank(FB_BLANK_POWERDOWN, mxc_fbi->ovfbi);
+               mxc_fbi_fg->next_blank = saved_blank;
+               console_unlock();
+       }
+
+       console_lock();
+       fb_set_suspend(fbi, 1);
+       saved_blank = mxc_fbi->cur_blank;
+       mxcfb_blank(FB_BLANK_POWERDOWN, fbi);
+       mxc_fbi->next_blank = saved_blank;
+       console_unlock();
+
+       return 0;
+}
+
+/*
+ * Resumes the framebuffer and unblanks the screen. Power management support
+ */
+static int mxcfb_resume(struct platform_device *pdev)
+{
+       struct fb_info *fbi = platform_get_drvdata(pdev);
+       struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+       console_lock();
+       mxcfb_blank(mxc_fbi->next_blank, fbi);
+       fb_set_suspend(fbi, 0);
+       console_unlock();
+
+       if (mxc_fbi->ovfbi) {
+               struct mxcfb_info *mxc_fbi_fg =
+                       (struct mxcfb_info *)mxc_fbi->ovfbi->par;
+               console_lock();
+               mxcfb_blank(mxc_fbi_fg->next_blank, mxc_fbi->ovfbi);
+               fb_set_suspend(mxc_fbi->ovfbi, 0);
+               console_unlock();
+       }
+
+       return 0;
+}
+
+/*
+ * Main framebuffer functions
+ */
+
+/*!
+ * Allocates the DRAM memory for the frame buffer.      This buffer is remapped
+ * into a non-cached, non-buffered, memory region to allow palette and pixel
+ * writes to occur without flushing the cache.  Once this area is remapped,
+ * all virtual memory access to the video memory should occur at the new region.
+ *
+ * @param       fbi     framebuffer information pointer
+ *
+ * @return      Error code indicating success or failure
+ */
+static int mxcfb_map_video_memory(struct fb_info *fbi)
+{
+       if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length)
+               fbi->fix.smem_len = fbi->var.yres_virtual *
+                                   fbi->fix.line_length;
+
+       fbi->screen_base = dma_alloc_writecombine(fbi->device,
+                               fbi->fix.smem_len,
+                               (dma_addr_t *)&fbi->fix.smem_start,
+                               GFP_DMA | GFP_KERNEL);
+       if (fbi->screen_base == 0) {
+               dev_err(fbi->device, "Unable to allocate framebuffer memory\n");
+               fbi->fix.smem_len = 0;
+               fbi->fix.smem_start = 0;
+               return -EBUSY;
+       }
+
+       dev_dbg(fbi->device, "allocated fb @ paddr=0x%08X, size=%d.\n",
+               (uint32_t) fbi->fix.smem_start, fbi->fix.smem_len);
+
+       fbi->screen_size = fbi->fix.smem_len;
+
+       /* Clear the screen */
+       memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
+
+       return 0;
+}
+
+/*!
+ * De-allocates the DRAM memory for the frame buffer.
+ *
+ * @param       fbi     framebuffer information pointer
+ *
+ * @return      Error code indicating success or failure
+ */
+static int mxcfb_unmap_video_memory(struct fb_info *fbi)
+{
+       dma_free_writecombine(fbi->device, fbi->fix.smem_len,
+                             fbi->screen_base, fbi->fix.smem_start);
+       fbi->screen_base = 0;
+       fbi->fix.smem_start = 0;
+       fbi->fix.smem_len = 0;
+       return 0;
+}
+
+/*!
+ * Initializes the framebuffer information pointer. After allocating
+ * sufficient memory for the framebuffer structure, the fields are
+ * filled with custom information passed in from the configurable
+ * structures.  This includes information such as bits per pixel,
+ * color maps, screen width/height and RGBA offsets.
+ *
+ * @return      Framebuffer structure initialized with our information
+ */
+static struct fb_info *mxcfb_init_fbinfo(struct device *dev, struct fb_ops *ops)
+{
+       struct fb_info *fbi;
+       struct mxcfb_info *mxcfbi;
+
+       /*
+        * Allocate sufficient memory for the fb structure
+        */
+       fbi = framebuffer_alloc(sizeof(struct mxcfb_info), dev);
+       if (!fbi)
+               return NULL;
+
+       mxcfbi = (struct mxcfb_info *)fbi->par;
+
+       fbi->var.activate = FB_ACTIVATE_NOW;
+
+       fbi->fbops = ops;
+       fbi->flags = FBINFO_FLAG_DEFAULT;
+       fbi->pseudo_palette = mxcfbi->pseudo_palette;
+
+       /*
+        * Allocate colormap
+        */
+       fb_alloc_cmap(&fbi->cmap, 16, 0);
+
+       return fbi;
+}
+
+static ssize_t show_disp_chan(struct device *dev,
+                             struct device_attribute *attr, char *buf)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct mxcfb_info *mxcfbi = (struct mxcfb_info *)info->par;
+
+       if (mxcfbi->ipu_ch == MEM_BG_SYNC)
+               return sprintf(buf, "2-layer-fb-bg\n");
+       else if (mxcfbi->ipu_ch == MEM_FG_SYNC)
+               return sprintf(buf, "2-layer-fb-fg\n");
+       else if (mxcfbi->ipu_ch == MEM_DC_SYNC)
+               return sprintf(buf, "1-layer-fb\n");
+       else
+               return sprintf(buf, "err: no display chan\n");
+}
+
+static ssize_t swap_disp_chan(struct device *dev,
+                             struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct mxcfb_info *mxcfbi = (struct mxcfb_info *)info->par;
+       struct mxcfb_info *fg_mxcfbi = NULL;
+
+       console_lock();
+       /* swap only happen between DP-BG and DC, while DP-FG disable */
+       if (((mxcfbi->ipu_ch == MEM_BG_SYNC) &&
+            (strstr(buf, "1-layer-fb") != NULL)) ||
+           ((mxcfbi->ipu_ch == MEM_DC_SYNC) &&
+            (strstr(buf, "2-layer-fb-bg") != NULL))) {
+               struct fb_info *fbi_fg;
+
+               fbi_fg = found_registered_fb(MEM_FG_SYNC, mxcfbi->ipu_id);
+               if (fbi_fg)
+                       fg_mxcfbi = (struct mxcfb_info *)fbi_fg->par;
+
+               if (!fg_mxcfbi ||
+                       fg_mxcfbi->cur_blank == FB_BLANK_UNBLANK) {
+                       dev_err(dev,
+                               "Can not switch while fb2(fb-fg) is on.\n");
+                       console_unlock();
+                       return count;
+               }
+
+               if (swap_channels(info) < 0)
+                       dev_err(dev, "Swap display channel failed.\n");
+       }
+
+       console_unlock();
+       return count;
+}
+static DEVICE_ATTR(fsl_disp_property, S_IWUSR | S_IRUGO,
+                  show_disp_chan, swap_disp_chan);
+
+static ssize_t show_disp_dev(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct mxcfb_info *mxcfbi = (struct mxcfb_info *)info->par;
+
+       if (mxcfbi->ipu_ch == MEM_FG_SYNC)
+               return sprintf(buf, "overlay\n");
+       else
+               return sprintf(buf, "%s\n", mxcfbi->dispdrv->drv->name);
+}
+static DEVICE_ATTR(fsl_disp_dev_property, S_IRUGO, show_disp_dev, NULL);
+
+static int mxcfb_dispdrv_init(struct platform_device *pdev,
+               struct fb_info *fbi)
+{
+       struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
+       struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par;
+       struct mxc_dispdrv_setting setting;
+       char disp_dev[32], *default_dev = "lcd";
+       int ret = 0;
+
+       setting.if_fmt = plat_data->interface_pix_fmt;
+       setting.dft_mode_str = plat_data->mode_str;
+       setting.default_bpp = plat_data->default_bpp;
+       if (!setting.default_bpp)
+               setting.default_bpp = 16;
+       setting.fbi = fbi;
+       if (!strlen(plat_data->disp_dev)) {
+               memcpy(disp_dev, default_dev, strlen(default_dev));
+               disp_dev[strlen(default_dev)] = '\0';
+       } else {
+               memcpy(disp_dev, plat_data->disp_dev,
+                               strlen(plat_data->disp_dev));
+               disp_dev[strlen(plat_data->disp_dev)] = '\0';
+       }
+
+       dev_info(&pdev->dev, "register mxc display driver %s\n", disp_dev);
+
+       mxcfbi->dispdrv = mxc_dispdrv_gethandle(disp_dev, &setting);
+       if (IS_ERR(mxcfbi->dispdrv)) {
+               ret = PTR_ERR(mxcfbi->dispdrv);
+               dev_err(&pdev->dev, "NO mxc display driver found!\n");
+               return ret;
+       } else {
+               /* fix-up  */
+               mxcfbi->ipu_di_pix_fmt = setting.if_fmt;
+               mxcfbi->default_bpp = setting.default_bpp;
+
+               /* setting */
+               mxcfbi->ipu_id = setting.dev_id;
+               mxcfbi->ipu_di = setting.disp_id;
+               dev_dbg(&pdev->dev, "di_pixfmt:0x%x, bpp:0x%x, di:%d, ipu:%d\n",
+                               setting.if_fmt, setting.default_bpp,
+                               setting.disp_id, setting.dev_id);
+       }
+
+       return ret;
+}
+
+/*
+ * Parse user specified options (`video=trident:')
+ * example:
+ *     video=mxcfb0:dev=lcd,800x480M-16@55,if=RGB565,bpp=16,noaccel
+ *     video=mxcfb0:dev=lcd,800x480M-16@55,if=RGB565,fbpix=RGB565
+ */
+static int mxcfb_option_setup(struct platform_device *pdev, struct fb_info *fbi)
+{
+       struct ipuv3_fb_platform_data *pdata = pdev->dev.platform_data;
+       char *options, *opt, *fb_mode_str = NULL;
+       char name[] = "mxcfb0";
+       uint32_t fb_pix_fmt = 0;
+
+       name[5] += pdev->id;
+       if (fb_get_options(name, &options)) {
+               dev_err(&pdev->dev, "Can't get fb option for %s!\n", name);
+               return -ENODEV;
+       }
+
+       if (!options || !*options)
+               return 0;
+
+       while ((opt = strsep(&options, ",")) != NULL) {
+               if (!*opt)
+                       continue;
+
+               if (!strncmp(opt, "dev=", 4)) {
+                       memcpy(pdata->disp_dev, opt + 4, strlen(opt) - 4);
+                       pdata->disp_dev[strlen(opt) - 4] = '\0';
+               } else if (!strncmp(opt, "if=", 3)) {
+                       if (!strncmp(opt+3, "RGB24", 5))
+                               pdata->interface_pix_fmt = IPU_PIX_FMT_RGB24;
+                       else if (!strncmp(opt+3, "BGR24", 5))
+                               pdata->interface_pix_fmt = IPU_PIX_FMT_BGR24;
+                       else if (!strncmp(opt+3, "GBR24", 5))
+                               pdata->interface_pix_fmt = IPU_PIX_FMT_GBR24;
+                       else if (!strncmp(opt+3, "RGB565", 6))
+                               pdata->interface_pix_fmt = IPU_PIX_FMT_RGB565;
+                       else if (!strncmp(opt+3, "RGB666", 6))
+                               pdata->interface_pix_fmt = IPU_PIX_FMT_RGB666;
+                       else if (!strncmp(opt+3, "YUV444", 6))
+                               pdata->interface_pix_fmt = IPU_PIX_FMT_YUV444;
+                       else if (!strncmp(opt+3, "LVDS666", 7))
+                               pdata->interface_pix_fmt = IPU_PIX_FMT_LVDS666;
+                       else if (!strncmp(opt+3, "YUYV16", 6))
+                               pdata->interface_pix_fmt = IPU_PIX_FMT_YUYV;
+                       else if (!strncmp(opt+3, "UYVY16", 6))
+                               pdata->interface_pix_fmt = IPU_PIX_FMT_UYVY;
+                       else if (!strncmp(opt+3, "YVYU16", 6))
+                               pdata->interface_pix_fmt = IPU_PIX_FMT_YVYU;
+                       else if (!strncmp(opt+3, "VYUY16", 6))
+                               pdata->interface_pix_fmt = IPU_PIX_FMT_VYUY;
+               } else if (!strncmp(opt, "fbpix=", 6)) {
+                       if (!strncmp(opt+6, "RGB24", 5))
+                               fb_pix_fmt = IPU_PIX_FMT_RGB24;
+                       else if (!strncmp(opt+6, "BGR24", 5))
+                               fb_pix_fmt = IPU_PIX_FMT_BGR24;
+                       else if (!strncmp(opt+6, "RGB32", 5))
+                               fb_pix_fmt = IPU_PIX_FMT_RGB32;
+                       else if (!strncmp(opt+6, "BGR32", 5))
+                               fb_pix_fmt = IPU_PIX_FMT_BGR32;
+                       else if (!strncmp(opt+6, "ABGR32", 6))
+                               fb_pix_fmt = IPU_PIX_FMT_ABGR32;
+                       else if (!strncmp(opt+6, "RGB565", 6))
+                               fb_pix_fmt = IPU_PIX_FMT_RGB565;
+
+                       if (fb_pix_fmt) {
+                               pixfmt_to_var(fb_pix_fmt, &fbi->var);
+                               pdata->default_bpp =
+                                       fbi->var.bits_per_pixel;
+                       }
+               } else if (!strncmp(opt, "int_clk", 7)) {
+                       pdata->int_clk = true;
+                       continue;
+               } else if (!strncmp(opt, "bpp=", 4)) {
+                       /* bpp setting cannot overwirte fbpix setting */
+                       if (fb_pix_fmt)
+                               continue;
+
+                       pdata->default_bpp =
+                               simple_strtoul(opt + 4, NULL, 0);
+
+                       fb_pix_fmt = bpp_to_pixfmt(pdata->default_bpp);
+                       if (fb_pix_fmt)
+                               pixfmt_to_var(fb_pix_fmt, &fbi->var);
+               } else
+                       fb_mode_str = opt;
+       }
+
+       if (fb_mode_str)
+               pdata->mode_str = fb_mode_str;
+
+       return 0;
+}
+
+static int mxcfb_register(struct fb_info *fbi)
+{
+       struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par;
+       struct fb_videomode m;
+       int ret = 0;
+       char bg0_id[] = "DISP3 BG";
+       char bg1_id[] = "DISP3 BG - DI1";
+       char fg_id[] = "DISP3 FG";
+
+       if (mxcfbi->ipu_di == 0) {
+               bg0_id[4] += mxcfbi->ipu_id;
+               strcpy(fbi->fix.id, bg0_id);
+       } else if (mxcfbi->ipu_di == 1) {
+               bg1_id[4] += mxcfbi->ipu_id;
+               strcpy(fbi->fix.id, bg1_id);
+       } else { /* Overlay */
+               fg_id[4] += mxcfbi->ipu_id;
+               strcpy(fbi->fix.id, fg_id);
+       }
+
+       mxcfb_check_var(&fbi->var, fbi);
+
+       mxcfb_set_fix(fbi);
+
+       /* Added first mode to fbi modelist. */
+       if (!fbi->modelist.next || !fbi->modelist.prev)
+               INIT_LIST_HEAD(&fbi->modelist);
+       fb_var_to_videomode(&m, &fbi->var);
+       fb_add_videomode(&m, &fbi->modelist);
+
+       if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq,
+               mxcfb_irq_handler, IPU_IRQF_ONESHOT, MXCFB_NAME, fbi) != 0) {
+               dev_err(fbi->device, "Error registering EOF irq handler.\n");
+               ret = -EBUSY;
+               goto err0;
+       }
+       ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq);
+       if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq,
+               mxcfb_nf_irq_handler, IPU_IRQF_ONESHOT, MXCFB_NAME, fbi) != 0) {
+               dev_err(fbi->device, "Error registering NFACK irq handler.\n");
+               ret = -EBUSY;
+               goto err1;
+       }
+       ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq);
+
+       if (mxcfbi->ipu_alp_ch_irq != -1)
+               if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq,
+                               mxcfb_alpha_irq_handler, IPU_IRQF_ONESHOT,
+                                       MXCFB_NAME, fbi) != 0) {
+                       dev_err(fbi->device, "Error registering alpha irq "
+                                       "handler.\n");
+                       ret = -EBUSY;
+                       goto err2;
+               }
+
+       if (!mxcfbi->late_init) {
+               fbi->var.activate |= FB_ACTIVATE_FORCE;
+               console_lock();
+               fbi->flags |= FBINFO_MISC_USEREVENT;
+               ret = fb_set_var(fbi, &fbi->var);
+               fbi->flags &= ~FBINFO_MISC_USEREVENT;
+               console_unlock();
+               if (ret < 0) {
+                       dev_err(fbi->device, "Error fb_set_var ret:%d\n", ret);
+                       goto err3;
+               }
+
+               if (mxcfbi->next_blank == FB_BLANK_UNBLANK) {
+                       console_lock();
+                       ret = fb_blank(fbi, FB_BLANK_UNBLANK);
+                       console_unlock();
+                       if (ret < 0) {
+                               dev_err(fbi->device,
+                                       "Error fb_blank ret:%d\n", ret);
+                               goto err4;
+                       }
+               }
+       } else {
+               /*
+                * Setup the channel again though bootloader
+                * has done this, then set_par() can stop the
+                * channel neatly and re-initialize it .
+                */
+               if (mxcfbi->next_blank == FB_BLANK_UNBLANK) {
+                       console_lock();
+                       _setup_disp_channel1(fbi);
+                       ipu_enable_channel(mxcfbi->ipu, mxcfbi->ipu_ch);
+                       console_unlock();
+               }
+       }
+
+
+       ret = register_framebuffer(fbi);
+       if (ret < 0)
+               goto err5;
+
+       return ret;
+err5:
+       if (mxcfbi->next_blank == FB_BLANK_UNBLANK) {
+               console_lock();
+               if (!mxcfbi->late_init)
+                       fb_blank(fbi, FB_BLANK_POWERDOWN);
+               else {
+                       ipu_disable_channel(mxcfbi->ipu, mxcfbi->ipu_ch,
+                                           true);
+                       ipu_uninit_channel(mxcfbi->ipu, mxcfbi->ipu_ch);
+               }
+               console_unlock();
+       }
+err4:
+err3:
+       if (mxcfbi->ipu_alp_ch_irq != -1)
+               ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq, fbi);
+err2:
+       ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq, fbi);
+err1:
+       ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq, fbi);
+err0:
+       return ret;
+}
+
+static void mxcfb_unregister(struct fb_info *fbi)
+{
+       struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par;
+
+       if (mxcfbi->ipu_alp_ch_irq != -1)
+               ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq, fbi);
+       if (mxcfbi->ipu_ch_irq)
+               ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq, fbi);
+       if (mxcfbi->ipu_ch_nf_irq)
+               ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq, fbi);
+
+       unregister_framebuffer(fbi);
+}
+
+static int mxcfb_setup_overlay(struct platform_device *pdev,
+               struct fb_info *fbi_bg, struct resource *res)
+{
+       struct fb_info *ovfbi;
+       struct mxcfb_info *mxcfbi_bg = (struct mxcfb_info *)fbi_bg->par;
+       struct mxcfb_info *mxcfbi_fg;
+       int ret = 0;
+
+       ovfbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);
+       if (!ovfbi) {
+               ret = -ENOMEM;
+               goto init_ovfbinfo_failed;
+       }
+       mxcfbi_fg = ovfbi->par;
+
+       mxcfbi_fg->ipu = ipu_get_soc(mxcfbi_bg->ipu_id);
+       if (IS_ERR(mxcfbi_fg->ipu)) {
+               ret = -ENODEV;
+               goto get_ipu_failed;
+       }
+       mxcfbi_fg->ipu_id = mxcfbi_bg->ipu_id;
+       mxcfbi_fg->ipu_ch_irq = IPU_IRQ_FG_SYNC_EOF;
+       mxcfbi_fg->ipu_ch_nf_irq = IPU_IRQ_FG_SYNC_NFACK;
+       mxcfbi_fg->ipu_alp_ch_irq = IPU_IRQ_FG_ALPHA_SYNC_EOF;
+       mxcfbi_fg->ipu_ch = MEM_FG_SYNC;
+       mxcfbi_fg->ipu_di = -1;
+       mxcfbi_fg->ipu_di_pix_fmt = mxcfbi_bg->ipu_di_pix_fmt;
+       mxcfbi_fg->overlay = true;
+       mxcfbi_fg->cur_blank = mxcfbi_fg->next_blank = FB_BLANK_POWERDOWN;
+
+       /* Need dummy values until real panel is configured */
+       ovfbi->var.xres = 240;
+       ovfbi->var.yres = 320;
+
+       if (res && res->start && res->end) {
+               ovfbi->fix.smem_len = res->end - res->start + 1;
+               ovfbi->fix.smem_start = res->start;
+               ovfbi->screen_base = ioremap(
+                                       ovfbi->fix.smem_start,
+                                       ovfbi->fix.smem_len);
+       }
+
+       ret = mxcfb_register(ovfbi);
+       if (ret < 0)
+               goto register_ov_failed;
+
+       mxcfbi_bg->ovfbi = ovfbi;
+
+       return ret;
+
+register_ov_failed:
+get_ipu_failed:
+       fb_dealloc_cmap(&ovfbi->cmap);
+       framebuffer_release(ovfbi);
+init_ovfbinfo_failed:
+       return ret;
+}
+
+static void mxcfb_unsetup_overlay(struct fb_info *fbi_bg)
+{
+       struct mxcfb_info *mxcfbi_bg = fbi_bg->par;
+       struct fb_info *ovfbi = mxcfbi_bg->ovfbi;
+
+       mxcfb_unregister(ovfbi);
+
+       if (&ovfbi->cmap)
+               fb_dealloc_cmap(&ovfbi->cmap);
+       framebuffer_release(ovfbi);
+}
+
+static bool ipu_usage[2][2];
+static int ipu_test_set_usage(int ipu, int di)
+{
+       if (ipu_usage[ipu][di])
+               return -EBUSY;
+       else
+               ipu_usage[ipu][di] = true;
+       return 0;
+}
+
+static void ipu_clear_usage(int ipu, int di)
+{
+       ipu_usage[ipu][di] = false;
+}
+
+static int mxcfb_get_of_property(struct platform_device *pdev,
+                               struct ipuv3_fb_platform_data *plat_data)
+{
+       struct device_node *np = pdev->dev.of_node;
+       const char *disp_dev;
+       const char *mode_str;
+       const char *pixfmt;
+       int err;
+       int len;
+       u32 bpp, int_clk;
+       u32 late_init;
+
+       err = of_property_read_string(np, "disp_dev", &disp_dev);
+       if (err < 0) {
+               dev_dbg(&pdev->dev, "get of property disp_dev fail\n");
+               return err;
+       }
+       err = of_property_read_string(np, "mode_str", &mode_str);
+       if (err < 0) {
+               dev_dbg(&pdev->dev, "get of property mode_str fail\n");
+               return err;
+       }
+       err = of_property_read_string(np, "interface_pix_fmt", &pixfmt);
+       if (err) {
+               dev_dbg(&pdev->dev, "get of property pix fmt fail\n");
+               return err;
+       }
+       err = of_property_read_u32(np, "default_bpp", &bpp);
+       if (err) {
+               dev_dbg(&pdev->dev, "get of property bpp fail\n");
+               return err;
+       }
+       err = of_property_read_u32(np, "int_clk", &int_clk);
+       if (err) {
+               dev_dbg(&pdev->dev, "get of property int_clk fail\n");
+               return err;
+       }
+       err = of_property_read_u32(np, "late_init", &late_init);
+       if (err) {
+               dev_dbg(&pdev->dev, "get of property late_init fail\n");
+               return err;
+       }
+
+       if (!strncmp(pixfmt, "RGB24", 5))
+               plat_data->interface_pix_fmt = IPU_PIX_FMT_RGB24;
+       else if (!strncmp(pixfmt, "BGR24", 5))
+               plat_data->interface_pix_fmt = IPU_PIX_FMT_BGR24;
+       else if (!strncmp(pixfmt, "GBR24", 5))
+               plat_data->interface_pix_fmt = IPU_PIX_FMT_GBR24;
+       else if (!strncmp(pixfmt, "RGB565", 6))
+               plat_data->interface_pix_fmt = IPU_PIX_FMT_RGB565;
+       else if (!strncmp(pixfmt, "RGB666", 6))
+               plat_data->interface_pix_fmt = IPU_PIX_FMT_RGB666;
+       else if (!strncmp(pixfmt, "YUV444", 6))
+               plat_data->interface_pix_fmt = IPU_PIX_FMT_YUV444;
+       else if (!strncmp(pixfmt, "LVDS666", 7))
+               plat_data->interface_pix_fmt = IPU_PIX_FMT_LVDS666;
+       else if (!strncmp(pixfmt, "YUYV16", 6))
+               plat_data->interface_pix_fmt = IPU_PIX_FMT_YUYV;
+       else if (!strncmp(pixfmt, "UYVY16", 6))
+               plat_data->interface_pix_fmt = IPU_PIX_FMT_UYVY;
+       else if (!strncmp(pixfmt, "YVYU16", 6))
+               plat_data->interface_pix_fmt = IPU_PIX_FMT_YVYU;
+       else if (!strncmp(pixfmt, "VYUY16", 6))
+                               plat_data->interface_pix_fmt = IPU_PIX_FMT_VYUY;
+       else {
+               dev_err(&pdev->dev, "err interface_pix_fmt!\n");
+               return -ENOENT;
+       }
+
+       len = min(sizeof(plat_data->disp_dev) - 1, strlen(disp_dev));
+       memcpy(plat_data->disp_dev, disp_dev, len);
+       plat_data->disp_dev[len] = '\0';
+       plat_data->mode_str = (char *)mode_str;
+       plat_data->default_bpp = bpp;
+       plat_data->int_clk = (bool)int_clk;
+       plat_data->late_init = (bool)late_init;
+       return err;
+}
+
+/*!
+ * Probe routine for the framebuffer driver. It is called during the
+ * driver binding process.      The following functions are performed in
+ * this routine: Framebuffer initialization, Memory allocation and
+ * mapping, Framebuffer registration, IPU initialization.
+ *
+ * @return      Appropriate error code to the kernel common code
+ */
+static int mxcfb_probe(struct platform_device *pdev)
+{
+       struct ipuv3_fb_platform_data *plat_data;
+       struct fb_info *fbi;
+       struct mxcfb_info *mxcfbi;
+       struct resource *res;
+       int ret = 0;
+
+       dev_dbg(&pdev->dev, "%s enter\n", __func__);
+       pdev->id = of_alias_get_id(pdev->dev.of_node, "mxcfb");
+       if (pdev->id < 0) {
+               dev_err(&pdev->dev, "can not get alias id\n");
+               return pdev->id;
+       }
+
+       plat_data = devm_kzalloc(&pdev->dev, sizeof(struct
+                                       ipuv3_fb_platform_data), GFP_KERNEL);
+       if (!plat_data)
+               return -ENOMEM;
+       pdev->dev.platform_data = plat_data;
+
+       ret = mxcfb_get_of_property(pdev, plat_data);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "get mxcfb of property fail\n");
+               return ret;
+       }
+
+       /* Initialize FB structures */
+       fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);
+       if (!fbi) {
+               ret = -ENOMEM;
+               goto init_fbinfo_failed;
+       }
+
+       ret = mxcfb_option_setup(pdev, fbi);
+       if (ret)
+               goto get_fb_option_failed;
+
+       mxcfbi = fbi->par;
+       mxcfbi->ipu_int_clk = plat_data->int_clk;
+       mxcfbi->late_init = plat_data->late_init;
+       mxcfbi->first_set_par = true;
+       ret = mxcfb_dispdrv_init(pdev, fbi);
+       if (ret < 0)
+               goto init_dispdrv_failed;
+
+       ret = ipu_test_set_usage(mxcfbi->ipu_id, mxcfbi->ipu_di);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "ipu%d-di%d already in use\n",
+                               mxcfbi->ipu_id, mxcfbi->ipu_di);
+               goto ipu_in_busy;
+       }
+
+       if (mxcfbi->dispdrv->drv->post_init) {
+               ret = mxcfbi->dispdrv->drv->post_init(mxcfbi->dispdrv,
+                                               mxcfbi->ipu_id,
+                                               mxcfbi->ipu_di);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "post init failed\n");
+                       goto post_init_failed;
+               }
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res && res->start && res->end) {
+               fbi->fix.smem_len = res->end - res->start + 1;
+               fbi->fix.smem_start = res->start;
+               fbi->screen_base = ioremap(fbi->fix.smem_start, fbi->fix.smem_len);
+               /* Do not clear the fb content drawn in bootloader. */
+               if (!mxcfbi->late_init)
+                       memset(fbi->screen_base, 0, fbi->fix.smem_len);
+       }
+
+       mxcfbi->ipu = ipu_get_soc(mxcfbi->ipu_id);
+       if (IS_ERR(mxcfbi->ipu)) {
+               ret = -ENODEV;
+               goto get_ipu_failed;
+       }
+
+       /* first user uses DP with alpha feature */
+       if (!g_dp_in_use[mxcfbi->ipu_id]) {
+               mxcfbi->ipu_ch_irq = IPU_IRQ_BG_SYNC_EOF;
+               mxcfbi->ipu_ch_nf_irq = IPU_IRQ_BG_SYNC_NFACK;
+               mxcfbi->ipu_alp_ch_irq = IPU_IRQ_BG_ALPHA_SYNC_EOF;
+               mxcfbi->ipu_ch = MEM_BG_SYNC;
+               /* Unblank the primary fb only by default */
+               if (pdev->id == 0)
+                       mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_UNBLANK;
+               else
+                       mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN;
+
+               ret = mxcfb_register(fbi);
+               if (ret < 0)
+                       goto mxcfb_register_failed;
+
+               ipu_disp_set_global_alpha(mxcfbi->ipu, mxcfbi->ipu_ch,
+                                         true, 0x80);
+               ipu_disp_set_color_key(mxcfbi->ipu, mxcfbi->ipu_ch, false, 0);
+
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+               ret = mxcfb_setup_overlay(pdev, fbi, res);
+
+               if (ret < 0) {
+                       mxcfb_unregister(fbi);
+                       goto mxcfb_setupoverlay_failed;
+               }
+
+               g_dp_in_use[mxcfbi->ipu_id] = true;
+
+               ret = device_create_file(mxcfbi->ovfbi->dev,
+                                        &dev_attr_fsl_disp_property);
+               if (ret)
+                       dev_err(mxcfbi->ovfbi->dev, "Error %d on creating "
+                                                   "file for disp property\n",
+                                                   ret);
+
+               ret = device_create_file(mxcfbi->ovfbi->dev,
+                                        &dev_attr_fsl_disp_dev_property);
+               if (ret)
+                       dev_err(mxcfbi->ovfbi->dev, "Error %d on creating "
+                                                   "file for disp device "
+                                                   "propety\n", ret);
+       } else {
+               mxcfbi->ipu_ch_irq = IPU_IRQ_DC_SYNC_EOF;
+               mxcfbi->ipu_ch_nf_irq = IPU_IRQ_DC_SYNC_NFACK;
+               mxcfbi->ipu_alp_ch_irq = -1;
+               mxcfbi->ipu_ch = MEM_DC_SYNC;
+               mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN;
+
+               ret = mxcfb_register(fbi);
+               if (ret < 0)
+                       goto mxcfb_register_failed;
+       }
+
+       platform_set_drvdata(pdev, fbi);
+
+       ret = device_create_file(fbi->dev, &dev_attr_fsl_disp_property);
+       if (ret)
+               dev_err(&pdev->dev, "Error %d on creating file for disp "
+                                   "property\n", ret);
+
+       ret = device_create_file(fbi->dev, &dev_attr_fsl_disp_dev_property);
+       if (ret)
+               dev_err(&pdev->dev, "Error %d on creating file for disp "
+                                   " device propety\n", ret);
+
+       return 0;
+
+mxcfb_setupoverlay_failed:
+mxcfb_register_failed:
+get_ipu_failed:
+post_init_failed:
+       ipu_clear_usage(mxcfbi->ipu_id, mxcfbi->ipu_di);
+ipu_in_busy:
+init_dispdrv_failed:
+       fb_dealloc_cmap(&fbi->cmap);
+       framebuffer_release(fbi);
+get_fb_option_failed:
+init_fbinfo_failed:
+       return ret;
+}
+
+static int mxcfb_remove(struct platform_device *pdev)
+{
+       struct fb_info *fbi = platform_get_drvdata(pdev);
+       struct mxcfb_info *mxc_fbi = fbi->par;
+
+       if (!fbi)
+               return 0;
+
+       device_remove_file(fbi->dev, &dev_attr_fsl_disp_dev_property);
+       device_remove_file(fbi->dev, &dev_attr_fsl_disp_property);
+       mxcfb_blank(FB_BLANK_POWERDOWN, fbi);
+       mxcfb_unregister(fbi);
+       mxcfb_unmap_video_memory(fbi);
+
+       if (mxc_fbi->ovfbi) {
+               device_remove_file(mxc_fbi->ovfbi->dev,
+                                  &dev_attr_fsl_disp_dev_property);
+               device_remove_file(mxc_fbi->ovfbi->dev,
+                                  &dev_attr_fsl_disp_property);
+               mxcfb_blank(FB_BLANK_POWERDOWN, mxc_fbi->ovfbi);
+               mxcfb_unsetup_overlay(fbi);
+               mxcfb_unmap_video_memory(mxc_fbi->ovfbi);
+       }
+
+       ipu_clear_usage(mxc_fbi->ipu_id, mxc_fbi->ipu_di);
+       if (&fbi->cmap)
+               fb_dealloc_cmap(&fbi->cmap);
+       framebuffer_release(fbi);
+       return 0;
+}
+
+static const struct of_device_id imx_mxcfb_dt_ids[] = {
+       { .compatible = "fsl,mxc_sdc_fb"},
+       { /* sentinel */ }
+};
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcfb_driver = {
+       .driver = {
+               .name = MXCFB_NAME,
+               .of_match_table = imx_mxcfb_dt_ids,
+       },
+       .probe = mxcfb_probe,
+       .remove = mxcfb_remove,
+       .suspend = mxcfb_suspend,
+       .resume = mxcfb_resume,
+};
+
+/*!
+ * Main entry function for the framebuffer. The function registers the power
+ * management callback functions with the kernel and also registers the MXCFB
+ * callback functions with the core Linux framebuffer driver \b fbmem.c
+ *
+ * @return      Error code indicating success or failure
+ */
+int __init mxcfb_init(void)
+{
+       return platform_driver_register(&mxcfb_driver);
+}
+
+void mxcfb_exit(void)
+{
+       platform_driver_unregister(&mxcfb_driver);
+}
+
+module_init(mxcfb_init);
+module_exit(mxcfb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC framebuffer driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("fb");