MLK-11431-1: IPU: forward IPU display drivers to 4.1.y kernel
authorSandor Yu <R01008@freescale.com>
Wed, 26 Aug 2015 08:42:02 +0000 (16:42 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:48:25 +0000 (14:48 -0500)
Forward imx_3.14.y IPU and display drivers to 4.1 kernel.
This includes IPU core driver, display driver, LDB and HDMI driver.

Signed-off-by: Sandor Yu <R01008@freescale.com>
46 files changed:
Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt [new file with mode: 0644]
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/mxc-hdmi-core.c [new file with mode: 0644]
drivers/mxc/Kconfig
drivers/mxc/Makefile
drivers/mxc/hdmi-cec/Kconfig [new file with mode: 0644]
drivers/mxc/hdmi-cec/Makefile [new file with mode: 0644]
drivers/mxc/hdmi-cec/mxc_hdmi-cec.c [new file with mode: 0644]
drivers/mxc/hdmi-cec/mxc_hdmi-cec.h [new file with mode: 0644]
drivers/mxc/ipu3/Kconfig [new file with mode: 0644]
drivers/mxc/ipu3/Makefile [new file with mode: 0644]
drivers/mxc/ipu3/ipu_calc_stripes_sizes.c [new file with mode: 0644]
drivers/mxc/ipu3/ipu_capture.c [new file with mode: 0644]
drivers/mxc/ipu3/ipu_common.c [new file with mode: 0644]
drivers/mxc/ipu3/ipu_device.c [new file with mode: 0644]
drivers/mxc/ipu3/ipu_disp.c [new file with mode: 0644]
drivers/mxc/ipu3/ipu_ic.c [new file with mode: 0644]
drivers/mxc/ipu3/ipu_param_mem.h [new file with mode: 0644]
drivers/mxc/ipu3/ipu_pixel_clk.c [new file with mode: 0644]
drivers/mxc/ipu3/ipu_prv.h [new file with mode: 0644]
drivers/mxc/ipu3/ipu_regs.h [new file with mode: 0644]
drivers/mxc/ipu3/pre-regs.h [new file with mode: 0644]
drivers/mxc/ipu3/pre.c [new file with mode: 0644]
drivers/mxc/ipu3/prg-regs.h [new file with mode: 0644]
drivers/mxc/ipu3/prg.c [new file with mode: 0644]
drivers/mxc/ipu3/vdoa.c [new file with mode: 0644]
drivers/mxc/ipu3/vdoa.h [new file with mode: 0644]
drivers/video/fbdev/mxc/Kconfig
drivers/video/fbdev/mxc/Makefile
drivers/video/fbdev/mxc/hannstar_cabc.c [new file with mode: 0644]
drivers/video/fbdev/mxc/ldb.c [new file with mode: 0644]
drivers/video/fbdev/mxc/mxc_dispdrv.c
drivers/video/fbdev/mxc/mxc_edid.c [new file with mode: 0644]
drivers/video/fbdev/mxc/mxc_hdmi.c [new file with mode: 0644]
drivers/video/fbdev/mxc/mxc_ipuv3_fb.c [new file with mode: 0644]
drivers/video/fbdev/mxc/mxc_lcdif.c [new file with mode: 0644]
include/linux/ipu-v3-pre.h [new file with mode: 0644]
include/linux/ipu-v3-prg.h [new file with mode: 0644]
include/linux/ipu-v3.h [new file with mode: 0644]
include/linux/ipu.h [new file with mode: 0644]
include/linux/mfd/mxc-hdmi-core.h [new file with mode: 0644]
include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
include/uapi/linux/ipu.h [new file with mode: 0644]
include/video/mxc_edid.h [new file with mode: 0644]
include/video/mxc_hdmi.h [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt b/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt
new file mode 100644 (file)
index 0000000..b8d27ef
--- /dev/null
@@ -0,0 +1,105 @@
+* FSL IPUv3 Display/FB
+
+The FSL IPUv3 is Image Processing Unit version 3, a part of video and graphics
+subsystem in an application processor. The goal of the IPU is to provide
+comprehensive support for the flow of data from an image sensor or/and to a
+display device.
+
+Two IPU units are on the imx6q SOC while only one IPU unit on the imx6dl SOC.
+Each IPU unit has two display interfaces.
+
+Required properties for IPU:
+- bypass_reset :Bypass reset to avoid display channel being.
+  stopped by probe since it may start to work in bootloader: 0 or 1.
+- compatible : should be "fsl,imx6q-ipu".
+- reg : the register address range.
+- interrupts : the error and sync interrupts request.
+- clocks : the clock sources that it depends on.
+- clock-names:  the related clock names.
+- resets : IPU reset specifier.  See reset.txt and fsl,imx-src.txt in
+  Documentation/devicetree/bindings/reset/ for details.
+
+Required properties for fb:
+- compatible : should be "fsl,mxc_sdc_fb".
+- disp_dev : display device: "ldb", "lcd", "hdmi", "mipi_dsi".
+- mode_str : "CLAA-WVGA" for lcd, "TRULY-WVGA" for TRULY mipi_dsi lcd panel,
+  "1920x1080M@60" for hdmi.
+- default_bpp : default bits per pixel: 8/16/24/32
+- int_clk : use internal clock as pixel clock: 0 or 1
+- late_init : to avoid display channel being re-initialized
+  as we've probably setup the channel in bootloader: 0 or 1
+- interface_pix_fmt : display interface pixel format as below:
+       RGB666          IPU_PIX_FMT_RGB666
+       RGB565          IPU_PIX_FMT_RGB565
+       RGB24           IPU_PIX_FMT_RGB24
+       BGR24           IPU_PIX_FMT_BGR24
+       GBR24           IPU_PIX_FMT_GBR24
+       YUV444          IPU_PIX_FMT_YUV444
+       YUYV            IPU_PIX_FMT_YUYV
+       UYVY            IPU_PIX_FMT_UYVY
+       YVYV            IPU_PIX_FMT_YVYU
+       VYUY            IPU_PIX_FMT_VYUY
+
+Required properties for display:
+- compatible : should be "fsl,lcd" for lcd panel
+- reg : the register address range if necessary to have.
+- interrupts : the error and sync interrupts if necessary to have.
+- clocks : the clock sources that it depends on if necessary to have.
+- clock-names: the related clock names if necessary to have.
+- ipu_id : ipu id for the first display device: 0 or 1
+- disp_id : display interface id for the first display interface: 0 or 1
+- default_ifmt : save as above display interface pixel format for lcd
+- pinctrl-names : should be "default"
+- pinctrl-0 : should be pinctrl_ipu1_1 or pinctrl_ipu2_1, which depends on the
+  IPU connected.
+- gpr : the mux controller for the display engine's display interfaces and the display encoder
+       (only valid for mipi dsi now).
+- disp-power-on-supply : the regulator to control display panel's power.
+                       (only valid for mipi dsi now).
+- resets : the gpio pin to reset the display device(only valid for mipi display panel now).
+- lcd_panel : the video mode name for the display device(only valid for mipi display panel now).
+- dev_id : the display engine's identity within the system, which intends to replace ipu_id
+          (only valid for mipi dsi now).
+
+Example for IPU:
+               ipu1: ipu@02400000 {
+                       compatible = "fsl,imx6q-ipu";
+                       reg = <0x02400000 0x400000>;
+                       interrupts = <0 6 0x4 0 5 0x4>;
+                       clocks = <&clks 130>, <&clks 131>, <&clks 132>,
+                                <&clks 39>, <&clks 40>,
+                                <&clks 135>, <&clks 136>;
+                       clock-names = "bus", "di0", "di1",
+                                     "di0_sel", "di1_sel",
+                                     "ldb_di0", "ldb_di1";
+                       resets = <&src 2>;
+                       bypass_reset = <0>;
+               };
+
+Example for fb:
+               fb0 {
+                       compatible = "fsl,mxc_sdc_fb";
+                       disp_dev = "ldb";
+                       interface_pix_fmt = "RGB666";
+                       mode_str ="LDB-XGA";
+                       default_bpp = <16>;
+                       int_clk = <0>;
+                       late_init = <0>;
+                       status = "okay";
+               };
+
+Example for mipi dsi display:
+               mipi_dsi: mipi@021e0000 {
+                       compatible = "fsl,imx6q-mipi-dsi";
+                       reg = <0x021e0000 0x4000>;
+                       interrupts = <0 102 0x04>;
+                       gpr = <&gpr>;
+                       clocks = <&clks 138>, <&clks 204>;
+                       clock-names = "mipi_pllref_clk", "mipi_cfg_clk";
+                       dev_id = <0>;
+                       disp_id = <0>;
+                       lcd_panel = "TRULY-WVGA";
+                       disp-power-on-supply = <&reg_mipi_dsi_pwr_on>
+                       resets = <&mipi_dsi_reset>;
+                       status = "okay";
+               };
index c6df644..6b45fda 100644 (file)
@@ -335,6 +335,13 @@ config MFD_MX25_TSADC
          i.MX25 processors. They consist of a conversion queue for general
          purpose ADC and a queue for Touchscreens.
 
+config MFD_MXC_HDMI
+       tristate "Freescale HDMI Core"
+       select MFD_CORE
+       help
+         This is the core driver for the Freescale i.MX6 on-chip HDMI.
+         This MFD driver connects with the video and audio drivers for HDMI.
+
 config MFD_HI6421_PMIC
        tristate "HiSilicon Hi6421 PMU/Codec IC"
        depends on OF
index 9834e66..df24f25 100644 (file)
@@ -204,6 +204,7 @@ obj-$(CONFIG_MFD_HI655X_PMIC)   += hi655x-pmic.o
 obj-$(CONFIG_MFD_DLN2)         += dln2.o
 obj-$(CONFIG_MFD_RT5033)       += rt5033.o
 obj-$(CONFIG_MFD_SKY81452)     += sky81452.o
+obj-$(CONFIG_MFD_MXC_HDMI)     += mxc-hdmi-core.o
 
 intel-soc-pmic-objs            := intel_soc_pmic_core.o intel_soc_pmic_crc.o
 intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC) += intel_soc_pmic_bxtwc.o
diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c
new file mode 100644 (file)
index 0000000..ca79e7e
--- /dev/null
@@ -0,0 +1,808 @@
+/*
+ * Copyright (C) 2011-2015 Freescale Semiconductor, Inc.
+
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <linux/platform_device.h>
+#include <linux/regulator/machine.h>
+#include <asm/mach-types.h>
+
+#include <video/mxc_hdmi.h>
+#include <linux/ipu-v3.h>
+#include <video/mxc_edid.h>
+#include "../mxc/ipu3/ipu_prv.h"
+#include <linux/mfd/mxc-hdmi-core.h>
+#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mfd/mxc-hdmi-core.h>
+
+struct mxc_hdmi_data {
+       struct platform_device *pdev;
+       unsigned long __iomem *reg_base;
+       unsigned long reg_phys_base;
+       struct device *dev;
+};
+
+static void __iomem *hdmi_base;
+static struct clk *isfr_clk;
+static struct clk *iahb_clk;
+static struct clk *mipi_core_clk;
+static spinlock_t irq_spinlock;
+static spinlock_t edid_spinlock;
+static unsigned int sample_rate;
+static unsigned long pixel_clk_rate;
+static struct clk *pixel_clk;
+static int hdmi_ratio;
+int mxc_hdmi_ipu_id;
+int mxc_hdmi_disp_id;
+static struct mxc_edid_cfg hdmi_core_edid_cfg;
+static int hdmi_core_init;
+static unsigned int hdmi_dma_running;
+static struct snd_pcm_substream *hdmi_audio_stream_playback;
+static unsigned int hdmi_cable_state;
+static unsigned int hdmi_blank_state;
+static unsigned int hdmi_abort_state;
+static spinlock_t hdmi_audio_lock, hdmi_blank_state_lock, hdmi_cable_state_lock;
+
+unsigned int hdmi_set_cable_state(unsigned int state)
+{
+       unsigned long flags;
+       struct snd_pcm_substream *substream = hdmi_audio_stream_playback;
+
+       spin_lock_irqsave(&hdmi_cable_state_lock, flags);
+       hdmi_cable_state = state;
+       spin_unlock_irqrestore(&hdmi_cable_state_lock, flags);
+
+#ifndef CONFIG_MFD_MXC_HDMI_ANDROID
+       if (check_hdmi_state() && substream && hdmi_abort_state) {
+               hdmi_abort_state = 0;
+               substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
+       }
+#endif
+       return 0;
+}
+EXPORT_SYMBOL(hdmi_set_cable_state);
+
+unsigned int hdmi_set_blank_state(unsigned int state)
+{
+       unsigned long flags;
+       struct snd_pcm_substream *substream = hdmi_audio_stream_playback;
+
+       spin_lock_irqsave(&hdmi_blank_state_lock, flags);
+       hdmi_blank_state = state;
+       spin_unlock_irqrestore(&hdmi_blank_state_lock, flags);
+
+#ifndef CONFIG_MFD_MXC_HDMI_ANDROID
+       if (check_hdmi_state() && substream && hdmi_abort_state) {
+               hdmi_abort_state = 0;
+               substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
+       }
+#endif
+
+       return 0;
+}
+EXPORT_SYMBOL(hdmi_set_blank_state);
+
+static void hdmi_audio_abort_stream(struct snd_pcm_substream *substream)
+{
+       unsigned long flags;
+
+       snd_pcm_stream_lock_irqsave(substream, flags);
+
+#ifndef CONFIG_MFD_MXC_HDMI_ANDROID
+       if (snd_pcm_running(substream)) {
+               hdmi_abort_state = 1;
+               substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
+       }
+#else
+       if (snd_pcm_running(substream))
+               snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
+#endif
+
+       snd_pcm_stream_unlock_irqrestore(substream, flags);
+}
+
+int mxc_hdmi_abort_stream(void)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&hdmi_audio_lock, flags);
+       if (hdmi_audio_stream_playback)
+               hdmi_audio_abort_stream(hdmi_audio_stream_playback);
+       spin_unlock_irqrestore(&hdmi_audio_lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(mxc_hdmi_abort_stream);
+
+int check_hdmi_state(void)
+{
+       unsigned long flags1, flags2;
+       unsigned int ret;
+
+       spin_lock_irqsave(&hdmi_cable_state_lock, flags1);
+       spin_lock_irqsave(&hdmi_blank_state_lock, flags2);
+
+       ret = hdmi_cable_state && hdmi_blank_state;
+
+       spin_unlock_irqrestore(&hdmi_blank_state_lock, flags2);
+       spin_unlock_irqrestore(&hdmi_cable_state_lock, flags1);
+
+       return ret;
+}
+EXPORT_SYMBOL(check_hdmi_state);
+
+int mxc_hdmi_register_audio(struct snd_pcm_substream *substream)
+{
+       unsigned long flags, flags1;
+       int ret = 0;
+
+       snd_pcm_stream_lock_irqsave(substream, flags);
+
+       if (substream && check_hdmi_state()) {
+               spin_lock_irqsave(&hdmi_audio_lock, flags1);
+               if (hdmi_audio_stream_playback) {
+                       pr_err("%s unconsist hdmi auido stream!\n", __func__);
+                       ret = -EINVAL;
+               }
+               hdmi_audio_stream_playback = substream;
+               hdmi_abort_state = 0;
+               spin_unlock_irqrestore(&hdmi_audio_lock, flags1);
+       } else
+               ret = -EINVAL;
+
+       snd_pcm_stream_unlock_irqrestore(substream, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(mxc_hdmi_register_audio);
+
+void mxc_hdmi_unregister_audio(struct snd_pcm_substream *substream)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&hdmi_audio_lock, flags);
+       hdmi_audio_stream_playback = NULL;
+       hdmi_abort_state = 0;
+       spin_unlock_irqrestore(&hdmi_audio_lock, flags);
+}
+EXPORT_SYMBOL(mxc_hdmi_unregister_audio);
+
+u8 hdmi_readb(unsigned int reg)
+{
+       u8 value;
+
+       value = __raw_readb(hdmi_base + reg);
+
+       return value;
+}
+EXPORT_SYMBOL(hdmi_readb);
+
+#ifdef DEBUG
+static bool overflow_lo;
+static bool overflow_hi;
+
+bool hdmi_check_overflow(void)
+{
+       u8 val, lo, hi;
+
+       val = hdmi_readb(HDMI_IH_FC_STAT2);
+       lo = (val & HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW) != 0;
+       hi = (val & HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW) != 0;
+
+       if ((lo != overflow_lo) || (hi != overflow_hi)) {
+               pr_debug("%s LowPriority=%d HighPriority=%d  <=======================\n",
+                       __func__, lo, hi);
+               overflow_lo = lo;
+               overflow_hi = hi;
+               return true;
+       }
+       return false;
+}
+#else
+bool hdmi_check_overflow(void)
+{
+       return false;
+}
+#endif
+EXPORT_SYMBOL(hdmi_check_overflow);
+
+void hdmi_writeb(u8 value, unsigned int reg)
+{
+       hdmi_check_overflow();
+       __raw_writeb(value, hdmi_base + reg);
+       hdmi_check_overflow();
+}
+EXPORT_SYMBOL(hdmi_writeb);
+
+void hdmi_mask_writeb(u8 data, unsigned int reg, u8 shift, u8 mask)
+{
+       u8 value = hdmi_readb(reg) & ~mask;
+       value |= (data << shift) & mask;
+       hdmi_writeb(value, reg);
+}
+EXPORT_SYMBOL(hdmi_mask_writeb);
+
+unsigned int hdmi_read4(unsigned int reg)
+{
+       /* read a four byte address from registers */
+       return (hdmi_readb(reg + 3) << 24) |
+               (hdmi_readb(reg + 2) << 16) |
+               (hdmi_readb(reg + 1) << 8) |
+               hdmi_readb(reg);
+}
+EXPORT_SYMBOL(hdmi_read4);
+
+void hdmi_write4(unsigned int value, unsigned int reg)
+{
+       /* write a four byte address to hdmi regs */
+       hdmi_writeb(value & 0xff, reg);
+       hdmi_writeb((value >> 8) & 0xff, reg + 1);
+       hdmi_writeb((value >> 16) & 0xff, reg + 2);
+       hdmi_writeb((value >> 24) & 0xff, reg + 3);
+}
+EXPORT_SYMBOL(hdmi_write4);
+
+static void initialize_hdmi_ih_mutes(void)
+{
+       u8 ih_mute;
+
+       /*
+        * Boot up defaults are:
+        * HDMI_IH_MUTE   = 0x03 (disabled)
+        * HDMI_IH_MUTE_* = 0x00 (enabled)
+        */
+
+       /* Disable top level interrupt bits in HDMI block */
+       ih_mute = hdmi_readb(HDMI_IH_MUTE) |
+                 HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
+                 HDMI_IH_MUTE_MUTE_ALL_INTERRUPT;
+
+       hdmi_writeb(ih_mute, HDMI_IH_MUTE);
+
+       /* by default mask all interrupts */
+       hdmi_writeb(0xff, HDMI_VP_MASK);
+       hdmi_writeb(0xff, HDMI_FC_MASK0);
+       hdmi_writeb(0xff, HDMI_FC_MASK1);
+       hdmi_writeb(0xff, HDMI_FC_MASK2);
+       hdmi_writeb(0xff, HDMI_PHY_MASK0);
+       hdmi_writeb(0xff, HDMI_PHY_I2CM_INT_ADDR);
+       hdmi_writeb(0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
+       hdmi_writeb(0xff, HDMI_AUD_INT);
+       hdmi_writeb(0xff, HDMI_AUD_SPDIFINT);
+       hdmi_writeb(0xff, HDMI_AUD_HBR_MASK);
+       hdmi_writeb(0xff, HDMI_GP_MASK);
+       hdmi_writeb(0xff, HDMI_A_APIINTMSK);
+       hdmi_writeb(0xff, HDMI_CEC_MASK);
+       hdmi_writeb(0xff, HDMI_I2CM_INT);
+       hdmi_writeb(0xff, HDMI_I2CM_CTLINT);
+
+       /* Disable interrupts in the IH_MUTE_* registers */
+       hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT0);
+       hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT1);
+       hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT2);
+       hdmi_writeb(0xff, HDMI_IH_MUTE_AS_STAT0);
+       hdmi_writeb(0xff, HDMI_IH_MUTE_PHY_STAT0);
+       hdmi_writeb(0xff, HDMI_IH_MUTE_I2CM_STAT0);
+       hdmi_writeb(0xff, HDMI_IH_MUTE_CEC_STAT0);
+       hdmi_writeb(0xff, HDMI_IH_MUTE_VP_STAT0);
+       hdmi_writeb(0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
+       hdmi_writeb(0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+       /* Enable top level interrupt bits in HDMI block */
+       ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
+                   HDMI_IH_MUTE_MUTE_ALL_INTERRUPT);
+       hdmi_writeb(ih_mute, HDMI_IH_MUTE);
+}
+
+static void hdmi_set_clock_regenerator_n(unsigned int value)
+{
+       u8 val;
+
+       if (!hdmi_dma_running) {
+               hdmi_writeb(value & 0xff, HDMI_AUD_N1);
+               hdmi_writeb(0, HDMI_AUD_N2);
+               hdmi_writeb(0, HDMI_AUD_N3);
+       }
+
+       hdmi_writeb(value & 0xff, HDMI_AUD_N1);
+       hdmi_writeb((value >> 8) & 0xff, HDMI_AUD_N2);
+       hdmi_writeb((value >> 16) & 0x0f, HDMI_AUD_N3);
+
+       /* nshift factor = 0 */
+       val = hdmi_readb(HDMI_AUD_CTS3);
+       val &= ~HDMI_AUD_CTS3_N_SHIFT_MASK;
+       hdmi_writeb(val, HDMI_AUD_CTS3);
+}
+
+static void hdmi_set_clock_regenerator_cts(unsigned int cts)
+{
+       u8 val;
+
+       if (!hdmi_dma_running) {
+               hdmi_writeb(cts & 0xff, HDMI_AUD_CTS1);
+               hdmi_writeb(0, HDMI_AUD_CTS2);
+               hdmi_writeb(0, HDMI_AUD_CTS3);
+       }
+
+       /* Must be set/cleared first */
+       val = hdmi_readb(HDMI_AUD_CTS3);
+       val &= ~HDMI_AUD_CTS3_CTS_MANUAL;
+       hdmi_writeb(val, HDMI_AUD_CTS3);
+
+       hdmi_writeb(cts & 0xff, HDMI_AUD_CTS1);
+       hdmi_writeb((cts >> 8) & 0xff, HDMI_AUD_CTS2);
+       hdmi_writeb(((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
+                   HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+}
+
+static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
+                                  unsigned int ratio)
+{
+       unsigned int n = (128 * freq) / 1000;
+
+       switch (freq) {
+       case 32000:
+               if (pixel_clk == 25174000)
+                       n = (ratio == 150) ? 9152 : 4576;
+               else if (pixel_clk == 27020000)
+                       n = (ratio == 150) ? 8192 : 4096;
+               else if (pixel_clk == 74170000 || pixel_clk == 148350000)
+                       n = 11648;
+               else if (pixel_clk == 297000000)
+                       n = (ratio == 150) ? 6144 : 3072;
+               else
+                       n = 4096;
+               break;
+
+       case 44100:
+               if (pixel_clk == 25174000)
+                       n = 7007;
+               else if (pixel_clk == 74170000)
+                       n = 17836;
+               else if (pixel_clk == 148350000)
+                       n = (ratio == 150) ? 17836 : 8918;
+               else if (pixel_clk == 297000000)
+                       n = (ratio == 150) ? 9408 : 4704;
+               else
+                       n = 6272;
+               break;
+
+       case 48000:
+               if (pixel_clk == 25174000)
+                       n = (ratio == 150) ? 9152 : 6864;
+               else if (pixel_clk == 27020000)
+                       n = (ratio == 150) ? 8192 : 6144;
+               else if (pixel_clk == 74170000)
+                       n = 11648;
+               else if (pixel_clk == 148350000)
+                       n = (ratio == 150) ? 11648 : 5824;
+               else if (pixel_clk == 297000000)
+                       n = (ratio == 150) ? 10240 : 5120;
+               else
+                       n = 6144;
+               break;
+
+       case 88200:
+               n = hdmi_compute_n(44100, pixel_clk, ratio) * 2;
+               break;
+
+       case 96000:
+               n = hdmi_compute_n(48000, pixel_clk, ratio) * 2;
+               break;
+
+       case 176400:
+               n = hdmi_compute_n(44100, pixel_clk, ratio) * 4;
+               break;
+
+       case 192000:
+               n = hdmi_compute_n(48000, pixel_clk, ratio) * 4;
+               break;
+
+       default:
+               break;
+       }
+
+       return n;
+}
+
+static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
+                                    unsigned int ratio)
+{
+       unsigned int cts = 0;
+       switch (freq) {
+       case 32000:
+               if (pixel_clk == 297000000) {
+                       cts = 222750;
+                       break;
+               } else if (pixel_clk == 25174000) {
+                       cts = 28125;
+                       break;
+               }
+       case 48000:
+       case 96000:
+       case 192000:
+               switch (pixel_clk) {
+               case 25200000:
+               case 27000000:
+               case 54000000:
+               case 74250000:
+               case 148500000:
+                       cts = pixel_clk / 1000;
+                       break;
+               case 297000000:
+                       cts = 247500;
+                       break;
+               case 25174000:
+                       cts = 28125l;
+                       break;
+               /*
+                * All other TMDS clocks are not supported by
+                * DWC_hdmi_tx. The TMDS clocks divided or
+                * multiplied by 1,001 coefficients are not
+                * supported.
+                */
+               default:
+                       break;
+               }
+               break;
+       case 44100:
+       case 88200:
+       case 176400:
+               switch (pixel_clk) {
+               case 25200000:
+                       cts = 28000;
+                       break;
+               case 25174000:
+                       cts = 31250;
+                       break;
+               case 27000000:
+                       cts = 30000;
+                       break;
+               case 54000000:
+                       cts = 60000;
+                       break;
+               case 74250000:
+                       cts = 82500;
+                       break;
+               case 148500000:
+                       cts = 165000;
+                       break;
+               case 297000000:
+                       cts = 247500;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+       if (ratio == 100)
+               return cts;
+       else
+               return (cts * ratio) / 100;
+}
+
+static void hdmi_set_clk_regenerator(void)
+{
+       unsigned int clk_n, clk_cts;
+
+       clk_n = hdmi_compute_n(sample_rate, pixel_clk_rate, hdmi_ratio);
+       clk_cts = hdmi_compute_cts(sample_rate, pixel_clk_rate, hdmi_ratio);
+
+       if (clk_cts == 0) {
+               pr_debug("%s: pixel clock not supported: %d\n",
+                       __func__, (int)pixel_clk_rate);
+               return;
+       }
+
+       pr_debug("%s: samplerate=%d  ratio=%d  pixelclk=%d  N=%d  cts=%d\n",
+               __func__, sample_rate, hdmi_ratio, (int)pixel_clk_rate,
+               clk_n, clk_cts);
+
+       hdmi_set_clock_regenerator_cts(clk_cts);
+       hdmi_set_clock_regenerator_n(clk_n);
+}
+
+static int hdmi_core_get_of_property(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       int err;
+       int ipu_id, disp_id;
+
+       err = of_property_read_u32(np, "ipu_id", &ipu_id);
+       if (err) {
+               dev_dbg(&pdev->dev, "get of property ipu_id fail\n");
+               return err;
+       }
+       err = of_property_read_u32(np, "disp_id", &disp_id);
+       if (err) {
+               dev_dbg(&pdev->dev, "get of property disp_id fail\n");
+               return err;
+       }
+
+       mxc_hdmi_ipu_id = ipu_id;
+       mxc_hdmi_disp_id = disp_id;
+
+       return err;
+}
+
+/* Need to run this before phy is enabled the first time to prevent
+ * overflow condition in HDMI_IH_FC_STAT2 */
+void hdmi_init_clk_regenerator(void)
+{
+       if (pixel_clk_rate == 0) {
+               pixel_clk_rate = 74250000;
+               hdmi_set_clk_regenerator();
+       }
+}
+EXPORT_SYMBOL(hdmi_init_clk_regenerator);
+
+void hdmi_clk_regenerator_update_pixel_clock(u32 pixclock)
+{
+
+       if (!pixclock)
+               return;
+       /* Translate pixel clock in ps (pico seconds) to Hz  */
+       pixel_clk_rate = PICOS2KHZ(pixclock) * 1000UL;
+       hdmi_set_clk_regenerator();
+}
+EXPORT_SYMBOL(hdmi_clk_regenerator_update_pixel_clock);
+
+void hdmi_set_dma_mode(unsigned int dma_running)
+{
+       hdmi_dma_running = dma_running;
+       hdmi_set_clk_regenerator();
+}
+EXPORT_SYMBOL(hdmi_set_dma_mode);
+
+void hdmi_set_sample_rate(unsigned int rate)
+{
+       sample_rate = rate;
+}
+EXPORT_SYMBOL(hdmi_set_sample_rate);
+
+void hdmi_set_edid_cfg(struct mxc_edid_cfg *cfg)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&edid_spinlock, flags);
+       memcpy(&hdmi_core_edid_cfg, cfg, sizeof(struct mxc_edid_cfg));
+       spin_unlock_irqrestore(&edid_spinlock, flags);
+}
+EXPORT_SYMBOL(hdmi_set_edid_cfg);
+
+void hdmi_get_edid_cfg(struct mxc_edid_cfg *cfg)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&edid_spinlock, flags);
+       memcpy(cfg, &hdmi_core_edid_cfg, sizeof(struct mxc_edid_cfg));
+       spin_unlock_irqrestore(&edid_spinlock, flags);
+}
+EXPORT_SYMBOL(hdmi_get_edid_cfg);
+
+void hdmi_set_registered(int registered)
+{
+       hdmi_core_init = registered;
+}
+EXPORT_SYMBOL(hdmi_set_registered);
+
+int hdmi_get_registered(void)
+{
+       return hdmi_core_init;
+}
+EXPORT_SYMBOL(hdmi_get_registered);
+
+static int mxc_hdmi_core_probe(struct platform_device *pdev)
+{
+       struct mxc_hdmi_data *hdmi_data;
+       struct resource *res;
+       unsigned long flags;
+       int ret = 0;
+
+#ifdef DEBUG
+       overflow_lo = false;
+       overflow_hi = false;
+#endif
+
+       hdmi_core_init = 0;
+       hdmi_dma_running = 0;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENOENT;
+
+       ret = hdmi_core_get_of_property(pdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "get hdmi of property fail\n");
+               return -ENOENT;
+       }
+
+       hdmi_data = devm_kzalloc(&pdev->dev, sizeof(struct mxc_hdmi_data), GFP_KERNEL);
+       if (!hdmi_data) {
+               dev_err(&pdev->dev, "Couldn't allocate mxc hdmi mfd device\n");
+               return -ENOMEM;
+       }
+       hdmi_data->pdev = pdev;
+
+       pixel_clk = NULL;
+       sample_rate = 48000;
+       pixel_clk_rate = 0;
+       hdmi_ratio = 100;
+
+       spin_lock_init(&irq_spinlock);
+       spin_lock_init(&edid_spinlock);
+
+
+       spin_lock_init(&hdmi_cable_state_lock);
+       spin_lock_init(&hdmi_blank_state_lock);
+       spin_lock_init(&hdmi_audio_lock);
+
+       spin_lock_irqsave(&hdmi_cable_state_lock, flags);
+       hdmi_cable_state = 0;
+       spin_unlock_irqrestore(&hdmi_cable_state_lock, flags);
+
+       spin_lock_irqsave(&hdmi_blank_state_lock, flags);
+       hdmi_blank_state = 0;
+       spin_unlock_irqrestore(&hdmi_blank_state_lock, flags);
+
+       spin_lock_irqsave(&hdmi_audio_lock, flags);
+       hdmi_audio_stream_playback = NULL;
+       hdmi_abort_state = 0;
+       spin_unlock_irqrestore(&hdmi_audio_lock, flags);
+
+       mipi_core_clk = clk_get(&hdmi_data->pdev->dev, "mipi_core");
+       if (IS_ERR(mipi_core_clk)) {
+               ret = PTR_ERR(mipi_core_clk);
+               dev_err(&hdmi_data->pdev->dev,
+                       "Unable to get mipi core clk: %d\n", ret);
+               goto eclkg;
+       }
+
+       ret = clk_prepare_enable(mipi_core_clk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Cannot enable mipi core clock: %d\n", ret);
+               goto eclke;
+       }
+
+       isfr_clk = clk_get(&hdmi_data->pdev->dev, "hdmi_isfr");
+       if (IS_ERR(isfr_clk)) {
+               ret = PTR_ERR(isfr_clk);
+               dev_err(&hdmi_data->pdev->dev,
+                       "Unable to get HDMI isfr clk: %d\n", ret);
+               goto eclkg1;
+       }
+
+       ret = clk_prepare_enable(isfr_clk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Cannot enable HDMI clock: %d\n", ret);
+               goto eclke1;
+       }
+
+       pr_debug("%s isfr_clk:%d\n", __func__,
+               (int)clk_get_rate(isfr_clk));
+
+       iahb_clk = clk_get(&hdmi_data->pdev->dev, "hdmi_iahb");
+       if (IS_ERR(iahb_clk)) {
+               ret = PTR_ERR(iahb_clk);
+               dev_err(&hdmi_data->pdev->dev,
+                       "Unable to get HDMI iahb clk: %d\n", ret);
+               goto eclkg2;
+       }
+
+       ret = clk_prepare_enable(iahb_clk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Cannot enable HDMI clock: %d\n", ret);
+               goto eclke2;
+       }
+
+       hdmi_data->reg_phys_base = res->start;
+       if (!request_mem_region(res->start, resource_size(res),
+                               dev_name(&pdev->dev))) {
+               dev_err(&pdev->dev, "request_mem_region failed\n");
+               ret = -EBUSY;
+               goto emem;
+       }
+
+       hdmi_data->reg_base = ioremap(res->start, resource_size(res));
+       if (!hdmi_data->reg_base) {
+               dev_err(&pdev->dev, "ioremap failed\n");
+               ret = -ENOMEM;
+               goto eirq;
+       }
+       hdmi_base = hdmi_data->reg_base;
+
+       pr_debug("\n%s hdmi hw base = 0x%08x\n\n", __func__, (int)res->start);
+
+       initialize_hdmi_ih_mutes();
+
+       /* Disable HDMI clocks until video/audio sub-drivers are initialized */
+       clk_disable_unprepare(isfr_clk);
+       clk_disable_unprepare(iahb_clk);
+       clk_disable_unprepare(mipi_core_clk);
+
+       /* Replace platform data coming in with a local struct */
+       platform_set_drvdata(pdev, hdmi_data);
+
+       return ret;
+
+eirq:
+       release_mem_region(res->start, resource_size(res));
+emem:
+       clk_disable_unprepare(iahb_clk);
+eclke2:
+       clk_put(iahb_clk);
+eclkg2:
+       clk_disable_unprepare(isfr_clk);
+eclke1:
+       clk_put(isfr_clk);
+eclkg1:
+       clk_disable_unprepare(mipi_core_clk);
+eclke:
+       clk_put(mipi_core_clk);
+eclkg:
+       return ret;
+}
+
+
+static int __exit mxc_hdmi_core_remove(struct platform_device *pdev)
+{
+       struct mxc_hdmi_data *hdmi_data = platform_get_drvdata(pdev);
+       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       iounmap(hdmi_data->reg_base);
+       release_mem_region(res->start, resource_size(res));
+
+       return 0;
+}
+
+static const struct of_device_id imx_hdmi_dt_ids[] = {
+       { .compatible = "fsl,imx6q-hdmi-core", },
+       { .compatible = "fsl,imx6dl-hdmi-core", },
+       { /* sentinel */ }
+};
+
+static struct platform_driver mxc_hdmi_core_driver = {
+       .driver = {
+               .name = "mxc_hdmi_core",
+               .of_match_table = imx_hdmi_dt_ids,
+               .owner = THIS_MODULE,
+       },
+       .remove = __exit_p(mxc_hdmi_core_remove),
+};
+
+static int __init mxc_hdmi_core_init(void)
+{
+       return platform_driver_probe(&mxc_hdmi_core_driver,
+                                    mxc_hdmi_core_probe);
+}
+
+static void __exit mxc_hdmi_core_exit(void)
+{
+       platform_driver_unregister(&mxc_hdmi_core_driver);
+}
+
+subsys_initcall(mxc_hdmi_core_init);
+module_exit(mxc_hdmi_core_exit);
+
+MODULE_DESCRIPTION("Core driver for Freescale i.Mx on-chip HDMI");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
index d52baf1..a81314e 100644 (file)
@@ -4,9 +4,18 @@ if ARCH_MXC
 
 menu "MXC support drivers"
 
+config MXC_IPU
+       bool "Image Processing Unit Driver"
+       select MXC_IPU_V3
+       help
+         If you plan to use the Image Processing unit, say
+         Y here. IPU is needed by Framebuffer and V4L2 drivers.
+
 source "drivers/mxc/mlb/Kconfig"
+source "drivers/mxc/ipu3/Kconfig"
 source "drivers/mxc/sim/Kconfig"
 source "drivers/mxc/vpu/Kconfig"
+source "drivers/mxc/hdmi-cec/Kconfig"
 
 endmenu
 
index 060dc06..170e681 100644 (file)
@@ -1,3 +1,5 @@
 obj-$(CONFIG_MXC_MLB) += mlb/
 obj-$(CONFIG_MXC_SIM) += sim/
 obj-$(CONFIG_MXC_VPU) += vpu/
+obj-$(CONFIG_MXC_IPU_V3) += ipu3/
+obj-$(CONFIG_MXC_HDMI_CEC) += hdmi-cec/
diff --git a/drivers/mxc/hdmi-cec/Kconfig b/drivers/mxc/hdmi-cec/Kconfig
new file mode 100644 (file)
index 0000000..c0be068
--- /dev/null
@@ -0,0 +1,11 @@
+
+menu "MXC HDMI CEC (Consumer Electronics Control) support"
+
+config MXC_HDMI_CEC
+       tristate "Support for MXC HDMI CEC (Consumer Electronics Control)"
+       depends on MFD_MXC_HDMI
+       depends on FB_MXC_HDMI
+       help
+         The HDMI CEC device implement low level protocol on i.MX6x platforms.
+
+endmenu
diff --git a/drivers/mxc/hdmi-cec/Makefile b/drivers/mxc/hdmi-cec/Makefile
new file mode 100644 (file)
index 0000000..07248b2
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_MXC_HDMI_CEC)                  += mxc_hdmi-cec.o
diff --git a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c
new file mode 100644 (file)
index 0000000..b89139f
--- /dev/null
@@ -0,0 +1,664 @@
+/*
+ * Copyright (C) 2012-2015 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
+ */
+
+/*!
+ * @file mxc_hdmi-cec.c
+ *
+ * @brief HDMI CEC system initialization and file operation implementation
+ *
+ * @ingroup HDMI
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/fsl_devices.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+#include <linux/sizes.h>
+
+#include <linux/console.h>
+#include <linux/types.h>
+#include <linux/mfd/mxc-hdmi-core.h>
+#include <linux/pinctrl/consumer.h>
+
+#include <video/mxc_hdmi.h>
+
+#include "mxc_hdmi-cec.h"
+
+
+#define MAX_MESSAGE_LEN                17
+
+#define MESSAGE_TYPE_RECEIVE_SUCCESS           1
+#define MESSAGE_TYPE_NOACK             2
+#define MESSAGE_TYPE_DISCONNECTED              3
+#define MESSAGE_TYPE_CONNECTED         4
+#define MESSAGE_TYPE_SEND_SUCCESS              5
+
+
+struct hdmi_cec_priv {
+       int  receive_error;
+       int  send_error;
+       u8 Logical_address;
+       bool cec_state;
+       u8 last_msg[MAX_MESSAGE_LEN];
+       u8 msg_len;
+       u8 latest_cec_stat;
+       spinlock_t irq_lock;
+       struct delayed_work hdmi_cec_work;
+       struct mutex lock;
+};
+
+struct hdmi_cec_event {
+       int event_type;
+       int msg_len;
+       u8 msg[MAX_MESSAGE_LEN];
+       struct list_head list;
+};
+
+static LIST_HEAD(head);
+
+static int hdmi_cec_major;
+static struct class *hdmi_cec_class;
+static struct hdmi_cec_priv hdmi_cec_data;
+static u8 open_count;
+
+static wait_queue_head_t hdmi_cec_queue;
+static irqreturn_t mxc_hdmi_cec_isr(int irq, void *data)
+{
+       struct hdmi_cec_priv *hdmi_cec = data;
+       u8 cec_stat = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hdmi_cec->irq_lock, flags);
+
+       hdmi_writeb(0x7f, HDMI_IH_MUTE_CEC_STAT0);
+
+       cec_stat = hdmi_readb(HDMI_IH_CEC_STAT0);
+       hdmi_writeb(cec_stat, HDMI_IH_CEC_STAT0);
+
+       if ((cec_stat & (HDMI_IH_CEC_STAT0_ERROR_INIT | \
+               HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | \
+               HDMI_IH_CEC_STAT0_DONE)) == 0) {
+               spin_unlock_irqrestore(&hdmi_cec->irq_lock, flags);
+               return IRQ_HANDLED;
+       }
+
+       pr_debug("HDMI CEC interrupt received\n");
+       hdmi_cec->latest_cec_stat = cec_stat;
+
+       schedule_delayed_work(&(hdmi_cec->hdmi_cec_work), msecs_to_jiffies(20));
+
+       spin_unlock_irqrestore(&hdmi_cec->irq_lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+void mxc_hdmi_cec_handle(u16 cec_stat)
+{
+       u8 val = 0, i = 0;
+       struct hdmi_cec_event *event = NULL;
+
+       /* The current transmission is successful (for initiator only). */
+       if (!open_count)
+               return;
+
+       if (cec_stat & HDMI_IH_CEC_STAT0_DONE) {
+
+               event = vmalloc(sizeof(struct hdmi_cec_event));
+               if (NULL == event) {
+                       pr_err("%s: Not enough memory!\n", __func__);
+                       return;
+               }
+
+               memset(event, 0, sizeof(struct hdmi_cec_event));
+               event->event_type = MESSAGE_TYPE_SEND_SUCCESS;
+
+               mutex_lock(&hdmi_cec_data.lock);
+               list_add_tail(&event->list, &head);
+               mutex_unlock(&hdmi_cec_data.lock);
+
+               wake_up(&hdmi_cec_queue);
+       }
+
+       /* EOM is detected so that the received data is ready
+        * in the receiver data buffer
+        */
+       if (cec_stat & HDMI_IH_CEC_STAT0_EOM) {
+
+               hdmi_writeb(0x02, HDMI_IH_CEC_STAT0);
+
+               event = vmalloc(sizeof(struct hdmi_cec_event));
+               if (NULL == event) {
+                       pr_err("%s: Not enough memory!\n", __func__);
+                       return;
+               }
+               memset(event, 0, sizeof(struct hdmi_cec_event));
+
+               event->msg_len = hdmi_readb(HDMI_CEC_RX_CNT);
+               if (!event->msg_len) {
+                       pr_err("%s: Invalid CEC message length!\n", __func__);
+                       return;
+               }
+               event->event_type = MESSAGE_TYPE_RECEIVE_SUCCESS;
+
+               for (i = 0; i < event->msg_len; i++)
+                       event->msg[i] = hdmi_readb(HDMI_CEC_RX_DATA0+i);
+               hdmi_writeb(0x0, HDMI_CEC_LOCK);
+
+               mutex_lock(&hdmi_cec_data.lock);
+               list_add_tail(&event->list, &head);
+               mutex_unlock(&hdmi_cec_data.lock);
+
+               wake_up(&hdmi_cec_queue);
+       }
+
+       /* An error is detected on cec line (for initiator only). */
+       if (cec_stat & HDMI_IH_CEC_STAT0_ERROR_INIT) {
+
+               mutex_lock(&hdmi_cec_data.lock);
+               hdmi_cec_data.send_error++;
+               if (hdmi_cec_data.send_error > 5) {
+                       pr_err("%s:Re-transmission is attempted more than 5 times!\n",
+                                       __func__);
+                       hdmi_cec_data.send_error = 0;
+                       mutex_unlock(&hdmi_cec_data.lock);
+                       return;
+               }
+
+               for (i = 0; i < hdmi_cec_data.msg_len; i++) {
+                       hdmi_writeb(hdmi_cec_data.last_msg[i],
+                                               HDMI_CEC_TX_DATA0 + i);
+               }
+               hdmi_writeb(hdmi_cec_data.msg_len, HDMI_CEC_TX_CNT);
+
+               val = hdmi_readb(HDMI_CEC_CTRL);
+               val |= 0x01;
+               hdmi_writeb(val, HDMI_CEC_CTRL);
+               mutex_unlock(&hdmi_cec_data.lock);
+       }
+
+       /* A frame is not acknowledged in a directly addressed message.
+        * Or a frame is negatively acknowledged in
+        * a broadcast message (for initiator only).
+        */
+       if (cec_stat & HDMI_IH_CEC_STAT0_NACK) {
+               event = vmalloc(sizeof(struct hdmi_cec_event));
+               if (NULL == event) {
+                       pr_err("%s: Not enough memory\n", __func__);
+                       return;
+               }
+               memset(event, 0, sizeof(struct hdmi_cec_event));
+               event->event_type = MESSAGE_TYPE_NOACK;
+
+               mutex_lock(&hdmi_cec_data.lock);
+               list_add_tail(&event->list, &head);
+               mutex_unlock(&hdmi_cec_data.lock);
+
+               wake_up(&hdmi_cec_queue);
+       }
+
+       /* An error is notified by a follower.
+        * Abnormal logic data bit error (for follower).
+        */
+       if (cec_stat & HDMI_IH_CEC_STAT0_ERROR_FOLL) {
+               hdmi_cec_data.receive_error++;
+       }
+
+       /* HDMI cable connected */
+       if (cec_stat & 0x80) {
+               event = vmalloc(sizeof(struct hdmi_cec_event));
+               if (NULL == event) {
+                       pr_err("%s: Not enough memory\n", __func__);
+                       return;
+               }
+               memset(event, 0, sizeof(struct hdmi_cec_event));
+               event->event_type = MESSAGE_TYPE_CONNECTED;
+
+               mutex_lock(&hdmi_cec_data.lock);
+               list_add_tail(&event->list, &head);
+               mutex_unlock(&hdmi_cec_data.lock);
+
+               wake_up(&hdmi_cec_queue);
+       }
+
+       /* HDMI cable disconnected */
+       if (cec_stat & 0x100) {
+               event = vmalloc(sizeof(struct hdmi_cec_event));
+               if (NULL == event) {
+                       pr_err("%s: Not enough memory!\n", __func__);
+                       return;
+               }
+               memset(event, 0, sizeof(struct hdmi_cec_event));
+               event->event_type = MESSAGE_TYPE_DISCONNECTED;
+
+               mutex_lock(&hdmi_cec_data.lock);
+               list_add_tail(&event->list, &head);
+               mutex_unlock(&hdmi_cec_data.lock);
+
+               wake_up(&hdmi_cec_queue);
+       }
+
+    return;
+}
+EXPORT_SYMBOL(mxc_hdmi_cec_handle);
+
+static void mxc_hdmi_cec_worker(struct work_struct *work)
+{
+       u8 val;
+
+       mxc_hdmi_cec_handle(hdmi_cec_data.latest_cec_stat);
+       val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL |
+                       HDMI_IH_CEC_STAT0_ARB_LOST;
+       hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0);
+}
+
+/*!
+ * @brief open function for vpu file operation
+ *
+ * @return  0 on success or negative error code on error
+ */
+static int hdmi_cec_open(struct inode *inode, struct file *filp)
+{
+       mutex_lock(&hdmi_cec_data.lock);
+       if (open_count) {
+               mutex_unlock(&hdmi_cec_data.lock);
+               return -EBUSY;
+       }
+
+       open_count = 1;
+       filp->private_data = (void *)(&hdmi_cec_data);
+       hdmi_cec_data.Logical_address = 15;
+       hdmi_cec_data.cec_state = false;
+       mutex_unlock(&hdmi_cec_data.lock);
+
+       return 0;
+}
+
+static ssize_t hdmi_cec_read(struct file *file, char __user *buf, size_t count,
+                           loff_t *ppos)
+{
+       struct hdmi_cec_event *event = NULL;
+
+       pr_debug("function : %s\n", __func__);
+       if (!open_count)
+               return -ENODEV;
+
+       mutex_lock(&hdmi_cec_data.lock);
+       if (false == hdmi_cec_data.cec_state) {
+               mutex_unlock(&hdmi_cec_data.lock);
+               return -EACCES;
+       }
+       mutex_unlock(&hdmi_cec_data.lock);
+
+       /* delete from list */
+       mutex_lock(&hdmi_cec_data.lock);
+       if (list_empty(&head)) {
+               mutex_unlock(&hdmi_cec_data.lock);
+               return -EACCES;
+       }
+       event = list_first_entry(&head, struct hdmi_cec_event, list);
+       list_del(&event->list);
+       mutex_unlock(&hdmi_cec_data.lock);
+
+       if (copy_to_user(buf, event,
+                       sizeof(struct hdmi_cec_event) - sizeof(struct list_head))) {
+               vfree(event);
+               return -EFAULT;
+       }
+       vfree(event);
+
+       return sizeof(struct hdmi_cec_event);
+}
+
+static ssize_t hdmi_cec_write(struct file *file, const char __user *buf,
+                            size_t count, loff_t *ppos)
+{
+       int ret = 0 , i = 0;
+       u8 msg[MAX_MESSAGE_LEN];
+       u8 msg_len = 0, val = 0;
+
+       pr_debug("function : %s\n", __func__);
+       if (!open_count)
+               return -ENODEV;
+
+       mutex_lock(&hdmi_cec_data.lock);
+       if (false == hdmi_cec_data.cec_state) {
+               mutex_unlock(&hdmi_cec_data.lock);
+               return -EACCES;
+       }
+       mutex_unlock(&hdmi_cec_data.lock);
+
+       if (count > MAX_MESSAGE_LEN)
+               return -EINVAL;
+
+       mutex_lock(&hdmi_cec_data.lock);
+       hdmi_cec_data.send_error = 0;
+       memset(&msg, 0, MAX_MESSAGE_LEN);
+       ret = copy_from_user(&msg, buf, count);
+       if (ret) {
+               ret = -EACCES;
+               goto end;
+       }
+
+       msg_len = count;
+       hdmi_writeb(msg_len, HDMI_CEC_TX_CNT);
+       for (i = 0; i < msg_len; i++) {
+               hdmi_writeb(msg[i], HDMI_CEC_TX_DATA0+i);
+       }
+
+       val = hdmi_readb(HDMI_CEC_CTRL);
+       val |= 0x01;
+       hdmi_writeb(val, HDMI_CEC_CTRL);
+       memcpy(hdmi_cec_data.last_msg, msg, msg_len);
+       hdmi_cec_data.msg_len = msg_len;
+
+       i = 0;
+       val = hdmi_readb(HDMI_CEC_CTRL);
+       while ((val & 0x01) == 0x1) {
+               msleep(50);
+               i++;
+               if (i > 3) {
+                       ret = -EIO;
+                       goto end;
+               }
+               val = hdmi_readb(HDMI_CEC_CTRL);
+       }
+
+end:
+       mutex_unlock(&hdmi_cec_data.lock);
+
+       return ret;
+}
+
+/*!
+ * @brief IO ctrl function for vpu file operation
+ * @param cmd IO ctrl command
+ * @return  0 on success or negative error code on error
+ */
+static long hdmi_cec_ioctl(struct file *filp, u_int cmd,
+                    u_long arg)
+{
+       int ret = 0, status = 0;
+       u8 val = 0, msg = 0;
+       struct mxc_edid_cfg hdmi_edid_cfg;
+
+       pr_debug("function : %s\n", __func__);
+       if (!open_count)
+               return -ENODEV;
+
+       switch (cmd) {
+       case HDMICEC_IOC_SETLOGICALADDRESS:
+               mutex_lock(&hdmi_cec_data.lock);
+               if (false == hdmi_cec_data.cec_state) {
+                       mutex_unlock(&hdmi_cec_data.lock);
+                       return -EACCES;
+               }
+
+               hdmi_cec_data.Logical_address = (u8)arg;
+
+               if (hdmi_cec_data.Logical_address <= 7) {
+                       val = 1 << hdmi_cec_data.Logical_address;
+                       hdmi_writeb(val, HDMI_CEC_ADDR_L);
+                       hdmi_writeb(0, HDMI_CEC_ADDR_H);
+               } else if (hdmi_cec_data.Logical_address > 7 &&
+                                       hdmi_cec_data.Logical_address <= 15) {
+                       val = 1 << (hdmi_cec_data.Logical_address - 8);
+                       hdmi_writeb(val, HDMI_CEC_ADDR_H);
+                       hdmi_writeb(0, HDMI_CEC_ADDR_L);
+               } else {
+                       ret = -EINVAL;
+               }
+
+               /* Send Polling message with same source
+                * and destination address
+                */
+               if (0 == ret && 15 != hdmi_cec_data.Logical_address) {
+                       msg = (hdmi_cec_data.Logical_address << 4) |
+                                       hdmi_cec_data.Logical_address;
+                       hdmi_writeb(1, HDMI_CEC_TX_CNT);
+                       hdmi_writeb(msg, HDMI_CEC_TX_DATA0);
+
+                       val = hdmi_readb(HDMI_CEC_CTRL);
+                       val |= 0x01;
+                       hdmi_writeb(val, HDMI_CEC_CTRL);
+               }
+
+               mutex_unlock(&hdmi_cec_data.lock);
+               break;
+
+       case HDMICEC_IOC_STARTDEVICE:
+               val = hdmi_readb(HDMI_MC_CLKDIS);
+               val &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
+               hdmi_writeb(val, HDMI_MC_CLKDIS);
+
+               hdmi_writeb(0x02, HDMI_CEC_CTRL);
+
+               val = HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_NACK |
+                       HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_DONE;
+               hdmi_writeb(val, HDMI_CEC_POLARITY);
+
+               val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL |
+                       HDMI_IH_CEC_STAT0_ARB_LOST;
+               hdmi_writeb(val, HDMI_CEC_MASK);
+               hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0);
+
+               mutex_lock(&hdmi_cec_data.lock);
+               hdmi_cec_data.cec_state = true;
+               mutex_unlock(&hdmi_cec_data.lock);
+               break;
+
+       case HDMICEC_IOC_STOPDEVICE:
+               hdmi_writeb(0x10, HDMI_CEC_CTRL);
+
+               val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL |
+                       HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_ARB_LOST |
+                       HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM |
+                       HDMI_IH_CEC_STAT0_DONE;
+               hdmi_writeb(val, HDMI_CEC_MASK);
+               hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0);
+
+               hdmi_writeb(0x0, HDMI_CEC_POLARITY);
+
+               val = hdmi_readb(HDMI_MC_CLKDIS);
+               val |= HDMI_MC_CLKDIS_CECCLK_DISABLE;
+               hdmi_writeb(val, HDMI_MC_CLKDIS);
+
+               mutex_lock(&hdmi_cec_data.lock);
+               hdmi_cec_data.cec_state = false;
+               mutex_unlock(&hdmi_cec_data.lock);
+               break;
+
+       case HDMICEC_IOC_GETPHYADDRESS:
+               hdmi_get_edid_cfg(&hdmi_edid_cfg);
+               status = copy_to_user((void __user *)arg,
+                                        &hdmi_edid_cfg.physical_address,
+                                        4*sizeof(u8));
+               if (status)
+                       ret = -EFAULT;
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/*!
+* @brief Release function for vpu file operation
+* @return  0 on success or negative error code on error
+*/
+static int hdmi_cec_release(struct inode *inode, struct file *filp)
+{
+       mutex_lock(&hdmi_cec_data.lock);
+
+       if (open_count) {
+               open_count = 0;
+               hdmi_cec_data.cec_state = false;
+               hdmi_cec_data.Logical_address = 15;
+       }
+
+       mutex_unlock(&hdmi_cec_data.lock);
+
+       return 0;
+}
+
+static unsigned int hdmi_cec_poll(struct file *file, poll_table *wait)
+{
+       unsigned int mask = 0;
+
+       pr_debug("function : %s\n", __func__);
+
+       if (!open_count)
+               return -ENODEV;
+
+       if (false == hdmi_cec_data.cec_state)
+               return -EACCES;
+
+       poll_wait(file, &hdmi_cec_queue, wait);
+
+       if (!list_empty(&head))
+               mask |= (POLLIN | POLLRDNORM);
+
+       return mask;
+}
+
+const struct file_operations hdmi_cec_fops = {
+       .owner = THIS_MODULE,
+       .read = hdmi_cec_read,
+       .write = hdmi_cec_write,
+       .open = hdmi_cec_open,
+       .unlocked_ioctl = hdmi_cec_ioctl,
+       .release = hdmi_cec_release,
+       .poll = hdmi_cec_poll,
+};
+
+static int hdmi_cec_dev_probe(struct platform_device *pdev)
+{
+       int err = 0;
+       struct device *temp_class;
+       struct resource *res;
+       struct pinctrl *pinctrl;
+       int irq = platform_get_irq(pdev, 0);
+
+       hdmi_cec_major = register_chrdev(hdmi_cec_major,
+                               "mxc_hdmi_cec", &hdmi_cec_fops);
+       if (hdmi_cec_major < 0) {
+               dev_err(&pdev->dev, "hdmi_cec: unable to get a major for HDMI CEC\n");
+               err = -EBUSY;
+               goto out;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (unlikely(res == NULL)) {
+               dev_err(&pdev->dev, "hdmi_cec:No HDMI irq line provided\n");
+               goto err_out_chrdev;
+       }
+
+       spin_lock_init(&hdmi_cec_data.irq_lock);
+
+       err = devm_request_irq(&pdev->dev, irq, mxc_hdmi_cec_isr, IRQF_SHARED,
+                       dev_name(&pdev->dev), &hdmi_cec_data);
+       if (err < 0) {
+               dev_err(&pdev->dev, "hdmi_cec:Unable to request irq: %d\n", err);
+               goto err_out_chrdev;
+       }
+
+       hdmi_cec_class = class_create(THIS_MODULE, "mxc_hdmi_cec");
+       if (IS_ERR(hdmi_cec_class)) {
+               err = PTR_ERR(hdmi_cec_class);
+               goto err_out_chrdev;
+       }
+
+       temp_class = device_create(hdmi_cec_class, NULL,
+                       MKDEV(hdmi_cec_major, 0), NULL, "mxc_hdmi_cec");
+       if (IS_ERR(temp_class)) {
+               err = PTR_ERR(temp_class);
+               goto err_out_class;
+       }
+
+       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(pinctrl)) {
+               dev_err(&pdev->dev, "can't get/select CEC pinctrl\n");
+               goto err_out_class;
+       }
+
+       init_waitqueue_head(&hdmi_cec_queue);
+
+       INIT_LIST_HEAD(&head);
+
+       mutex_init(&hdmi_cec_data.lock);
+
+       hdmi_cec_data.Logical_address = 15;
+
+       platform_set_drvdata(pdev, &hdmi_cec_data);
+
+       INIT_DELAYED_WORK(&hdmi_cec_data.hdmi_cec_work, mxc_hdmi_cec_worker);
+
+       dev_info(&pdev->dev, "HDMI CEC initialized\n");
+       goto out;
+
+err_out_class:
+       device_destroy(hdmi_cec_class, MKDEV(hdmi_cec_major, 0));
+       class_destroy(hdmi_cec_class);
+err_out_chrdev:
+       unregister_chrdev(hdmi_cec_major, "mxc_hdmi_cec");
+out:
+       return err;
+}
+
+static int hdmi_cec_dev_remove(struct platform_device *pdev)
+{
+       if (hdmi_cec_major > 0) {
+               device_destroy(hdmi_cec_class, MKDEV(hdmi_cec_major, 0));
+               class_destroy(hdmi_cec_class);
+               unregister_chrdev(hdmi_cec_major, "mxc_hdmi_cec");
+               hdmi_cec_major = 0;
+       }
+       return 0;
+}
+
+static const struct of_device_id imx_hdmi_cec_match[] = {
+       { .compatible = "fsl,imx6q-hdmi-cec", },
+       { .compatible = "fsl,imx6dl-hdmi-cec", },
+       { /* sentinel */ }
+};
+
+static struct platform_driver mxc_hdmi_cec_driver = {
+       .probe = hdmi_cec_dev_probe,
+       .remove = hdmi_cec_dev_remove,
+       .driver = {
+               .name = "mxc_hdmi_cec",
+               .of_match_table = imx_hdmi_cec_match,
+       },
+};
+
+module_platform_driver(mxc_hdmi_cec_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Linux HDMI CEC driver for Freescale i.MX/MXC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mxc_hdmi_cec");
+
diff --git a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.h b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.h
new file mode 100644 (file)
index 0000000..5c402af
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2005-2015 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
+ */
+#ifndef _HDMICEC_H_
+#define _HDMICEC_H_
+#include <linux/ioctl.h>
+
+/*
+ * Ioctl definitions
+ */
+
+/* Use 'k' as magic number */
+#define HDMICEC_IOC_MAGIC  'H'
+/*
+ * S means "Set" through a ptr,
+ * T means "Tell" directly with the argument value
+ * G means "Get": reply by setting through a pointer
+ * Q means "Query": response is on the return value
+ * X means "eXchange": G and S atomically
+ * H means "sHift": T and Q atomically
+ */
+#define HDMICEC_IOC_SETLOGICALADDRESS  \
+                               _IOW(HDMICEC_IOC_MAGIC, 1, unsigned char)
+#define HDMICEC_IOC_STARTDEVICE        _IO(HDMICEC_IOC_MAGIC,  2)
+#define HDMICEC_IOC_STOPDEVICE _IO(HDMICEC_IOC_MAGIC,  3)
+#define HDMICEC_IOC_GETPHYADDRESS      \
+                               _IOR(HDMICEC_IOC_MAGIC, 4, unsigned char[4])
+
+#endif                         /* !_HDMICEC_H_ */
diff --git a/drivers/mxc/ipu3/Kconfig b/drivers/mxc/ipu3/Kconfig
new file mode 100644 (file)
index 0000000..c702efe
--- /dev/null
@@ -0,0 +1,21 @@
+config MXC_IPU_V3
+       bool
+
+config MXC_IPU_V3_PRG
+       tristate "i.MX IPUv3 prefetch gasket engine"
+       depends on MXC_IPU_V3 && MXC_IPU_V3_PRE
+       help
+         This enables support for the IPUv3 prefetch gasket engine to
+         support double buffer handshake control bewteen IPUv3 and
+         prefetch engine(PRE), snoop the AXI interface for display
+         refresh requests to memory and modify the request address to
+         fetch the double buffered row of blocks in OCRAM.
+
+config MXC_IPU_V3_PRE
+       tristate "i.MX IPUv3 prefetch engine"
+       depends on MXC_IPU_V3
+       select MXC_IPU_V3_PRG
+       help
+         This enables support for the IPUv3 prefetch engine to improve
+         the system memory performance.  The engine has the capability
+         to resolve framebuffers in tile pixel format to linear.
diff --git a/drivers/mxc/ipu3/Makefile b/drivers/mxc/ipu3/Makefile
new file mode 100644 (file)
index 0000000..d48f112
--- /dev/null
@@ -0,0 +1,7 @@
+obj-$(CONFIG_MXC_IPU_V3) = mxc_ipu.o
+
+obj-$(CONFIG_MXC_IPU_V3_PRG) += prg.o
+obj-$(CONFIG_MXC_IPU_V3_PRE) += pre.o
+
+mxc_ipu-objs := ipu_common.o ipu_ic.o ipu_disp.o ipu_capture.o ipu_device.o \
+               ipu_calc_stripes_sizes.o vdoa.o ipu_pixel_clk.o
diff --git a/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c b/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c
new file mode 100644 (file)
index 0000000..512404d
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ * Copyright 2009-2015 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
+ */
+
+/*
+ * @file ipu_calc_stripes_sizes.c
+ *
+ * @brief IPU IC functions
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/ipu-v3.h>
+#include <linux/module.h>
+#include <linux/math64.h>
+
+#define BPP_32 0
+#define BPP_16 3
+#define BPP_8 5
+#define BPP_24 1
+#define BPP_12 4
+#define BPP_18 2
+
+static u32 truncate(u32 up, /* 0: down; else: up */
+                                       u64 a, /* must be non-negative */
+                                       u32 b)
+{
+       u32 d;
+       u64 div;
+       div = div_u64(a, b);
+       d = b * (div >> 32);
+       if (up && (a > (((u64)d) << 32)))
+               return d+b;
+       else
+               return d;
+}
+
+static unsigned int f_calc(unsigned int pfs, unsigned int bpp, unsigned int *write)
+{/* return input_f */
+       unsigned int f_calculated = 0;
+       switch (pfs) {
+       case IPU_PIX_FMT_YVU422P:
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_YUV444P:
+               f_calculated = 16;
+               break;
+
+       case IPU_PIX_FMT_RGB565:
+       case IPU_PIX_FMT_YUYV:
+       case IPU_PIX_FMT_UYVY:
+               f_calculated = 8;
+               break;
+
+       case IPU_PIX_FMT_NV12:
+               f_calculated = 8;
+               break;
+
+       default:
+               f_calculated = 0;
+               break;
+
+       }
+       if (!f_calculated) {
+               switch (bpp) {
+               case BPP_32:
+                       f_calculated = 2;
+                       break;
+
+               case BPP_16:
+                       f_calculated = 4;
+                       break;
+
+               case BPP_8:
+               case BPP_24:
+                       f_calculated = 8;
+                       break;
+
+               case BPP_12:
+                       f_calculated = 16;
+                       break;
+
+               case BPP_18:
+                       f_calculated = 32;
+                       break;
+
+               default:
+                       f_calculated = 0;
+                       break;
+                       }
+               }
+       return f_calculated;
+}
+
+
+static unsigned int m_calc(unsigned int pfs)
+{
+       unsigned int m_calculated = 0;
+       switch (pfs) {
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+       case IPU_PIX_FMT_YVU422P:
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_YUV444P:
+               m_calculated = 16;
+               break;
+
+       case IPU_PIX_FMT_NV12:
+       case IPU_PIX_FMT_YUYV:
+       case IPU_PIX_FMT_UYVY:
+               m_calculated = 8;
+               break;
+
+       default:
+               m_calculated = 8;
+               break;
+
+       }
+       return m_calculated;
+}
+
+static int calc_split_resize_coeffs(unsigned int inSize, unsigned int outSize,
+                                   unsigned int *resizeCoeff,
+                                   unsigned int *downsizeCoeff)
+{
+       uint32_t tempSize;
+       uint32_t tempDownsize;
+
+       if (inSize > 4096) {
+               pr_debug("IC input size(%d) cannot exceed 4096\n",
+                       inSize);
+               return -EINVAL;
+       }
+
+       if (outSize > 1024) {
+               pr_debug("IC output size(%d) cannot exceed 1024\n",
+                       outSize);
+               return -EINVAL;
+       }
+
+       if ((outSize << 3) < inSize) {
+               pr_debug("IC cannot downsize more than 8:1\n");
+               return -EINVAL;
+       }
+
+       /* Compute downsizing coefficient */
+       /* Output of downsizing unit cannot be more than 1024 */
+       tempDownsize = 0;
+       tempSize = inSize;
+       while (((tempSize > 1024) || (tempSize >= outSize * 2)) &&
+              (tempDownsize < 2)) {
+               tempSize >>= 1;
+               tempDownsize++;
+       }
+       *downsizeCoeff = tempDownsize;
+
+       /* compute resizing coefficient using the following equation:
+          resizeCoeff = M*(SI -1)/(SO - 1)
+          where M = 2^13, SI - input size, SO - output size    */
+       *resizeCoeff = (8192L * (tempSize - 1)) / (outSize - 1);
+       if (*resizeCoeff >= 16384L) {
+               pr_debug("Overflow on IC resize coefficient.\n");
+               return -EINVAL;
+       }
+
+       pr_debug("resizing from %u -> %u pixels, "
+               "downsize=%u, resize=%u.%lu (reg=%u)\n", inSize, outSize,
+               *downsizeCoeff, (*resizeCoeff >= 8192L) ? 1 : 0,
+               ((*resizeCoeff & 0x1FFF) * 10000L) / 8192L, *resizeCoeff);
+
+       return 0;
+}
+
+/* Stripe parameters calculator */
+/**************************************************************************
+Notes:
+MSW = the maximal width allowed for a stripe
+       i.MX31: 720, i.MX35: 800, i.MX37/51/53: 1024
+cirr = the maximal inverse resizing ratio for which overlap in the input
+       is requested; typically cirr~2
+flags
+       bit 0 - equal_stripes
+               0  each stripe is allowed to have independent parameters
+               for maximal image quality
+               1  the stripes are requested to have identical parameters
+       (except the base address), for maximal performance
+       bit 1 - vertical/horizontal
+               0 horizontal
+               1 vertical
+
+If performance is the top priority (above image quality)
+       Avoid overlap, by setting CIRR = 0
+               This will also force effectively identical_stripes = 1
+       Choose IF & OF that corresponds to the same IOX/SX for both stripes
+       Choose IFW & OFW such that
+       IFW/IM, IFW/IF, OFW/OM, OFW/OF are even integers
+       The function returns an error status:
+       0: no error
+       1: invalid input parameters -> aborted without result
+               Valid parameters should satisfy the following conditions
+               IFW <= OFW, otherwise downsizing is required
+                                        - which is not supported yet
+               4 <= IFW,OFW, so some interpolation may be needed even without overlap
+               IM, OM, IF, OF should not vanish
+               2*IF <= IFW
+               so the frame can be split to two equal stripes, even without overlap
+               2*(OF+IF/irr_opt) <= OFW
+               so a valid positive INW exists even for equal stripes
+               OF <= MSW, otherwise, the left stripe cannot be sufficiently large
+               MSW < OFW, so splitting to stripes is required
+               OFW <= 2*MSW, so two stripes are sufficient
+               (this also implies that 2<=MSW)
+       2: OF is not a multiple of OM - not fully-supported yet
+       Output is produced but OW is not guaranited to be a multiple of OM
+       4: OFW reduced to be a multiple of OM
+       8: CIRR > 1: truncated to 1
+       Overlap is not supported (and not needed) y for upsizing)
+**************************************************************************/
+int ipu_calc_stripes_sizes(const unsigned int input_frame_width,
+                          /* input frame width;>1 */
+                          unsigned int output_frame_width, /* output frame width; >1 */
+                          const unsigned int maximal_stripe_width,
+                          /* the maximal width allowed for a stripe */
+                          const unsigned long long cirr, /* see above */
+                          const unsigned int flags, /* see above */
+                          u32 input_pixelformat,/* pixel format after of read channel*/
+                          u32 output_pixelformat,/* pixel format after of write channel*/
+                          struct stripe_param *left,
+                          struct stripe_param *right)
+{
+       const unsigned int irr_frac_bits = 13;
+       const unsigned long irr_steps = 1 << irr_frac_bits;
+       const u64 dirr = ((u64)1) << (32 - 2);
+       /* The maximum relative difference allowed between the irrs */
+       const u64 cr = ((u64)4) << 32;
+       /* The importance ratio between the two terms in the cost function below */
+
+       unsigned int status;
+       unsigned int temp;
+       unsigned int onw_min;
+       unsigned int inw = 0, onw = 0, inw_best = 0;
+       /* number of pixels in the left stripe NOT hidden by the right stripe */
+       u64 irr_opt; /* the optimal inverse resizing ratio */
+       u64 rr_opt; /* the optimal resizing ratio = 1/irr_opt*/
+       u64 dinw; /* the misalignment between the stripes */
+       /* (measured in units of input columns) */
+       u64 difwl, difwr = 0;
+       /* The number of input columns not reflected in the output */
+       /* the resizing ratio used for the right stripe is */
+       /*   left->irr and right->irr respectively */
+       u64 cost, cost_min;
+       u64 div; /* result of division */
+       bool equal_stripes = (flags & 0x1) != 0;
+       bool vertical =      (flags & 0x2) != 0;
+
+       unsigned int input_m, input_f, output_m, output_f; /* parameters for upsizing by stripes */
+       unsigned int resize_coeff;
+       unsigned int downsize_coeff;
+
+       status = 0;
+
+       if (vertical) {
+               input_f = 2;
+               input_m = 8;
+               output_f = 8;
+               output_m = 2;
+       } else {
+               input_f = f_calc(input_pixelformat, 0, NULL);
+               input_m = m_calc(input_pixelformat);
+               output_f = input_m;
+               output_m = m_calc(output_pixelformat);
+       }
+       if ((input_frame_width < 4) || (output_frame_width < 4))
+               return 1;
+
+       irr_opt = div_u64((((u64)(input_frame_width - 1)) << 32),
+                         (output_frame_width - 1));
+       rr_opt = div_u64((((u64)(output_frame_width - 1)) << 32),
+                        (input_frame_width - 1));
+
+       if ((input_m == 0) || (output_m == 0) || (input_f == 0) || (output_f == 0)
+           || (input_frame_width < (2 * input_f))
+           || ((((u64)output_frame_width) << 32) <
+               (2 * ((((u64)output_f) << 32) + (input_f * rr_opt))))
+           || (maximal_stripe_width < output_f)
+           || ((output_frame_width <= maximal_stripe_width)
+               && (equal_stripes == 0))
+           || ((2 * maximal_stripe_width) < output_frame_width))
+               return 1;
+
+       if (output_f % output_m)
+               status += 2;
+
+       temp = truncate(0, (((u64)output_frame_width) << 32), output_m);
+       if (temp < output_frame_width) {
+               output_frame_width = temp;
+               status += 4;
+       }
+
+       pr_debug("---------------->\n"
+                  "if  = %d\n"
+                  "im  = %d\n"
+                  "of = %d\n"
+                  "om = %d\n"
+                  "irr_opt  = %llu\n"
+                  "rr_opt   = %llu\n"
+                  "cirr     = %llu\n"
+                  "pixel in  = %08x\n"
+                  "pixel out = %08x\n"
+                  "ifw = %d\n"
+                  "ofwidth = %d\n",
+                  input_f,
+                  input_m,
+                  output_f,
+                  output_m,
+                  irr_opt,
+                  rr_opt,
+                  cirr,
+                  input_pixelformat,
+                  output_pixelformat,
+                  input_frame_width,
+                  output_frame_width
+                  );
+
+       if (equal_stripes) {
+               if ((irr_opt > cirr) /* overlap in the input is not requested */
+                   && ((input_frame_width % (input_m << 1)) == 0)
+                   && ((input_frame_width % (input_f << 1)) == 0)
+                   && ((output_frame_width % (output_m << 1)) == 0)
+                   && ((output_frame_width % (output_f << 1)) == 0)) {
+                       /* without overlap */
+                       left->input_width = right->input_width = right->input_column =
+                               input_frame_width >> 1;
+                       left->output_width = right->output_width = right->output_column =
+                               output_frame_width >> 1;
+                       left->input_column = 0;
+                       left->output_column = 0;
+                       div = div_u64(((((u64)irr_steps) << 32) *
+                                      (right->input_width - 1)), (right->output_width - 1));
+                       left->irr = right->irr = truncate(0, div, 1);
+               } else { /* with overlap */
+                       onw = truncate(0, (((u64)output_frame_width - 1) << 32) >> 1,
+                                      output_f);
+                       inw = truncate(0, onw * irr_opt, input_f);
+                       /* this is the maximal inw which allows the same resizing ratio */
+                       /* in both stripes */
+                       onw = truncate(1, (inw * rr_opt), output_f);
+                       div = div_u64((((u64)(irr_steps * inw)) <<
+                                      32), onw);
+                       left->irr = right->irr = truncate(0, div, 1);
+                       left->output_width = right->output_width =
+                               output_frame_width - onw;
+                       /* These are valid assignments for output_width, */
+                       /* assuming output_f is a multiple of output_m */
+                       div = (((u64)(left->output_width-1) * (left->irr)) << 32);
+                       div = (((u64)1) << 32) + div_u64(div, irr_steps);
+
+                       left->input_width = right->input_width = truncate(1, div, input_m);
+
+                       div = div_u64((((u64)((right->output_width - 1) * right->irr)) <<
+                                      32), irr_steps);
+                       difwr = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
+                       div = div_u64((difwr + (((u64)input_f) << 32)), 2);
+                       left->input_column = truncate(0, div, input_f);
+
+
+                       /* This splits the truncated input columns evenly */
+                       /*    between the left and right margins */
+                       right->input_column = left->input_column + inw;
+                       left->output_column = 0;
+                       right->output_column = onw;
+               }
+               if (left->input_width > left->output_width) {
+                       if (calc_split_resize_coeffs(left->input_width,
+                                                    left->output_width,
+                                                    &resize_coeff,
+                                                    &downsize_coeff) < 0)
+                               return -EINVAL;
+
+                       if (downsize_coeff > 0) {
+                               left->irr = right->irr =
+                                       (downsize_coeff << 14) | resize_coeff;
+                       }
+               }
+               pr_debug("inw %d, onw %d, ilw %d, ilc %d, olw %d,"
+                        " irw %d, irc %d, orw %d, orc %d, "
+                        "difwr  %llu, lirr %u\n",
+                        inw, onw, left->input_width,
+                        left->input_column, left->output_width,
+                        right->input_width, right->input_column,
+                        right->output_width,
+                        right->output_column, difwr, left->irr);
+               } else { /* independent stripes */
+               onw_min = output_frame_width - maximal_stripe_width;
+               /* onw is a multiple of output_f, in the range */
+               /* [max(output_f,output_frame_width-maximal_stripe_width),*/
+               /*min(output_frame_width-2,maximal_stripe_width)] */
+               /* definitely beyond the cost of any valid setting */
+               cost_min = (((u64)input_frame_width) << 32) + cr;
+               onw = truncate(0, ((u64)maximal_stripe_width), output_f);
+               if (output_frame_width - onw == 1)
+                       onw -= output_f; /*  => onw and output_frame_width-1-onw are positive */
+               inw = truncate(0, onw * irr_opt, input_f);
+               /* this is the maximal inw which allows the same resizing ratio */
+               /* in both stripes */
+               onw = truncate(1, inw * rr_opt, output_f);
+               do {
+                       div = div_u64((((u64)(irr_steps * inw)) << 32), onw);
+                       left->irr = truncate(0, div, 1);
+                       div = div_u64((((u64)(onw * left->irr)) << 32),
+                                     irr_steps);
+                       dinw = (((u64)inw) << 32) - div;
+
+                       div = div_u64((((u64)((output_frame_width - 1 - onw) * left->irr)) <<
+                                      32), irr_steps);
+
+                       difwl = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
+
+                       cost = difwl + (((u64)(cr * dinw)) >> 32);
+
+                       if (cost < cost_min) {
+                               inw_best = inw;
+                               cost_min = cost;
+                       }
+
+                       inw -= input_f;
+                       onw = truncate(1, inw * rr_opt, output_f);
+                       /* This is the minimal onw which allows the same resizing ratio */
+                       /*     in both stripes */
+               } while (onw >= onw_min);
+
+               inw = inw_best;
+               onw = truncate(1, inw * rr_opt, output_f);
+               div = div_u64((((u64)(irr_steps * inw)) << 32), onw);
+               left->irr = truncate(0, div, 1);
+
+               left->output_width = onw;
+               right->output_width = output_frame_width - onw;
+               /* These are valid assignments for output_width, */
+               /* assuming output_f is a multiple of output_m */
+               left->input_width = truncate(1, ((u64)(inw + 1)) << 32, input_m);
+               right->input_width = truncate(1, ((u64)(input_frame_width - inw)) <<
+                                             32, input_m);
+
+               div = div_u64((((u64)(irr_steps * (input_frame_width - 1 - inw))) <<
+                              32), (right->output_width - 1));
+               right->irr = truncate(0, div, 1);
+               temp = truncate(0, ((u64)left->irr) * ((((u64)1) << 32) + dirr), 1);
+               if (temp < right->irr)
+                       right->irr = temp;
+               div = div_u64(((u64)((right->output_width - 1) * right->irr) <<
+                              32), irr_steps);
+               difwr = (u64)(input_frame_width - 1 - inw) - div;
+
+
+               div = div_u64((difwr + (((u64)input_f) << 32)), 2);
+               left->input_column = truncate(0, div, input_f);
+
+               /* This splits the truncated input columns evenly */
+               /*    between the left and right margins */
+               right->input_column = left->input_column + inw;
+               left->output_column = 0;
+               right->output_column = onw;
+               if (left->input_width > left->output_width) {
+                       if (calc_split_resize_coeffs(left->input_width,
+                                                    left->output_width,
+                                                    &resize_coeff,
+                                                    &downsize_coeff) < 0)
+                               return -EINVAL;
+                       left->irr = (downsize_coeff << 14) | resize_coeff;
+               }
+               if (right->input_width > right->output_width) {
+                       if (calc_split_resize_coeffs(right->input_width,
+                                                    right->output_width,
+                                                    &resize_coeff,
+                                                    &downsize_coeff) < 0)
+                               return -EINVAL;
+                       right->irr = (downsize_coeff << 14) | resize_coeff;
+               }
+       }
+       return status;
+}
+EXPORT_SYMBOL(ipu_calc_stripes_sizes);
diff --git a/drivers/mxc/ipu3/ipu_capture.c b/drivers/mxc/ipu3/ipu_capture.c
new file mode 100644 (file)
index 0000000..09bb6d9
--- /dev/null
@@ -0,0 +1,816 @@
+/*
+ * Copyright 2008-2015 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
+ */
+
+/*!
+ * @file ipu_capture.c
+ *
+ * @brief IPU capture dase functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ipu-v3.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+
+/*!
+ * _ipu_csi_mclk_set
+ *
+ * @param      ipu             ipu handler
+ * @param      pixel_clk   desired pixel clock frequency in Hz
+ * @param      csi         csi 0 or csi 1
+ *
+ * @return     Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_mclk_set(struct ipu_soc *ipu, uint32_t pixel_clk, uint32_t csi)
+{
+       uint32_t temp;
+       uint32_t div_ratio;
+
+       div_ratio = (clk_get_rate(ipu->ipu_clk) / pixel_clk) - 1;
+
+       if (div_ratio > 0xFF || div_ratio < 0) {
+               dev_dbg(ipu->dev, "value of pixel_clk extends normal range\n");
+               return -EINVAL;
+       }
+
+       temp = ipu_csi_read(ipu, csi, CSI_SENS_CONF);
+       temp &= ~CSI_SENS_CONF_DIVRATIO_MASK;
+       ipu_csi_write(ipu, csi, temp |
+                       (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT),
+                       CSI_SENS_CONF);
+
+       return 0;
+}
+
+/*!
+ * ipu_csi_init_interface
+ *     Sets initial values for the CSI registers.
+ *     The width and height of the sensor and the actual frame size will be
+ *     set to the same values.
+ * @param      ipu             ipu handler
+ * @param      width           Sensor width
+ * @param       height         Sensor height
+ * @param       pixel_fmt      pixel format
+ * @param       cfg_param      ipu_csi_signal_cfg_t structure
+ * @param       csi             csi 0 or csi 1
+ *
+ * @return      0 for success, -EINVAL for error
+ */
+int32_t
+ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height,
+       uint32_t pixel_fmt, ipu_csi_signal_cfg_t cfg_param)
+{
+       uint32_t data = 0;
+       uint32_t csi = cfg_param.csi;
+
+       /* Set SENS_DATA_FORMAT bits (8, 9 and 10)
+          RGB or YUV444 is 0 which is current value in data so not set
+          explicitly
+          This is also the default value if attempts are made to set it to
+          something invalid. */
+       switch (pixel_fmt) {
+       case IPU_PIX_FMT_YUYV:
+               cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
+               break;
+       case IPU_PIX_FMT_UYVY:
+               cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
+               break;
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_BGR24:
+               cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444;
+               break;
+       case IPU_PIX_FMT_GENERIC:
+       case IPU_PIX_FMT_GENERIC_16:
+               cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
+               break;
+       case IPU_PIX_FMT_RGB565:
+               cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565;
+               break;
+       case IPU_PIX_FMT_RGB555:
+               cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* Set the CSI_SENS_CONF register remaining fields */
+       data |= cfg_param.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
+               cfg_param.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
+               cfg_param.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
+               cfg_param.Vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
+               cfg_param.Hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
+               cfg_param.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
+               cfg_param.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
+               cfg_param.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
+               cfg_param.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT |
+               cfg_param.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT |
+               cfg_param.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT;
+
+       _ipu_get(ipu);
+
+       mutex_lock(&ipu->mutex_lock);
+
+       ipu_csi_write(ipu, csi, data, CSI_SENS_CONF);
+
+       /* Setup sensor frame size */
+       ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_SENS_FRM_SIZE);
+
+       /* Set CCIR registers */
+       if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) {
+               ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1);
+               ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
+       } else if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED) {
+               if (width == 720 && height == 625) {
+                       /* PAL case */
+                       /*
+                        * Field0BlankEnd = 0x6, Field0BlankStart = 0x2,
+                        * Field0ActiveEnd = 0x4, Field0ActiveStart = 0
+                        */
+                       ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_1);
+                       /*
+                        * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
+                        * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
+                        */
+                       ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_2);
+
+                       ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
+
+               } else if (width == 720 && height == 525) {
+                       /* NTSC case */
+                       /*
+                        * Field0BlankEnd = 0x7, Field0BlankStart = 0x3,
+                        * Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1
+                        */
+                       ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_1);
+                       /*
+                        * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
+                        * Field1ActiveEnd = 0x4, Field1ActiveStart = 0
+                        */
+                       ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_2);
+                       ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
+               } else {
+                       dev_err(ipu->dev, "Unsupported CCIR656 interlaced "
+                                       "video mode\n");
+                       mutex_unlock(&ipu->mutex_lock);
+                       _ipu_put(ipu);
+                       return -EINVAL;
+               }
+               _ipu_csi_ccir_err_detection_enable(ipu, csi);
+       } else if ((cfg_param.clk_mode ==
+                       IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR) ||
+               (cfg_param.clk_mode ==
+                       IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR) ||
+               (cfg_param.clk_mode ==
+                       IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR) ||
+               (cfg_param.clk_mode ==
+                       IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR)) {
+               ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1);
+               ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
+               _ipu_csi_ccir_err_detection_enable(ipu, csi);
+       } else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_GATED_CLK) ||
+                  (cfg_param.clk_mode == IPU_CSI_CLK_MODE_NONGATED_CLK)) {
+               _ipu_csi_ccir_err_detection_disable(ipu, csi);
+       }
+
+       dev_dbg(ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
+               ipu_csi_read(ipu, csi, CSI_SENS_CONF));
+       dev_dbg(ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
+               ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE));
+
+       mutex_unlock(&ipu->mutex_lock);
+
+       _ipu_put(ipu);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_csi_init_interface);
+
+/*!
+ * ipu_csi_get_sensor_protocol
+ *
+ * @param      ipu             ipu handler
+ * @param      csi         csi 0 or csi 1
+ *
+ * @return     Returns sensor protocol
+ */
+int32_t ipu_csi_get_sensor_protocol(struct ipu_soc *ipu, uint32_t csi)
+{
+       int ret;
+       _ipu_get(ipu);
+       ret = (ipu_csi_read(ipu, csi, CSI_SENS_CONF) &
+               CSI_SENS_CONF_SENS_PRTCL_MASK) >>
+               CSI_SENS_CONF_SENS_PRTCL_SHIFT;
+       _ipu_put(ipu);
+       return ret;
+}
+EXPORT_SYMBOL(ipu_csi_get_sensor_protocol);
+
+/*!
+ * ipu_csi_enable_mclk
+ *
+ * @param      ipu             ipu handler
+ * @param      csi         csi 0 or csi 1
+ * @param       flag        true to enable mclk, false to disable mclk
+ * @param       wait        true to wait 100ms make clock stable, false not wait
+ *
+ * @return      Returns 0 on success
+ */
+int ipu_csi_enable_mclk(struct ipu_soc *ipu, int csi, bool flag, bool wait)
+{
+       /* Return immediately if there is no csi_clk to manage */
+       if (ipu->csi_clk[csi] == NULL)
+               return 0;
+
+       if (flag) {
+               clk_enable(ipu->csi_clk[csi]);
+               if (wait == true)
+                       msleep(10);
+       } else {
+               clk_disable(ipu->csi_clk[csi]);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_csi_enable_mclk);
+
+/*!
+ * ipu_csi_get_window_size
+ *
+ * @param      ipu             ipu handler
+ * @param      width   pointer to window width
+ * @param      height  pointer to window height
+ * @param      csi     csi 0 or csi 1
+ */
+void ipu_csi_get_window_size(struct ipu_soc *ipu, uint32_t *width, uint32_t *height, uint32_t csi)
+{
+       uint32_t reg;
+
+       _ipu_get(ipu);
+
+       mutex_lock(&ipu->mutex_lock);
+
+       reg = ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE);
+       *width = (reg & 0xFFFF) + 1;
+       *height = (reg >> 16 & 0xFFFF) + 1;
+
+       mutex_unlock(&ipu->mutex_lock);
+
+       _ipu_put(ipu);
+}
+EXPORT_SYMBOL(ipu_csi_get_window_size);
+
+/*!
+ * ipu_csi_set_window_size
+ *
+ * @param      ipu             ipu handler
+ * @param      width   window width
+ * @param       height window height
+ * @param       csi    csi 0 or csi 1
+ */
+void ipu_csi_set_window_size(struct ipu_soc *ipu, uint32_t width, uint32_t height, uint32_t csi)
+{
+       _ipu_get(ipu);
+
+       mutex_lock(&ipu->mutex_lock);
+
+       ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_ACT_FRM_SIZE);
+
+       mutex_unlock(&ipu->mutex_lock);
+
+       _ipu_put(ipu);
+}
+EXPORT_SYMBOL(ipu_csi_set_window_size);
+
+/*!
+ * ipu_csi_set_window_pos
+ *
+ * @param      ipu             ipu handler
+ * @param       left   uint32 window x start
+ * @param       top    uint32 window y start
+ * @param       csi    csi 0 or csi 1
+ */
+void ipu_csi_set_window_pos(struct ipu_soc *ipu, uint32_t left, uint32_t top, uint32_t csi)
+{
+       uint32_t temp;
+
+       _ipu_get(ipu);
+
+       mutex_lock(&ipu->mutex_lock);
+
+       temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
+       temp &= ~(CSI_HSC_MASK | CSI_VSC_MASK);
+       temp |= ((top << CSI_VSC_SHIFT) | (left << CSI_HSC_SHIFT));
+       ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
+
+       mutex_unlock(&ipu->mutex_lock);
+
+       _ipu_put(ipu);
+}
+EXPORT_SYMBOL(ipu_csi_set_window_pos);
+
+/*!
+ * _ipu_csi_horizontal_downsize_enable
+ *     Enable horizontal downsizing(decimation) by 2.
+ *
+ * @param      ipu             ipu handler
+ * @param      csi     csi 0 or csi 1
+ */
+void _ipu_csi_horizontal_downsize_enable(struct ipu_soc *ipu, uint32_t csi)
+{
+       uint32_t temp;
+
+       temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
+       temp |= CSI_HORI_DOWNSIZE_EN;
+       ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
+}
+
+/*!
+ * _ipu_csi_horizontal_downsize_disable
+ *     Disable horizontal downsizing(decimation) by 2.
+ *
+ * @param      ipu             ipu handler
+ * @param      csi     csi 0 or csi 1
+ */
+void _ipu_csi_horizontal_downsize_disable(struct ipu_soc *ipu, uint32_t csi)
+{
+       uint32_t temp;
+
+       temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
+       temp &= ~CSI_HORI_DOWNSIZE_EN;
+       ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
+}
+
+/*!
+ * _ipu_csi_vertical_downsize_enable
+ *     Enable vertical downsizing(decimation) by 2.
+ *
+ * @param      ipu             ipu handler
+ * @param      csi     csi 0 or csi 1
+ */
+void _ipu_csi_vertical_downsize_enable(struct ipu_soc *ipu, uint32_t csi)
+{
+       uint32_t temp;
+
+       temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
+       temp |= CSI_VERT_DOWNSIZE_EN;
+       ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
+}
+
+/*!
+ * _ipu_csi_vertical_downsize_disable
+ *     Disable vertical downsizing(decimation) by 2.
+ *
+ * @param      ipu             ipu handler
+ * @param      csi     csi 0 or csi 1
+ */
+void _ipu_csi_vertical_downsize_disable(struct ipu_soc *ipu, uint32_t csi)
+{
+       uint32_t temp;
+
+       temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
+       temp &= ~CSI_VERT_DOWNSIZE_EN;
+       ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
+}
+
+/*!
+ * _ipu_csi_set_test_generator
+ *
+ * @param      ipu             ipu handler
+ * @param      active       1 for active and 0 for inactive
+ * @param       r_value             red value for the generated pattern of even pixel
+ * @param       g_value      green value for the generated pattern of even
+ *                          pixel
+ * @param       b_value      blue value for the generated pattern of even pixel
+ * @param      pixel_clk   desired pixel clock frequency in Hz
+ * @param       csi          csi 0 or csi 1
+ */
+void _ipu_csi_set_test_generator(struct ipu_soc *ipu, bool active, uint32_t r_value,
+       uint32_t g_value, uint32_t b_value, uint32_t pix_clk, uint32_t csi)
+{
+       uint32_t temp;
+
+       temp = ipu_csi_read(ipu, csi, CSI_TST_CTRL);
+
+       if (active == false) {
+               temp &= ~CSI_TEST_GEN_MODE_EN;
+               ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL);
+       } else {
+               /* Set sensb_mclk div_ratio*/
+               _ipu_csi_mclk_set(ipu, pix_clk, csi);
+
+               temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK |
+                       CSI_TEST_GEN_B_MASK);
+               temp |= CSI_TEST_GEN_MODE_EN;
+               temp |= (r_value << CSI_TEST_GEN_R_SHIFT) |
+                       (g_value << CSI_TEST_GEN_G_SHIFT) |
+                       (b_value << CSI_TEST_GEN_B_SHIFT);
+               ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL);
+       }
+}
+
+/*!
+ * _ipu_csi_ccir_err_detection_en
+ *     Enable error detection and correction for
+ *     CCIR interlaced mode with protection bit.
+ *
+ * @param      ipu             ipu handler
+ * @param      csi     csi 0 or csi 1
+ */
+void _ipu_csi_ccir_err_detection_enable(struct ipu_soc *ipu, uint32_t csi)
+{
+       uint32_t temp;
+
+       temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1);
+       temp |= CSI_CCIR_ERR_DET_EN;
+       ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1);
+
+}
+
+/*!
+ * _ipu_csi_ccir_err_detection_disable
+ *     Disable error detection and correction for
+ *     CCIR interlaced mode with protection bit.
+ *
+ * @param      ipu             ipu handler
+ * @param      csi     csi 0 or csi 1
+ */
+void _ipu_csi_ccir_err_detection_disable(struct ipu_soc *ipu, uint32_t csi)
+{
+       uint32_t temp;
+
+       temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1);
+       temp &= ~CSI_CCIR_ERR_DET_EN;
+       ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1);
+
+}
+
+/*!
+ * _ipu_csi_set_mipi_di
+ *
+ * @param      ipu             ipu handler
+ * @param      num     MIPI data identifier 0-3 handled by CSI
+ * @param      di_val  data identifier value
+ * @param      csi     csi 0 or csi 1
+ *
+ * @return     Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_set_mipi_di(struct ipu_soc *ipu, uint32_t num, uint32_t di_val, uint32_t csi)
+{
+       uint32_t temp;
+       int retval = 0;
+
+       if (di_val > 0xFFL) {
+               retval = -EINVAL;
+               goto err;
+       }
+
+       temp = ipu_csi_read(ipu, csi, CSI_MIPI_DI);
+
+       switch (num) {
+       case IPU_CSI_MIPI_DI0:
+               temp &= ~CSI_MIPI_DI0_MASK;
+               temp |= (di_val << CSI_MIPI_DI0_SHIFT);
+               ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
+               break;
+       case IPU_CSI_MIPI_DI1:
+               temp &= ~CSI_MIPI_DI1_MASK;
+               temp |= (di_val << CSI_MIPI_DI1_SHIFT);
+               ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
+               break;
+       case IPU_CSI_MIPI_DI2:
+               temp &= ~CSI_MIPI_DI2_MASK;
+               temp |= (di_val << CSI_MIPI_DI2_SHIFT);
+               ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
+               break;
+       case IPU_CSI_MIPI_DI3:
+               temp &= ~CSI_MIPI_DI3_MASK;
+               temp |= (di_val << CSI_MIPI_DI3_SHIFT);
+               ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
+               break;
+       default:
+               retval = -EINVAL;
+       }
+
+err:
+       return retval;
+}
+
+/*!
+ * _ipu_csi_set_skip_isp
+ *
+ * @param      ipu             ipu handler
+ * @param      skip            select frames to be skipped and set the
+ *                             correspond bits to 1
+ * @param      max_ratio       number of frames in a skipping set and the
+ *                             maximum value of max_ratio is 5
+ * @param      csi             csi 0 or csi 1
+ *
+ * @return     Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_set_skip_isp(struct ipu_soc *ipu, uint32_t skip, uint32_t max_ratio, uint32_t csi)
+{
+       uint32_t temp;
+       int retval = 0;
+
+       if (max_ratio > 5) {
+               retval = -EINVAL;
+               goto err;
+       }
+
+       temp = ipu_csi_read(ipu, csi, CSI_SKIP);
+       temp &= ~(CSI_MAX_RATIO_SKIP_ISP_MASK | CSI_SKIP_ISP_MASK);
+       temp |= (max_ratio << CSI_MAX_RATIO_SKIP_ISP_SHIFT) |
+               (skip << CSI_SKIP_ISP_SHIFT);
+       ipu_csi_write(ipu, csi, temp, CSI_SKIP);
+
+err:
+       return retval;
+}
+
+/*!
+ * _ipu_csi_set_skip_smfc
+ *
+ * @param      ipu             ipu handler
+ * @param      skip            select frames to be skipped and set the
+ *                             correspond bits to 1
+ * @param      max_ratio       number of frames in a skipping set and the
+ *                             maximum value of max_ratio is 5
+ * @param      id              csi to smfc skipping id
+ * @param      csi             csi 0 or csi 1
+ *
+ * @return     Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_set_skip_smfc(struct ipu_soc *ipu, uint32_t skip,
+       uint32_t max_ratio, uint32_t id, uint32_t csi)
+{
+       uint32_t temp;
+       int retval = 0;
+
+       if (max_ratio > 5 || id > 3) {
+               retval = -EINVAL;
+               goto err;
+       }
+
+       temp = ipu_csi_read(ipu, csi, CSI_SKIP);
+       temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK |
+                       CSI_SKIP_SMFC_MASK);
+       temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) |
+                       (id << CSI_ID_2_SKIP_SHIFT) |
+                       (skip << CSI_SKIP_SMFC_SHIFT);
+       ipu_csi_write(ipu, csi, temp, CSI_SKIP);
+
+err:
+       return retval;
+}
+
+/*!
+ * _ipu_smfc_init
+ *     Map CSI frames to IDMAC channels.
+ *
+ * @param      ipu             ipu handler
+ * @param      channel         IDMAC channel 0-3
+ * @param      mipi_id         mipi id number 0-3
+ * @param      csi             csi0 or csi1
+ */
+void _ipu_smfc_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t mipi_id, uint32_t csi)
+{
+       uint32_t temp;
+
+       temp = ipu_smfc_read(ipu, SMFC_MAP);
+
+       switch (channel) {
+       case CSI_MEM0:
+               temp &= ~SMFC_MAP_CH0_MASK;
+               temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH0_SHIFT;
+               break;
+       case CSI_MEM1:
+               temp &= ~SMFC_MAP_CH1_MASK;
+               temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH1_SHIFT;
+               break;
+       case CSI_MEM2:
+               temp &= ~SMFC_MAP_CH2_MASK;
+               temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH2_SHIFT;
+               break;
+       case CSI_MEM3:
+               temp &= ~SMFC_MAP_CH3_MASK;
+               temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH3_SHIFT;
+               break;
+       default:
+               return;
+       }
+
+       ipu_smfc_write(ipu, temp, SMFC_MAP);
+}
+
+/*!
+ * _ipu_smfc_set_wmc
+ *     Caution: The number of required channels,  the enabled channels
+ *     and the FIFO size per channel are configured restrictedly.
+ *
+ * @param      ipu             ipu handler
+ * @param      channel         IDMAC channel 0-3
+ * @param      set             set 1 or clear 0
+ * @param      level           water mark level when FIFO is on the
+ *                             relative size
+ */
+void _ipu_smfc_set_wmc(struct ipu_soc *ipu, ipu_channel_t channel, bool set, uint32_t level)
+{
+       uint32_t temp;
+
+       temp = ipu_smfc_read(ipu, SMFC_WMC);
+
+       switch (channel) {
+       case CSI_MEM0:
+               if (set == true) {
+                       temp &= ~SMFC_WM0_SET_MASK;
+                       temp |= level << SMFC_WM0_SET_SHIFT;
+               } else {
+                       temp &= ~SMFC_WM0_CLR_MASK;
+                       temp |= level << SMFC_WM0_CLR_SHIFT;
+               }
+               break;
+       case CSI_MEM1:
+               if (set == true) {
+                       temp &= ~SMFC_WM1_SET_MASK;
+                       temp |= level << SMFC_WM1_SET_SHIFT;
+               } else {
+                       temp &= ~SMFC_WM1_CLR_MASK;
+                       temp |= level << SMFC_WM1_CLR_SHIFT;
+               }
+               break;
+       case CSI_MEM2:
+               if (set == true) {
+                       temp &= ~SMFC_WM2_SET_MASK;
+                       temp |= level << SMFC_WM2_SET_SHIFT;
+               } else {
+                       temp &= ~SMFC_WM2_CLR_MASK;
+                       temp |= level << SMFC_WM2_CLR_SHIFT;
+               }
+               break;
+       case CSI_MEM3:
+               if (set == true) {
+                       temp &= ~SMFC_WM3_SET_MASK;
+                       temp |= level << SMFC_WM3_SET_SHIFT;
+               } else {
+                       temp &= ~SMFC_WM3_CLR_MASK;
+                       temp |= level << SMFC_WM3_CLR_SHIFT;
+               }
+               break;
+       default:
+               return;
+       }
+
+       ipu_smfc_write(ipu, temp, SMFC_WMC);
+}
+
+/*!
+ * _ipu_smfc_set_burst_size
+ *
+ * @param      ipu             ipu handler
+ * @param      channel         IDMAC channel 0-3
+ * @param      bs              burst size of IDMAC channel,
+ *                             the value programmed here shoud be BURST_SIZE-1
+ */
+void _ipu_smfc_set_burst_size(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t bs)
+{
+       uint32_t temp;
+
+       temp = ipu_smfc_read(ipu, SMFC_BS);
+
+       switch (channel) {
+       case CSI_MEM0:
+               temp &= ~SMFC_BS0_MASK;
+               temp |= bs << SMFC_BS0_SHIFT;
+               break;
+       case CSI_MEM1:
+               temp &= ~SMFC_BS1_MASK;
+               temp |= bs << SMFC_BS1_SHIFT;
+               break;
+       case CSI_MEM2:
+               temp &= ~SMFC_BS2_MASK;
+               temp |= bs << SMFC_BS2_SHIFT;
+               break;
+       case CSI_MEM3:
+               temp &= ~SMFC_BS3_MASK;
+               temp |= bs << SMFC_BS3_SHIFT;
+               break;
+       default:
+               return;
+       }
+
+       ipu_smfc_write(ipu, temp, SMFC_BS);
+}
+
+/*!
+ * _ipu_csi_init
+ *
+ * @param      ipu             ipu handler
+ * @param      channel      IDMAC channel
+ * @param      csi          csi 0 or csi 1
+ *
+ * @return     Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi)
+{
+       uint32_t csi_sens_conf, csi_dest;
+       int retval = 0;
+
+       switch (channel) {
+       case CSI_MEM0:
+       case CSI_MEM1:
+       case CSI_MEM2:
+       case CSI_MEM3:
+               csi_dest = CSI_DATA_DEST_IDMAC;
+               break;
+       case CSI_PRP_ENC_MEM:
+       case CSI_PRP_VF_MEM:
+               csi_dest = CSI_DATA_DEST_IC;
+               break;
+       default:
+               retval = -EINVAL;
+               goto err;
+       }
+
+       csi_sens_conf = ipu_csi_read(ipu, csi, CSI_SENS_CONF);
+       csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK;
+       ipu_csi_write(ipu, csi, csi_sens_conf | (csi_dest <<
+               CSI_SENS_CONF_DATA_DEST_SHIFT), CSI_SENS_CONF);
+err:
+       return retval;
+}
+
+/*!
+ * csi_irq_handler
+ *
+ * @param      irq             interrupt id
+ * @param      dev_id          pointer to ipu handler
+ *
+ * @return     Returns if irq is handled
+ */
+static irqreturn_t csi_irq_handler(int irq, void *dev_id)
+{
+       struct ipu_soc *ipu = dev_id;
+       struct completion *comp = &ipu->csi_comp;
+
+       complete(comp);
+       return IRQ_HANDLED;
+}
+
+/*!
+ * _ipu_csi_wait4eof
+ *
+ * @param      ipu             ipu handler
+ * @param      channel      IDMAC channel
+ *
+ */
+void _ipu_csi_wait4eof(struct ipu_soc *ipu, ipu_channel_t channel)
+{
+       int ret;
+       int irq = 0;
+
+       if (channel == CSI_MEM0)
+               irq = IPU_IRQ_CSI0_OUT_EOF;
+       else if (channel == CSI_MEM1)
+               irq = IPU_IRQ_CSI1_OUT_EOF;
+       else if (channel == CSI_MEM2)
+               irq = IPU_IRQ_CSI2_OUT_EOF;
+       else if (channel == CSI_MEM3)
+               irq = IPU_IRQ_CSI3_OUT_EOF;
+       else if (channel == CSI_PRP_ENC_MEM)
+               irq = IPU_IRQ_PRP_ENC_OUT_EOF;
+       else if (channel == CSI_PRP_VF_MEM)
+               irq = IPU_IRQ_PRP_VF_OUT_EOF;
+       else{
+               dev_err(ipu->dev, "Not a CSI channel\n");
+               return;
+       }
+
+       init_completion(&ipu->csi_comp);
+       ret = ipu_request_irq(ipu, irq, csi_irq_handler, 0, NULL, ipu);
+       if (ret < 0) {
+               dev_err(ipu->dev, "CSI irq %d in use\n", irq);
+               return;
+       }
+       ret = wait_for_completion_timeout(&ipu->csi_comp, msecs_to_jiffies(500));
+       ipu_free_irq(ipu, irq, ipu);
+       dev_dbg(ipu->dev, "CSI stop timeout - %d * 10ms\n", 5 - ret);
+}
diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c
new file mode 100644 (file)
index 0000000..8ba5f99
--- /dev/null
@@ -0,0 +1,3458 @@
+/*
+ * Copyright 2005-2015 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
+ */
+
+/*!
+ * @file ipu_common.c
+ *
+ * @brief This file contains the IPU driver common API functions.
+ *
+ * @ingroup IPU
+ */
+#include <linux/busfreq-imx.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ipu-v3.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/cacheflush.h>
+
+#include "ipu_param_mem.h"
+#include "ipu_regs.h"
+
+static struct ipu_soc ipu_array[MXC_IPU_MAX_NUM];
+
+/* Static functions */
+static irqreturn_t ipu_sync_irq_handler(int irq, void *desc);
+static irqreturn_t ipu_err_irq_handler(int irq, void *desc);
+
+static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type)
+{
+       return ((uint32_t) ch >> (6 * type)) & 0x3F;
+};
+
+static inline int _ipu_is_ic_chan(uint32_t dma_chan)
+{
+       return (((dma_chan >= 11) && (dma_chan <= 22) && (dma_chan != 17) &&
+               (dma_chan != 18)));
+}
+
+static inline int _ipu_is_vdi_out_chan(uint32_t dma_chan)
+{
+       return (dma_chan == 5);
+}
+
+static inline int _ipu_is_ic_graphic_chan(uint32_t dma_chan)
+{
+       return (dma_chan == 14 || dma_chan == 15);
+}
+
+/* Either DP BG or DP FG can be graphic window */
+static inline int _ipu_is_dp_graphic_chan(uint32_t dma_chan)
+{
+       return (dma_chan == 23 || dma_chan == 27);
+}
+
+static inline int _ipu_is_irt_chan(uint32_t dma_chan)
+{
+       return ((dma_chan >= 45) && (dma_chan <= 50));
+}
+
+static inline int _ipu_is_dmfc_chan(uint32_t dma_chan)
+{
+       return ((dma_chan >= 23) && (dma_chan <= 29));
+}
+
+static inline int _ipu_is_trb_chan(struct ipu_soc *ipu, uint32_t dma_chan)
+{
+       return (((dma_chan == 8) || (dma_chan == 9) ||
+                (dma_chan == 10) || (dma_chan == 13) ||
+                (dma_chan == 21) || (dma_chan == 23) ||
+                (dma_chan == 27) || (dma_chan == 28)) &&
+               (ipu->devtype >= IPUv3EX));
+}
+
+/*
+ * We usually use IDMAC 23 as full plane and IDMAC 27 as partial
+ * plane.
+ * IDMAC 23/24/28/41 can drive a display respectively - primary
+ * IDMAC 27 depends on IDMAC 23 - nonprimary
+ */
+static inline int _ipu_is_primary_disp_chan(uint32_t dma_chan)
+{
+       return ((dma_chan == 23) || (dma_chan == 24) ||
+               (dma_chan == 28) || (dma_chan == 41));
+}
+
+static inline int _ipu_is_sync_irq(uint32_t irq)
+{
+       /* sync interrupt register number */
+       int reg_num = irq / 32 + 1;
+
+       return ((reg_num == 1)  || (reg_num == 2)  || (reg_num == 3)  ||
+               (reg_num == 4)  || (reg_num == 7)  || (reg_num == 8)  ||
+               (reg_num == 11) || (reg_num == 12) || (reg_num == 13) ||
+               (reg_num == 14) || (reg_num == 15));
+}
+
+#define idma_is_valid(ch)      (ch != NO_DMA)
+#define idma_mask(ch)          (idma_is_valid(ch) ? (1UL << (ch & 0x1F)) : 0)
+#define tri_cur_buf_mask(ch)   (idma_mask(ch*2) * 3)
+#define tri_cur_buf_shift(ch)  (ffs(idma_mask(ch*2)) - 1)
+
+static inline bool idma_is_set(struct ipu_soc *ipu, uint32_t reg, uint32_t dma)
+{
+       return !!(ipu_idmac_read(ipu, reg) & idma_mask(dma));
+}
+
+static int ipu_clk_setup_enable(struct ipu_soc *ipu)
+{
+       char pixel_clk_0[] = "ipu1_pclk_0";
+       char pixel_clk_1[] = "ipu1_pclk_1";
+       char pixel_clk_0_sel[] = "ipu1_pclk0_sel";
+       char pixel_clk_1_sel[] = "ipu1_pclk1_sel";
+       char pixel_clk_0_div[] = "ipu1_pclk0_div";
+       char pixel_clk_1_div[] = "ipu1_pclk1_div";
+       char *ipu_pixel_clk_sel[] = { "ipu1", "ipu1_di0", "ipu1_di1", };
+       char *pclk_sel;
+       struct clk *clk;
+       int ret;
+       int i;
+
+       pixel_clk_0[3] += ipu->id;
+       pixel_clk_1[3] += ipu->id;
+       pixel_clk_0_sel[3] += ipu->id;
+       pixel_clk_1_sel[3] += ipu->id;
+       pixel_clk_0_div[3] += ipu->id;
+       pixel_clk_1_div[3] += ipu->id;
+       for (i = 0; i < ARRAY_SIZE(ipu_pixel_clk_sel); i++) {
+               pclk_sel = ipu_pixel_clk_sel[i];
+               pclk_sel[3] += ipu->id;
+       }
+       dev_dbg(ipu->dev, "ipu_clk = %lu\n", clk_get_rate(ipu->ipu_clk));
+
+       clk = clk_register_mux_pix_clk(ipu->dev, pixel_clk_0_sel,
+                       (const char **)ipu_pixel_clk_sel,
+                       ARRAY_SIZE(ipu_pixel_clk_sel),
+                       0, ipu->id, 0, 0);
+       if (IS_ERR(clk)) {
+               dev_err(ipu->dev, "clk_register mux di0 failed");
+               return PTR_ERR(clk);
+       }
+       ipu->pixel_clk_sel[0] = clk;
+       clk = clk_register_mux_pix_clk(ipu->dev, pixel_clk_1_sel,
+                       (const char **)ipu_pixel_clk_sel,
+                       ARRAY_SIZE(ipu_pixel_clk_sel),
+                       0, ipu->id, 1, 0);
+       if (IS_ERR(clk)) {
+               dev_err(ipu->dev, "clk_register mux di1 failed");
+               return PTR_ERR(clk);
+       }
+       ipu->pixel_clk_sel[1] = clk;
+
+       clk = clk_register_div_pix_clk(ipu->dev, pixel_clk_0_div,
+                               pixel_clk_0_sel, 0, ipu->id, 0, 0);
+       if (IS_ERR(clk)) {
+               dev_err(ipu->dev, "clk register di0 div failed");
+               return PTR_ERR(clk);
+       }
+       clk = clk_register_div_pix_clk(ipu->dev, pixel_clk_1_div,
+                       pixel_clk_1_sel, CLK_SET_RATE_PARENT, ipu->id, 1, 0);
+       if (IS_ERR(clk)) {
+               dev_err(ipu->dev, "clk register di1 div failed");
+               return PTR_ERR(clk);
+       }
+
+       ipu->pixel_clk[0] = clk_register_gate_pix_clk(ipu->dev, pixel_clk_0,
+                               pixel_clk_0_div, CLK_SET_RATE_PARENT,
+                               ipu->id, 0, 0);
+       if (IS_ERR(ipu->pixel_clk[0])) {
+               dev_err(ipu->dev, "clk register di0 gate failed");
+               return PTR_ERR(ipu->pixel_clk[0]);
+       }
+       ipu->pixel_clk[1] = clk_register_gate_pix_clk(ipu->dev, pixel_clk_1,
+                               pixel_clk_1_div, CLK_SET_RATE_PARENT,
+                               ipu->id, 1, 0);
+       if (IS_ERR(ipu->pixel_clk[1])) {
+               dev_err(ipu->dev, "clk register di1 gate failed");
+               return PTR_ERR(ipu->pixel_clk[1]);
+       }
+
+       ret = clk_set_parent(ipu->pixel_clk_sel[0], ipu->ipu_clk);
+       if (ret) {
+               dev_err(ipu->dev, "clk set parent failed");
+               return ret;
+       }
+
+       ret = clk_set_parent(ipu->pixel_clk_sel[1], ipu->ipu_clk);
+       if (ret) {
+               dev_err(ipu->dev, "clk set parent failed");
+               return ret;
+       }
+
+       ipu->di_clk[0] = devm_clk_get(ipu->dev, "di0");
+       if (IS_ERR(ipu->di_clk[0])) {
+               dev_err(ipu->dev, "clk_get di0 failed");
+               return PTR_ERR(ipu->di_clk[0]);
+       }
+       ipu->di_clk[1] = devm_clk_get(ipu->dev, "di1");
+       if (IS_ERR(ipu->di_clk[1])) {
+               dev_err(ipu->dev, "clk_get di1 failed");
+               return PTR_ERR(ipu->di_clk[1]);
+       }
+
+       ipu->di_clk_sel[0] = devm_clk_get(ipu->dev, "di0_sel");
+       if (IS_ERR(ipu->di_clk_sel[0])) {
+               dev_err(ipu->dev, "clk_get di0_sel failed");
+               return PTR_ERR(ipu->di_clk_sel[0]);
+       }
+       ipu->di_clk_sel[1] = devm_clk_get(ipu->dev, "di1_sel");
+       if (IS_ERR(ipu->di_clk_sel[1])) {
+               dev_err(ipu->dev, "clk_get di1_sel failed");
+               return PTR_ERR(ipu->di_clk_sel[1]);
+       }
+
+       return 0;
+}
+
+static int ipu_mem_reset(struct ipu_soc *ipu)
+{
+       int timeout = 1000;
+
+       ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
+
+       while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
+               if (!timeout--)
+                       return -ETIME;
+               msleep(1);
+       }
+
+       return 0;
+}
+
+struct ipu_soc *ipu_get_soc(int id)
+{
+       if (id >= MXC_IPU_MAX_NUM)
+               return ERR_PTR(-ENODEV);
+       else if (!ipu_array[id].online)
+               return ERR_PTR(-ENODEV);
+       else
+               return &(ipu_array[id]);
+}
+EXPORT_SYMBOL_GPL(ipu_get_soc);
+
+void _ipu_get(struct ipu_soc *ipu)
+{
+       int ret;
+
+       ret = clk_enable(ipu->ipu_clk);
+       if (ret < 0)
+               BUG();
+}
+
+void _ipu_put(struct ipu_soc *ipu)
+{
+       clk_disable(ipu->ipu_clk);
+}
+
+void ipu_disable_hsp_clk(struct ipu_soc *ipu)
+{
+       _ipu_put(ipu);
+}
+EXPORT_SYMBOL(ipu_disable_hsp_clk);
+
+struct ipu_devtype {
+       const char *name;
+       unsigned long cm_ofs;
+       unsigned long idmac_ofs;
+       unsigned long ic_ofs;
+       unsigned long csi0_ofs;
+       unsigned long csi1_ofs;
+       unsigned long di0_ofs;
+       unsigned long di1_ofs;
+       unsigned long smfc_ofs;
+       unsigned long dc_ofs;
+       unsigned long dmfc_ofs;
+       unsigned long vdi_ofs;
+       unsigned long cpmem_ofs;
+       unsigned long srm_ofs;
+       unsigned long tpm_ofs;
+       unsigned long dc_tmpl_ofs;
+       enum ipuv3_type type;
+       bool idmac_used_bufs_present;
+};
+
+struct ipu_platform_type {
+       struct ipu_devtype devtype;
+       unsigned int ch0123_axi;
+       unsigned int ch23_axi;
+       unsigned int ch27_axi;
+       unsigned int ch28_axi;
+       unsigned int normal_axi;
+       bool smfc_idmac_12bit_3planar_bs_fixup; /* workaround little stripes */
+       bool idmac_used_bufs_en_r;
+       bool idmac_used_bufs_en_w;
+       unsigned int idmac_used_bufs_max_r;
+       unsigned int idmac_used_bufs_max_w;
+};
+
+static struct ipu_platform_type ipu_type_imx51 = {
+       .devtype = {
+               .name =         "IPUv3EX",
+               .cm_ofs =       0x1E000000,
+               .idmac_ofs =    0x1E008000,
+               .ic_ofs =       0x1E020000,
+               .csi0_ofs =     0x1E030000,
+               .csi1_ofs =     0x1E038000,
+               .di0_ofs =      0x1E040000,
+               .di1_ofs =      0x1E048000,
+               .smfc_ofs =     0x1E050000,
+               .dc_ofs =       0x1E058000,
+               .dmfc_ofs =     0x1E060000,
+               .vdi_ofs =      0x1E068000,
+               .cpmem_ofs =    0x1F000000,
+               .srm_ofs =      0x1F040000,
+               .tpm_ofs =      0x1F060000,
+               .dc_tmpl_ofs =  0x1F080000,
+               .type =         IPUv3EX,
+               .idmac_used_bufs_present = false,
+       },
+       .ch0123_axi = 1,
+       .ch23_axi = 1,
+       .ch23_axi = 1,
+       .ch27_axi = 1,
+       .ch28_axi = 1,
+       .normal_axi = 0,
+       .smfc_idmac_12bit_3planar_bs_fixup = false,
+};
+
+static struct ipu_platform_type ipu_type_imx53 = {
+       .devtype = {
+               .name =         "IPUv3M",
+               .cm_ofs =       0x06000000,
+               .idmac_ofs =    0x06008000,
+               .ic_ofs =       0x06020000,
+               .csi0_ofs =     0x06030000,
+               .csi1_ofs =     0x06038000,
+               .di0_ofs =      0x06040000,
+               .di1_ofs =      0x06048000,
+               .smfc_ofs =     0x06050000,
+               .dc_ofs =       0x06058000,
+               .dmfc_ofs =     0x06060000,
+               .vdi_ofs =      0x06068000,
+               .cpmem_ofs =    0x07000000,
+               .srm_ofs =      0x07040000,
+               .tpm_ofs =      0x07060000,
+               .dc_tmpl_ofs =  0x07080000,
+               .type =         IPUv3M,
+               .idmac_used_bufs_present = true,
+       },
+       .ch0123_axi = 1,
+       .ch23_axi = 1,
+       .ch27_axi = 1,
+       .ch28_axi = 1,
+       .normal_axi = 0,
+       .idmac_used_bufs_en_r = false,
+       .idmac_used_bufs_en_w = false,
+       .smfc_idmac_12bit_3planar_bs_fixup = false,
+};
+
+static struct ipu_platform_type ipu_type_imx6q = {
+       .devtype = {
+               .name = "IPUv3H",
+               .cm_ofs =       0x00200000,
+               .idmac_ofs =    0x00208000,
+               .ic_ofs =       0x00220000,
+               .csi0_ofs =     0x00230000,
+               .csi1_ofs =     0x00238000,
+               .di0_ofs =      0x00240000,
+               .di1_ofs =      0x00248000,
+               .smfc_ofs =     0x00250000,
+               .dc_ofs =       0x00258000,
+               .dmfc_ofs =     0x00260000,
+               .vdi_ofs =      0x00268000,
+               .cpmem_ofs =    0x00300000,
+               .srm_ofs =      0x00340000,
+               .tpm_ofs =      0x00360000,
+               .dc_tmpl_ofs =  0x00380000,
+               .type =         IPUv3H,
+               .idmac_used_bufs_present = true,
+       },
+       .ch0123_axi = 0,
+       .ch23_axi = 0,
+       .ch27_axi = 0,
+       .ch28_axi = 0,
+       .normal_axi = 1,
+       .idmac_used_bufs_en_r = false,
+       .idmac_used_bufs_en_w = false,
+       .smfc_idmac_12bit_3planar_bs_fixup = false,
+};
+
+static struct ipu_platform_type ipu_type_imx6qp = {
+       .devtype = {
+               .name = "IPUv3H",
+               .cm_ofs =       0x00200000,
+               .idmac_ofs =    0x00208000,
+               .ic_ofs =       0x00220000,
+               .csi0_ofs =     0x00230000,
+               .csi1_ofs =     0x00238000,
+               .di0_ofs =      0x00240000,
+               .di1_ofs =      0x00248000,
+               .smfc_ofs =     0x00250000,
+               .dc_ofs =       0x00258000,
+               .dmfc_ofs =     0x00260000,
+               .vdi_ofs =      0x00268000,
+               .cpmem_ofs =    0x00300000,
+               .srm_ofs =      0x00340000,
+               .tpm_ofs =      0x00360000,
+               .dc_tmpl_ofs =  0x00380000,
+               .type =         IPUv3H,
+               .idmac_used_bufs_present = true,
+       },
+       .ch0123_axi = 0,
+       .ch23_axi = 0,
+       .ch27_axi = 2,
+       .ch28_axi = 3,
+       .normal_axi = 1,
+       .idmac_used_bufs_en_r = true,
+       .idmac_used_bufs_en_w = true,
+       .idmac_used_bufs_max_r = 0x3,
+       .idmac_used_bufs_max_w = 0x3,
+       .smfc_idmac_12bit_3planar_bs_fixup = true,
+ };
+
+static const struct of_device_id imx_ipuv3_dt_ids[] = {
+       { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
+       { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
+       { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
+       { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6qp, },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_ipuv3_dt_ids);
+
+/*!
+ * This function is called by the driver framework to initialize the IPU
+ * hardware.
+ *
+ * @param      dev     The device structure for the IPU passed in by the
+ *                     driver framework.
+ *
+ * @return      Returns 0 on success or negative error code on error
+ */
+static int ipu_probe(struct platform_device *pdev)
+{
+       struct ipu_soc *ipu;
+       struct resource *res;
+       unsigned long ipu_base;
+       const struct of_device_id *of_id =
+                       of_match_device(imx_ipuv3_dt_ids, &pdev->dev);
+       const struct ipu_platform_type *iputype = of_id->data;
+       const struct ipu_devtype *devtype = &iputype->devtype;
+       int ret = 0, id;
+       u32 bypass_reset, reg;
+
+       dev_dbg(&pdev->dev, "<%s>\n", __func__);
+
+       ret = of_property_read_u32(pdev->dev.of_node,
+                                       "bypass_reset", &bypass_reset);
+       if (ret < 0) {
+               dev_dbg(&pdev->dev, "can not get bypass_reset\n");
+               return ret;
+       }
+
+       id = of_alias_get_id(pdev->dev.of_node, "ipu");
+       if (id < 0) {
+               dev_dbg(&pdev->dev, "can not get alias id\n");
+               return id;
+       }
+
+       ipu = &ipu_array[id];
+       memset(ipu, 0, sizeof(struct ipu_soc));
+       ipu->bypass_reset = (bool)bypass_reset;
+       ipu->dev = &pdev->dev;
+       ipu->id = id;
+       ipu->devtype = devtype->type;
+       ipu->ch0123_axi = iputype->ch0123_axi;
+       ipu->ch23_axi = iputype->ch23_axi;
+       ipu->ch27_axi = iputype->ch27_axi;
+       ipu->ch28_axi = iputype->ch28_axi;
+       ipu->normal_axi = iputype->normal_axi;
+       ipu->smfc_idmac_12bit_3planar_bs_fixup =
+                       iputype->smfc_idmac_12bit_3planar_bs_fixup;
+       spin_lock_init(&ipu->int_reg_spin_lock);
+       spin_lock_init(&ipu->rdy_reg_spin_lock);
+       mutex_init(&ipu->mutex_lock);
+
+       dev_dbg(&pdev->dev, "revision is %s\n", devtype->name);
+
+       ipu->irq_sync = platform_get_irq(pdev, 0);
+       ipu->irq_err = platform_get_irq(pdev, 1);
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       if (!res || ipu->irq_sync < 0 || ipu->irq_err < 0) {
+               dev_err(&pdev->dev, "can't get device resources\n");
+               return -ENODEV;
+       }
+
+       if (!devm_request_mem_region(&pdev->dev, res->start,
+                                    resource_size(res), pdev->name))
+               return -EBUSY;
+
+       ret = devm_request_irq(&pdev->dev, ipu->irq_sync,
+                       ipu_sync_irq_handler, 0, pdev->name, ipu);
+       if (ret) {
+               dev_err(ipu->dev, "request SYNC interrupt failed\n");
+               return ret;
+       }
+       ret = devm_request_irq(&pdev->dev, ipu->irq_err,
+                       ipu_err_irq_handler, 0, pdev->name, ipu);
+       if (ret) {
+               dev_err(ipu->dev, "request ERR interrupt failed\n");
+               return ret;
+       }
+
+       ipu_base = res->start;
+
+       ipu->cm_reg = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->cm_ofs, PAGE_SIZE);
+       ipu->ic_reg = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->ic_ofs, PAGE_SIZE);
+       ipu->idmac_reg = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->idmac_ofs, PAGE_SIZE);
+       /* DP Registers are accessed thru the SRM */
+       ipu->dp_reg = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->srm_ofs, PAGE_SIZE);
+       ipu->dc_reg = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->dc_ofs, PAGE_SIZE);
+       ipu->dmfc_reg = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->dmfc_ofs, PAGE_SIZE);
+       ipu->di_reg[0] = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->di0_ofs, PAGE_SIZE);
+       ipu->di_reg[1] = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->di1_ofs, PAGE_SIZE);
+       ipu->smfc_reg = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->smfc_ofs, PAGE_SIZE);
+       ipu->csi_reg[0] = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->csi0_ofs, PAGE_SIZE);
+       ipu->csi_reg[1] = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->csi1_ofs, PAGE_SIZE);
+       ipu->cpmem_base = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->cpmem_ofs, SZ_128K);
+       ipu->tpmem_base = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->tpm_ofs, SZ_64K);
+       ipu->dc_tmpl_reg = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->dc_tmpl_ofs, SZ_128K);
+       ipu->vdi_reg = devm_ioremap(&pdev->dev,
+                               ipu_base + devtype->vdi_ofs, PAGE_SIZE);
+       if (!ipu->cm_reg || !ipu->ic_reg || !ipu->idmac_reg ||
+               !ipu->dp_reg || !ipu->dc_reg || !ipu->dmfc_reg ||
+               !ipu->di_reg[0] || !ipu->di_reg[1] || !ipu->smfc_reg ||
+               !ipu->csi_reg[0] || !ipu->csi_reg[1] || !ipu->cpmem_base ||
+               !ipu->tpmem_base || !ipu->dc_tmpl_reg || !ipu->vdi_reg)
+               return -ENOMEM;
+
+       dev_dbg(ipu->dev, "IPU CM Regs = %p\n", ipu->cm_reg);
+       dev_dbg(ipu->dev, "IPU IC Regs = %p\n", ipu->ic_reg);
+       dev_dbg(ipu->dev, "IPU IDMAC Regs = %p\n", ipu->idmac_reg);
+       dev_dbg(ipu->dev, "IPU DP Regs = %p\n", ipu->dp_reg);
+       dev_dbg(ipu->dev, "IPU DC Regs = %p\n", ipu->dc_reg);
+       dev_dbg(ipu->dev, "IPU DMFC Regs = %p\n", ipu->dmfc_reg);
+       dev_dbg(ipu->dev, "IPU DI0 Regs = %p\n", ipu->di_reg[0]);
+       dev_dbg(ipu->dev, "IPU DI1 Regs = %p\n", ipu->di_reg[1]);
+       dev_dbg(ipu->dev, "IPU SMFC Regs = %p\n", ipu->smfc_reg);
+       dev_dbg(ipu->dev, "IPU CSI0 Regs = %p\n", ipu->csi_reg[0]);
+       dev_dbg(ipu->dev, "IPU CSI1 Regs = %p\n", ipu->csi_reg[1]);
+       dev_dbg(ipu->dev, "IPU CPMem = %p\n", ipu->cpmem_base);
+       dev_dbg(ipu->dev, "IPU TPMem = %p\n", ipu->tpmem_base);
+       dev_dbg(ipu->dev, "IPU DC Template Mem = %p\n", ipu->dc_tmpl_reg);
+       dev_dbg(ipu->dev, "IPU VDI Regs = %p\n", ipu->vdi_reg);
+
+       ipu->ipu_clk = devm_clk_get(ipu->dev, "bus");
+       if (IS_ERR(ipu->ipu_clk)) {
+               dev_err(ipu->dev, "clk_get ipu failed");
+               return PTR_ERR(ipu->ipu_clk);
+       }
+
+       /* ipu_clk is always prepared */
+       ret = clk_prepare_enable(ipu->ipu_clk);
+       if (ret < 0) {
+               dev_err(ipu->dev, "ipu clk enable failed\n");
+               return ret;
+       }
+
+       ipu->prg_clk = devm_clk_get(ipu->dev, "prg");
+       if (IS_ERR(ipu->prg_clk))
+               ipu->prg_clk = NULL;
+
+       ipu->online = true;
+
+       platform_set_drvdata(pdev, ipu);
+
+       if (!bypass_reset) {
+               ret = device_reset(&pdev->dev);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to reset: %d\n", ret);
+                       return ret;
+               }
+
+               ipu_mem_reset(ipu);
+
+               ipu_disp_init(ipu);
+
+               /* Set MCU_T to divide MCU access window into 2 */
+               ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
+                            IPU_DISP_GEN);
+       }
+
+       /* setup ipu clk tree after ipu reset  */
+       ret = ipu_clk_setup_enable(ipu);
+       if (ret < 0) {
+               dev_err(ipu->dev, "ipu clk setup failed\n");
+               ipu->online = false;
+               return ret;
+       }
+
+       if (devtype->idmac_used_bufs_present) {
+               reg = ipu_idmac_read(ipu, IDMAC_CONF);
+               if (iputype->idmac_used_bufs_en_r)
+                       reg |= IDMAC_CONF_USED_BUFS_EN_R;
+               else
+                       reg &= ~IDMAC_CONF_USED_BUFS_EN_R;
+               if (iputype->idmac_used_bufs_en_w)
+                       reg |= IDMAC_CONF_USED_BUFS_EN_W;
+               else
+                       reg &= ~IDMAC_CONF_USED_BUFS_EN_W;
+
+               reg &= ~IDMAC_CONF_USED_BUFS_MAX_R_MASK;
+               reg |= (iputype->idmac_used_bufs_max_r <<
+                       IDMAC_CONF_USED_BUFS_MAX_R_OFFSET);
+               reg &= ~IDMAC_CONF_USED_BUFS_MAX_W_MASK;
+               reg |= (iputype->idmac_used_bufs_max_w <<
+                       IDMAC_CONF_USED_BUFS_MAX_W_OFFSET);
+               ipu_idmac_write(ipu, reg, IDMAC_CONF);
+       }
+
+       /* Set sync refresh channels and CSI->mem channel as high priority */
+       ipu_idmac_write(ipu, 0x18800003L, IDMAC_CHA_PRI(0));
+
+       /* Enable error interrupts by default */
+       ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(5));
+       ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(6));
+       ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(9));
+       ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(10));
+
+       if (!bypass_reset)
+               clk_disable(ipu->ipu_clk);
+
+       register_ipu_device(ipu, id);
+
+       pm_runtime_enable(&pdev->dev);
+
+       return ret;
+}
+
+int ipu_remove(struct platform_device *pdev)
+{
+       struct ipu_soc *ipu = platform_get_drvdata(pdev);
+
+       unregister_ipu_device(ipu, ipu->id);
+
+       clk_put(ipu->ipu_clk);
+
+       return 0;
+}
+
+void ipu_dump_registers(struct ipu_soc *ipu)
+{
+       dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", ipu_cm_read(ipu, IPU_CONF));
+       dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", ipu_idmac_read(ipu, IDMAC_CONF));
+       dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n",
+              ipu_idmac_read(ipu, IDMAC_CHA_EN(0)));
+       dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n",
+              ipu_idmac_read(ipu, IDMAC_CHA_EN(32)));
+       dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n",
+              ipu_idmac_read(ipu, IDMAC_CHA_PRI(0)));
+       dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n",
+              ipu_idmac_read(ipu, IDMAC_CHA_PRI(32)));
+       dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n",
+              ipu_idmac_read(ipu, IDMAC_BAND_EN(ipu->devtype, 0)));
+       dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n",
+              ipu_idmac_read(ipu, IDMAC_BAND_EN(ipu->devtype, 32)));
+       dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
+              ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0)));
+       dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
+              ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32)));
+       if (ipu->devtype >= IPUv3EX) {
+               dev_dbg(ipu->dev, "IPU_CHA_TRB_MODE_SEL0 = \t0x%08X\n",
+                      ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(ipu->devtype, 0)));
+               dev_dbg(ipu->dev, "IPU_CHA_TRB_MODE_SEL1 = \t0x%08X\n",
+                      ipu_cm_read(ipu,
+                               IPU_CHA_TRB_MODE_SEL(ipu->devtype, 32)));
+       }
+       dev_dbg(ipu->dev, "DMFC_WR_CHAN = \t0x%08X\n",
+              ipu_dmfc_read(ipu, DMFC_WR_CHAN));
+       dev_dbg(ipu->dev, "DMFC_WR_CHAN_DEF = \t0x%08X\n",
+              ipu_dmfc_read(ipu, DMFC_WR_CHAN_DEF));
+       dev_dbg(ipu->dev, "DMFC_DP_CHAN = \t0x%08X\n",
+              ipu_dmfc_read(ipu, DMFC_DP_CHAN));
+       dev_dbg(ipu->dev, "DMFC_DP_CHAN_DEF = \t0x%08X\n",
+              ipu_dmfc_read(ipu, DMFC_DP_CHAN_DEF));
+       dev_dbg(ipu->dev, "DMFC_IC_CTRL = \t0x%08X\n",
+              ipu_dmfc_read(ipu, DMFC_IC_CTRL));
+       dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
+              ipu_cm_read(ipu, IPU_FS_PROC_FLOW1));
+       dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
+              ipu_cm_read(ipu, IPU_FS_PROC_FLOW2));
+       dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
+              ipu_cm_read(ipu, IPU_FS_PROC_FLOW3));
+       dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
+              ipu_cm_read(ipu, IPU_FS_DISP_FLOW1));
+       dev_dbg(ipu->dev, "IPU_VDIC_VDI_FSIZE = \t0x%08X\n",
+              ipu_vdi_read(ipu, VDI_FSIZE));
+       dev_dbg(ipu->dev, "IPU_VDIC_VDI_C = \t0x%08X\n",
+              ipu_vdi_read(ipu, VDI_C));
+       dev_dbg(ipu->dev, "IPU_IC_CONF = \t0x%08X\n",
+              ipu_ic_read(ipu, IC_CONF));
+}
+
+/*!
+ * This function is called to initialize a logical IPU channel.
+ *
+ * @param      ipu     ipu handler
+ * @param       channel Input parameter for the logical channel ID to init.
+ *
+ * @param       params  Input parameter containing union of channel
+ *                      initialization parameters.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params)
+{
+       int ret = 0;
+       bool bad_pixfmt;
+       uint32_t ipu_conf, reg, in_g_pixel_fmt, sec_dma;
+
+       dev_dbg(ipu->dev, "init channel = %d\n", IPU_CHAN_ID(channel));
+
+       ret = pm_runtime_get_sync(ipu->dev);
+       if (ret < 0) {
+               dev_err(ipu->dev, "ch = %d, pm_runtime_get failed:%d!\n",
+                               IPU_CHAN_ID(channel), ret);
+               dump_stack();
+               return ret;
+       }
+       /*
+        * Here, ret could be 1 if the device's runtime PM status was
+        * already 'active', so clear it to be 0.
+        */
+       ret = 0;
+
+       _ipu_get(ipu);
+
+       mutex_lock(&ipu->mutex_lock);
+
+       /* Re-enable error interrupts every time a channel is initialized */
+       ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(5));
+       ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(6));
+       ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(9));
+       ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(10));
+
+       if (ipu->channel_init_mask & (1L << IPU_CHAN_ID(channel))) {
+               dev_warn(ipu->dev, "Warning: channel already initialized %d\n",
+                       IPU_CHAN_ID(channel));
+       }
+
+       ipu_conf = ipu_cm_read(ipu, IPU_CONF);
+
+       switch (channel) {
+       case CSI_MEM0:
+       case CSI_MEM1:
+       case CSI_MEM2:
+       case CSI_MEM3:
+               if (params->csi_mem.csi > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               if (params->csi_mem.interlaced)
+                       ipu->chan_is_interlaced[channel_2_dma(channel,
+                               IPU_OUTPUT_BUFFER)] = true;
+               else
+                       ipu->chan_is_interlaced[channel_2_dma(channel,
+                               IPU_OUTPUT_BUFFER)] = false;
+
+               ipu->smfc_use_count++;
+               ipu->csi_channel[params->csi_mem.csi] = channel;
+
+               /*SMFC setting*/
+               if (params->csi_mem.mipi_en) {
+                       ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+                               params->csi_mem.csi));
+                       _ipu_smfc_init(ipu, channel, params->csi_mem.mipi_vc,
+                               params->csi_mem.csi);
+                       _ipu_csi_set_mipi_di(ipu, params->csi_mem.mipi_vc,
+                               params->csi_mem.mipi_id, params->csi_mem.csi);
+               } else {
+                       ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+                               params->csi_mem.csi));
+                       _ipu_smfc_init(ipu, channel, 0, params->csi_mem.csi);
+               }
+
+               /*CSI data (include compander) dest*/
+               _ipu_csi_init(ipu, channel, params->csi_mem.csi);
+               break;
+       case CSI_PRP_ENC_MEM:
+               if (params->csi_prp_enc_mem.csi > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               if ((ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) ||
+                       (ipu->using_ic_dirct_ch == MEM_VDI_MEM)) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               ipu->using_ic_dirct_ch = CSI_PRP_ENC_MEM;
+
+               ipu->ic_use_count++;
+               ipu->csi_channel[params->csi_prp_enc_mem.csi] = channel;
+
+               if (params->csi_prp_enc_mem.mipi_en) {
+                       ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+                               params->csi_prp_enc_mem.csi));
+                       _ipu_csi_set_mipi_di(ipu,
+                               params->csi_prp_enc_mem.mipi_vc,
+                               params->csi_prp_enc_mem.mipi_id,
+                               params->csi_prp_enc_mem.csi);
+               } else
+                       ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+                               params->csi_prp_enc_mem.csi));
+
+               /*CSI0/1 feed into IC*/
+               ipu_conf &= ~IPU_CONF_IC_INPUT;
+               if (params->csi_prp_enc_mem.csi)
+                       ipu_conf |= IPU_CONF_CSI_SEL;
+               else
+                       ipu_conf &= ~IPU_CONF_CSI_SEL;
+
+               /*PRP skip buffer in memory, only valid when RWS_EN is true*/
+               reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+               ipu_cm_write(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1);
+
+               /*CSI data (include compander) dest*/
+               _ipu_csi_init(ipu, channel, params->csi_prp_enc_mem.csi);
+               _ipu_ic_init_prpenc(ipu, params, true);
+               break;
+       case CSI_PRP_VF_MEM:
+               if (params->csi_prp_vf_mem.csi > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               if ((ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) ||
+                       (ipu->using_ic_dirct_ch == MEM_VDI_MEM)) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               ipu->using_ic_dirct_ch = CSI_PRP_VF_MEM;
+
+               ipu->ic_use_count++;
+               ipu->csi_channel[params->csi_prp_vf_mem.csi] = channel;
+
+               if (params->csi_prp_vf_mem.mipi_en) {
+                       ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+                               params->csi_prp_vf_mem.csi));
+                       _ipu_csi_set_mipi_di(ipu,
+                               params->csi_prp_vf_mem.mipi_vc,
+                               params->csi_prp_vf_mem.mipi_id,
+                               params->csi_prp_vf_mem.csi);
+               } else
+                       ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+                               params->csi_prp_vf_mem.csi));
+
+               /*CSI0/1 feed into IC*/
+               ipu_conf &= ~IPU_CONF_IC_INPUT;
+               if (params->csi_prp_vf_mem.csi)
+                       ipu_conf |= IPU_CONF_CSI_SEL;
+               else
+                       ipu_conf &= ~IPU_CONF_CSI_SEL;
+
+               /*PRP skip buffer in memory, only valid when RWS_EN is true*/
+               reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+               ipu_cm_write(ipu, reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
+
+               /*CSI data (include compander) dest*/
+               _ipu_csi_init(ipu, channel, params->csi_prp_vf_mem.csi);
+               _ipu_ic_init_prpvf(ipu, params, true);
+               break;
+       case MEM_PRP_VF_MEM:
+               if (params->mem_prp_vf_mem.graphics_combine_en) {
+                       sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+                       in_g_pixel_fmt = params->mem_prp_vf_mem.in_g_pixel_fmt;
+                       bad_pixfmt =
+                               _ipu_ch_param_bad_alpha_pos(in_g_pixel_fmt);
+
+                       if (params->mem_prp_vf_mem.alpha_chan_en) {
+                               if (bad_pixfmt) {
+                                       dev_err(ipu->dev, "bad pixel format "
+                                               "for graphics plane from "
+                                               "ch%d\n", sec_dma);
+                                       ret = -EINVAL;
+                                       goto err;
+                               }
+                               ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true;
+                       }
+                       ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true;
+               }
+
+               reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+               ipu_cm_write(ipu, reg | FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
+
+               _ipu_ic_init_prpvf(ipu, params, false);
+               ipu->ic_use_count++;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) ||
+                       (ipu->using_ic_dirct_ch == MEM_VDI_MEM) ||
+                    (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               ipu->using_ic_dirct_ch = MEM_VDI_PRP_VF_MEM;
+               ipu->ic_use_count++;
+               ipu->vdi_use_count++;
+               reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+               reg &= ~FS_VDI_SRC_SEL_MASK;
+               ipu_cm_write(ipu, reg , IPU_FS_PROC_FLOW1);
+
+               if (params->mem_prp_vf_mem.graphics_combine_en)
+                       ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true;
+               _ipu_ic_init_prpvf(ipu, params, false);
+               _ipu_vdi_init(ipu, channel, params);
+               break;
+       case MEM_VDI_PRP_VF_MEM_P:
+       case MEM_VDI_PRP_VF_MEM_N:
+       case MEM_VDI_MEM_P:
+       case MEM_VDI_MEM_N:
+               _ipu_vdi_init(ipu, channel, params);
+               break;
+       case MEM_VDI_MEM:
+               if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) ||
+                       (ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) ||
+                    (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               ipu->using_ic_dirct_ch = MEM_VDI_MEM;
+               ipu->ic_use_count++;
+               ipu->vdi_use_count++;
+               _ipu_vdi_init(ipu, channel, params);
+               break;
+       case MEM_ROT_VF_MEM:
+               ipu->ic_use_count++;
+               ipu->rot_use_count++;
+               _ipu_ic_init_rotate_vf(ipu, params);
+               break;
+       case MEM_PRP_ENC_MEM:
+               ipu->ic_use_count++;
+               reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+               ipu_cm_write(ipu, reg | FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1);
+               _ipu_ic_init_prpenc(ipu, params, false);
+               break;
+       case MEM_ROT_ENC_MEM:
+               ipu->ic_use_count++;
+               ipu->rot_use_count++;
+               _ipu_ic_init_rotate_enc(ipu, params);
+               break;
+       case MEM_PP_MEM:
+               if (params->mem_pp_mem.graphics_combine_en) {
+                       sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+                       in_g_pixel_fmt = params->mem_pp_mem.in_g_pixel_fmt;
+                       bad_pixfmt =
+                               _ipu_ch_param_bad_alpha_pos(in_g_pixel_fmt);
+
+                       if (params->mem_pp_mem.alpha_chan_en) {
+                               if (bad_pixfmt) {
+                                       dev_err(ipu->dev, "bad pixel format "
+                                               "for graphics plane from "
+                                               "ch%d\n", sec_dma);
+                                       ret = -EINVAL;
+                                       goto err;
+                               }
+                               ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true;
+                       }
+
+                       ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true;
+               }
+
+               _ipu_ic_init_pp(ipu, params);
+               ipu->ic_use_count++;
+               break;
+       case MEM_ROT_PP_MEM:
+               _ipu_ic_init_rotate_pp(ipu, params);
+               ipu->ic_use_count++;
+               ipu->rot_use_count++;
+               break;
+       case MEM_DC_SYNC:
+               if (params->mem_dc_sync.di > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               ipu->dc_di_assignment[1] = params->mem_dc_sync.di;
+               _ipu_dc_init(ipu, 1, params->mem_dc_sync.di,
+                            params->mem_dc_sync.interlaced,
+                            params->mem_dc_sync.out_pixel_fmt);
+               ipu->di_use_count[params->mem_dc_sync.di]++;
+               ipu->dc_use_count++;
+               ipu->dmfc_use_count++;
+               break;
+       case MEM_BG_SYNC:
+               if (params->mem_dp_bg_sync.di > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               if (params->mem_dp_bg_sync.alpha_chan_en)
+                       ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true;
+
+               ipu->dc_di_assignment[5] = params->mem_dp_bg_sync.di;
+               _ipu_dp_init(ipu, channel, params->mem_dp_bg_sync.in_pixel_fmt,
+                            params->mem_dp_bg_sync.out_pixel_fmt);
+               _ipu_dc_init(ipu, 5, params->mem_dp_bg_sync.di,
+                            params->mem_dp_bg_sync.interlaced,
+                            params->mem_dp_bg_sync.out_pixel_fmt);
+               ipu->di_use_count[params->mem_dp_bg_sync.di]++;
+               ipu->dc_use_count++;
+               ipu->dp_use_count++;
+               ipu->dmfc_use_count++;
+               break;
+       case MEM_FG_SYNC:
+               _ipu_dp_init(ipu, channel, params->mem_dp_fg_sync.in_pixel_fmt,
+                            params->mem_dp_fg_sync.out_pixel_fmt);
+
+               if (params->mem_dp_fg_sync.alpha_chan_en)
+                       ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true;
+
+               ipu->dc_use_count++;
+               ipu->dp_use_count++;
+               ipu->dmfc_use_count++;
+               break;
+       case DIRECT_ASYNC0:
+               if (params->direct_async.di > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               ipu->dc_di_assignment[8] = params->direct_async.di;
+               _ipu_dc_init(ipu, 8, params->direct_async.di, false, IPU_PIX_FMT_GENERIC);
+               ipu->di_use_count[params->direct_async.di]++;
+               ipu->dc_use_count++;
+               break;
+       case DIRECT_ASYNC1:
+               if (params->direct_async.di > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               ipu->dc_di_assignment[9] = params->direct_async.di;
+               _ipu_dc_init(ipu, 9, params->direct_async.di, false, IPU_PIX_FMT_GENERIC);
+               ipu->di_use_count[params->direct_async.di]++;
+               ipu->dc_use_count++;
+               break;
+       default:
+               dev_err(ipu->dev, "Missing channel initialization\n");
+               break;
+       }
+
+       ipu->channel_init_mask |= 1L << IPU_CHAN_ID(channel);
+
+       ipu_cm_write(ipu, ipu_conf, IPU_CONF);
+
+err:
+       mutex_unlock(&ipu->mutex_lock);
+       return ret;
+}
+EXPORT_SYMBOL(ipu_init_channel);
+
+/*!
+ * This function is called to uninitialize a logical IPU channel.
+ *
+ * @param      ipu     ipu handler
+ * @param       channel Input parameter for the logical channel ID to uninit.
+ */
+void ipu_uninit_channel(struct ipu_soc *ipu, ipu_channel_t channel)
+{
+       uint32_t reg;
+       uint32_t in_dma, out_dma = 0;
+       uint32_t ipu_conf;
+       uint32_t dc_chan = 0;
+       int ret;
+
+       mutex_lock(&ipu->mutex_lock);
+
+       if ((ipu->channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) {
+               dev_dbg(ipu->dev, "Channel already uninitialized %d\n",
+                       IPU_CHAN_ID(channel));
+               mutex_unlock(&ipu->mutex_lock);
+               return;
+       }
+
+       /* Make sure channel is disabled */
+       /* Get input and output dma channels */
+       in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
+       out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+
+       if (idma_is_set(ipu, IDMAC_CHA_EN(in_dma), in_dma) ||
+           idma_is_set(ipu, IDMAC_CHA_EN(out_dma), out_dma)) {
+               dev_err(ipu->dev,
+                       "Channel %d is not disabled, disable first\n",
+                       IPU_CHAN_ID(channel));
+               mutex_unlock(&ipu->mutex_lock);
+               return;
+       }
+
+       ipu_conf = ipu_cm_read(ipu, IPU_CONF);
+
+       /* Reset the double buffer */
+       reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(in_dma));
+       ipu_cm_write(ipu, reg & ~idma_mask(in_dma), IPU_CHA_DB_MODE_SEL(in_dma));
+       reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(out_dma));
+       ipu_cm_write(ipu, reg & ~idma_mask(out_dma), IPU_CHA_DB_MODE_SEL(out_dma));
+
+       /* Reset the triple buffer */
+       reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(ipu->devtype, in_dma));
+       ipu_cm_write(ipu, reg & ~idma_mask(in_dma),
+                               IPU_CHA_TRB_MODE_SEL(ipu->devtype, in_dma));
+       reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(ipu->devtype, out_dma));
+       ipu_cm_write(ipu, reg & ~idma_mask(out_dma),
+                               IPU_CHA_TRB_MODE_SEL(ipu->devtype, out_dma));
+
+       if (_ipu_is_ic_chan(in_dma) || _ipu_is_dp_graphic_chan(in_dma)) {
+               ipu->sec_chan_en[IPU_CHAN_ID(channel)] = false;
+               ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = false;
+       }
+
+       switch (channel) {
+       case CSI_MEM0:
+       case CSI_MEM1:
+       case CSI_MEM2:
+       case CSI_MEM3:
+               ipu->smfc_use_count--;
+               if (ipu->csi_channel[0] == channel) {
+                       ipu->csi_channel[0] = CHAN_NONE;
+               } else if (ipu->csi_channel[1] == channel) {
+                       ipu->csi_channel[1] = CHAN_NONE;
+               }
+               break;
+       case CSI_PRP_ENC_MEM:
+               ipu->ic_use_count--;
+               if (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)
+                       ipu->using_ic_dirct_ch = 0;
+               _ipu_ic_uninit_prpenc(ipu);
+               if (ipu->csi_channel[0] == channel) {
+                       ipu->csi_channel[0] = CHAN_NONE;
+               } else if (ipu->csi_channel[1] == channel) {
+                       ipu->csi_channel[1] = CHAN_NONE;
+               }
+               break;
+       case CSI_PRP_VF_MEM:
+               ipu->ic_use_count--;
+               if (ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM)
+                       ipu->using_ic_dirct_ch = 0;
+               _ipu_ic_uninit_prpvf(ipu);
+               if (ipu->csi_channel[0] == channel) {
+                       ipu->csi_channel[0] = CHAN_NONE;
+               } else if (ipu->csi_channel[1] == channel) {
+                       ipu->csi_channel[1] = CHAN_NONE;
+               }
+               break;
+       case MEM_PRP_VF_MEM:
+               ipu->ic_use_count--;
+               _ipu_ic_uninit_prpvf(ipu);
+               reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+               ipu_cm_write(ipu, reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               ipu->ic_use_count--;
+               ipu->vdi_use_count--;
+               if (ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM)
+                       ipu->using_ic_dirct_ch = 0;
+               _ipu_ic_uninit_prpvf(ipu);
+               _ipu_vdi_uninit(ipu);
+               reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+               ipu_cm_write(ipu, reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
+               break;
+       case MEM_VDI_MEM:
+               ipu->ic_use_count--;
+               ipu->vdi_use_count--;
+               if (ipu->using_ic_dirct_ch == MEM_VDI_MEM)
+                       ipu->using_ic_dirct_ch = 0;
+               _ipu_vdi_uninit(ipu);
+               break;
+       case MEM_VDI_PRP_VF_MEM_P:
+       case MEM_VDI_PRP_VF_MEM_N:
+       case MEM_VDI_MEM_P:
+       case MEM_VDI_MEM_N:
+               break;
+       case MEM_ROT_VF_MEM:
+               ipu->rot_use_count--;
+               ipu->ic_use_count--;
+               _ipu_ic_uninit_rotate_vf(ipu);
+               break;
+       case MEM_PRP_ENC_MEM:
+               ipu->ic_use_count--;
+               _ipu_ic_uninit_prpenc(ipu);
+               reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+               ipu_cm_write(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1);
+               break;
+       case MEM_ROT_ENC_MEM:
+               ipu->rot_use_count--;
+               ipu->ic_use_count--;
+               _ipu_ic_uninit_rotate_enc(ipu);
+               break;
+       case MEM_PP_MEM:
+               ipu->ic_use_count--;
+               _ipu_ic_uninit_pp(ipu);
+               break;
+       case MEM_ROT_PP_MEM:
+               ipu->rot_use_count--;
+               ipu->ic_use_count--;
+               _ipu_ic_uninit_rotate_pp(ipu);
+               break;
+       case MEM_DC_SYNC:
+               dc_chan = 1;
+               _ipu_dc_uninit(ipu, 1);
+               ipu->di_use_count[ipu->dc_di_assignment[1]]--;
+               ipu->dc_use_count--;
+               ipu->dmfc_use_count--;
+               break;
+       case MEM_BG_SYNC:
+               dc_chan = 5;
+               _ipu_dp_uninit(ipu, channel);
+               _ipu_dc_uninit(ipu, 5);
+               ipu->di_use_count[ipu->dc_di_assignment[5]]--;
+               ipu->dc_use_count--;
+               ipu->dp_use_count--;
+               ipu->dmfc_use_count--;
+               break;
+       case MEM_FG_SYNC:
+               _ipu_dp_uninit(ipu, channel);
+               ipu->dc_use_count--;
+               ipu->dp_use_count--;
+               ipu->dmfc_use_count--;
+               break;
+       case DIRECT_ASYNC0:
+               dc_chan = 8;
+               _ipu_dc_uninit(ipu, 8);
+               ipu->di_use_count[ipu->dc_di_assignment[8]]--;
+               ipu->dc_use_count--;
+               break;
+       case DIRECT_ASYNC1:
+               dc_chan = 9;
+               _ipu_dc_uninit(ipu, 9);
+               ipu->di_use_count[ipu->dc_di_assignment[9]]--;
+               ipu->dc_use_count--;
+               break;
+       default:
+               break;
+       }
+
+       if (ipu->ic_use_count == 0)
+               ipu_conf &= ~IPU_CONF_IC_EN;
+       if (ipu->vdi_use_count == 0) {
+               ipu_conf &= ~IPU_CONF_ISP_EN;
+               ipu_conf &= ~IPU_CONF_VDI_EN;
+               ipu_conf &= ~IPU_CONF_IC_INPUT;
+       }
+       if (ipu->rot_use_count == 0)
+               ipu_conf &= ~IPU_CONF_ROT_EN;
+       if (ipu->dc_use_count == 0)
+               ipu_conf &= ~IPU_CONF_DC_EN;
+       if (ipu->dp_use_count == 0)
+               ipu_conf &= ~IPU_CONF_DP_EN;
+       if (ipu->dmfc_use_count == 0)
+               ipu_conf &= ~IPU_CONF_DMFC_EN;
+       if (ipu->di_use_count[0] == 0) {
+               ipu_conf &= ~IPU_CONF_DI0_EN;
+       }
+       if (ipu->di_use_count[1] == 0) {
+               ipu_conf &= ~IPU_CONF_DI1_EN;
+       }
+       if (ipu->smfc_use_count == 0)
+               ipu_conf &= ~IPU_CONF_SMFC_EN;
+
+       ipu_cm_write(ipu, ipu_conf, IPU_CONF);
+
+       ipu->channel_init_mask &= ~(1L << IPU_CHAN_ID(channel));
+
+       /*
+        * Disable pixel clk and its parent clock(if the parent clock
+        * usecount is 1) after clearing DC/DP/DI bits in IPU_CONF
+        * register to prevent LVDS display channel starvation.
+        */
+       if (_ipu_is_primary_disp_chan(in_dma) &&
+           ipu->pixel_clk_en[ipu->dc_di_assignment[dc_chan]]) {
+               clk_disable_unprepare(ipu->pixel_clk[ipu->dc_di_assignment[dc_chan]]);
+               ipu->pixel_clk_en[ipu->dc_di_assignment[dc_chan]] = false;
+       }
+
+       mutex_unlock(&ipu->mutex_lock);
+
+       _ipu_put(ipu);
+
+       ret = pm_runtime_put_sync_suspend(ipu->dev);
+       if (ret < 0) {
+               dev_err(ipu->dev, "ch = %d, pm_runtime_put failed:%d!\n",
+                               IPU_CHAN_ID(channel), ret);
+               dump_stack();
+       }
+
+       WARN_ON(ipu->ic_use_count < 0);
+       WARN_ON(ipu->vdi_use_count < 0);
+       WARN_ON(ipu->rot_use_count < 0);
+       WARN_ON(ipu->dc_use_count < 0);
+       WARN_ON(ipu->dp_use_count < 0);
+       WARN_ON(ipu->dmfc_use_count < 0);
+       WARN_ON(ipu->smfc_use_count < 0);
+}
+EXPORT_SYMBOL(ipu_uninit_channel);
+
+/*!
+ * This function is called to initialize buffer(s) for logical IPU channel.
+ *
+ * @param      ipu             ipu handler
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to initialize.
+ *
+ * @param       pixel_fmt       Input parameter for pixel format of buffer.
+ *                              Pixel format is a FOURCC ASCII code.
+ *
+ * @param       width           Input parameter for width of buffer in pixels.
+ *
+ * @param       height          Input parameter for height of buffer in pixels.
+ *
+ * @param       stride          Input parameter for stride length of buffer
+ *                              in pixels.
+ *
+ * @param       rot_mode        Input parameter for rotation setting of buffer.
+ *                              A rotation setting other than
+ *                              IPU_ROTATE_VERT_FLIP
+ *                              should only be used for input buffers of
+ *                              rotation channels.
+ *
+ * @param       phyaddr_0       Input parameter buffer 0 physical address.
+ *
+ * @param       phyaddr_1       Input parameter buffer 1 physical address.
+ *                              Setting this to a value other than NULL enables
+ *                              double buffering mode.
+ *
+ * @param       phyaddr_2       Input parameter buffer 2 physical address.
+ *                              Setting this to a value other than NULL enables
+ *                              triple buffering mode, phyaddr_1 should not be
+ *                              NULL then.
+ *
+ * @param       u              private u offset for additional cropping,
+ *                             zero if not used.
+ *
+ * @param       v              private v offset for additional cropping,
+ *                             zero if not used.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_init_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel,
+                               ipu_buffer_t type,
+                               uint32_t pixel_fmt,
+                               uint16_t width, uint16_t height,
+                               uint32_t stride,
+                               ipu_rotate_mode_t rot_mode,
+                               dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
+                               dma_addr_t phyaddr_2,
+                               uint32_t u, uint32_t v)
+{
+       uint32_t reg;
+       uint32_t dma_chan;
+       uint32_t burst_size;
+
+       dma_chan = channel_2_dma(channel, type);
+       if (!idma_is_valid(dma_chan))
+               return -EINVAL;
+
+       if (stride < width * bytes_per_pixel(pixel_fmt))
+               stride = width * bytes_per_pixel(pixel_fmt);
+
+       if (stride % 4) {
+               dev_err(ipu->dev,
+                       "Stride not 32-bit aligned, stride = %d\n", stride);
+               return -EINVAL;
+       }
+       /* IC & IRT channels' width must be multiple of 8 pixels */
+       if ((_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan))
+               && (width % 8)) {
+               dev_err(ipu->dev, "Width must be 8 pixel multiple\n");
+               return -EINVAL;
+       }
+
+       if (_ipu_is_vdi_out_chan(dma_chan) &&
+               ((width < 16) || (height < 16) || (width % 2) || (height % 4))) {
+               dev_err(ipu->dev, "vdi width/height limited err\n");
+               return -EINVAL;
+       }
+
+       /* IPUv3EX and IPUv3M support triple buffer */
+       if ((!_ipu_is_trb_chan(ipu, dma_chan)) && phyaddr_2) {
+               dev_err(ipu->dev, "Chan%d doesn't support triple buffer "
+                                  "mode\n", dma_chan);
+               return -EINVAL;
+       }
+       if (!phyaddr_1 && phyaddr_2) {
+               dev_err(ipu->dev, "Chan%d's buf1 physical addr is NULL for "
+                                  "triple buffer mode\n", dma_chan);
+               return -EINVAL;
+       }
+
+       mutex_lock(&ipu->mutex_lock);
+
+       /* Build parameter memory data for DMA channel */
+       _ipu_ch_param_init(ipu, dma_chan, pixel_fmt, width, height, stride, u, v, 0,
+                          phyaddr_0, phyaddr_1, phyaddr_2);
+
+       /* Set correlative channel parameter of local alpha channel */
+       if ((_ipu_is_ic_graphic_chan(dma_chan) ||
+            _ipu_is_dp_graphic_chan(dma_chan)) &&
+           (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] == true)) {
+               _ipu_ch_param_set_alpha_use_separate_channel(ipu, dma_chan, true);
+               _ipu_ch_param_set_alpha_buffer_memory(ipu, dma_chan);
+               _ipu_ch_param_set_alpha_condition_read(ipu, dma_chan);
+               /* fix alpha width as 8 and burst size as 16*/
+               _ipu_ch_params_set_alpha_width(ipu, dma_chan, 8);
+               _ipu_ch_param_set_burst_size(ipu, dma_chan, 16);
+       } else if (_ipu_is_ic_graphic_chan(dma_chan) &&
+                  ipu_pixel_format_has_alpha(pixel_fmt))
+               _ipu_ch_param_set_alpha_use_separate_channel(ipu, dma_chan, false);
+
+       if (rot_mode)
+               _ipu_ch_param_set_rotation(ipu, dma_chan, rot_mode);
+
+       /* IC and ROT channels have restriction of 8 or 16 pix burst length */
+       if (_ipu_is_ic_chan(dma_chan) || _ipu_is_vdi_out_chan(dma_chan)) {
+               if ((width % 16) == 0)
+                       _ipu_ch_param_set_burst_size(ipu, dma_chan, 16);
+               else
+                       _ipu_ch_param_set_burst_size(ipu, dma_chan, 8);
+       } else if (_ipu_is_irt_chan(dma_chan)) {
+               _ipu_ch_param_set_burst_size(ipu, dma_chan, 8);
+               _ipu_ch_param_set_block_mode(ipu, dma_chan);
+       } else if (_ipu_is_dmfc_chan(dma_chan)) {
+               burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan);
+               _ipu_dmfc_set_wait4eot(ipu, dma_chan, width);
+               _ipu_dmfc_set_burst_size(ipu, dma_chan, burst_size);
+       }
+
+       if (_ipu_disp_chan_is_interlaced(ipu, channel) ||
+               ipu->chan_is_interlaced[dma_chan])
+               _ipu_ch_param_set_interlaced_scan(ipu, dma_chan);
+
+       if (_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan) ||
+               _ipu_is_vdi_out_chan(dma_chan)) {
+               burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan);
+               _ipu_ic_idma_init(ipu, dma_chan, width, height, burst_size,
+                       rot_mode);
+       } else if (_ipu_is_smfc_chan(dma_chan)) {
+               burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan);
+               /*
+                * This is different from IPUv3 spec, but it is confirmed
+                * in IPUforum that SMFC burst size should be NPB[6:3]
+                * when IDMAC works in 16-bit generic data mode.
+                */
+               if (pixel_fmt == IPU_PIX_FMT_GENERIC)
+                       /* 8 bits per pixel */
+                       burst_size = burst_size >> 4;
+               else if (pixel_fmt == IPU_PIX_FMT_GENERIC_16)
+                       /* 16 bits per pixel */
+                       burst_size = burst_size >> 3;
+               else
+                       burst_size = burst_size >> 2;
+               _ipu_smfc_set_burst_size(ipu, channel, burst_size-1);
+       }
+
+       switch (dma_chan) {
+       case 0:
+       case 1:
+       case 2:
+       case 3:
+               _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch0123_axi);
+               break;
+       case 23:
+               _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch23_axi);
+               break;
+       case 27:
+               _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch27_axi);
+               break;
+       case 28:
+               _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch28_axi);
+               break;
+       default:
+               _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->normal_axi);
+               break;
+       }
+
+       if (idma_is_set(ipu, IDMAC_CHA_PRI(dma_chan), dma_chan) &&
+           ipu->devtype == IPUv3H) {
+               uint32_t reg = IDMAC_CH_LOCK_EN_1(ipu->devtype);
+               uint32_t value = 0;
+
+               switch (dma_chan) {
+               case 5:
+                       value = 0x3;
+                       break;
+               case 11:
+                       value = 0x3 << 2;
+                       break;
+               case 12:
+                       value = 0x3 << 4;
+                       break;
+               case 14:
+                       value = 0x3 << 6;
+                       break;
+               case 15:
+                       value = 0x3 << 8;
+                       break;
+               case 20:
+                       value = 0x3 << 10;
+                       break;
+               case 21:
+                       value = 0x3 << 12;
+                       break;
+               case 22:
+                       value = 0x3 << 14;
+                       break;
+               case 23:
+                       value = 0x3 << 16;
+                       break;
+               case 27:
+                       value = 0x3 << 18;
+                       break;
+               case 28:
+                       value = 0x3 << 20;
+                       break;
+               case 45:
+                       reg = IDMAC_CH_LOCK_EN_2(ipu->devtype);
+                       value = 0x3 << 0;
+                       break;
+               case 46:
+                       reg = IDMAC_CH_LOCK_EN_2(ipu->devtype);
+                       value = 0x3 << 2;
+                       break;
+               case 47:
+                       reg = IDMAC_CH_LOCK_EN_2(ipu->devtype);
+                       value = 0x3 << 4;
+                       break;
+               case 48:
+                       reg = IDMAC_CH_LOCK_EN_2(ipu->devtype);
+                       value = 0x3 << 6;
+                       break;
+               case 49:
+                       reg = IDMAC_CH_LOCK_EN_2(ipu->devtype);
+                       value = 0x3 << 8;
+                       break;
+               case 50:
+                       reg = IDMAC_CH_LOCK_EN_2(ipu->devtype);
+                       value = 0x3 << 10;
+                       break;
+               default:
+                       break;
+               }
+               value |= ipu_idmac_read(ipu, reg);
+               ipu_idmac_write(ipu, value, reg);
+       }
+
+       _ipu_ch_param_dump(ipu, dma_chan);
+
+       if (phyaddr_2 && ipu->devtype >= IPUv3EX) {
+               reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(dma_chan));
+               reg &= ~idma_mask(dma_chan);
+               ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(dma_chan));
+
+               reg = ipu_cm_read(ipu,
+                               IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan));
+               reg |= idma_mask(dma_chan);
+               ipu_cm_write(ipu, reg,
+                               IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan));
+
+               /* Set IDMAC third buffer's cpmem number */
+               /* See __ipu_ch_get_third_buf_cpmem_num() for mapping */
+               ipu_idmac_write(ipu, 0x00444047L,
+                               IDMAC_SUB_ADDR_4(ipu->devtype));
+               ipu_idmac_write(ipu, 0x46004241L,
+                               IDMAC_SUB_ADDR_3(ipu->devtype));
+               ipu_idmac_write(ipu, 0x00000045L,
+                               IDMAC_SUB_ADDR_1(ipu->devtype));
+
+               /* Reset to buffer 0 */
+               ipu_cm_write(ipu, tri_cur_buf_mask(dma_chan),
+                               IPU_CHA_TRIPLE_CUR_BUF(ipu->devtype, dma_chan));
+       } else {
+               reg = ipu_cm_read(ipu,
+                               IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan));
+               reg &= ~idma_mask(dma_chan);
+               ipu_cm_write(ipu, reg,
+                               IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan));
+
+               reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(dma_chan));
+               if (phyaddr_1)
+                       reg |= idma_mask(dma_chan);
+               else
+                       reg &= ~idma_mask(dma_chan);
+               ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(dma_chan));
+
+               /* Reset to buffer 0 */
+               ipu_cm_write(ipu, idma_mask(dma_chan),
+                               IPU_CHA_CUR_BUF(ipu->devtype, dma_chan));
+
+       }
+
+       mutex_unlock(&ipu->mutex_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_init_channel_buffer);
+
+/*!
+ * This function is called to update the physical address of a buffer for
+ * a logical IPU channel.
+ *
+ * @param      ipu             ipu handler
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to initialize.
+ *
+ * @param       bufNum          Input parameter for buffer number to update.
+ *                              0 or 1 are the only valid values.
+ *
+ * @param       phyaddr         Input parameter buffer physical address.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail. This function will fail if the buffer is set to ready.
+ */
+int32_t ipu_update_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel,
+                               ipu_buffer_t type, uint32_t bufNum, dma_addr_t phyaddr)
+{
+       uint32_t reg;
+       int ret = 0;
+       uint32_t dma_chan = channel_2_dma(channel, type);
+       unsigned long lock_flags;
+
+       if (dma_chan == IDMA_CHAN_INVALID)
+               return -EINVAL;
+
+       spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags);
+       if (bufNum == 0)
+               reg = ipu_cm_read(ipu,
+                               IPU_CHA_BUF0_RDY(ipu->devtype, dma_chan));
+       else if (bufNum == 1)
+               reg = ipu_cm_read(ipu,
+                               IPU_CHA_BUF1_RDY(ipu->devtype, dma_chan));
+       else
+               reg = ipu_cm_read(ipu,
+                               IPU_CHA_BUF2_RDY(ipu->devtype, dma_chan));
+
+       if ((reg & idma_mask(dma_chan)) == 0)
+               _ipu_ch_param_set_buffer(ipu, dma_chan, bufNum, phyaddr);
+       else
+               ret = -EACCES;
+       spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(ipu_update_channel_buffer);
+
+/*!
+ * This function is called to update the band mode setting for
+ * a logical IPU channel.
+ *
+ * @param      ipu             ipu handler
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to initialize.
+ *
+ * @param       band_height     Input parameter for band lines:
+ *                             shoule be log2(4/8/16/32/64/128/256).
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_set_channel_bandmode(struct ipu_soc *ipu, ipu_channel_t channel,
+                                ipu_buffer_t type, uint32_t band_height)
+{
+       uint32_t reg;
+       int ret = 0;
+       uint32_t dma_chan = channel_2_dma(channel, type);
+
+       if ((2 > band_height) || (8 < band_height))
+               return -EINVAL;
+
+       mutex_lock(&ipu->mutex_lock);
+
+       reg = ipu_idmac_read(ipu, IDMAC_BAND_EN(ipu->devtype, dma_chan));
+       reg |= 1 << (dma_chan % 32);
+       ipu_idmac_write(ipu, reg, IDMAC_BAND_EN(ipu->devtype, dma_chan));
+
+       _ipu_ch_param_set_bandmode(ipu, dma_chan, band_height);
+       dev_dbg(ipu->dev, "dma_chan:%d, band_height:%d.\n\n",
+                               dma_chan, 1 << band_height);
+       mutex_unlock(&ipu->mutex_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(ipu_set_channel_bandmode);
+
+/*!
+ * This function is called to initialize a buffer for logical IPU channel.
+ *
+ * @param      ipu             ipu handler
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to initialize.
+ *
+ * @param       pixel_fmt       Input parameter for pixel format of buffer.
+ *                              Pixel format is a FOURCC ASCII code.
+ *
+ * @param       width           Input parameter for width of buffer in pixels.
+ *
+ * @param       height          Input parameter for height of buffer in pixels.
+ *
+ * @param       stride          Input parameter for stride length of buffer
+ *                              in pixels.
+ *
+ * @param       u              predefined private u offset for additional cropping,
+ *                                                             zero if not used.
+ *
+ * @param       v              predefined private v offset for additional cropping,
+ *                                                             zero if not used.
+ *
+ * @param                      vertical_offset vertical offset for Y coordinate
+ *                                                             in the existed frame
+ *
+ *
+ * @param                      horizontal_offset horizontal offset for X coordinate
+ *                                                             in the existed frame
+ *
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ *              This function will fail if any buffer is set to ready.
+ */
+
+int32_t ipu_update_channel_offset(struct ipu_soc *ipu,
+                               ipu_channel_t channel, ipu_buffer_t type,
+                               uint32_t pixel_fmt,
+                               uint16_t width, uint16_t height,
+                               uint32_t stride,
+                               uint32_t u, uint32_t v,
+                               uint32_t vertical_offset, uint32_t horizontal_offset)
+{
+       int ret = 0;
+       uint32_t dma_chan = channel_2_dma(channel, type);
+       unsigned long lock_flags;
+
+       if (dma_chan == IDMA_CHAN_INVALID)
+               return -EINVAL;
+
+       spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags);
+       if ((ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(ipu->devtype, dma_chan)) &
+            idma_mask(dma_chan)) ||
+           (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(ipu->devtype, dma_chan)) &
+            idma_mask(dma_chan)) ||
+           ((ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(ipu->devtype, dma_chan)) &
+            idma_mask(dma_chan)) &&
+            (ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan)) &
+             idma_mask(dma_chan)) && _ipu_is_trb_chan(ipu, dma_chan)))
+               ret = -EACCES;
+       else
+               _ipu_ch_offset_update(ipu, dma_chan, pixel_fmt, width, height, stride,
+                                     u, v, 0, vertical_offset, horizontal_offset);
+       spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(ipu_update_channel_offset);
+
+int32_t ipu_get_channel_offset(uint32_t pixel_fmt,
+                              uint16_t width, uint16_t height,
+                              uint32_t stride,
+                              uint32_t u, uint32_t v,
+                              uint32_t vertical_offset, uint32_t horizontal_offset,
+                              uint32_t *u_offset, uint32_t *v_offset)
+{
+       return __ipu_ch_offset_calc(pixel_fmt, width, height, stride,
+                                   u, v, 0,
+                                   vertical_offset, horizontal_offset,
+                                   u_offset, v_offset);
+}
+EXPORT_SYMBOL(ipu_get_channel_offset);
+
+/*!
+ * This function is called to set a channel's buffer as ready.
+ *
+ * @param      ipu             ipu handler
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to initialize.
+ *
+ * @param       bufNum          Input parameter for which buffer number set to
+ *                              ready state.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_select_buffer(struct ipu_soc *ipu, ipu_channel_t channel,
+                       ipu_buffer_t type, uint32_t bufNum)
+{
+       uint32_t dma_chan = channel_2_dma(channel, type);
+       unsigned long lock_flags;
+
+       if (dma_chan == IDMA_CHAN_INVALID)
+               return -EINVAL;
+
+       spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags);
+       /* Mark buffer to be ready. */
+       if (bufNum == 0)
+               ipu_cm_write(ipu, idma_mask(dma_chan),
+                            IPU_CHA_BUF0_RDY(ipu->devtype, dma_chan));
+       else if (bufNum == 1)
+               ipu_cm_write(ipu, idma_mask(dma_chan),
+                            IPU_CHA_BUF1_RDY(ipu->devtype, dma_chan));
+       else
+               ipu_cm_write(ipu, idma_mask(dma_chan),
+                            IPU_CHA_BUF2_RDY(ipu->devtype, dma_chan));
+       spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_select_buffer);
+
+/*!
+ * This function is called to set a channel's buffer as ready.
+ *
+ * @param      ipu             ipu handler
+ * @param       bufNum          Input parameter for which buffer number set to
+ *                              ready state.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_select_multi_vdi_buffer(struct ipu_soc *ipu, uint32_t bufNum)
+{
+
+       uint32_t dma_chan = channel_2_dma(MEM_VDI_PRP_VF_MEM, IPU_INPUT_BUFFER);
+       uint32_t mask_bit =
+               idma_mask(channel_2_dma(MEM_VDI_PRP_VF_MEM_P, IPU_INPUT_BUFFER))|
+               idma_mask(dma_chan)|
+               idma_mask(channel_2_dma(MEM_VDI_PRP_VF_MEM_N, IPU_INPUT_BUFFER));
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags);
+       /* Mark buffers to be ready. */
+       if (bufNum == 0)
+               ipu_cm_write(ipu, mask_bit,
+                               IPU_CHA_BUF0_RDY(ipu->devtype, dma_chan));
+       else
+               ipu_cm_write(ipu, mask_bit,
+                               IPU_CHA_BUF1_RDY(ipu->devtype, dma_chan));
+       spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_select_multi_vdi_buffer);
+
+#define NA     -1
+static int proc_dest_sel[] = {
+       0, 1, 1, 3, 5, 5, 4, 7, 8, 9, 10, 11, 12, 14, 15, 16,
+       0, 1, 1, 5, 5, 5, 5, 5, 7, 8, 9, 10, 11, 12, 14, 31 };
+static int proc_src_sel[] = { 0, 6, 7, 6, 7, 8, 5, NA, NA, NA,
+  NA, NA, NA, NA, NA,  1,  2,  3,  4,  7,  8, NA, 8, NA };
+static int disp_src_sel[] = { 0, 6, 7, 8, 3, 4, 5, NA, NA, NA,
+  NA, NA, NA, NA, NA,  1, NA,  2, NA,  3,  4,  4,  4,  4 };
+
+
+/*!
+ * This function links 2 channels together for automatic frame
+ * synchronization. The output of the source channel is linked to the input of
+ * the destination channel.
+ *
+ * @param      ipu             ipu handler
+ * @param       src_ch          Input parameter for the logical channel ID of
+ *                              the source channel.
+ *
+ * @param       dest_ch         Input parameter for the logical channel ID of
+ *                              the destination channel.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_link_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel_t dest_ch)
+{
+       int retval = 0;
+       uint32_t fs_proc_flow1;
+       uint32_t fs_proc_flow2;
+       uint32_t fs_proc_flow3;
+       uint32_t fs_disp_flow1;
+
+       mutex_lock(&ipu->mutex_lock);
+
+       fs_proc_flow1 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+       fs_proc_flow2 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW2);
+       fs_proc_flow3 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW3);
+       fs_disp_flow1 = ipu_cm_read(ipu, IPU_FS_DISP_FLOW1);
+
+       switch (src_ch) {
+       case CSI_MEM0:
+               fs_proc_flow3 &= ~FS_SMFC0_DEST_SEL_MASK;
+               fs_proc_flow3 |=
+                       proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                       FS_SMFC0_DEST_SEL_OFFSET;
+               break;
+       case CSI_MEM1:
+               fs_proc_flow3 &= ~FS_SMFC1_DEST_SEL_MASK;
+               fs_proc_flow3 |=
+                       proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                       FS_SMFC1_DEST_SEL_OFFSET;
+               break;
+       case CSI_MEM2:
+               fs_proc_flow3 &= ~FS_SMFC2_DEST_SEL_MASK;
+               fs_proc_flow3 |=
+                       proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                       FS_SMFC2_DEST_SEL_OFFSET;
+               break;
+       case CSI_MEM3:
+               fs_proc_flow3 &= ~FS_SMFC3_DEST_SEL_MASK;
+               fs_proc_flow3 |=
+                       proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                       FS_SMFC3_DEST_SEL_OFFSET;
+               break;
+       case CSI_PRP_ENC_MEM:
+               fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                       proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                       FS_PRPENC_DEST_SEL_OFFSET;
+               break;
+       case CSI_PRP_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                       proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                       FS_PRPVF_DEST_SEL_OFFSET;
+               break;
+       case MEM_PP_MEM:
+               fs_proc_flow2 &= ~FS_PP_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PP_DEST_SEL_OFFSET;
+               break;
+       case MEM_ROT_PP_MEM:
+               fs_proc_flow2 &= ~FS_PP_ROT_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PP_ROT_DEST_SEL_OFFSET;
+               break;
+       case MEM_PRP_ENC_MEM:
+               fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PRPENC_DEST_SEL_OFFSET;
+               break;
+       case MEM_ROT_ENC_MEM:
+               fs_proc_flow2 &= ~FS_PRPENC_ROT_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PRPENC_ROT_DEST_SEL_OFFSET;
+               break;
+       case MEM_PRP_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PRPVF_DEST_SEL_OFFSET;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PRPVF_DEST_SEL_OFFSET;
+               break;
+       case MEM_ROT_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_ROT_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PRPVF_ROT_DEST_SEL_OFFSET;
+               break;
+       case MEM_VDOA_MEM:
+               fs_proc_flow3 &= ~FS_VDOA_DEST_SEL_MASK;
+               if (MEM_VDI_MEM == dest_ch)
+                       fs_proc_flow3 |= FS_VDOA_DEST_SEL_VDI;
+               else if (MEM_PP_MEM == dest_ch)
+                       fs_proc_flow3 |= FS_VDOA_DEST_SEL_IC;
+               else {
+                       retval = -EINVAL;
+                       goto err;
+               }
+               break;
+       default:
+               retval = -EINVAL;
+               goto err;
+       }
+
+       switch (dest_ch) {
+       case MEM_PP_MEM:
+               fs_proc_flow1 &= ~FS_PP_SRC_SEL_MASK;
+               if (MEM_VDOA_MEM == src_ch)
+                       fs_proc_flow1 |= FS_PP_SRC_SEL_VDOA;
+               else
+                       fs_proc_flow1 |= proc_src_sel[IPU_CHAN_ID(src_ch)] <<
+                                               FS_PP_SRC_SEL_OFFSET;
+               break;
+       case MEM_ROT_PP_MEM:
+               fs_proc_flow1 &= ~FS_PP_ROT_SRC_SEL_MASK;
+               fs_proc_flow1 |=
+                   proc_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_PP_ROT_SRC_SEL_OFFSET;
+               break;
+       case MEM_PRP_ENC_MEM:
+               fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+               fs_proc_flow1 |=
+                   proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET;
+               break;
+       case MEM_ROT_ENC_MEM:
+               fs_proc_flow1 &= ~FS_PRPENC_ROT_SRC_SEL_MASK;
+               fs_proc_flow1 |=
+                   proc_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_PRPENC_ROT_SRC_SEL_OFFSET;
+               break;
+       case MEM_PRP_VF_MEM:
+               fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+               fs_proc_flow1 |=
+                   proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+               fs_proc_flow1 |=
+                   proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET;
+               break;
+       case MEM_ROT_VF_MEM:
+               fs_proc_flow1 &= ~FS_PRPVF_ROT_SRC_SEL_MASK;
+               fs_proc_flow1 |=
+                   proc_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_PRPVF_ROT_SRC_SEL_OFFSET;
+               break;
+       case MEM_DC_SYNC:
+               fs_disp_flow1 &= ~FS_DC1_SRC_SEL_MASK;
+               fs_disp_flow1 |=
+                   disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DC1_SRC_SEL_OFFSET;
+               break;
+       case MEM_BG_SYNC:
+               fs_disp_flow1 &= ~FS_DP_SYNC0_SRC_SEL_MASK;
+               fs_disp_flow1 |=
+                   disp_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_DP_SYNC0_SRC_SEL_OFFSET;
+               break;
+       case MEM_FG_SYNC:
+               fs_disp_flow1 &= ~FS_DP_SYNC1_SRC_SEL_MASK;
+               fs_disp_flow1 |=
+                   disp_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_DP_SYNC1_SRC_SEL_OFFSET;
+               break;
+       case MEM_DC_ASYNC:
+               fs_disp_flow1 &= ~FS_DC2_SRC_SEL_MASK;
+               fs_disp_flow1 |=
+                   disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DC2_SRC_SEL_OFFSET;
+               break;
+       case MEM_BG_ASYNC0:
+               fs_disp_flow1 &= ~FS_DP_ASYNC0_SRC_SEL_MASK;
+               fs_disp_flow1 |=
+                   disp_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_DP_ASYNC0_SRC_SEL_OFFSET;
+               break;
+       case MEM_FG_ASYNC0:
+               fs_disp_flow1 &= ~FS_DP_ASYNC1_SRC_SEL_MASK;
+               fs_disp_flow1 |=
+                   disp_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_DP_ASYNC1_SRC_SEL_OFFSET;
+               break;
+       case MEM_VDI_MEM:
+               fs_proc_flow1 &= ~FS_VDI_SRC_SEL_MASK;
+               if (MEM_VDOA_MEM == src_ch)
+                       fs_proc_flow1 |= FS_VDI_SRC_SEL_VDOA;
+               else {
+                       retval = -EINVAL;
+                       goto err;
+               }
+               break;
+       default:
+               retval = -EINVAL;
+               goto err;
+       }
+
+       ipu_cm_write(ipu, fs_proc_flow1, IPU_FS_PROC_FLOW1);
+       ipu_cm_write(ipu, fs_proc_flow2, IPU_FS_PROC_FLOW2);
+       ipu_cm_write(ipu, fs_proc_flow3, IPU_FS_PROC_FLOW3);
+       ipu_cm_write(ipu, fs_disp_flow1, IPU_FS_DISP_FLOW1);
+
+err:
+       mutex_unlock(&ipu->mutex_lock);
+       return retval;
+}
+EXPORT_SYMBOL(ipu_link_channels);
+
+/*!
+ * This function unlinks 2 channels and disables automatic frame
+ * synchronization.
+ *
+ * @param      ipu             ipu handler
+ * @param       src_ch          Input parameter for the logical channel ID of
+ *                              the source channel.
+ *
+ * @param       dest_ch         Input parameter for the logical channel ID of
+ *                              the destination channel.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_unlink_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel_t dest_ch)
+{
+       int retval = 0;
+       uint32_t fs_proc_flow1;
+       uint32_t fs_proc_flow2;
+       uint32_t fs_proc_flow3;
+       uint32_t fs_disp_flow1;
+
+       mutex_lock(&ipu->mutex_lock);
+
+       fs_proc_flow1 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+       fs_proc_flow2 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW2);
+       fs_proc_flow3 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW3);
+       fs_disp_flow1 = ipu_cm_read(ipu, IPU_FS_DISP_FLOW1);
+
+       switch (src_ch) {
+       case CSI_MEM0:
+               fs_proc_flow3 &= ~FS_SMFC0_DEST_SEL_MASK;
+               break;
+       case CSI_MEM1:
+               fs_proc_flow3 &= ~FS_SMFC1_DEST_SEL_MASK;
+               break;
+       case CSI_MEM2:
+               fs_proc_flow3 &= ~FS_SMFC2_DEST_SEL_MASK;
+               break;
+       case CSI_MEM3:
+               fs_proc_flow3 &= ~FS_SMFC3_DEST_SEL_MASK;
+               break;
+       case CSI_PRP_ENC_MEM:
+               fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK;
+               break;
+       case CSI_PRP_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+               break;
+       case MEM_PP_MEM:
+               fs_proc_flow2 &= ~FS_PP_DEST_SEL_MASK;
+               break;
+       case MEM_ROT_PP_MEM:
+               fs_proc_flow2 &= ~FS_PP_ROT_DEST_SEL_MASK;
+               break;
+       case MEM_PRP_ENC_MEM:
+               fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK;
+               break;
+       case MEM_ROT_ENC_MEM:
+               fs_proc_flow2 &= ~FS_PRPENC_ROT_DEST_SEL_MASK;
+               break;
+       case MEM_PRP_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+               break;
+       case MEM_ROT_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_ROT_DEST_SEL_MASK;
+               break;
+       case MEM_VDOA_MEM:
+               fs_proc_flow3 &= ~FS_VDOA_DEST_SEL_MASK;
+               break;
+       default:
+               retval = -EINVAL;
+               goto err;
+       }
+
+       switch (dest_ch) {
+       case MEM_PP_MEM:
+               fs_proc_flow1 &= ~FS_PP_SRC_SEL_MASK;
+               break;
+       case MEM_ROT_PP_MEM:
+               fs_proc_flow1 &= ~FS_PP_ROT_SRC_SEL_MASK;
+               break;
+       case MEM_PRP_ENC_MEM:
+               fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+               break;
+       case MEM_ROT_ENC_MEM:
+               fs_proc_flow1 &= ~FS_PRPENC_ROT_SRC_SEL_MASK;
+               break;
+       case MEM_PRP_VF_MEM:
+               fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+               break;
+       case MEM_ROT_VF_MEM:
+               fs_proc_flow1 &= ~FS_PRPVF_ROT_SRC_SEL_MASK;
+               break;
+       case MEM_DC_SYNC:
+               fs_disp_flow1 &= ~FS_DC1_SRC_SEL_MASK;
+               break;
+       case MEM_BG_SYNC:
+               fs_disp_flow1 &= ~FS_DP_SYNC0_SRC_SEL_MASK;
+               break;
+       case MEM_FG_SYNC:
+               fs_disp_flow1 &= ~FS_DP_SYNC1_SRC_SEL_MASK;
+               break;
+       case MEM_DC_ASYNC:
+               fs_disp_flow1 &= ~FS_DC2_SRC_SEL_MASK;
+               break;
+       case MEM_BG_ASYNC0:
+               fs_disp_flow1 &= ~FS_DP_ASYNC0_SRC_SEL_MASK;
+               break;
+       case MEM_FG_ASYNC0:
+               fs_disp_flow1 &= ~FS_DP_ASYNC1_SRC_SEL_MASK;
+               break;
+       case MEM_VDI_MEM:
+               fs_proc_flow1 &= ~FS_VDI_SRC_SEL_MASK;
+               break;
+       default:
+               retval = -EINVAL;
+               goto err;
+       }
+
+       ipu_cm_write(ipu, fs_proc_flow1, IPU_FS_PROC_FLOW1);
+       ipu_cm_write(ipu, fs_proc_flow2, IPU_FS_PROC_FLOW2);
+       ipu_cm_write(ipu, fs_proc_flow3, IPU_FS_PROC_FLOW3);
+       ipu_cm_write(ipu, fs_disp_flow1, IPU_FS_DISP_FLOW1);
+
+err:
+       mutex_unlock(&ipu->mutex_lock);
+       return retval;
+}
+EXPORT_SYMBOL(ipu_unlink_channels);
+
+/*!
+ * This function check whether a logical channel was enabled.
+ *
+ * @param      ipu             ipu handler
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @return      This function returns 1 while request channel is enabled or
+ *              0 for not enabled.
+ */
+int32_t ipu_is_channel_busy(struct ipu_soc *ipu, ipu_channel_t channel)
+{
+       uint32_t reg;
+       uint32_t in_dma;
+       uint32_t out_dma;
+
+       out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+       in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
+
+       reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(in_dma));
+       if (reg & idma_mask(in_dma))
+               return 1;
+       reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(out_dma));
+       if (reg & idma_mask(out_dma))
+               return 1;
+       return 0;
+}
+EXPORT_SYMBOL(ipu_is_channel_busy);
+
+/*!
+ * This function enables a logical channel.
+ *
+ * @param      ipu             ipu handler
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_enable_channel(struct ipu_soc *ipu, ipu_channel_t channel)
+{
+       uint32_t reg;
+       uint32_t ipu_conf;
+       uint32_t in_dma;
+       uint32_t out_dma;
+       uint32_t sec_dma;
+       uint32_t thrd_dma;
+
+       mutex_lock(&ipu->mutex_lock);
+
+       if (ipu->channel_enable_mask & (1L << IPU_CHAN_ID(channel))) {
+               dev_err(ipu->dev, "Warning: channel already enabled %d\n",
+                       IPU_CHAN_ID(channel));
+               mutex_unlock(&ipu->mutex_lock);
+               return -EACCES;
+       }
+
+       /* Get input and output dma channels */
+       out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+       in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
+
+       ipu_conf = ipu_cm_read(ipu, IPU_CONF);
+       if (ipu->di_use_count[0] > 0) {
+               ipu_conf |= IPU_CONF_DI0_EN;
+       }
+       if (ipu->di_use_count[1] > 0) {
+               ipu_conf |= IPU_CONF_DI1_EN;
+       }
+       if (ipu->dp_use_count > 0)
+               ipu_conf |= IPU_CONF_DP_EN;
+       if (ipu->dc_use_count > 0)
+               ipu_conf |= IPU_CONF_DC_EN;
+       if (ipu->dmfc_use_count > 0)
+               ipu_conf |= IPU_CONF_DMFC_EN;
+       if (ipu->ic_use_count > 0)
+               ipu_conf |= IPU_CONF_IC_EN;
+       if (ipu->vdi_use_count > 0) {
+               ipu_conf |= IPU_CONF_ISP_EN;
+               ipu_conf |= IPU_CONF_VDI_EN;
+               ipu_conf |= IPU_CONF_IC_INPUT;
+       }
+       if (ipu->rot_use_count > 0)
+               ipu_conf |= IPU_CONF_ROT_EN;
+       if (ipu->smfc_use_count > 0)
+               ipu_conf |= IPU_CONF_SMFC_EN;
+       ipu_cm_write(ipu, ipu_conf, IPU_CONF);
+
+       if (idma_is_valid(in_dma)) {
+               reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(in_dma));
+               ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma));
+       }
+       if (idma_is_valid(out_dma)) {
+               reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(out_dma));
+               ipu_idmac_write(ipu, reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma));
+       }
+
+       if ((ipu->sec_chan_en[IPU_CHAN_ID(channel)]) &&
+               ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM) ||
+                (channel == MEM_VDI_PRP_VF_MEM))) {
+               sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+               reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(sec_dma));
+               ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma));
+       }
+       if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) &&
+               ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM))) {
+               thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
+               reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma));
+               ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
+
+               sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+               reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA);
+               ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_SEP_ALPHA);
+       } else if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) &&
+                  ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC))) {
+               thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
+               reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma));
+               ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
+               reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA);
+               ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_SEP_ALPHA);
+       }
+
+       if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) ||
+           (channel == MEM_FG_SYNC)) {
+               reg = ipu_idmac_read(ipu, IDMAC_WM_EN(in_dma));
+               ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_WM_EN(in_dma));
+
+               _ipu_dp_dc_enable(ipu, channel);
+       }
+
+       if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) ||
+               _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma) ||
+               _ipu_is_vdi_out_chan(out_dma))
+               _ipu_ic_enable_task(ipu, channel);
+
+       ipu->channel_enable_mask |= 1L << IPU_CHAN_ID(channel);
+
+       if (ipu->prg_clk)
+               clk_prepare_enable(ipu->prg_clk);
+
+       mutex_unlock(&ipu->mutex_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_enable_channel);
+
+/*!
+ * This function check buffer ready for a logical channel.
+ *
+ * @param      ipu             ipu handler
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to clear.
+ *
+ * @param       bufNum          Input parameter for which buffer number clear
+ *                             ready state.
+ *
+ */
+int32_t ipu_check_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type,
+               uint32_t bufNum)
+{
+       uint32_t dma_chan = channel_2_dma(channel, type);
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       if (dma_chan == IDMA_CHAN_INVALID)
+               return -EINVAL;
+
+       spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags);
+       if (bufNum == 0)
+               reg = ipu_cm_read(ipu,
+                               IPU_CHA_BUF0_RDY(ipu->devtype, dma_chan));
+       else if (bufNum == 1)
+               reg = ipu_cm_read(ipu,
+                               IPU_CHA_BUF1_RDY(ipu->devtype, dma_chan));
+       else
+               reg = ipu_cm_read(ipu,
+                               IPU_CHA_BUF2_RDY(ipu->devtype, dma_chan));
+       spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags);
+
+       if (reg & idma_mask(dma_chan))
+               return 1;
+       else
+               return 0;
+}
+EXPORT_SYMBOL(ipu_check_buffer_ready);
+
+/*!
+ * This function clear buffer ready for a logical channel.
+ *
+ * @param      ipu             ipu handler
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to clear.
+ *
+ * @param       bufNum          Input parameter for which buffer number clear
+ *                             ready state.
+ *
+ */
+void _ipu_clear_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type,
+               uint32_t bufNum)
+{
+       uint32_t dma_ch = channel_2_dma(channel, type);
+
+       if (!idma_is_valid(dma_ch))
+               return;
+
+       ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */
+       if (bufNum == 0)
+               ipu_cm_write(ipu, idma_mask(dma_ch),
+                               IPU_CHA_BUF0_RDY(ipu->devtype, dma_ch));
+       else if (bufNum == 1)
+               ipu_cm_write(ipu, idma_mask(dma_ch),
+                               IPU_CHA_BUF1_RDY(ipu->devtype, dma_ch));
+       else
+               ipu_cm_write(ipu, idma_mask(dma_ch),
+                               IPU_CHA_BUF2_RDY(ipu->devtype, dma_ch));
+       ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
+}
+
+void ipu_clear_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type,
+               uint32_t bufNum)
+{
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags);
+       _ipu_clear_buffer_ready(ipu, channel, type, bufNum);
+       spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags);
+}
+EXPORT_SYMBOL(ipu_clear_buffer_ready);
+
+/*!
+ * This function disables a logical channel.
+ *
+ * @param      ipu             ipu handler
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       wait_for_stop   Flag to set whether to wait for channel end
+ *                              of frame or return immediately.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_disable_channel(struct ipu_soc *ipu, ipu_channel_t channel, bool wait_for_stop)
+{
+       uint32_t reg;
+       uint32_t in_dma;
+       uint32_t out_dma;
+       uint32_t sec_dma = NO_DMA;
+       uint32_t thrd_dma = NO_DMA;
+       uint16_t fg_pos_x, fg_pos_y;
+       unsigned long lock_flags;
+
+       mutex_lock(&ipu->mutex_lock);
+
+       if ((ipu->channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) {
+               dev_dbg(ipu->dev, "Channel already disabled %d\n",
+                       IPU_CHAN_ID(channel));
+               mutex_unlock(&ipu->mutex_lock);
+               return -EACCES;
+       }
+
+       /* Get input and output dma channels */
+       out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+       in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
+
+       if ((idma_is_valid(in_dma) &&
+               !idma_is_set(ipu, IDMAC_CHA_EN(in_dma), in_dma))
+               && (idma_is_valid(out_dma) &&
+               !idma_is_set(ipu, IDMAC_CHA_EN(out_dma), out_dma))) {
+               mutex_unlock(&ipu->mutex_lock);
+               return -EINVAL;
+       }
+
+       if (ipu->sec_chan_en[IPU_CHAN_ID(channel)])
+               sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+       if (ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) {
+               sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+               thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
+       }
+
+       if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) ||
+           (channel == MEM_DC_SYNC)) {
+               if (channel == MEM_FG_SYNC) {
+                       _ipu_disp_get_window_pos(ipu, channel, &fg_pos_x, &fg_pos_y);
+                       _ipu_disp_set_window_pos(ipu, channel, 0, 0);
+               }
+
+               _ipu_dp_dc_disable(ipu, channel, false);
+
+               /*
+                * wait for BG channel EOF then disable FG-IDMAC,
+                * it avoid FG NFB4EOF error.
+                */
+               if ((channel == MEM_FG_SYNC) && (ipu_is_channel_busy(ipu, MEM_BG_SYNC))) {
+                       int timeout = 50;
+
+                       ipu_cm_write(ipu, IPUIRQ_2_MASK(IPU_IRQ_BG_SYNC_EOF),
+                                       IPUIRQ_2_STATREG(ipu->devtype,
+                                                       IPU_IRQ_BG_SYNC_EOF));
+                       while ((ipu_cm_read(ipu,
+                               IPUIRQ_2_STATREG(ipu->devtype,
+                               IPU_IRQ_BG_SYNC_EOF)) &
+                               IPUIRQ_2_MASK(IPU_IRQ_BG_SYNC_EOF)) == 0) {
+                               msleep(10);
+                               timeout -= 10;
+                               if (timeout <= 0) {
+                                       dev_err(ipu->dev, "warning: wait for bg sync eof timeout\n");
+                                       break;
+                               }
+                       }
+               }
+       } else if (wait_for_stop && !_ipu_is_smfc_chan(out_dma) &&
+                  channel != CSI_PRP_VF_MEM && channel != CSI_PRP_ENC_MEM) {
+               while (idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype, in_dma),
+                                       in_dma) ||
+                      idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype, out_dma),
+                                       out_dma) ||
+                       (ipu->sec_chan_en[IPU_CHAN_ID(channel)] &&
+                       idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype, sec_dma),
+                                       sec_dma)) ||
+                       (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] &&
+                       idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype, thrd_dma),
+                                       thrd_dma))) {
+                       uint32_t irq = 0xffffffff;
+                       int timeout = 50000;
+
+                       if (idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype,
+                                       out_dma), out_dma))
+                               irq = out_dma;
+                       if (ipu->sec_chan_en[IPU_CHAN_ID(channel)] &&
+                               idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype,
+                                               sec_dma), sec_dma))
+                               irq = sec_dma;
+                       if (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] &&
+                               idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype,
+                                               thrd_dma), thrd_dma))
+                               irq = thrd_dma;
+                       if (idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype,
+                                               in_dma), in_dma))
+                               irq = in_dma;
+
+                       if (irq == 0xffffffff) {
+                               dev_dbg(ipu->dev, "warning: no channel busy, break\n");
+                               break;
+                       }
+
+                       ipu_cm_write(ipu, IPUIRQ_2_MASK(irq),
+                                       IPUIRQ_2_STATREG(ipu->devtype, irq));
+
+                       dev_dbg(ipu->dev, "warning: channel %d busy, need wait\n", irq);
+
+                       while (((ipu_cm_read(ipu,
+                                       IPUIRQ_2_STATREG(ipu->devtype, irq))
+                               & IPUIRQ_2_MASK(irq)) == 0) &&
+                               (idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype,
+                                                       irq), irq))) {
+                               udelay(10);
+                               timeout -= 10;
+                               if (timeout <= 0) {
+                                       ipu_dump_registers(ipu);
+                                       dev_err(ipu->dev, "warning: disable ipu dma channel %d during its busy state\n", irq);
+                                       break;
+                               }
+                       }
+                       dev_dbg(ipu->dev, "wait_time:%d\n", 50000 - timeout);
+
+               }
+       }
+
+       if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) ||
+           (channel == MEM_DC_SYNC)) {
+               reg = ipu_idmac_read(ipu, IDMAC_WM_EN(in_dma));
+               ipu_idmac_write(ipu, reg & ~idma_mask(in_dma), IDMAC_WM_EN(in_dma));
+       }
+
+       /* Disable IC task */
+       if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) ||
+               _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma) ||
+               _ipu_is_vdi_out_chan(out_dma))
+               _ipu_ic_disable_task(ipu, channel);
+
+       /* Disable DMA channel(s) */
+       if (idma_is_valid(in_dma)) {
+               reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(in_dma));
+               ipu_idmac_write(ipu, reg & ~idma_mask(in_dma), IDMAC_CHA_EN(in_dma));
+               ipu_cm_write(ipu, idma_mask(in_dma),
+                               IPU_CHA_CUR_BUF(ipu->devtype, in_dma));
+               ipu_cm_write(ipu, tri_cur_buf_mask(in_dma),
+                               IPU_CHA_TRIPLE_CUR_BUF(ipu->devtype, in_dma));
+       }
+       if (idma_is_valid(out_dma)) {
+               reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(out_dma));
+               ipu_idmac_write(ipu, reg & ~idma_mask(out_dma), IDMAC_CHA_EN(out_dma));
+               ipu_cm_write(ipu, idma_mask(out_dma),
+                               IPU_CHA_CUR_BUF(ipu->devtype, out_dma));
+               ipu_cm_write(ipu, tri_cur_buf_mask(out_dma),
+                               IPU_CHA_TRIPLE_CUR_BUF(ipu->devtype, out_dma));
+       }
+       if (ipu->sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(sec_dma)) {
+               reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(sec_dma));
+               ipu_idmac_write(ipu, reg & ~idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma));
+               ipu_cm_write(ipu, idma_mask(sec_dma),
+                               IPU_CHA_CUR_BUF(ipu->devtype, sec_dma));
+       }
+       if (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(thrd_dma)) {
+               reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma));
+               ipu_idmac_write(ipu, reg & ~idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
+               if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) {
+                       reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA);
+                       ipu_idmac_write(ipu, reg & ~idma_mask(in_dma), IDMAC_SEP_ALPHA);
+               } else {
+                       reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA);
+                       ipu_idmac_write(ipu, reg & ~idma_mask(sec_dma), IDMAC_SEP_ALPHA);
+               }
+               ipu_cm_write(ipu, idma_mask(thrd_dma),
+                               IPU_CHA_CUR_BUF(ipu->devtype, thrd_dma));
+       }
+
+       if (channel == MEM_FG_SYNC)
+               _ipu_disp_set_window_pos(ipu, channel, fg_pos_x, fg_pos_y);
+
+       spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags);
+       /* Set channel buffers NOT to be ready */
+       if (idma_is_valid(in_dma)) {
+               _ipu_clear_buffer_ready(ipu, channel, IPU_VIDEO_IN_BUFFER, 0);
+               _ipu_clear_buffer_ready(ipu, channel, IPU_VIDEO_IN_BUFFER, 1);
+               _ipu_clear_buffer_ready(ipu, channel, IPU_VIDEO_IN_BUFFER, 2);
+       }
+       if (idma_is_valid(out_dma)) {
+               _ipu_clear_buffer_ready(ipu, channel, IPU_OUTPUT_BUFFER, 0);
+               _ipu_clear_buffer_ready(ipu, channel, IPU_OUTPUT_BUFFER, 1);
+       }
+       if (ipu->sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(sec_dma)) {
+               _ipu_clear_buffer_ready(ipu, channel, IPU_GRAPH_IN_BUFFER, 0);
+               _ipu_clear_buffer_ready(ipu, channel, IPU_GRAPH_IN_BUFFER, 1);
+       }
+       if (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(thrd_dma)) {
+               _ipu_clear_buffer_ready(ipu, channel, IPU_ALPHA_IN_BUFFER, 0);
+               _ipu_clear_buffer_ready(ipu, channel, IPU_ALPHA_IN_BUFFER, 1);
+       }
+       spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags);
+
+       ipu->channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel));
+
+       if (ipu->prg_clk)
+               clk_disable_unprepare(ipu->prg_clk);
+
+       mutex_unlock(&ipu->mutex_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_disable_channel);
+
+/*!
+ * This function enables CSI.
+ *
+ * @param      ipu             ipu handler
+ * @param       csi    csi num 0 or 1
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_enable_csi(struct ipu_soc *ipu, uint32_t csi)
+{
+       uint32_t reg;
+
+       if (csi > 1) {
+               dev_err(ipu->dev, "Wrong csi num_%d\n", csi);
+               return -EINVAL;
+       }
+
+       _ipu_get(ipu);
+       mutex_lock(&ipu->mutex_lock);
+       ipu->csi_use_count[csi]++;
+
+       if (ipu->csi_use_count[csi] == 1) {
+               reg = ipu_cm_read(ipu, IPU_CONF);
+               if (csi == 0)
+                       ipu_cm_write(ipu, reg | IPU_CONF_CSI0_EN, IPU_CONF);
+               else
+                       ipu_cm_write(ipu, reg | IPU_CONF_CSI1_EN, IPU_CONF);
+       }
+       mutex_unlock(&ipu->mutex_lock);
+       _ipu_put(ipu);
+       return 0;
+}
+EXPORT_SYMBOL(ipu_enable_csi);
+
+/*!
+ * This function disables CSI.
+ *
+ * @param      ipu             ipu handler
+ * @param       csi    csi num 0 or 1
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_disable_csi(struct ipu_soc *ipu, uint32_t csi)
+{
+       uint32_t reg;
+
+       if (csi > 1) {
+               dev_err(ipu->dev, "Wrong csi num_%d\n", csi);
+               return -EINVAL;
+       }
+       _ipu_get(ipu);
+       mutex_lock(&ipu->mutex_lock);
+       ipu->csi_use_count[csi]--;
+       if (ipu->csi_use_count[csi] == 0) {
+               _ipu_csi_wait4eof(ipu, ipu->csi_channel[csi]);
+               reg = ipu_cm_read(ipu, IPU_CONF);
+               if (csi == 0)
+                       ipu_cm_write(ipu, reg & ~IPU_CONF_CSI0_EN, IPU_CONF);
+               else
+                       ipu_cm_write(ipu, reg & ~IPU_CONF_CSI1_EN, IPU_CONF);
+       }
+       mutex_unlock(&ipu->mutex_lock);
+       _ipu_put(ipu);
+       return 0;
+}
+EXPORT_SYMBOL(ipu_disable_csi);
+
+static irqreturn_t ipu_sync_irq_handler(int irq, void *desc)
+{
+       struct ipu_soc *ipu = desc;
+       int i;
+       uint32_t line, bit, int_stat, int_ctrl;
+       irqreturn_t result = IRQ_NONE;
+       const int int_reg[] = { 1, 2, 3, 4, 11, 12, 13, 14, 15, 0 };
+
+       spin_lock(&ipu->int_reg_spin_lock);
+
+       for (i = 0; int_reg[i] != 0; i++) {
+               int_stat = ipu_cm_read(ipu,
+                               IPU_INT_STAT(ipu->devtype, int_reg[i]));
+               int_ctrl = ipu_cm_read(ipu, IPU_INT_CTRL(int_reg[i]));
+               int_stat &= int_ctrl;
+               ipu_cm_write(ipu, int_stat,
+                               IPU_INT_STAT(ipu->devtype, int_reg[i]));
+               while ((line = ffs(int_stat)) != 0) {
+                       bit = --line;
+                       int_stat &= ~(1UL << line);
+                       line += (int_reg[i] - 1) * 32;
+                       result |=
+                           ipu->irq_list[line].handler(line,
+                                                      ipu->irq_list[line].
+                                                      dev_id);
+                       if (ipu->irq_list[line].flags & IPU_IRQF_ONESHOT) {
+                               int_ctrl &= ~(1UL << bit);
+                               ipu_cm_write(ipu, int_ctrl,
+                                               IPU_INT_CTRL(int_reg[i]));
+                       }
+               }
+       }
+
+       spin_unlock(&ipu->int_reg_spin_lock);
+
+       return result;
+}
+
+static irqreturn_t ipu_err_irq_handler(int irq, void *desc)
+{
+       struct ipu_soc *ipu = desc;
+       int i;
+       uint32_t int_stat;
+       const int err_reg[] = { 5, 6, 9, 10, 0 };
+
+       spin_lock(&ipu->int_reg_spin_lock);
+
+       for (i = 0; err_reg[i] != 0; i++) {
+               int_stat = ipu_cm_read(ipu,
+                               IPU_INT_STAT(ipu->devtype, err_reg[i]));
+               int_stat &= ipu_cm_read(ipu, IPU_INT_CTRL(err_reg[i]));
+               if (int_stat) {
+                       ipu_cm_write(ipu, int_stat,
+                               IPU_INT_STAT(ipu->devtype, err_reg[i]));
+                       dev_warn(ipu->dev,
+                               "IPU Warning - IPU_INT_STAT_%d = 0x%08X\n",
+                               err_reg[i], int_stat);
+                       /* Disable interrupts so we only get error once */
+                       int_stat = ipu_cm_read(ipu, IPU_INT_CTRL(err_reg[i])) &
+                                       ~int_stat;
+                       ipu_cm_write(ipu, int_stat, IPU_INT_CTRL(err_reg[i]));
+               }
+       }
+
+       spin_unlock(&ipu->int_reg_spin_lock);
+
+       return IRQ_HANDLED;
+}
+
+/*!
+ * This function enables the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param      ipu             ipu handler
+ * @param       irq             Interrupt line to enable interrupt for.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int ipu_enable_irq(struct ipu_soc *ipu, uint32_t irq)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+       int ret = 0;
+
+       _ipu_get(ipu);
+
+       spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags);
+
+       /*
+        * Check sync interrupt handler only, since we do nothing for
+        * error interrupts but than print out register values in the
+        * error interrupt source handler.
+        */
+       if (_ipu_is_sync_irq(irq) && (ipu->irq_list[irq].handler == NULL)) {
+               dev_err(ipu->dev, "handler hasn't been registered on sync "
+                                 "irq %d\n", irq);
+               ret = -EACCES;
+               goto out;
+       }
+
+       reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq));
+       reg |= IPUIRQ_2_MASK(irq);
+       ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq));
+out:
+       spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags);
+
+       _ipu_put(ipu);
+
+       return ret;
+}
+EXPORT_SYMBOL(ipu_enable_irq);
+
+/*!
+ * This function disables the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param      ipu             ipu handler
+ * @param       irq             Interrupt line to disable interrupt for.
+ *
+ */
+void ipu_disable_irq(struct ipu_soc *ipu, uint32_t irq)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       _ipu_get(ipu);
+
+       spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags);
+
+       reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq));
+       reg &= ~IPUIRQ_2_MASK(irq);
+       ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq));
+
+       spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags);
+
+       _ipu_put(ipu);
+}
+EXPORT_SYMBOL(ipu_disable_irq);
+
+/*!
+ * This function clears the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param      ipu             ipu handler
+ * @param       irq             Interrupt line to clear interrupt for.
+ *
+ */
+void ipu_clear_irq(struct ipu_soc *ipu, uint32_t irq)
+{
+       unsigned long lock_flags;
+
+       _ipu_get(ipu);
+
+       spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags);
+
+       ipu_cm_write(ipu, IPUIRQ_2_MASK(irq),
+                       IPUIRQ_2_STATREG(ipu->devtype, irq));
+
+       spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags);
+
+       _ipu_put(ipu);
+}
+EXPORT_SYMBOL(ipu_clear_irq);
+
+/*!
+ * This function returns the current interrupt status for the specified
+ * interrupt line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param      ipu             ipu handler
+ * @param       irq             Interrupt line to get status for.
+ *
+ * @return      Returns true if the interrupt is pending/asserted or false if
+ *              the interrupt is not pending.
+ */
+bool ipu_get_irq_status(struct ipu_soc *ipu, uint32_t irq)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       _ipu_get(ipu);
+
+       spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags);
+       reg = ipu_cm_read(ipu, IPUIRQ_2_STATREG(ipu->devtype, irq));
+       spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags);
+
+       _ipu_put(ipu);
+
+       if (reg & IPUIRQ_2_MASK(irq))
+               return true;
+       else
+               return false;
+}
+EXPORT_SYMBOL(ipu_get_irq_status);
+
+/*!
+ * This function registers an interrupt handler function for the specified
+ * interrupt line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param      ipu             ipu handler
+ * @param       irq             Interrupt line to get status for.
+ *
+ * @param       handler         Input parameter for address of the handler
+ *                              function.
+ *
+ * @param       irq_flags       Flags for interrupt mode. Currently not used.
+ *
+ * @param       devname         Input parameter for string name of driver
+ *                              registering the handler.
+ *
+ * @param       dev_id          Input parameter for pointer of data to be
+ *                              passed to the handler.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int ipu_request_irq(struct ipu_soc *ipu, uint32_t irq,
+                   irqreturn_t(*handler) (int, void *),
+                   uint32_t irq_flags, const char *devname, void *dev_id)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+       int ret = 0;
+
+       BUG_ON(irq >= IPU_IRQ_COUNT);
+
+       _ipu_get(ipu);
+
+       spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags);
+
+       if (ipu->irq_list[irq].handler != NULL) {
+               dev_err(ipu->dev,
+                       "handler already installed on irq %d\n", irq);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /*
+        * Check sync interrupt handler only, since we do nothing for
+        * error interrupts but than print out register values in the
+        * error interrupt source handler.
+        */
+       if (_ipu_is_sync_irq(irq) && (handler == NULL)) {
+               dev_err(ipu->dev, "handler is NULL for sync irq %d\n", irq);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ipu->irq_list[irq].handler = handler;
+       ipu->irq_list[irq].flags = irq_flags;
+       ipu->irq_list[irq].dev_id = dev_id;
+       ipu->irq_list[irq].name = devname;
+
+       /* clear irq stat for previous use */
+       ipu_cm_write(ipu, IPUIRQ_2_MASK(irq),
+                       IPUIRQ_2_STATREG(ipu->devtype, irq));
+       /* enable the interrupt */
+       reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq));
+       reg |= IPUIRQ_2_MASK(irq);
+       ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq));
+out:
+       spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags);
+
+       _ipu_put(ipu);
+
+       return ret;
+}
+EXPORT_SYMBOL(ipu_request_irq);
+
+/*!
+ * This function unregisters an interrupt handler for the specified interrupt
+ * line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param      ipu             ipu handler
+ * @param       irq             Interrupt line to get status for.
+ *
+ * @param       dev_id          Input parameter for pointer of data to be passed
+ *                              to the handler. This must match value passed to
+ *                              ipu_request_irq().
+ *
+ */
+void ipu_free_irq(struct ipu_soc *ipu, uint32_t irq, void *dev_id)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       _ipu_get(ipu);
+
+       spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags);
+
+       /* disable the interrupt */
+       reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq));
+       reg &= ~IPUIRQ_2_MASK(irq);
+       ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq));
+       if (ipu->irq_list[irq].dev_id == dev_id)
+               memset(&ipu->irq_list[irq], 0, sizeof(ipu->irq_list[irq]));
+
+       spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags);
+
+       _ipu_put(ipu);
+}
+EXPORT_SYMBOL(ipu_free_irq);
+
+uint32_t ipu_get_cur_buffer_idx(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type)
+{
+       uint32_t reg, dma_chan;
+
+       dma_chan = channel_2_dma(channel, type);
+       if (!idma_is_valid(dma_chan))
+               return -EINVAL;
+
+       reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan));
+       if ((reg & idma_mask(dma_chan)) && _ipu_is_trb_chan(ipu, dma_chan)) {
+               reg = ipu_cm_read(ipu,
+                               IPU_CHA_TRIPLE_CUR_BUF(ipu->devtype, dma_chan));
+               return (reg & tri_cur_buf_mask(dma_chan)) >>
+                               tri_cur_buf_shift(dma_chan);
+       } else {
+               reg = ipu_cm_read(ipu, IPU_CHA_CUR_BUF(ipu->devtype, dma_chan));
+               if (reg & idma_mask(dma_chan))
+                       return 1;
+               else
+                       return 0;
+       }
+}
+EXPORT_SYMBOL(ipu_get_cur_buffer_idx);
+
+uint32_t _ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel)
+{
+       uint32_t stat = 0;
+       uint32_t task_stat_reg = ipu_cm_read(ipu,
+                                       IPU_PROC_TASK_STAT(ipu->devtype));
+
+       switch (channel) {
+       case MEM_PRP_VF_MEM:
+               stat = (task_stat_reg & TSTAT_VF_MASK) >> TSTAT_VF_OFFSET;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               stat = (task_stat_reg & TSTAT_VF_MASK) >> TSTAT_VF_OFFSET;
+               break;
+       case MEM_ROT_VF_MEM:
+               stat =
+                   (task_stat_reg & TSTAT_VF_ROT_MASK) >> TSTAT_VF_ROT_OFFSET;
+               break;
+       case MEM_PRP_ENC_MEM:
+               stat = (task_stat_reg & TSTAT_ENC_MASK) >> TSTAT_ENC_OFFSET;
+               break;
+       case MEM_ROT_ENC_MEM:
+               stat =
+                   (task_stat_reg & TSTAT_ENC_ROT_MASK) >>
+                   TSTAT_ENC_ROT_OFFSET;
+               break;
+       case MEM_PP_MEM:
+               stat = (task_stat_reg & TSTAT_PP_MASK) >> TSTAT_PP_OFFSET;
+               break;
+       case MEM_ROT_PP_MEM:
+               stat =
+                   (task_stat_reg & TSTAT_PP_ROT_MASK) >> TSTAT_PP_ROT_OFFSET;
+               break;
+
+       default:
+               stat = TASK_STAT_IDLE;
+               break;
+       }
+       return stat;
+}
+
+/*!
+ * This function check for  a logical channel status
+ *
+ * @param      ipu             ipu handler
+ * @param      channel         Input parameter for the logical channel ID.
+ *
+ * @return      This function returns 0 on idle and 1 on busy.
+ *
+ */
+uint32_t ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel)
+{
+       uint32_t dma_status;
+
+       _ipu_get(ipu);
+       mutex_lock(&ipu->mutex_lock);
+       dma_status = ipu_is_channel_busy(ipu, channel);
+       mutex_unlock(&ipu->mutex_lock);
+       _ipu_put(ipu);
+
+       dev_dbg(ipu->dev, "%s, dma_status:%d.\n", __func__, dma_status);
+
+       return dma_status;
+}
+EXPORT_SYMBOL(ipu_channel_status);
+
+int32_t ipu_swap_channel(struct ipu_soc *ipu, ipu_channel_t from_ch, ipu_channel_t to_ch)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+       int from_dma = channel_2_dma(from_ch, IPU_INPUT_BUFFER);
+       int to_dma = channel_2_dma(to_ch, IPU_INPUT_BUFFER);
+
+       mutex_lock(&ipu->mutex_lock);
+
+       /* enable target channel */
+       reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(to_dma));
+       ipu_idmac_write(ipu, reg | idma_mask(to_dma), IDMAC_CHA_EN(to_dma));
+
+       ipu->channel_enable_mask |= 1L << IPU_CHAN_ID(to_ch);
+
+       /* switch dp dc */
+       _ipu_dp_dc_disable(ipu, from_ch, true);
+
+       /* disable source channel */
+       reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(from_dma));
+       ipu_idmac_write(ipu, reg & ~idma_mask(from_dma), IDMAC_CHA_EN(from_dma));
+       ipu_cm_write(ipu, idma_mask(from_dma),
+                               IPU_CHA_CUR_BUF(ipu->devtype, from_dma));
+       ipu_cm_write(ipu, tri_cur_buf_mask(from_dma),
+                               IPU_CHA_TRIPLE_CUR_BUF(ipu->devtype, from_dma));
+
+       ipu->channel_enable_mask &= ~(1L << IPU_CHAN_ID(from_ch));
+
+       spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags);
+       _ipu_clear_buffer_ready(ipu, from_ch, IPU_VIDEO_IN_BUFFER, 0);
+       _ipu_clear_buffer_ready(ipu, from_ch, IPU_VIDEO_IN_BUFFER, 1);
+       _ipu_clear_buffer_ready(ipu, from_ch, IPU_VIDEO_IN_BUFFER, 2);
+       spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags);
+
+       mutex_unlock(&ipu->mutex_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_swap_channel);
+
+uint32_t bytes_per_pixel(uint32_t fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_GENERIC:       /*generic data */
+       case IPU_PIX_FMT_RGB332:
+       case IPU_PIX_FMT_YUV420P:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YUV444P:
+       case IPU_PIX_FMT_NV12:
+       case PRE_PIX_FMT_NV21:
+       case IPU_PIX_FMT_NV16:
+       case PRE_PIX_FMT_NV61:
+               return 1;
+               break;
+       case IPU_PIX_FMT_GENERIC_16:    /* generic data */
+       case IPU_PIX_FMT_RGB565:
+       case IPU_PIX_FMT_BGRA4444:
+       case IPU_PIX_FMT_BGRA5551:
+       case IPU_PIX_FMT_YUYV:
+       case IPU_PIX_FMT_UYVY:
+       case IPU_PIX_FMT_GPU16_SB_ST:
+       case IPU_PIX_FMT_GPU16_SB_SRT:
+       case IPU_PIX_FMT_GPU16_ST:
+       case IPU_PIX_FMT_GPU16_SRT:
+               return 2;
+               break;
+       case IPU_PIX_FMT_BGR24:
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_YUV444:
+               return 3;
+               break;
+       case IPU_PIX_FMT_GENERIC_32:    /*generic data */
+       case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_RGB32:
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_ABGR32:
+       case IPU_PIX_FMT_GPU32_SB_ST:
+       case IPU_PIX_FMT_GPU32_SB_SRT:
+       case IPU_PIX_FMT_GPU32_ST:
+       case IPU_PIX_FMT_GPU32_SRT:
+       case IPU_PIX_FMT_AYUV:
+               return 4;
+               break;
+       default:
+               return 1;
+               break;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(bytes_per_pixel);
+
+ipu_color_space_t format_to_colorspace(uint32_t fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_RGB666:
+       case IPU_PIX_FMT_RGB565:
+       case IPU_PIX_FMT_BGRA4444:
+       case IPU_PIX_FMT_BGRA5551:
+       case IPU_PIX_FMT_BGR24:
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_GBR24:
+       case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_RGB32:
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_ABGR32:
+       case IPU_PIX_FMT_LVDS666:
+       case IPU_PIX_FMT_LVDS888:
+       case IPU_PIX_FMT_GPU32_SB_ST:
+       case IPU_PIX_FMT_GPU32_SB_SRT:
+       case IPU_PIX_FMT_GPU32_ST:
+       case IPU_PIX_FMT_GPU32_SRT:
+       case IPU_PIX_FMT_GPU16_SB_ST:
+       case IPU_PIX_FMT_GPU16_SB_SRT:
+       case IPU_PIX_FMT_GPU16_ST:
+       case IPU_PIX_FMT_GPU16_SRT:
+               return RGB;
+               break;
+
+       default:
+               return YCbCr;
+               break;
+       }
+       return RGB;
+}
+EXPORT_SYMBOL(format_to_colorspace);
+
+bool ipu_pixel_format_has_alpha(uint32_t fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_ABGR32:
+               return true;
+               break;
+       default:
+               return false;
+               break;
+       }
+       return false;
+}
+
+bool ipu_ch_param_bad_alpha_pos(uint32_t pixel_fmt)
+{
+       return _ipu_ch_param_bad_alpha_pos(pixel_fmt);
+}
+EXPORT_SYMBOL(ipu_ch_param_bad_alpha_pos);
+
+bool ipu_pixel_format_is_gpu_tile(uint32_t fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_GPU32_SB_ST:
+       case IPU_PIX_FMT_GPU32_SB_SRT:
+       case IPU_PIX_FMT_GPU32_ST:
+       case IPU_PIX_FMT_GPU32_SRT:
+       case IPU_PIX_FMT_GPU16_SB_ST:
+       case IPU_PIX_FMT_GPU16_SB_SRT:
+       case IPU_PIX_FMT_GPU16_ST:
+       case IPU_PIX_FMT_GPU16_SRT:
+               return true;
+       default:
+               return false;
+       }
+}
+EXPORT_SYMBOL(ipu_pixel_format_is_gpu_tile);
+
+bool ipu_pixel_format_is_split_gpu_tile(uint32_t fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_GPU32_SB_ST:
+       case IPU_PIX_FMT_GPU32_SB_SRT:
+       case IPU_PIX_FMT_GPU16_SB_ST:
+       case IPU_PIX_FMT_GPU16_SB_SRT:
+               return true;
+       default:
+               return false;
+       }
+}
+EXPORT_SYMBOL(ipu_pixel_format_is_split_gpu_tile);
+
+bool ipu_pixel_format_is_pre_yuv(uint32_t fmt)
+{
+       switch (fmt) {
+       case PRE_PIX_FMT_NV21:
+       case PRE_PIX_FMT_NV61:
+               return true;
+       default:
+               return false;
+       }
+}
+EXPORT_SYMBOL(ipu_pixel_format_is_pre_yuv);
+
+bool ipu_pixel_format_is_multiplanar_yuv(uint32_t fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_YVU410P:
+       case IPU_PIX_FMT_YUV420P:
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YVU422P:
+       case IPU_PIX_FMT_YUV444P:
+       case IPU_PIX_FMT_NV12:
+       case PRE_PIX_FMT_NV21:
+       case IPU_PIX_FMT_NV16:
+       case PRE_PIX_FMT_NV61:
+               return true;
+       default:
+               return false;
+       }
+}
+EXPORT_SYMBOL(ipu_pixel_format_is_multiplanar_yuv);
+
+int ipu_ch_param_get_axi_id(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type)
+{
+       uint32_t dma_chan = channel_2_dma(channel, type);
+       int axi_id;
+
+       if (!idma_is_valid(dma_chan))
+               return -EINVAL;
+
+       _ipu_get(ipu);
+       mutex_lock(&ipu->mutex_lock);
+       axi_id = _ipu_ch_param_get_axi_id(ipu, dma_chan);
+       mutex_unlock(&ipu->mutex_lock);
+       _ipu_put(ipu);
+
+       return axi_id;
+}
+EXPORT_SYMBOL(ipu_ch_param_get_axi_id);
+
+#ifdef CONFIG_PM
+int ipu_runtime_suspend(struct device *dev)
+{
+       release_bus_freq(BUS_FREQ_HIGH);
+       dev_dbg(dev, "ipu busfreq high release.\n");
+
+       return 0;
+}
+
+int ipu_runtime_resume(struct device *dev)
+{
+       request_bus_freq(BUS_FREQ_HIGH);
+       dev_dbg(dev, "ipu busfreq high requst.\n");
+
+       return 0;
+}
+
+static const struct dev_pm_ops ipu_pm_ops = {
+       SET_RUNTIME_PM_OPS(ipu_runtime_suspend, ipu_runtime_resume, NULL)
+};
+#endif
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcipu_driver = {
+       .driver = {
+                       .name           = "imx-ipuv3",
+                       .of_match_table = imx_ipuv3_dt_ids,
+               #ifdef CONFIG_PM
+                       .pm     = &ipu_pm_ops,
+               #endif
+       },
+       .probe          = ipu_probe,
+       .remove         = ipu_remove,
+};
+
+int32_t __init ipu_gen_init(void)
+{
+       int32_t ret;
+
+       ret = platform_driver_register(&mxcipu_driver);
+       return 0;
+}
+
+subsys_initcall(ipu_gen_init);
+
+static void __exit ipu_gen_uninit(void)
+{
+       platform_driver_unregister(&mxcipu_driver);
+}
+
+module_exit(ipu_gen_uninit);
diff --git a/drivers/mxc/ipu3/ipu_device.c b/drivers/mxc/ipu3/ipu_device.c
new file mode 100644 (file)
index 0000000..f672b0e
--- /dev/null
@@ -0,0 +1,3739 @@
+/*
+ * Copyright 2005-2015 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
+ */
+
+/*!
+ * @file ipu_device.c
+ *
+ * @brief This file contains the IPUv3 driver device interface and fops functions.
+ *
+ * @ingroup IPU
+ */
+#include <linux/clk.h>
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ipu-v3.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/sched/rt.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+
+#include <asm/cacheflush.h>
+#include <asm/outercache.h>
+
+#include "ipu_param_mem.h"
+#include "ipu_regs.h"
+#include "vdoa.h"
+
+#define CHECK_RETCODE(cont, str, err, label, ret)                      \
+do {                                                                   \
+       if (cont) {                                                     \
+               dev_err(t->dev, "ERR:[0x%p]-no:0x%x "#str" ret:%d,"     \
+                               "line:%d\n", t, t->task_no, ret, __LINE__);\
+               if (ret != -EACCES) {                                   \
+                       t->state = err;                                 \
+                       goto label;                                     \
+               }                                                       \
+       }                                                               \
+} while (0)
+
+#define CHECK_RETCODE_CONT(cont, str, err, ret)                                \
+do {                                                                   \
+       if (cont) {                                                     \
+               dev_err(t->dev, "ERR:[0x%p]-no:0x%x"#str" ret:%d,"      \
+                               "line:%d\n", t, t->task_no, ret, __LINE__);\
+               if (ret != -EACCES) {                                   \
+                       if (t->state == STATE_OK)                       \
+                               t->state = err;                         \
+               }                                                       \
+       }                                                               \
+} while (0)
+
+#undef DBG_IPU_PERF
+#ifdef DBG_IPU_PERF
+#define CHECK_PERF(ts)                                                 \
+do {                                                                   \
+       getnstimeofday(ts);                                             \
+} while (0)
+
+#define DECLARE_PERF_VAR                                               \
+       struct timespec ts_queue;                                       \
+       struct timespec ts_dotask;                                      \
+       struct timespec ts_waitirq;                                     \
+       struct timespec ts_sche;                                        \
+       struct timespec ts_rel;                                         \
+       struct timespec ts_frame
+
+#define PRINT_TASK_STATISTICS                                          \
+do {                                                                   \
+       ts_queue = timespec_sub(tsk->ts_dotask, tsk->ts_queue);         \
+       ts_dotask = timespec_sub(tsk->ts_waitirq, tsk->ts_dotask);      \
+       ts_waitirq = timespec_sub(tsk->ts_inirq, tsk->ts_waitirq);      \
+       ts_sche = timespec_sub(tsk->ts_wakeup, tsk->ts_inirq);          \
+       ts_rel = timespec_sub(tsk->ts_rel, tsk->ts_wakeup);             \
+       ts_frame = timespec_sub(tsk->ts_rel, tsk->ts_queue);            \
+       dev_dbg(tsk->dev, "[0x%p] no-0x%x, ts_q:%ldus, ts_do:%ldus,"    \
+               "ts_waitirq:%ldus,ts_sche:%ldus, ts_rel:%ldus,"         \
+               "ts_frame: %ldus\n", tsk, tsk->task_no,                 \
+       ts_queue.tv_nsec / NSEC_PER_USEC + ts_queue.tv_sec * USEC_PER_SEC,\
+       ts_dotask.tv_nsec / NSEC_PER_USEC + ts_dotask.tv_sec * USEC_PER_SEC,\
+       ts_waitirq.tv_nsec / NSEC_PER_USEC + ts_waitirq.tv_sec * USEC_PER_SEC,\
+       ts_sche.tv_nsec / NSEC_PER_USEC + ts_sche.tv_sec * USEC_PER_SEC,\
+       ts_rel.tv_nsec / NSEC_PER_USEC + ts_rel.tv_sec * USEC_PER_SEC,\
+       ts_frame.tv_nsec / NSEC_PER_USEC + ts_frame.tv_sec * USEC_PER_SEC); \
+       if ((ts_frame.tv_nsec/NSEC_PER_USEC + ts_frame.tv_sec*USEC_PER_SEC) > \
+               80000)  \
+               dev_dbg(tsk->dev, "ts_frame larger than 80ms [0x%p] no-0x%x.\n"\
+                               , tsk, tsk->task_no);   \
+} while (0)
+#else
+#define CHECK_PERF(ts)
+#define DECLARE_PERF_VAR
+#define PRINT_TASK_STATISTICS
+#endif
+
+#define        IPU_PP_CH_VF    (IPU_TASK_ID_VF - 1)
+#define        IPU_PP_CH_PP    (IPU_TASK_ID_PP - 1)
+#define MAX_PP_CH      (IPU_TASK_ID_MAX - 1)
+#define VDOA_DEF_TIMEOUT_MS    (HZ/2)
+
+/* Strucutures and variables for exporting MXC IPU as device*/
+typedef enum {
+       STATE_OK = 0,
+       STATE_QUEUE,
+       STATE_IN_PROGRESS,
+       STATE_ERR,
+       STATE_TIMEOUT,
+       STATE_RES_TIMEOUT,
+       STATE_NO_IPU,
+       STATE_NO_IRQ,
+       STATE_IPU_BUSY,
+       STATE_IRQ_FAIL,
+       STATE_IRQ_TIMEOUT,
+       STATE_ENABLE_CHAN_FAIL,
+       STATE_DISABLE_CHAN_FAIL,
+       STATE_SEL_BUF_FAIL,
+       STATE_INIT_CHAN_FAIL,
+       STATE_LINK_CHAN_FAIL,
+       STATE_UNLINK_CHAN_FAIL,
+       STATE_INIT_CHAN_BUF_FAIL,
+       STATE_INIT_CHAN_BAND_FAIL,
+       STATE_SYS_NO_MEM,
+       STATE_VDOA_IRQ_TIMEOUT,
+       STATE_VDOA_IRQ_FAIL,
+       STATE_VDOA_TASK_FAIL,
+} ipu_state_t;
+
+enum {
+       INPUT_CHAN_VDI_P = 1,
+       INPUT_CHAN,
+       INPUT_CHAN_VDI_N,
+};
+
+struct ipu_state_msg {
+       int state;
+       char *msg;
+} state_msg[] = {
+       {STATE_OK, "ok"},
+       {STATE_QUEUE, "split queue"},
+       {STATE_IN_PROGRESS, "split in progress"},
+       {STATE_ERR, "error"},
+       {STATE_TIMEOUT, "split task timeout"},
+       {STATE_RES_TIMEOUT, "wait resource timeout"},
+       {STATE_NO_IPU, "no ipu found"},
+       {STATE_NO_IRQ, "no irq found for task"},
+       {STATE_IPU_BUSY, "ipu busy"},
+       {STATE_IRQ_FAIL, "request irq failed"},
+       {STATE_IRQ_TIMEOUT, "wait for irq timeout"},
+       {STATE_ENABLE_CHAN_FAIL, "ipu enable channel fail"},
+       {STATE_DISABLE_CHAN_FAIL, "ipu disable channel fail"},
+       {STATE_SEL_BUF_FAIL, "ipu select buf fail"},
+       {STATE_INIT_CHAN_FAIL, "ipu init channel fail"},
+       {STATE_LINK_CHAN_FAIL, "ipu link channel fail"},
+       {STATE_UNLINK_CHAN_FAIL, "ipu unlink channel fail"},
+       {STATE_INIT_CHAN_BUF_FAIL, "ipu init channel buffer fail"},
+       {STATE_INIT_CHAN_BAND_FAIL, "ipu init channel band mode fail"},
+       {STATE_SYS_NO_MEM, "sys no mem: -ENOMEM"},
+       {STATE_VDOA_IRQ_TIMEOUT, "wait for vdoa irq timeout"},
+       {STATE_VDOA_IRQ_FAIL, "vdoa irq fail"},
+       {STATE_VDOA_TASK_FAIL, "vdoa task fail"},
+};
+
+struct stripe_setting {
+       u32 iw;
+       u32 ih;
+       u32 ow;
+       u32 oh;
+       u32 outh_resize_ratio;
+       u32 outv_resize_ratio;
+       u32 i_left_pos;
+       u32 i_right_pos;
+       u32 i_top_pos;
+       u32 i_bottom_pos;
+       u32 o_left_pos;
+       u32 o_right_pos;
+       u32 o_top_pos;
+       u32 o_bottom_pos;
+       u32 rl_split_line;
+       u32 ud_split_line;
+};
+
+struct task_set {
+#define        NULL_MODE       0x0
+#define        IC_MODE         0x1
+#define        ROT_MODE        0x2
+#define        VDI_MODE        0x4
+#define IPU_PREPROCESS_MODE_MASK       (IC_MODE | ROT_MODE | VDI_MODE)
+/* VDOA_MODE means this task use vdoa, and VDOA has two modes:
+ * BAND MODE and non-BAND MODE. Non-band mode will do transfer data
+ * to memory. BAND mode needs hareware sync with IPU, it is used default
+ * if connected to VDIC.
+ */
+#define        VDOA_MODE       0x8
+#define        VDOA_BAND_MODE  0x10
+       u8      mode;
+#define IC_VF  0x1
+#define IC_PP  0x2
+#define ROT_VF 0x4
+#define ROT_PP 0x8
+#define VDI_VF 0x10
+#define        VDOA_ONLY       0x20
+       u8      task;
+#define NO_SPLIT       0x0
+#define RL_SPLIT       0x1
+#define UD_SPLIT       0x2
+#define LEFT_STRIPE    0x1
+#define RIGHT_STRIPE   0x2
+#define UP_STRIPE      0x4
+#define DOWN_STRIPE    0x8
+#define SPLIT_MASK     0xF
+       u8      split_mode;
+       u8      band_lines;
+       ipu_channel_t ic_chan;
+       ipu_channel_t rot_chan;
+       ipu_channel_t vdi_ic_p_chan;
+       ipu_channel_t vdi_ic_n_chan;
+
+       u32 i_off;
+       u32 i_uoff;
+       u32 i_voff;
+       u32 istride;
+
+       u32 ov_off;
+       u32 ov_uoff;
+       u32 ov_voff;
+       u32 ovstride;
+
+       u32 ov_alpha_off;
+       u32 ov_alpha_stride;
+
+       u32 o_off;
+       u32 o_uoff;
+       u32 o_voff;
+       u32 ostride;
+
+       u32 r_fmt;
+       u32 r_width;
+       u32 r_height;
+       u32 r_stride;
+       dma_addr_t r_paddr;
+
+       struct stripe_setting sp_setting;
+};
+
+struct ipu_split_task {
+       struct ipu_task task;
+       struct ipu_task_entry *parent_task;
+       struct ipu_task_entry *child_task;
+       u32 task_no;
+};
+
+struct ipu_task_entry {
+       struct ipu_input input;
+       struct ipu_output output;
+
+       bool overlay_en;
+       struct ipu_overlay overlay;
+#define DEF_TIMEOUT_MS 1000
+#define DEF_DELAY_MS 20
+       int     timeout;
+       int     irq;
+
+       u8      task_id;
+       u8      ipu_id;
+       u8      task_in_list;
+       u8      split_done;
+       struct mutex split_lock;
+       struct mutex vdic_lock;
+       wait_queue_head_t split_waitq;
+
+       struct list_head node;
+       struct list_head split_list;
+       struct ipu_soc *ipu;
+       struct device *dev;
+       struct task_set set;
+       wait_queue_head_t task_waitq;
+       struct completion irq_comp;
+       struct kref refcount;
+       ipu_state_t state;
+       u32 task_no;
+       atomic_t done;
+       atomic_t res_free;
+       atomic_t res_get;
+
+       struct ipu_task_entry *parent;
+       char *vditmpbuf[2];
+       u32 old_save_lines;
+       u32 old_size;
+       bool buf1filled;
+       bool buf0filled;
+
+       vdoa_handle_t vdoa_handle;
+       struct vdoa_output_mem {
+               void *vaddr;
+               dma_addr_t paddr;
+               int size;
+       } vdoa_dma;
+
+#ifdef DBG_IPU_PERF
+       struct timespec ts_queue;
+       struct timespec ts_dotask;
+       struct timespec ts_waitirq;
+       struct timespec ts_inirq;
+       struct timespec ts_wakeup;
+       struct timespec ts_rel;
+#endif
+};
+
+struct ipu_channel_tabel {
+       struct mutex    lock;
+       u8              used[MXC_IPU_MAX_NUM][MAX_PP_CH];
+       u8              vdoa_used;
+};
+
+struct ipu_thread_data {
+       struct ipu_soc *ipu;
+       u32     id;
+       u32     is_vdoa;
+};
+
+struct ipu_alloc_list {
+       struct list_head list;
+       dma_addr_t phy_addr;
+       void *cpu_addr;
+       u32 size;
+       void *file_index;
+};
+
+static LIST_HEAD(ipu_alloc_list);
+static DEFINE_MUTEX(ipu_alloc_lock);
+static struct ipu_channel_tabel        ipu_ch_tbl;
+static LIST_HEAD(ipu_task_list);
+static DEFINE_SPINLOCK(ipu_task_list_lock);
+static DECLARE_WAIT_QUEUE_HEAD(thread_waitq);
+static DECLARE_WAIT_QUEUE_HEAD(res_waitq);
+static atomic_t req_cnt;
+static atomic_t file_index = ATOMIC_INIT(1);
+static int major;
+static int max_ipu_no;
+static int thread_id;
+static atomic_t frame_no;
+static struct class *ipu_class;
+static struct device *ipu_dev;
+static int debug;
+module_param(debug, int, 0600);
+#ifdef DBG_IPU_PERF
+static struct timespec ts_frame_max;
+static u32 ts_frame_avg;
+static atomic_t frame_cnt;
+#endif
+
+static bool deinterlace_3_field(struct ipu_task_entry *t)
+{
+       return ((t->set.mode & VDI_MODE) &&
+               (t->input.deinterlace.motion != HIGH_MOTION));
+}
+
+static u32 tiled_filed_size(struct ipu_task_entry *t)
+{
+       u32 field_size;
+
+       /* note: page_align is required by VPU hw ouput buffer */
+       field_size = TILED_NV12_FRAME_SIZE(t->input.width, t->input.height/2);
+       return field_size;
+}
+
+static bool only_ic(u8 mode)
+{
+       mode = mode & IPU_PREPROCESS_MODE_MASK;
+       return ((mode == IC_MODE) || (mode == VDI_MODE));
+}
+
+static bool only_rot(u8 mode)
+{
+       mode = mode & IPU_PREPROCESS_MODE_MASK;
+       return (mode == ROT_MODE);
+}
+
+static bool ic_and_rot(u8 mode)
+{
+       mode = mode & IPU_PREPROCESS_MODE_MASK;
+       return ((mode == (IC_MODE | ROT_MODE)) ||
+                (mode == (VDI_MODE | ROT_MODE)));
+}
+
+static bool need_split(struct ipu_task_entry *t)
+{
+       return ((t->set.split_mode != NO_SPLIT) || (t->task_no & SPLIT_MASK));
+}
+
+unsigned int fmt_to_bpp(unsigned int pixelformat)
+{
+       u32 bpp;
+
+       switch (pixelformat) {
+       case IPU_PIX_FMT_RGB565:
+       /*interleaved 422*/
+       case IPU_PIX_FMT_YUYV:
+       case IPU_PIX_FMT_UYVY:
+       /*non-interleaved 422*/
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YVU422P:
+               bpp = 16;
+               break;
+       case IPU_PIX_FMT_BGR24:
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_YUV444:
+       case IPU_PIX_FMT_YUV444P:
+               bpp = 24;
+               break;
+       case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_RGB32:
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_ABGR32:
+               bpp = 32;
+               break;
+       /*non-interleaved 420*/
+       case IPU_PIX_FMT_YUV420P:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_NV12:
+               bpp = 12;
+               break;
+       default:
+               bpp = 8;
+               break;
+       }
+       return bpp;
+}
+EXPORT_SYMBOL_GPL(fmt_to_bpp);
+
+cs_t colorspaceofpixel(int fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_RGB565:
+       case IPU_PIX_FMT_RGB666:
+       case IPU_PIX_FMT_BGR24:
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_RGB32:
+       case IPU_PIX_FMT_ABGR32:
+               return RGB_CS;
+               break;
+       case IPU_PIX_FMT_UYVY:
+       case IPU_PIX_FMT_YUYV:
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_YVU422P:
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YUV444:
+       case IPU_PIX_FMT_YUV444P:
+       case IPU_PIX_FMT_NV12:
+       case IPU_PIX_FMT_TILED_NV12:
+       case IPU_PIX_FMT_TILED_NV12F:
+               return YUV_CS;
+               break;
+       default:
+               return NULL_CS;
+       }
+}
+EXPORT_SYMBOL_GPL(colorspaceofpixel);
+
+int need_csc(int ifmt, int ofmt)
+{
+       cs_t ics, ocs;
+
+       ics = colorspaceofpixel(ifmt);
+       ocs = colorspaceofpixel(ofmt);
+
+       if ((ics == NULL_CS) || (ocs == NULL_CS))
+               return -1;
+       else if (ics != ocs)
+               return 1;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(need_csc);
+
+static int soc_max_in_width(u32 is_vdoa)
+{
+       return is_vdoa ? 8192 : 4096;
+}
+
+static int soc_max_vdi_in_width(struct ipu_soc *ipu)
+{
+       int i;
+
+       if (!ipu) {
+               for (i = 0; i < max_ipu_no; i++) {
+                       ipu = ipu_get_soc(i);
+                       if (!IS_ERR_OR_NULL(ipu))
+                               break;
+               }
+
+               if (i == max_ipu_no)
+                       return 720;
+       }
+       return IPU_MAX_VDI_IN_WIDTH(ipu->devtype);
+}
+static int soc_max_in_height(void)
+{
+       return 4096;
+}
+
+static int soc_max_out_width(void)
+{
+       /* mx51/mx53/mx6q is 1024*/
+       return 1024;
+}
+
+static int soc_max_out_height(void)
+{
+       /* mx51/mx53/mx6q is 1024*/
+       return 1024;
+}
+
+static void dump_task_info(struct ipu_task_entry *t)
+{
+       if (!debug)
+               return;
+       dev_dbg(t->dev, "[0x%p]input:\n", (void *)t);
+       dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->input.format);
+       dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->input.width);
+       dev_dbg(t->dev, "[0x%p]\theight = %d\n", (void *)t, t->input.height);
+       dev_dbg(t->dev, "[0x%p]\tcrop.w = %d\n", (void *)t, t->input.crop.w);
+       dev_dbg(t->dev, "[0x%p]\tcrop.h = %d\n", (void *)t, t->input.crop.h);
+       dev_dbg(t->dev, "[0x%p]\tcrop.pos.x = %d\n",
+                       (void *)t, t->input.crop.pos.x);
+       dev_dbg(t->dev, "[0x%p]\tcrop.pos.y = %d\n",
+                       (void *)t, t->input.crop.pos.y);
+       dev_dbg(t->dev, "[0x%p]input buffer:\n", (void *)t);
+       dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", (void *)t, t->input.paddr);
+       dev_dbg(t->dev, "[0x%p]\ti_off = 0x%x\n", (void *)t, t->set.i_off);
+       dev_dbg(t->dev, "[0x%p]\ti_uoff = 0x%x\n", (void *)t, t->set.i_uoff);
+       dev_dbg(t->dev, "[0x%p]\ti_voff = 0x%x\n", (void *)t, t->set.i_voff);
+       dev_dbg(t->dev, "[0x%p]\tistride = %d\n", (void *)t, t->set.istride);
+       if (t->input.deinterlace.enable) {
+               dev_dbg(t->dev, "[0x%p]deinterlace enabled with:\n", (void *)t);
+               if (t->input.deinterlace.motion != HIGH_MOTION) {
+                       dev_dbg(t->dev, "[0x%p]\tlow/medium motion\n", (void *)t);
+                       dev_dbg(t->dev, "[0x%p]\tpaddr_n = 0x%x\n",
+                               (void *)t, t->input.paddr_n);
+               } else
+                       dev_dbg(t->dev, "[0x%p]\thigh motion\n", (void *)t);
+       }
+
+       dev_dbg(t->dev, "[0x%p]output:\n", (void *)t);
+       dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->output.format);
+       dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->output.width);
+       dev_dbg(t->dev, "[0x%p]\theight = %d\n", (void *)t, t->output.height);
+       dev_dbg(t->dev, "[0x%p]\tcrop.w = %d\n", (void *)t, t->output.crop.w);
+       dev_dbg(t->dev, "[0x%p]\tcrop.h = %d\n", (void *)t, t->output.crop.h);
+       dev_dbg(t->dev, "[0x%p]\tcrop.pos.x = %d\n",
+                       (void *)t, t->output.crop.pos.x);
+       dev_dbg(t->dev, "[0x%p]\tcrop.pos.y = %d\n",
+                       (void *)t, t->output.crop.pos.y);
+       dev_dbg(t->dev, "[0x%p]\trotate = %d\n", (void *)t, t->output.rotate);
+       dev_dbg(t->dev, "[0x%p]output buffer:\n", (void *)t);
+       dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", (void *)t, t->output.paddr);
+       dev_dbg(t->dev, "[0x%p]\to_off = 0x%x\n", (void *)t, t->set.o_off);
+       dev_dbg(t->dev, "[0x%p]\to_uoff = 0x%x\n", (void *)t, t->set.o_uoff);
+       dev_dbg(t->dev, "[0x%p]\to_voff = 0x%x\n", (void *)t, t->set.o_voff);
+       dev_dbg(t->dev, "[0x%p]\tostride = %d\n", (void *)t, t->set.ostride);
+
+       if (t->overlay_en) {
+               dev_dbg(t->dev, "[0x%p]overlay:\n", (void *)t);
+               dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n",
+                               (void *)t, t->overlay.format);
+               dev_dbg(t->dev, "[0x%p]\twidth = %d\n",
+                               (void *)t, t->overlay.width);
+               dev_dbg(t->dev, "[0x%p]\theight = %d\n",
+                               (void *)t, t->overlay.height);
+               dev_dbg(t->dev, "[0x%p]\tcrop.w = %d\n",
+                               (void *)t, t->overlay.crop.w);
+               dev_dbg(t->dev, "[0x%p]\tcrop.h = %d\n",
+                               (void *)t, t->overlay.crop.h);
+               dev_dbg(t->dev, "[0x%p]\tcrop.pos.x = %d\n",
+                               (void *)t, t->overlay.crop.pos.x);
+               dev_dbg(t->dev, "[0x%p]\tcrop.pos.y = %d\n",
+                               (void *)t, t->overlay.crop.pos.y);
+               dev_dbg(t->dev, "[0x%p]overlay buffer:\n", (void *)t);
+               dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n",
+                               (void *)t, t->overlay.paddr);
+               dev_dbg(t->dev, "[0x%p]\tov_off = 0x%x\n",
+                               (void *)t, t->set.ov_off);
+               dev_dbg(t->dev, "[0x%p]\tov_uoff = 0x%x\n",
+                               (void *)t, t->set.ov_uoff);
+               dev_dbg(t->dev, "[0x%p]\tov_voff = 0x%x\n",
+                               (void *)t, t->set.ov_voff);
+               dev_dbg(t->dev, "[0x%p]\tovstride = %d\n",
+                               (void *)t, t->set.ovstride);
+               if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) {
+                       dev_dbg(t->dev, "[0x%p]local alpha enabled with:\n",
+                                       (void *)t);
+                       dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n",
+                                       (void *)t, t->overlay.alpha.loc_alp_paddr);
+                       dev_dbg(t->dev, "[0x%p]\tov_alpha_off = 0x%x\n",
+                                       (void *)t, t->set.ov_alpha_off);
+                       dev_dbg(t->dev, "[0x%p]\tov_alpha_stride = %d\n",
+                                       (void *)t, t->set.ov_alpha_stride);
+               } else
+                       dev_dbg(t->dev, "[0x%p]globle alpha enabled with value 0x%x\n",
+                                       (void *)t, t->overlay.alpha.gvalue);
+               if (t->overlay.colorkey.enable)
+                       dev_dbg(t->dev, "[0x%p]colorkey enabled with value 0x%x\n",
+                                       (void *)t, t->overlay.colorkey.value);
+       }
+
+       dev_dbg(t->dev, "[0x%p]want task_id = %d\n", (void *)t, t->task_id);
+       dev_dbg(t->dev, "[0x%p]want task mode is 0x%x\n",
+                               (void *)t, t->set.mode);
+       dev_dbg(t->dev, "[0x%p]\tIC_MODE = 0x%x\n", (void *)t, IC_MODE);
+       dev_dbg(t->dev, "[0x%p]\tROT_MODE = 0x%x\n", (void *)t, ROT_MODE);
+       dev_dbg(t->dev, "[0x%p]\tVDI_MODE = 0x%x\n", (void *)t, VDI_MODE);
+       dev_dbg(t->dev, "[0x%p]\tTask_no = 0x%x\n\n\n", (void *)t, t->task_no);
+}
+
+static void dump_check_err(struct device *dev, int err)
+{
+       switch (err) {
+       case IPU_CHECK_ERR_INPUT_CROP:
+               dev_err(dev, "input crop setting error\n");
+               break;
+       case IPU_CHECK_ERR_OUTPUT_CROP:
+               dev_err(dev, "output crop setting error\n");
+               break;
+       case IPU_CHECK_ERR_OVERLAY_CROP:
+               dev_err(dev, "overlay crop setting error\n");
+               break;
+       case IPU_CHECK_ERR_INPUT_OVER_LIMIT:
+               dev_err(dev, "input over limitation\n");
+               break;
+       case IPU_CHECK_ERR_OVERLAY_WITH_VDI:
+               dev_err(dev, "do not support overlay with deinterlace\n");
+               break;
+       case IPU_CHECK_ERR_OV_OUT_NO_FIT:
+               dev_err(dev,
+                       "width/height of overlay and ic output should be same\n");
+               break;
+       case IPU_CHECK_ERR_PROC_NO_NEED:
+               dev_err(dev, "no ipu processing need\n");
+               break;
+       case IPU_CHECK_ERR_SPLIT_INPUTW_OVER:
+               dev_err(dev, "split mode input width overflow\n");
+               break;
+       case IPU_CHECK_ERR_SPLIT_INPUTH_OVER:
+               dev_err(dev, "split mode input height overflow\n");
+               break;
+       case IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER:
+               dev_err(dev, "split mode output width overflow\n");
+               break;
+       case IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER:
+               dev_err(dev, "split mode output height overflow\n");
+               break;
+       case IPU_CHECK_ERR_SPLIT_WITH_ROT:
+               dev_err(dev, "not support split mode with rotation\n");
+               break;
+       case IPU_CHECK_ERR_W_DOWNSIZE_OVER:
+               dev_err(dev, "horizontal downsizing ratio overflow\n");
+               break;
+       case IPU_CHECK_ERR_H_DOWNSIZE_OVER:
+               dev_err(dev, "vertical downsizing ratio overflow\n");
+               break;
+       default:
+               break;
+       }
+}
+
+static void dump_check_warn(struct device *dev, int warn)
+{
+       if (warn & IPU_CHECK_WARN_INPUT_OFFS_NOT8ALIGN)
+               dev_warn(dev, "input u/v offset not 8 align\n");
+       if (warn & IPU_CHECK_WARN_OUTPUT_OFFS_NOT8ALIGN)
+               dev_warn(dev, "output u/v offset not 8 align\n");
+       if (warn & IPU_CHECK_WARN_OVERLAY_OFFS_NOT8ALIGN)
+               dev_warn(dev, "overlay u/v offset not 8 align\n");
+}
+
+static int set_crop(struct ipu_crop *crop, int width, int height, int fmt)
+{
+       if ((width == 0) || (height == 0)) {
+               pr_err("Invalid param: width=%d, height=%d\n", width, height);
+               return -EINVAL;
+       }
+
+       if ((IPU_PIX_FMT_TILED_NV12 == fmt) ||
+               (IPU_PIX_FMT_TILED_NV12F == fmt)) {
+               if (crop->w || crop->h) {
+                       if (((crop->w + crop->pos.x) > width)
+                       || ((crop->h + crop->pos.y) > height)
+                       || (0 != (crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN))
+                       || (0 != (crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN))
+                       || (0 != (crop->pos.x % IPU_PIX_FMT_TILED_NV12_MBALIGN))
+                       || (0 != (crop->pos.y % IPU_PIX_FMT_TILED_NV12_MBALIGN))
+                       ) {
+                               pr_err("set_crop error MB align.\n");
+                               return -EINVAL;
+                       }
+               } else {
+                       crop->pos.x = 0;
+                       crop->pos.y = 0;
+                       crop->w = width;
+                       crop->h = height;
+                       if ((0 != (crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN))
+                       || (0 != (crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN))) {
+                               pr_err("set_crop error w/h MB align.\n");
+                               return -EINVAL;
+                       }
+               }
+       } else {
+               if (crop->w || crop->h) {
+                       if (((crop->w + crop->pos.x) > (width + 16))
+                       || ((crop->h + crop->pos.y) > height + 16)) {
+                               pr_err("set_crop error exceeds width/height.\n");
+                               return -EINVAL;
+                       }
+               } else {
+                       crop->pos.x = 0;
+                       crop->pos.y = 0;
+                       crop->w = width;
+                       crop->h = height;
+               }
+               crop->w -= crop->w%8;
+               crop->h -= crop->h%8;
+       }
+
+       if ((crop->w == 0) || (crop->h == 0)) {
+               pr_err("Invalid crop param: crop.w=%d, crop.h=%d\n",
+                       crop->w, crop->h);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void update_offset(unsigned int fmt,
+                               unsigned int width, unsigned int height,
+                               unsigned int pos_x, unsigned int pos_y,
+                               int *off, int *uoff, int *voff, int *stride)
+{
+       /* NOTE: u v offset should based on start point of off*/
+       switch (fmt) {
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+               *off = pos_y * width + pos_x;
+               *uoff = (width * (height - pos_y) - pos_x)
+                       + (width/2) * (pos_y/2) + pos_x/2;
+               /* In case height is odd, round up to even */
+               *voff = *uoff + (width/2) * ((height+1)/2);
+               break;
+       case IPU_PIX_FMT_YVU420P:
+               *off = pos_y * width + pos_x;
+               *voff = (width * (height - pos_y) - pos_x)
+                       + (width/2) * (pos_y/2) + pos_x/2;
+               /* In case height is odd, round up to even */
+               *uoff = *voff + (width/2) * ((height+1)/2);
+               break;
+       case IPU_PIX_FMT_YVU422P:
+               *off = pos_y * width + pos_x;
+               *voff = (width * (height - pos_y) - pos_x)
+                       + (width/2) * pos_y + pos_x/2;
+               *uoff = *voff + (width/2) * height;
+               break;
+       case IPU_PIX_FMT_YUV422P:
+               *off = pos_y * width + pos_x;
+               *uoff = (width * (height - pos_y) - pos_x)
+                       + (width/2) * pos_y + pos_x/2;
+               *voff = *uoff + (width/2) * height;
+               break;
+       case IPU_PIX_FMT_YUV444P:
+               *off = pos_y * width + pos_x;
+               *uoff = width * height;
+               *voff = width * height * 2;
+               break;
+       case IPU_PIX_FMT_NV12:
+               *off = pos_y * width + pos_x;
+               *uoff = (width * (height - pos_y) - pos_x)
+                       + width * (pos_y/2) + pos_x;
+               break;
+       case IPU_PIX_FMT_TILED_NV12:
+               /*
+                * tiled format, progressive:
+                * assuming that line is aligned with MB height (aligned to 16)
+                * offset = line * stride + (pixel / MB_width) * pixels_in_MB
+                * = line * stride + (pixel / 16) * 256
+                * = line * stride + pixel * 16
+                */
+               *off = pos_y * width + (pos_x << 4);
+               *uoff = ALIGN(width * height, SZ_4K) + (*off >> 1) - *off;
+               break;
+       case IPU_PIX_FMT_TILED_NV12F:
+               /*
+                * tiled format, interlaced:
+                * same as above, only number of pixels in MB is 128,
+                * instead of 256
+                */
+               *off = (pos_y >> 1) * width + (pos_x << 3);
+               *uoff = ALIGN(width * height/2, SZ_4K) + (*off >> 1) - *off;
+               break;
+       default:
+               *off = (pos_y * width + pos_x) * fmt_to_bpp(fmt)/8;
+               break;
+       }
+       *stride = width * bytes_per_pixel(fmt);
+}
+
+static int update_split_setting(struct ipu_task_entry *t, bool vdi_split)
+{
+       struct stripe_param left_stripe;
+       struct stripe_param right_stripe;
+       struct stripe_param up_stripe;
+       struct stripe_param down_stripe;
+       u32 iw, ih, ow, oh;
+       u32 max_width;
+       int ret;
+
+       if (t->output.rotate >= IPU_ROTATE_90_RIGHT)
+               return IPU_CHECK_ERR_SPLIT_WITH_ROT;
+
+       iw = t->input.crop.w;
+       ih = t->input.crop.h;
+
+       ow = t->output.crop.w;
+       oh = t->output.crop.h;
+
+       memset(&left_stripe, 0, sizeof(left_stripe));
+       memset(&right_stripe, 0, sizeof(right_stripe));
+       memset(&up_stripe, 0, sizeof(up_stripe));
+       memset(&down_stripe, 0, sizeof(down_stripe));
+
+       if (t->set.split_mode & RL_SPLIT) {
+               /*
+                * We do want equal strips: initialize stripes in case
+                * calc_stripes returns before actually doing the calculation
+                */
+               left_stripe.input_width = iw / 2;
+               left_stripe.output_width = ow / 2;
+               right_stripe.input_column = iw / 2;
+               right_stripe.output_column = ow / 2;
+
+               if (vdi_split)
+                       max_width = soc_max_vdi_in_width(t->ipu);
+               else
+                       max_width = soc_max_out_width();
+               ret = ipu_calc_stripes_sizes(iw,
+                               ow,
+                               max_width,
+                               (((unsigned long long)1) << 32), /* 32bit for fractional*/
+                               1, /* equal stripes */
+                               t->input.format,
+                               t->output.format,
+                               &left_stripe,
+                               &right_stripe);
+               if (ret < 0)
+                       return IPU_CHECK_ERR_W_DOWNSIZE_OVER;
+               else if (ret)
+                       dev_dbg(t->dev, "Warn: no:0x%x,calc_stripes ret:%d\n",
+                                t->task_no, ret);
+               t->set.sp_setting.iw = left_stripe.input_width;
+               t->set.sp_setting.ow = left_stripe.output_width;
+               t->set.sp_setting.outh_resize_ratio = left_stripe.irr;
+               t->set.sp_setting.i_left_pos = left_stripe.input_column;
+               t->set.sp_setting.o_left_pos = left_stripe.output_column;
+               t->set.sp_setting.i_right_pos = right_stripe.input_column;
+               t->set.sp_setting.o_right_pos = right_stripe.output_column;
+       } else {
+               t->set.sp_setting.iw = iw;
+               t->set.sp_setting.ow = ow;
+               t->set.sp_setting.outh_resize_ratio = 0;
+               t->set.sp_setting.i_left_pos = 0;
+               t->set.sp_setting.o_left_pos = 0;
+               t->set.sp_setting.i_right_pos = 0;
+               t->set.sp_setting.o_right_pos = 0;
+       }
+       if ((t->set.sp_setting.iw + t->set.sp_setting.i_right_pos) > (iw+16))
+               return IPU_CHECK_ERR_SPLIT_INPUTW_OVER;
+       if (((t->set.sp_setting.ow + t->set.sp_setting.o_right_pos) > ow)
+               || (t->set.sp_setting.ow > soc_max_out_width()))
+               return IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER;
+       if (rounddown(t->set.sp_setting.ow, 8) * 8 <=
+           rounddown(t->set.sp_setting.iw, 8))
+               return IPU_CHECK_ERR_W_DOWNSIZE_OVER;
+
+       if (t->set.split_mode & UD_SPLIT) {
+               /*
+                * We do want equal strips: initialize stripes in case
+                * calc_stripes returns before actually doing the calculation
+                */
+               up_stripe.input_width = ih / 2;
+               up_stripe.output_width = oh / 2;
+               down_stripe.input_column = ih / 2;
+               down_stripe.output_column = oh / 2;
+               ret = ipu_calc_stripes_sizes(ih,
+                               oh,
+                               soc_max_out_height(),
+                               (((unsigned long long)1) << 32), /* 32bit for fractional*/
+                               0x1 | 0x2, /* equal stripes and vertical */
+                               t->input.format,
+                               t->output.format,
+                               &up_stripe,
+                               &down_stripe);
+               if (ret < 0)
+                       return IPU_CHECK_ERR_H_DOWNSIZE_OVER;
+               else if (ret)
+                       dev_err(t->dev, "Warn: no:0x%x,calc_stripes ret:%d\n",
+                                t->task_no, ret);
+               t->set.sp_setting.ih = up_stripe.input_width;
+               t->set.sp_setting.oh = up_stripe.output_width;
+               t->set.sp_setting.outv_resize_ratio = up_stripe.irr;
+               t->set.sp_setting.i_top_pos = up_stripe.input_column;
+               t->set.sp_setting.o_top_pos = up_stripe.output_column;
+               t->set.sp_setting.i_bottom_pos = down_stripe.input_column;
+               t->set.sp_setting.o_bottom_pos = down_stripe.output_column;
+       } else {
+               t->set.sp_setting.ih = ih;
+               t->set.sp_setting.oh = oh;
+               t->set.sp_setting.outv_resize_ratio = 0;
+               t->set.sp_setting.i_top_pos = 0;
+               t->set.sp_setting.o_top_pos = 0;
+               t->set.sp_setting.i_bottom_pos = 0;
+               t->set.sp_setting.o_bottom_pos = 0;
+       }
+
+       /* downscale case: enforce limits */
+       if (((t->set.sp_setting.ih + t->set.sp_setting.i_bottom_pos) > (ih))
+            && (t->set.sp_setting.ih >= t->set.sp_setting.oh))
+               return IPU_CHECK_ERR_SPLIT_INPUTH_OVER;
+       /* upscale case: relax limits because ipu_calc_stripes_sizes() may
+          create input stripe that falls just outside of the input window */
+       else if ((t->set.sp_setting.ih + t->set.sp_setting.i_bottom_pos)
+                > (ih+16))
+               return IPU_CHECK_ERR_SPLIT_INPUTH_OVER;
+       if (((t->set.sp_setting.oh + t->set.sp_setting.o_bottom_pos) > oh)
+               || (t->set.sp_setting.oh > soc_max_out_height()))
+               return IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER;
+       if (rounddown(t->set.sp_setting.oh, 8) * 8 <=
+           rounddown(t->set.sp_setting.ih, 8))
+               return IPU_CHECK_ERR_H_DOWNSIZE_OVER;
+
+       return IPU_CHECK_OK;
+}
+
+static int check_task(struct ipu_task_entry *t)
+{
+       int tmp;
+       int ret = IPU_CHECK_OK;
+       int timeout;
+       bool vdi_split = false;
+       int ocw, och;
+
+       if ((IPU_PIX_FMT_TILED_NV12 == t->overlay.format) ||
+               (IPU_PIX_FMT_TILED_NV12F == t->overlay.format) ||
+               (IPU_PIX_FMT_TILED_NV12 == t->output.format) ||
+               (IPU_PIX_FMT_TILED_NV12F == t->output.format) ||
+               ((IPU_PIX_FMT_TILED_NV12F == t->input.format) &&
+                       !t->input.deinterlace.enable)) {
+               ret = IPU_CHECK_ERR_NOT_SUPPORT;
+               goto done;
+       }
+
+       /* check input */
+       ret = set_crop(&t->input.crop, t->input.width, t->input.height,
+               t->input.format);
+       if (ret < 0) {
+               ret = IPU_CHECK_ERR_INPUT_CROP;
+               goto done;
+       } else
+               update_offset(t->input.format, t->input.width, t->input.height,
+                               t->input.crop.pos.x, t->input.crop.pos.y,
+                               &t->set.i_off, &t->set.i_uoff,
+                               &t->set.i_voff, &t->set.istride);
+
+       /* check output */
+       ret = set_crop(&t->output.crop, t->output.width, t->output.height,
+               t->output.format);
+       if (ret < 0) {
+               ret = IPU_CHECK_ERR_OUTPUT_CROP;
+               goto done;
+       } else
+               update_offset(t->output.format,
+                               t->output.width, t->output.height,
+                               t->output.crop.pos.x, t->output.crop.pos.y,
+                               &t->set.o_off, &t->set.o_uoff,
+                               &t->set.o_voff, &t->set.ostride);
+
+       if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
+               /*
+                * Cache output width and height and
+                * swap them so that we may check
+                * downsize overflow correctly.
+                */
+               ocw = t->output.crop.h;
+               och = t->output.crop.w;
+       } else {
+               ocw = t->output.crop.w;
+               och = t->output.crop.h;
+       }
+
+       if (ocw * 8 <= t->input.crop.w) {
+               ret = IPU_CHECK_ERR_W_DOWNSIZE_OVER;
+               goto done;
+       }
+
+       if (och * 8 <= t->input.crop.h) {
+               ret = IPU_CHECK_ERR_H_DOWNSIZE_OVER;
+               goto done;
+       }
+
+       if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) ||
+               (IPU_PIX_FMT_TILED_NV12F == t->input.format)) {
+               if ((t->input.crop.w > soc_max_in_width(1)) ||
+                       (t->input.crop.h > soc_max_in_height())) {
+                       ret = IPU_CHECK_ERR_INPUT_OVER_LIMIT;
+                       goto done;
+               }
+               /* output fmt: NV12 and YUYV, now don't support resize */
+               if (((IPU_PIX_FMT_NV12 != t->output.format) &&
+                               (IPU_PIX_FMT_YUYV != t->output.format)) ||
+                       (t->input.crop.w != t->output.crop.w) ||
+                       (t->input.crop.h != t->output.crop.h)) {
+                       ret = IPU_CHECK_ERR_NOT_SUPPORT;
+                       goto done;
+               }
+       }
+
+       /* check overlay if there is */
+       if (t->overlay_en) {
+               if (t->input.deinterlace.enable) {
+                       ret = IPU_CHECK_ERR_OVERLAY_WITH_VDI;
+                       goto done;
+               }
+
+               ret = set_crop(&t->overlay.crop, t->overlay.width,
+                       t->overlay.height, t->overlay.format);
+               if (ret < 0) {
+                       ret = IPU_CHECK_ERR_OVERLAY_CROP;
+                       goto done;
+               } else {
+                       ocw = t->output.crop.w;
+                       och = t->output.crop.h;
+
+                       if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
+                               ocw = t->output.crop.h;
+                               och = t->output.crop.w;
+                       }
+                       if ((t->overlay.crop.w != ocw) ||
+                           (t->overlay.crop.h != och)) {
+                               ret = IPU_CHECK_ERR_OV_OUT_NO_FIT;
+                               goto done;
+                       }
+
+                       update_offset(t->overlay.format,
+                                       t->overlay.width, t->overlay.height,
+                                       t->overlay.crop.pos.x, t->overlay.crop.pos.y,
+                                       &t->set.ov_off, &t->set.ov_uoff,
+                                       &t->set.ov_voff, &t->set.ovstride);
+                       if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) {
+                               t->set.ov_alpha_stride = t->overlay.width;
+                               t->set.ov_alpha_off = t->overlay.crop.pos.y *
+                                       t->overlay.width + t->overlay.crop.pos.x;
+                       }
+               }
+       }
+
+       /* input overflow? */
+       if (!((IPU_PIX_FMT_TILED_NV12 == t->input.format) ||
+               (IPU_PIX_FMT_TILED_NV12F == t->input.format))) {
+               if ((t->input.crop.w > soc_max_in_width(0)) ||
+                       (t->input.crop.h > soc_max_in_height())) {
+                               ret = IPU_CHECK_ERR_INPUT_OVER_LIMIT;
+                               goto done;
+               }
+       }
+
+       /* check task mode */
+       t->set.mode = NULL_MODE;
+       t->set.split_mode = NO_SPLIT;
+
+       if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
+               /*output swap*/
+               tmp = t->output.crop.w;
+               t->output.crop.w = t->output.crop.h;
+               t->output.crop.h = tmp;
+       }
+
+       if (t->output.rotate >= IPU_ROTATE_90_RIGHT)
+               t->set.mode |= ROT_MODE;
+
+       /*need resize or CSC?*/
+       if ((t->input.crop.w != t->output.crop.w) ||
+                       (t->input.crop.h != t->output.crop.h) ||
+                       need_csc(t->input.format, t->output.format))
+               t->set.mode |= IC_MODE;
+
+       /*need cropping?*/
+       if ((t->input.crop.w != t->input.width)       ||
+               (t->input.crop.h != t->input.height)  ||
+               (t->output.crop.w != t->output.width) ||
+               (t->output.crop.h != t->output.height))
+               t->set.mode |= IC_MODE;
+
+       /*need flip?*/
+       if ((t->set.mode == NULL_MODE) && (t->output.rotate > IPU_ROTATE_NONE))
+               t->set.mode |= IC_MODE;
+
+       /*need IDMAC do format(same color space)?*/
+       if ((t->set.mode == NULL_MODE) && (t->input.format != t->output.format))
+               t->set.mode |= IC_MODE;
+
+       /*overlay support*/
+       if (t->overlay_en)
+               t->set.mode |= IC_MODE;
+
+       /*deinterlace*/
+       if (t->input.deinterlace.enable) {
+               t->set.mode &= ~IC_MODE;
+               t->set.mode |= VDI_MODE;
+       }
+       if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) ||
+               (IPU_PIX_FMT_TILED_NV12F == t->input.format)) {
+               if (t->set.mode & ROT_MODE) {
+                       ret = IPU_CHECK_ERR_NOT_SUPPORT;
+                       goto done;
+               }
+               t->set.mode |= VDOA_MODE;
+               if (IPU_PIX_FMT_TILED_NV12F == t->input.format)
+                       t->set.mode |= VDOA_BAND_MODE;
+               t->set.mode &= ~IC_MODE;
+       }
+
+       if ((t->set.mode & (IC_MODE | VDI_MODE)) &&
+               (IPU_PIX_FMT_TILED_NV12F != t->input.format)) {
+               if (t->output.crop.w > soc_max_out_width())
+                       t->set.split_mode |= RL_SPLIT;
+               if (t->output.crop.h > soc_max_out_height())
+                       t->set.split_mode |= UD_SPLIT;
+               if (!t->set.split_mode && (t->set.mode & VDI_MODE) &&
+                               (t->input.crop.w >
+                                soc_max_vdi_in_width(t->ipu))) {
+                       t->set.split_mode |= RL_SPLIT;
+                       vdi_split = true;
+               }
+               if (t->set.split_mode) {
+                       if ((t->set.split_mode == RL_SPLIT) ||
+                                (t->set.split_mode == UD_SPLIT))
+                               timeout = DEF_TIMEOUT_MS * 2 + DEF_DELAY_MS;
+                       else
+                               timeout = DEF_TIMEOUT_MS * 4 + DEF_DELAY_MS;
+                       if (t->timeout < timeout)
+                               t->timeout = timeout;
+
+                       ret = update_split_setting(t, vdi_split);
+                       if (ret > IPU_CHECK_ERR_MIN)
+                               goto done;
+               }
+       }
+
+       if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
+               /*output swap*/
+               tmp = t->output.crop.w;
+               t->output.crop.w = t->output.crop.h;
+               t->output.crop.h = tmp;
+       }
+
+       if (t->set.mode == NULL_MODE) {
+               ret = IPU_CHECK_ERR_PROC_NO_NEED;
+               goto done;
+       }
+
+       if ((t->set.i_uoff % 8) || (t->set.i_voff % 8))
+               ret |= IPU_CHECK_WARN_INPUT_OFFS_NOT8ALIGN;
+       if ((t->set.o_uoff % 8) || (t->set.o_voff % 8))
+               ret |= IPU_CHECK_WARN_OUTPUT_OFFS_NOT8ALIGN;
+       if (t->overlay_en && ((t->set.ov_uoff % 8) || (t->set.ov_voff % 8)))
+               ret |= IPU_CHECK_WARN_OVERLAY_OFFS_NOT8ALIGN;
+
+done:
+       /* dump msg */
+       if (debug) {
+               if (ret > IPU_CHECK_ERR_MIN)
+                       dump_check_err(t->dev, ret);
+               else if (ret != IPU_CHECK_OK)
+                       dump_check_warn(t->dev, ret);
+       }
+
+       return ret;
+}
+
+static int prepare_task(struct ipu_task_entry *t)
+{
+       int ret = 0;
+
+       ret = check_task(t);
+       if (ret > IPU_CHECK_ERR_MIN)
+               return -EINVAL;
+
+       if (t->set.mode & VDI_MODE) {
+               t->task_id = IPU_TASK_ID_VF;
+               t->set.task = VDI_VF;
+               if (t->set.mode & ROT_MODE)
+                       t->set.task |= ROT_VF;
+       }
+
+       if (VDOA_MODE == t->set.mode) {
+               if (t->set.task != 0) {
+                       dev_err(t->dev, "ERR: vdoa only task:0x%x, [0x%p].\n",
+                                       t->set.task, t);
+                       return -EINVAL;
+               }
+               t->set.task |= VDOA_ONLY;
+       }
+
+       if (VDOA_BAND_MODE & t->set.mode) {
+               /* to save band size: 1<<3 = 8 lines */
+               t->set.band_lines = 3;
+       }
+
+       dump_task_info(t);
+
+       return ret;
+}
+
+static uint32_t ic_vf_pp_is_busy(struct ipu_soc *ipu, bool is_vf)
+{
+       uint32_t        status;
+       uint32_t        status_vf;
+       uint32_t        status_rot;
+
+       if (is_vf) {
+               status = ipu_channel_status(ipu, MEM_VDI_PRP_VF_MEM);
+               status_vf = ipu_channel_status(ipu, MEM_PRP_VF_MEM);
+               status_rot = ipu_channel_status(ipu, MEM_ROT_VF_MEM);
+               return status || status_vf || status_rot;
+       } else {
+               status = ipu_channel_status(ipu, MEM_PP_MEM);
+               status_rot = ipu_channel_status(ipu, MEM_ROT_PP_MEM);
+               return status || status_rot;
+       }
+}
+
+static int _get_vdoa_ipu_res(struct ipu_task_entry *t)
+{
+       int             i;
+       struct ipu_soc  *ipu;
+       u8              *used;
+       uint32_t        found_ipu = 0;
+       uint32_t        found_vdoa = 0;
+       struct ipu_channel_tabel        *tbl = &ipu_ch_tbl;
+
+       mutex_lock(&tbl->lock);
+       if (t->set.mode & VDOA_MODE) {
+               if (NULL != t->vdoa_handle)
+                       found_vdoa = 1;
+               else {
+                       found_vdoa = tbl->vdoa_used ? 0 : 1;
+                       if (found_vdoa) {
+                               tbl->vdoa_used = 1;
+                               vdoa_get_handle(&t->vdoa_handle);
+                       } else
+                               /* first get vdoa->ipu resource sequence */
+                               goto out;
+                       if (t->set.task & VDOA_ONLY)
+                               goto out;
+               }
+       }
+
+       for (i = 0; i < max_ipu_no; i++) {
+               ipu = ipu_get_soc(i);
+               if (IS_ERR(ipu))
+                       dev_err(t->dev, "no:0x%x,found_vdoa:%d, ipu:%d\n",
+                                t->task_no, found_vdoa, i);
+
+               used = &tbl->used[i][IPU_PP_CH_VF];
+               if (t->set.mode & VDI_MODE) {
+                       if (0 == *used) {
+                               *used = 1;
+                               found_ipu = 1;
+                               break;
+                       }
+               } else if ((t->set.mode & IC_MODE) || only_rot(t->set.mode)) {
+                       if (0 == *used) {
+                               t->task_id = IPU_TASK_ID_VF;
+                               if (t->set.mode & IC_MODE)
+                                       t->set.task |= IC_VF;
+                               if (t->set.mode & ROT_MODE)
+                                       t->set.task |= ROT_VF;
+                               *used = 1;
+                               found_ipu = 1;
+                               break;
+                       }
+               } else
+                       dev_err(t->dev, "no:0x%x,found_vdoa:%d, mode:0x%x\n",
+                                t->task_no, found_vdoa, t->set.mode);
+       }
+       if (found_ipu)
+               goto next;
+
+       for (i = 0; i < max_ipu_no; i++) {
+               ipu = ipu_get_soc(i);
+               if (IS_ERR(ipu))
+                       dev_err(t->dev, "no:0x%x,found_vdoa:%d, ipu:%d\n",
+                                t->task_no, found_vdoa, i);
+
+               if ((t->set.mode & IC_MODE) || only_rot(t->set.mode)) {
+                       used = &tbl->used[i][IPU_PP_CH_PP];
+                       if (0 == *used) {
+                               t->task_id = IPU_TASK_ID_PP;
+                               if (t->set.mode & IC_MODE)
+                                       t->set.task |= IC_PP;
+                               if (t->set.mode & ROT_MODE)
+                                       t->set.task |= ROT_PP;
+                               *used = 1;
+                               found_ipu = 1;
+                               break;
+                       }
+               }
+       }
+
+next:
+       if (found_ipu) {
+               t->ipu = ipu;
+               t->ipu_id = i;
+               t->dev = ipu->dev;
+               if (atomic_inc_return(&t->res_get) == 2)
+                       dev_err(t->dev,
+                               "ERR no:0x%x,found_vdoa:%d,get ipu twice\n",
+                                t->task_no, found_vdoa);
+       }
+out:
+       dev_dbg(t->dev,
+               "%s:no:0x%x,found_vdoa:%d, found_ipu:%d\n",
+                __func__, t->task_no, found_vdoa, found_ipu);
+       mutex_unlock(&tbl->lock);
+       if (t->set.task & VDOA_ONLY)
+               return found_vdoa;
+       else if (t->set.mode & VDOA_MODE)
+               return found_vdoa && found_ipu;
+       else
+               return found_ipu;
+}
+
+static void put_vdoa_ipu_res(struct ipu_task_entry *tsk, int vdoa_only)
+{
+       int ret;
+       int rel_vdoa = 0, rel_ipu = 0;
+       struct ipu_channel_tabel        *tbl = &ipu_ch_tbl;
+
+       mutex_lock(&tbl->lock);
+       if (tsk->set.mode & VDOA_MODE) {
+               if (!tbl->vdoa_used && tsk->vdoa_handle)
+                       dev_err(tsk->dev,
+                               "ERR no:0x%x,vdoa not used,mode:0x%x\n",
+                                tsk->task_no, tsk->set.mode);
+               if (tbl->vdoa_used && tsk->vdoa_handle) {
+                       tbl->vdoa_used = 0;
+                       vdoa_put_handle(&tsk->vdoa_handle);
+                       if (tsk->ipu)
+                               tsk->ipu->vdoa_en = 0;
+                       rel_vdoa = 1;
+                       if (vdoa_only || (tsk->set.task & VDOA_ONLY))
+                               goto out;
+               }
+       }
+
+       tbl->used[tsk->ipu_id][tsk->task_id - 1] = 0;
+       rel_ipu = 1;
+       ret = atomic_inc_return(&tsk->res_free);
+       if (ret == 2)
+               dev_err(tsk->dev,
+                       "ERR no:0x%x,rel_vdoa:%d,put ipu twice\n",
+                        tsk->task_no, rel_vdoa);
+out:
+       dev_dbg(tsk->dev,
+               "%s:no:0x%x,rel_vdoa:%d, rel_ipu:%d\n",
+                __func__, tsk->task_no, rel_vdoa, rel_ipu);
+       mutex_unlock(&tbl->lock);
+}
+
+static int get_vdoa_ipu_res(struct ipu_task_entry *t)
+{
+       int             ret;
+       uint32_t        found = 0;
+
+       found = _get_vdoa_ipu_res(t);
+       if (!found) {
+               t->ipu_id = -1;
+               t->ipu = NULL;
+               /* blocking to get resource */
+               ret = atomic_inc_return(&req_cnt);
+               dev_dbg(t->dev,
+                       "wait_res:no:0x%x,req_cnt:%d\n", t->task_no, ret);
+               ret = wait_event_timeout(res_waitq, _get_vdoa_ipu_res(t),
+                                msecs_to_jiffies(t->timeout - DEF_DELAY_MS));
+               if (ret == 0) {
+                       dev_err(t->dev, "ERR[0x%p,no-0x%x] wait_res timeout:%dms!\n",
+                                        t, t->task_no, t->timeout - DEF_DELAY_MS);
+                       ret = -ETIMEDOUT;
+                       t->state = STATE_RES_TIMEOUT;
+                       goto out;
+               } else {
+                       if (!(t->set.task & VDOA_ONLY) && (!t->ipu))
+                               dev_err(t->dev,
+                                       "ERR[no-0x%x] can not get ipu!\n",
+                                       t->task_no);
+                       ret = atomic_read(&req_cnt);
+                       if (ret > 0)
+                               ret = atomic_dec_return(&req_cnt);
+                       else
+                               dev_err(t->dev,
+                                       "ERR[no-0x%x] req_cnt:%d mismatch!\n",
+                                       t->task_no, ret);
+                       dev_dbg(t->dev, "no-0x%x,[0x%p],req_cnt:%d, got_res!\n",
+                                               t->task_no, t, ret);
+                       found = 1;
+               }
+       }
+
+out:
+       return found;
+}
+
+static struct ipu_task_entry *create_task_entry(struct ipu_task *task)
+{
+       struct ipu_task_entry *tsk;
+
+       tsk = kzalloc(sizeof(struct ipu_task_entry), GFP_KERNEL);
+       if (!tsk)
+               return ERR_PTR(-ENOMEM);
+       kref_init(&tsk->refcount);
+       tsk->state = -EINVAL;
+       tsk->ipu_id = -1;
+       tsk->dev = ipu_dev;
+       tsk->input = task->input;
+       tsk->output = task->output;
+       tsk->overlay_en = task->overlay_en;
+       if (tsk->overlay_en)
+               tsk->overlay = task->overlay;
+       if (task->timeout > DEF_TIMEOUT_MS)
+               tsk->timeout = task->timeout;
+       else
+               tsk->timeout = DEF_TIMEOUT_MS;
+
+       return tsk;
+}
+
+static void task_mem_free(struct kref *ref)
+{
+       struct ipu_task_entry *tsk =
+                       container_of(ref, struct ipu_task_entry, refcount);
+       kfree(tsk);
+}
+
+int create_split_child_task(struct ipu_split_task *sp_task)
+{
+       int ret = 0;
+       struct ipu_task_entry *tsk;
+
+       tsk = create_task_entry(&sp_task->task);
+       if (IS_ERR(tsk))
+               return PTR_ERR(tsk);
+
+       sp_task->child_task = tsk;
+       tsk->task_no = sp_task->task_no;
+
+       ret = prepare_task(tsk);
+       if (ret < 0)
+               goto err;
+
+       tsk->parent = sp_task->parent_task;
+       tsk->set.sp_setting = sp_task->parent_task->set.sp_setting;
+
+       list_add(&tsk->node, &tsk->parent->split_list);
+       dev_dbg(tsk->dev, "[0x%p] sp_tsk Q list,no-0x%x\n", tsk, tsk->task_no);
+       tsk->state = STATE_QUEUE;
+       CHECK_PERF(&tsk->ts_queue);
+err:
+       return ret;
+}
+
+static inline int sp_task_check_done(struct ipu_split_task *sp_task,
+                       struct ipu_task_entry *parent, int num, int *idx)
+{
+       int i;
+       int ret = 0;
+       struct ipu_task_entry *tsk;
+       struct mutex *lock = &parent->split_lock;
+
+       *idx = -EINVAL;
+       mutex_lock(lock);
+       for (i = 0; i < num; i++) {
+               tsk = sp_task[i].child_task;
+               if (tsk && tsk->split_done) {
+                       *idx = i;
+                       ret = 1;
+                       goto out;
+               }
+       }
+
+out:
+       mutex_unlock(lock);
+       return ret;
+}
+
+static int create_split_task(
+               int stripe,
+               struct ipu_split_task *sp_task)
+{
+       struct ipu_task *task = &(sp_task->task);
+       struct ipu_task_entry *t = sp_task->parent_task;
+       int ret;
+
+       sp_task->task_no |= stripe;
+
+       task->input = t->input;
+       task->output = t->output;
+       task->overlay_en = t->overlay_en;
+       if (task->overlay_en)
+               task->overlay = t->overlay;
+       task->task_id = t->task_id;
+       if ((t->set.split_mode == RL_SPLIT) ||
+                (t->set.split_mode == UD_SPLIT))
+               task->timeout = t->timeout / 2;
+       else
+               task->timeout = t->timeout / 4;
+
+       task->input.crop.w = t->set.sp_setting.iw;
+       task->input.crop.h = t->set.sp_setting.ih;
+       if (task->overlay_en) {
+               task->overlay.crop.w = t->set.sp_setting.ow;
+               task->overlay.crop.h = t->set.sp_setting.oh;
+       }
+       if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
+               task->output.crop.w = t->set.sp_setting.oh;
+               task->output.crop.h = t->set.sp_setting.ow;
+               t->set.sp_setting.rl_split_line = t->set.sp_setting.o_bottom_pos;
+               t->set.sp_setting.ud_split_line = t->set.sp_setting.o_right_pos;
+
+       } else {
+               task->output.crop.w = t->set.sp_setting.ow;
+               task->output.crop.h = t->set.sp_setting.oh;
+               t->set.sp_setting.rl_split_line = t->set.sp_setting.o_right_pos;
+               t->set.sp_setting.ud_split_line = t->set.sp_setting.o_bottom_pos;
+       }
+
+       if (stripe & LEFT_STRIPE)
+               task->input.crop.pos.x += t->set.sp_setting.i_left_pos;
+       else if (stripe & RIGHT_STRIPE)
+               task->input.crop.pos.x += t->set.sp_setting.i_right_pos;
+       if (stripe & UP_STRIPE)
+               task->input.crop.pos.y += t->set.sp_setting.i_top_pos;
+       else if (stripe & DOWN_STRIPE)
+               task->input.crop.pos.y += t->set.sp_setting.i_bottom_pos;
+
+       if (task->overlay_en) {
+               if (stripe & LEFT_STRIPE)
+                       task->overlay.crop.pos.x += t->set.sp_setting.o_left_pos;
+               else if (stripe & RIGHT_STRIPE)
+                       task->overlay.crop.pos.x += t->set.sp_setting.o_right_pos;
+               if (stripe & UP_STRIPE)
+                       task->overlay.crop.pos.y += t->set.sp_setting.o_top_pos;
+               else if (stripe & DOWN_STRIPE)
+                       task->overlay.crop.pos.y += t->set.sp_setting.o_bottom_pos;
+       }
+
+       switch (t->output.rotate) {
+       case IPU_ROTATE_NONE:
+               if (stripe & LEFT_STRIPE)
+                       task->output.crop.pos.x += t->set.sp_setting.o_left_pos;
+               else if (stripe & RIGHT_STRIPE)
+                       task->output.crop.pos.x += t->set.sp_setting.o_right_pos;
+               if (stripe & UP_STRIPE)
+                       task->output.crop.pos.y += t->set.sp_setting.o_top_pos;
+               else if (stripe & DOWN_STRIPE)
+                       task->output.crop.pos.y += t->set.sp_setting.o_bottom_pos;
+               break;
+       case IPU_ROTATE_VERT_FLIP:
+               if (stripe & LEFT_STRIPE)
+                       task->output.crop.pos.x += t->set.sp_setting.o_left_pos;
+               else if (stripe & RIGHT_STRIPE)
+                       task->output.crop.pos.x += t->set.sp_setting.o_right_pos;
+               if (stripe & UP_STRIPE)
+                       task->output.crop.pos.y =
+                                       t->output.crop.pos.y + t->output.crop.h
+                                       - t->set.sp_setting.o_top_pos - t->set.sp_setting.oh;
+               else if (stripe & DOWN_STRIPE)
+                       task->output.crop.pos.y =
+                                       t->output.crop.pos.y + t->output.crop.h
+                                       - t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh;
+               break;
+       case IPU_ROTATE_HORIZ_FLIP:
+               if (stripe & LEFT_STRIPE)
+                       task->output.crop.pos.x =
+                                       t->output.crop.pos.x + t->output.crop.w
+                                       - t->set.sp_setting.o_left_pos - t->set.sp_setting.ow;
+               else if (stripe & RIGHT_STRIPE)
+                       task->output.crop.pos.x =
+                                       t->output.crop.pos.x + t->output.crop.w
+                                       - t->set.sp_setting.o_right_pos - t->set.sp_setting.ow;
+               if (stripe & UP_STRIPE)
+                       task->output.crop.pos.y += t->set.sp_setting.o_top_pos;
+               else if (stripe & DOWN_STRIPE)
+                       task->output.crop.pos.y += t->set.sp_setting.o_bottom_pos;
+               break;
+       case IPU_ROTATE_180:
+               if (stripe & LEFT_STRIPE)
+                       task->output.crop.pos.x =
+                                       t->output.crop.pos.x + t->output.crop.w
+                                       - t->set.sp_setting.o_left_pos - t->set.sp_setting.ow;
+               else if (stripe & RIGHT_STRIPE)
+                       task->output.crop.pos.x =
+                                       t->output.crop.pos.x + t->output.crop.w
+                                       - t->set.sp_setting.o_right_pos - t->set.sp_setting.ow;
+               if (stripe & UP_STRIPE)
+                       task->output.crop.pos.y =
+                                       t->output.crop.pos.y + t->output.crop.h
+                                       - t->set.sp_setting.o_top_pos - t->set.sp_setting.oh;
+               else if (stripe & DOWN_STRIPE)
+                       task->output.crop.pos.y =
+                                       t->output.crop.pos.y + t->output.crop.h
+                                       - t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh;
+               break;
+       case IPU_ROTATE_90_RIGHT:
+               if (stripe & UP_STRIPE)
+                       task->output.crop.pos.x =
+                                       t->output.crop.pos.x + t->output.crop.w
+                                       - t->set.sp_setting.o_top_pos - t->set.sp_setting.oh;
+               else if (stripe & DOWN_STRIPE)
+                       task->output.crop.pos.x =
+                                       t->output.crop.pos.x + t->output.crop.w
+                                       - t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh;
+               if (stripe & LEFT_STRIPE)
+                       task->output.crop.pos.y += t->set.sp_setting.o_left_pos;
+               else if (stripe & RIGHT_STRIPE)
+                       task->output.crop.pos.y += t->set.sp_setting.o_right_pos;
+               break;
+       case IPU_ROTATE_90_RIGHT_HFLIP:
+               if (stripe & UP_STRIPE)
+                       task->output.crop.pos.x += t->set.sp_setting.o_top_pos;
+               else if (stripe & DOWN_STRIPE)
+                       task->output.crop.pos.x += t->set.sp_setting.o_bottom_pos;
+               if (stripe & LEFT_STRIPE)
+                       task->output.crop.pos.y += t->set.sp_setting.o_left_pos;
+               else if (stripe & RIGHT_STRIPE)
+                       task->output.crop.pos.y += t->set.sp_setting.o_right_pos;
+               break;
+       case IPU_ROTATE_90_RIGHT_VFLIP:
+               if (stripe & UP_STRIPE)
+                       task->output.crop.pos.x =
+                                       t->output.crop.pos.x + t->output.crop.w
+                                       - t->set.sp_setting.o_top_pos - t->set.sp_setting.oh;
+               else if (stripe & DOWN_STRIPE)
+                       task->output.crop.pos.x =
+                                       t->output.crop.pos.x + t->output.crop.w
+                                       - t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh;
+               if (stripe & LEFT_STRIPE)
+                       task->output.crop.pos.y =
+                                       t->output.crop.pos.y + t->output.crop.h
+                                       - t->set.sp_setting.o_left_pos - t->set.sp_setting.ow;
+               else if (stripe & RIGHT_STRIPE)
+                       task->output.crop.pos.y =
+                                       t->output.crop.pos.y + t->output.crop.h
+                                       - t->set.sp_setting.o_right_pos - t->set.sp_setting.ow;
+               break;
+       case IPU_ROTATE_90_LEFT:
+               if (stripe & UP_STRIPE)
+                       task->output.crop.pos.x += t->set.sp_setting.o_top_pos;
+               else if (stripe & DOWN_STRIPE)
+                       task->output.crop.pos.x += t->set.sp_setting.o_bottom_pos;
+               if (stripe & LEFT_STRIPE)
+                       task->output.crop.pos.y =
+                                       t->output.crop.pos.y + t->output.crop.h
+                                       - t->set.sp_setting.o_left_pos - t->set.sp_setting.ow;
+               else if (stripe & RIGHT_STRIPE)
+                       task->output.crop.pos.y =
+                                       t->output.crop.pos.y + t->output.crop.h
+                                       - t->set.sp_setting.o_right_pos - t->set.sp_setting.ow;
+               break;
+       default:
+               dev_err(t->dev, "ERR:should not be here\n");
+               break;
+       }
+
+       ret = create_split_child_task(sp_task);
+       if (ret < 0)
+               dev_err(t->dev, "ERR:create_split_child_task() ret:%d\n", ret);
+       return ret;
+}
+
+static int queue_split_task(struct ipu_task_entry *t,
+                               struct ipu_split_task *sp_task, uint32_t size)
+{
+       int err[4];
+       int ret = 0;
+       int i, j;
+       struct ipu_task_entry *tsk = NULL;
+       struct mutex *lock = &t->split_lock;
+       struct mutex *vdic_lock = &t->vdic_lock;
+
+       dev_dbg(t->dev, "Split task 0x%p, no-0x%x, size:%d\n",
+                        t, t->task_no, size);
+       mutex_init(lock);
+       mutex_init(vdic_lock);
+       init_waitqueue_head(&t->split_waitq);
+       INIT_LIST_HEAD(&t->split_list);
+       for (j = 0; j < size; j++) {
+               memset(&sp_task[j], 0, sizeof(*sp_task));
+               sp_task[j].parent_task = t;
+               sp_task[j].task_no = t->task_no;
+       }
+
+       if (t->set.split_mode == RL_SPLIT) {
+               i = 0;
+               err[i] = create_split_task(RIGHT_STRIPE, &sp_task[i]);
+               if (err[i] < 0)
+                       goto err_start;
+               i = 1;
+               err[i] = create_split_task(LEFT_STRIPE, &sp_task[i]);
+       } else if (t->set.split_mode == UD_SPLIT) {
+               i = 0;
+               err[i] = create_split_task(DOWN_STRIPE, &sp_task[i]);
+               if (err[i] < 0)
+                       goto err_start;
+               i = 1;
+               err[i] = create_split_task(UP_STRIPE, &sp_task[i]);
+       } else {
+               i = 0;
+               err[i] = create_split_task(RIGHT_STRIPE | DOWN_STRIPE, &sp_task[i]);
+               if (err[i] < 0)
+                       goto err_start;
+               i = 1;
+               err[i] = create_split_task(LEFT_STRIPE | DOWN_STRIPE, &sp_task[i]);
+               if (err[i] < 0)
+                       goto err_start;
+               i = 2;
+               err[i] = create_split_task(RIGHT_STRIPE | UP_STRIPE, &sp_task[i]);
+               if (err[i] < 0)
+                       goto err_start;
+               i = 3;
+               err[i] = create_split_task(LEFT_STRIPE | UP_STRIPE, &sp_task[i]);
+       }
+
+err_start:
+       for (j = 0; j < (i + 1); j++) {
+               if (err[j] < 0) {
+                       if (sp_task[j].child_task)
+                               dev_err(t->dev,
+                                "sp_task[%d],no-0x%x fail state:%d, queue err:%d.\n",
+                               j, sp_task[j].child_task->task_no,
+                               sp_task[j].child_task->state, err[j]);
+                       goto err_exit;
+               }
+               dev_dbg(t->dev, "[0x%p] sp_task[%d], no-0x%x state:%s, queue ret:%d.\n",
+                       sp_task[j].child_task, j, sp_task[j].child_task->task_no,
+                       state_msg[sp_task[j].child_task->state].msg, err[j]);
+       }
+
+       return ret;
+
+err_exit:
+       for (j = 0; j < (i + 1); j++) {
+               if (err[j] < 0 && !ret)
+                       ret = err[j];
+               tsk = sp_task[j].child_task;
+               if (!tsk)
+                       continue;
+               kfree(tsk);
+       }
+       t->state = STATE_ERR;
+       return ret;
+
+}
+
+static int init_tiled_buf(struct ipu_soc *ipu, struct ipu_task_entry *t,
+                               ipu_channel_t channel, uint32_t ch_type)
+{
+       int ret = 0;
+       int i;
+       uint32_t ipu_fmt;
+       dma_addr_t inbuf_base = 0;
+       u32 field_size;
+       struct vdoa_params param;
+       struct vdoa_ipu_buf buf;
+       struct ipu_soc *ipu_idx;
+       u32 ipu_stride, obuf_size;
+       u32 height, width;
+       ipu_buffer_t type;
+
+       if ((IPU_PIX_FMT_YUYV != t->output.format) &&
+               (IPU_PIX_FMT_NV12 != t->output.format)) {
+               dev_err(t->dev, "ERR:[0x%d] output format\n", t->task_no);
+               return -EINVAL;
+       }
+
+       memset(&param, 0, sizeof(param));
+       /* init channel tiled bufs */
+       if (deinterlace_3_field(t) &&
+               (IPU_PIX_FMT_TILED_NV12F == t->input.format)) {
+               field_size = tiled_filed_size(t);
+               if (INPUT_CHAN_VDI_P == ch_type) {
+                       inbuf_base = t->input.paddr + field_size;
+                       param.vfield_buf.prev_veba = inbuf_base + t->set.i_off;
+               } else if (INPUT_CHAN == ch_type) {
+                       inbuf_base = t->input.paddr_n;
+                       param.vfield_buf.cur_veba = inbuf_base + t->set.i_off;
+               } else if (INPUT_CHAN_VDI_N == ch_type) {
+                       inbuf_base = t->input.paddr_n + field_size;
+                       param.vfield_buf.next_veba = inbuf_base + t->set.i_off;
+               } else
+                       return -EINVAL;
+               height = t->input.crop.h >> 1; /* field format for vdoa */
+               width = t->input.crop.w;
+               param.vfield_buf.vubo = t->set.i_uoff;
+               param.interlaced = 1;
+               param.scan_order = 1;
+               type = IPU_INPUT_BUFFER;
+       } else if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) &&
+                       (INPUT_CHAN == ch_type)) {
+               height = t->input.crop.h;
+               width = t->input.crop.w;
+               param.vframe_buf.veba = t->input.paddr + t->set.i_off;
+               param.vframe_buf.vubo = t->set.i_uoff;
+               type = IPU_INPUT_BUFFER;
+       } else
+               return -EINVAL;
+
+       param.band_mode = (t->set.mode & VDOA_BAND_MODE) ? 1 : 0;
+       if (param.band_mode && (t->set.band_lines != 3) &&
+                (t->set.band_lines != 4) && (t->set.band_lines != 5))
+               return -EINVAL;
+       else if (param.band_mode)
+               param.band_lines = (1 << t->set.band_lines);
+       for (i = 0; i < max_ipu_no; i++) {
+               ipu_idx = ipu_get_soc(i);
+               if (!IS_ERR(ipu_idx) && ipu_idx == ipu)
+                       break;
+       }
+       if (t->set.task & VDOA_ONLY)
+               /* dummy, didn't need ipu res */
+               i = 0;
+       if (max_ipu_no == i) {
+               dev_err(t->dev, "ERR:[0x%p] get ipu num\n", t);
+               return -EINVAL;
+       }
+
+       param.ipu_num = i;
+       param.vpu_stride = t->input.width;
+       param.height = height;
+       param.width = width;
+       if (IPU_PIX_FMT_NV12 == t->output.format)
+               param.pfs = VDOA_PFS_NV12;
+       else
+               param.pfs = VDOA_PFS_YUYV;
+       ipu_fmt = (param.pfs == VDOA_PFS_YUYV) ? IPU_PIX_FMT_YUYV :
+                               IPU_PIX_FMT_NV12;
+       ipu_stride = param.width * bytes_per_pixel(ipu_fmt);
+       obuf_size = PAGE_ALIGN(param.width * param.height *
+                               fmt_to_bpp(ipu_fmt)/8);
+       dev_dbg(t->dev, "band_mode:%d, band_lines:%d\n",
+                       param.band_mode, param.band_lines);
+       if (!param.band_mode) {
+               /* note: if only for tiled -> raster convert and
+                  no other post-processing, we don't need alloc buf
+                  and use output buffer directly.
+               */
+               if (t->set.task & VDOA_ONLY)
+                       param.ieba0 = t->output.paddr;
+               else {
+                       dev_err(t->dev, "ERR:[0x%d] vdoa task\n", t->task_no);
+                       return -EINVAL;
+               }
+       } else {
+               if (IPU_PIX_FMT_TILED_NV12F != t->input.format) {
+                       dev_err(t->dev, "ERR [0x%d] vdoa task\n", t->task_no);
+                       return -EINVAL;
+               }
+       }
+       ret = vdoa_setup(t->vdoa_handle, &param);
+       if (ret)
+               goto done;
+       vdoa_get_output_buf(t->vdoa_handle, &buf);
+       if (t->set.task & VDOA_ONLY)
+               goto done;
+
+       ret = ipu_init_channel_buffer(ipu,
+                       channel,
+                       type,
+                       ipu_fmt,
+                       width,
+                       height,
+                       ipu_stride,
+                       IPU_ROTATE_NONE,
+                       buf.ieba0,
+                       buf.ieba1,
+                       0,
+                       buf.iubo,
+                       0);
+       if (ret < 0) {
+               t->state = STATE_INIT_CHAN_BUF_FAIL;
+               goto done;
+       }
+
+       if (param.band_mode) {
+               ret = ipu_set_channel_bandmode(ipu, channel,
+                               type, t->set.band_lines);
+               if (ret < 0) {
+                       t->state = STATE_INIT_CHAN_BAND_FAIL;
+                       goto done;
+               }
+       }
+done:
+       return ret;
+}
+
+static int init_tiled_ch_bufs(struct ipu_soc *ipu, struct ipu_task_entry *t)
+{
+       int ret = 0;
+
+       if (IPU_PIX_FMT_TILED_NV12 == t->input.format) {
+               ret = init_tiled_buf(ipu, t, t->set.ic_chan, INPUT_CHAN);
+               CHECK_RETCODE(ret < 0, "init tiled_ch", t->state, done, ret);
+       } else if (IPU_PIX_FMT_TILED_NV12F == t->input.format) {
+               ret = init_tiled_buf(ipu, t, t->set.ic_chan, INPUT_CHAN);
+               CHECK_RETCODE(ret < 0, "init tiled_ch-c", t->state, done, ret);
+               ret = init_tiled_buf(ipu, t, t->set.vdi_ic_p_chan,
+                                       INPUT_CHAN_VDI_P);
+               CHECK_RETCODE(ret < 0, "init tiled_ch-p", t->state, done, ret);
+               ret = init_tiled_buf(ipu, t, t->set.vdi_ic_n_chan,
+                                       INPUT_CHAN_VDI_N);
+               CHECK_RETCODE(ret < 0, "init tiled_ch-n", t->state, done, ret);
+       } else {
+               ret = -EINVAL;
+               dev_err(t->dev, "ERR[no-0x%x] invalid fmt:0x%x!\n",
+                       t->task_no, t->input.format);
+       }
+
+done:
+       return ret;
+}
+
+static int init_ic(struct ipu_soc *ipu, struct ipu_task_entry *t)
+{
+       int ret = 0;
+       ipu_channel_params_t params;
+       dma_addr_t inbuf = 0, ovbuf = 0, ov_alp_buf = 0;
+       dma_addr_t inbuf_p = 0, inbuf_n = 0;
+       dma_addr_t outbuf = 0;
+       int out_uoff = 0, out_voff = 0, out_rot;
+       int out_w = 0, out_h = 0, out_stride;
+       int out_fmt;
+       u32 vdi_frame_idx = 0;
+
+       memset(&params, 0, sizeof(params));
+
+       /* is it need link a rot channel */
+       if (ic_and_rot(t->set.mode)) {
+               outbuf = t->set.r_paddr;
+               out_w = t->set.r_width;
+               out_h = t->set.r_height;
+               out_stride = t->set.r_stride;
+               out_fmt = t->set.r_fmt;
+               out_uoff = 0;
+               out_voff = 0;
+               out_rot = IPU_ROTATE_NONE;
+       } else {
+               outbuf = t->output.paddr + t->set.o_off;
+               out_w = t->output.crop.w;
+               out_h = t->output.crop.h;
+               out_stride = t->set.ostride;
+               out_fmt = t->output.format;
+               out_uoff = t->set.o_uoff;
+               out_voff = t->set.o_voff;
+               out_rot = t->output.rotate;
+       }
+
+       /* settings */
+       params.mem_prp_vf_mem.in_width = t->input.crop.w;
+       params.mem_prp_vf_mem.out_width = out_w;
+       params.mem_prp_vf_mem.in_height = t->input.crop.h;
+       params.mem_prp_vf_mem.out_height = out_h;
+       params.mem_prp_vf_mem.in_pixel_fmt = t->input.format;
+       params.mem_prp_vf_mem.out_pixel_fmt = out_fmt;
+       params.mem_prp_vf_mem.motion_sel = t->input.deinterlace.motion;
+
+       params.mem_prp_vf_mem.outh_resize_ratio =
+                       t->set.sp_setting.outh_resize_ratio;
+       params.mem_prp_vf_mem.outv_resize_ratio =
+                       t->set.sp_setting.outv_resize_ratio;
+
+       if (t->overlay_en) {
+               params.mem_prp_vf_mem.in_g_pixel_fmt = t->overlay.format;
+               params.mem_prp_vf_mem.graphics_combine_en = 1;
+               if (t->overlay.alpha.mode == IPU_ALPHA_MODE_GLOBAL)
+                       params.mem_prp_vf_mem.global_alpha_en = 1;
+               else if (t->overlay.alpha.loc_alp_paddr)
+                       params.mem_prp_vf_mem.alpha_chan_en = 1;
+               /* otherwise, alpha bending per pixel is used. */
+               params.mem_prp_vf_mem.alpha = t->overlay.alpha.gvalue;
+               if (t->overlay.colorkey.enable) {
+                       params.mem_prp_vf_mem.key_color_en = 1;
+                       params.mem_prp_vf_mem.key_color = t->overlay.colorkey.value;
+               }
+       }
+
+       if (t->input.deinterlace.enable) {
+               if (t->input.deinterlace.field_fmt & IPU_DEINTERLACE_FIELD_MASK)
+                       params.mem_prp_vf_mem.field_fmt =
+                               IPU_DEINTERLACE_FIELD_BOTTOM;
+               else
+                       params.mem_prp_vf_mem.field_fmt =
+                               IPU_DEINTERLACE_FIELD_TOP;
+
+               if (t->input.deinterlace.field_fmt & IPU_DEINTERLACE_RATE_EN)
+                       vdi_frame_idx = t->input.deinterlace.field_fmt &
+                                               IPU_DEINTERLACE_RATE_FRAME1;
+       }
+
+       if (t->set.mode & VDOA_MODE)
+               ipu->vdoa_en = 1;
+
+       /* init channels */
+       if (!(t->set.task & VDOA_ONLY)) {
+               ret = ipu_init_channel(ipu, t->set.ic_chan, &params);
+               if (ret < 0) {
+                       t->state = STATE_INIT_CHAN_FAIL;
+                       goto done;
+               }
+       }
+
+       if (deinterlace_3_field(t)) {
+               ret = ipu_init_channel(ipu, t->set.vdi_ic_p_chan, &params);
+               if (ret < 0) {
+                       t->state = STATE_INIT_CHAN_FAIL;
+                       goto done;
+               }
+               ret = ipu_init_channel(ipu, t->set.vdi_ic_n_chan, &params);
+               if (ret < 0) {
+                       t->state = STATE_INIT_CHAN_FAIL;
+                       goto done;
+               }
+       }
+
+       /* init channel bufs */
+       if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) ||
+               (IPU_PIX_FMT_TILED_NV12F == t->input.format)) {
+               ret = init_tiled_ch_bufs(ipu, t);
+               if (ret < 0)
+                       goto done;
+       } else {
+               if ((deinterlace_3_field(t)) &&
+                       (IPU_PIX_FMT_TILED_NV12F != t->input.format)) {
+                       if (params.mem_prp_vf_mem.field_fmt ==
+                               IPU_DEINTERLACE_FIELD_TOP) {
+                               if (vdi_frame_idx) {
+                                       inbuf_p = t->input.paddr + t->set.istride +
+                                                       t->set.i_off;
+                                       inbuf = t->input.paddr_n + t->set.i_off;
+                                       inbuf_n = t->input.paddr_n + t->set.istride +
+                                                       t->set.i_off;
+                                       params.mem_prp_vf_mem.field_fmt =
+                                               IPU_DEINTERLACE_FIELD_BOTTOM;
+                               } else {
+                                       inbuf_p = t->input.paddr + t->set.i_off;
+                                       inbuf = t->input.paddr + t->set.istride + t->set.i_off;
+                                       inbuf_n = t->input.paddr_n + t->set.i_off;
+                               }
+                       } else {
+                               if (vdi_frame_idx) {
+                                       inbuf_p = t->input.paddr + t->set.i_off;
+                                       inbuf = t->input.paddr_n + t->set.istride + t->set.i_off;
+                                       inbuf_n = t->input.paddr_n + t->set.i_off;
+                                       params.mem_prp_vf_mem.field_fmt =
+                                               IPU_DEINTERLACE_FIELD_TOP;
+                               } else {
+                                       inbuf_p = t->input.paddr + t->set.istride +
+                                                       t->set.i_off;
+                                       inbuf = t->input.paddr + t->set.i_off;
+                                       inbuf_n = t->input.paddr_n + t->set.istride +
+                                                       t->set.i_off;
+                               }
+                       }
+               } else {
+                       if (t->input.deinterlace.enable) {
+                               if (params.mem_prp_vf_mem.field_fmt ==
+                                       IPU_DEINTERLACE_FIELD_TOP) {
+                                       if (vdi_frame_idx) {
+                                               inbuf = t->input.paddr + t->set.istride + t->set.i_off;
+                                               params.mem_prp_vf_mem.field_fmt =
+                                                       IPU_DEINTERLACE_FIELD_BOTTOM;
+                                       } else
+                                               inbuf = t->input.paddr + t->set.i_off;
+                               } else {
+                                       if (vdi_frame_idx) {
+                                               inbuf = t->input.paddr + t->set.i_off;
+                                               params.mem_prp_vf_mem.field_fmt =
+                                                       IPU_DEINTERLACE_FIELD_TOP;
+                                       } else
+                                               inbuf = t->input.paddr + t->set.istride + t->set.i_off;
+                               }
+                       } else
+                               inbuf = t->input.paddr + t->set.i_off;
+               }
+
+               if (t->overlay_en)
+                       ovbuf = t->overlay.paddr + t->set.ov_off;
+       }
+       if (t->overlay_en && (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL))
+               ov_alp_buf = t->overlay.alpha.loc_alp_paddr
+                       + t->set.ov_alpha_off;
+
+       if ((IPU_PIX_FMT_TILED_NV12 != t->input.format) &&
+               (IPU_PIX_FMT_TILED_NV12F != t->input.format)) {
+               ret = ipu_init_channel_buffer(ipu,
+                               t->set.ic_chan,
+                               IPU_INPUT_BUFFER,
+                               t->input.format,
+                               t->input.crop.w,
+                               t->input.crop.h,
+                               t->set.istride,
+                               IPU_ROTATE_NONE,
+                               inbuf,
+                               0,
+                               0,
+                               t->set.i_uoff,
+                               t->set.i_voff);
+               if (ret < 0) {
+                       t->state = STATE_INIT_CHAN_BUF_FAIL;
+                       goto done;
+               }
+       }
+       if (deinterlace_3_field(t) &&
+               (IPU_PIX_FMT_TILED_NV12F != t->input.format)) {
+               ret = ipu_init_channel_buffer(ipu,
+                               t->set.vdi_ic_p_chan,
+                               IPU_INPUT_BUFFER,
+                               t->input.format,
+                               t->input.crop.w,
+                               t->input.crop.h,
+                               t->set.istride,
+                               IPU_ROTATE_NONE,
+                               inbuf_p,
+                               0,
+                               0,
+                               t->set.i_uoff,
+                               t->set.i_voff);
+               if (ret < 0) {
+                       t->state = STATE_INIT_CHAN_BUF_FAIL;
+                       goto done;
+               }
+
+               ret = ipu_init_channel_buffer(ipu,
+                               t->set.vdi_ic_n_chan,
+                               IPU_INPUT_BUFFER,
+                               t->input.format,
+                               t->input.crop.w,
+                               t->input.crop.h,
+                               t->set.istride,
+                               IPU_ROTATE_NONE,
+                               inbuf_n,
+                               0,
+                               0,
+                               t->set.i_uoff,
+                               t->set.i_voff);
+               if (ret < 0) {
+                       t->state = STATE_INIT_CHAN_BUF_FAIL;
+                       goto done;
+               }
+       }
+
+       if (t->overlay_en) {
+               ret = ipu_init_channel_buffer(ipu,
+                               t->set.ic_chan,
+                               IPU_GRAPH_IN_BUFFER,
+                               t->overlay.format,
+                               t->overlay.crop.w,
+                               t->overlay.crop.h,
+                               t->set.ovstride,
+                               IPU_ROTATE_NONE,
+                               ovbuf,
+                               0,
+                               0,
+                               t->set.ov_uoff,
+                               t->set.ov_voff);
+               if (ret < 0) {
+                       t->state = STATE_INIT_CHAN_BUF_FAIL;
+                       goto done;
+               }
+       }
+
+       if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) {
+               ret = ipu_init_channel_buffer(ipu,
+                               t->set.ic_chan,
+                               IPU_ALPHA_IN_BUFFER,
+                               IPU_PIX_FMT_GENERIC,
+                               t->overlay.crop.w,
+                               t->overlay.crop.h,
+                               t->set.ov_alpha_stride,
+                               IPU_ROTATE_NONE,
+                               ov_alp_buf,
+                               0,
+                               0,
+                               0, 0);
+               if (ret < 0) {
+                       t->state = STATE_INIT_CHAN_BUF_FAIL;
+                       goto done;
+               }
+       }
+
+       if (!(t->set.task & VDOA_ONLY)) {
+               ret = ipu_init_channel_buffer(ipu,
+                               t->set.ic_chan,
+                               IPU_OUTPUT_BUFFER,
+                               out_fmt,
+                               out_w,
+                               out_h,
+                               out_stride,
+                               out_rot,
+                               outbuf,
+                               0,
+                               0,
+                               out_uoff,
+                               out_voff);
+               if (ret < 0) {
+                       t->state = STATE_INIT_CHAN_BUF_FAIL;
+                       goto done;
+               }
+       }
+
+       if ((t->set.mode & VDOA_BAND_MODE) && (t->set.task & VDI_VF)) {
+               ret = ipu_link_channels(ipu, MEM_VDOA_MEM, t->set.ic_chan);
+               CHECK_RETCODE(ret < 0, "ipu_link_ch vdoa_ic",
+                               STATE_LINK_CHAN_FAIL, done, ret);
+       }
+
+done:
+       return ret;
+}
+
+static void uninit_ic(struct ipu_soc *ipu, struct ipu_task_entry *t)
+{
+       int ret;
+
+       if ((t->set.mode & VDOA_BAND_MODE) && (t->set.task & VDI_VF)) {
+               ret = ipu_unlink_channels(ipu, MEM_VDOA_MEM, t->set.ic_chan);
+               CHECK_RETCODE_CONT(ret < 0, "ipu_unlink_ch vdoa_ic",
+                               STATE_UNLINK_CHAN_FAIL, ret);
+       }
+       ipu_uninit_channel(ipu, t->set.ic_chan);
+       if (deinterlace_3_field(t)) {
+               ipu_uninit_channel(ipu, t->set.vdi_ic_p_chan);
+               ipu_uninit_channel(ipu, t->set.vdi_ic_n_chan);
+       }
+}
+
+static int init_rot(struct ipu_soc *ipu, struct ipu_task_entry *t)
+{
+       int ret = 0;
+       dma_addr_t inbuf = 0, outbuf = 0;
+       int in_uoff = 0, in_voff = 0;
+       int in_fmt, in_width, in_height, in_stride;
+
+       /* init channel */
+       ret = ipu_init_channel(ipu, t->set.rot_chan, NULL);
+       if (ret < 0) {
+               t->state = STATE_INIT_CHAN_FAIL;
+               goto done;
+       }
+
+       /* init channel buf */
+       /* is it need link to a ic channel */
+       if (ic_and_rot(t->set.mode)) {
+               in_fmt = t->set.r_fmt;
+               in_width = t->set.r_width;
+               in_height = t->set.r_height;
+               in_stride = t->set.r_stride;
+               inbuf = t->set.r_paddr;
+               in_uoff = 0;
+               in_voff = 0;
+       } else {
+               in_fmt = t->input.format;
+               in_width = t->input.crop.w;
+               in_height = t->input.crop.h;
+               in_stride = t->set.istride;
+               inbuf = t->input.paddr + t->set.i_off;
+               in_uoff = t->set.i_uoff;
+               in_voff = t->set.i_voff;
+       }
+       outbuf = t->output.paddr + t->set.o_off;
+
+       ret = ipu_init_channel_buffer(ipu,
+                       t->set.rot_chan,
+                       IPU_INPUT_BUFFER,
+                       in_fmt,
+                       in_width,
+                       in_height,
+                       in_stride,
+                       t->output.rotate,
+                       inbuf,
+                       0,
+                       0,
+                       in_uoff,
+                       in_voff);
+       if (ret < 0) {
+               t->state = STATE_INIT_CHAN_BUF_FAIL;
+               goto done;
+       }
+
+       ret = ipu_init_channel_buffer(ipu,
+                       t->set.rot_chan,
+                       IPU_OUTPUT_BUFFER,
+                       t->output.format,
+                       t->output.crop.w,
+                       t->output.crop.h,
+                       t->set.ostride,
+                       IPU_ROTATE_NONE,
+                       outbuf,
+                       0,
+                       0,
+                       t->set.o_uoff,
+                       t->set.o_voff);
+       if (ret < 0) {
+               t->state = STATE_INIT_CHAN_BUF_FAIL;
+               goto done;
+       }
+
+done:
+       return ret;
+}
+
+static void uninit_rot(struct ipu_soc *ipu, struct ipu_task_entry *t)
+{
+       ipu_uninit_channel(ipu, t->set.rot_chan);
+}
+
+static int get_irq(struct ipu_task_entry *t)
+{
+       int irq;
+       ipu_channel_t chan;
+
+       if (only_ic(t->set.mode))
+               chan = t->set.ic_chan;
+       else
+               chan = t->set.rot_chan;
+
+       switch (chan) {
+       case MEM_ROT_VF_MEM:
+               irq = IPU_IRQ_PRP_VF_ROT_OUT_EOF;
+               break;
+       case MEM_ROT_PP_MEM:
+               irq = IPU_IRQ_PP_ROT_OUT_EOF;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+       case MEM_PRP_VF_MEM:
+               irq = IPU_IRQ_PRP_VF_OUT_EOF;
+               break;
+       case MEM_PP_MEM:
+               irq = IPU_IRQ_PP_OUT_EOF;
+               break;
+       case MEM_VDI_MEM:
+               irq = IPU_IRQ_VDIC_OUT_EOF;
+               break;
+       default:
+               irq = -EINVAL;
+       }
+
+       return irq;
+}
+
+static irqreturn_t task_irq_handler(int irq, void *dev_id)
+{
+       struct ipu_task_entry *prev_tsk = dev_id;
+
+       CHECK_PERF(&prev_tsk->ts_inirq);
+       complete(&prev_tsk->irq_comp);
+       dev_dbg(prev_tsk->dev, "[0x%p] no-0x%x in-irq!",
+                                prev_tsk, prev_tsk->task_no);
+
+       return IRQ_HANDLED;
+}
+
+/* Fix deinterlace up&down split mode medium line */
+static void vdi_split_process(struct ipu_soc *ipu, struct ipu_task_entry *t)
+{
+       u32 vdi_size;
+       u32 vdi_save_lines;
+       u32 stripe_mode;
+       u32 task_no;
+       u32 i, offset_addr;
+       u32 line_size;
+       unsigned char  *base_off;
+       struct ipu_task_entry *parent = t->parent;
+       struct mutex *lock = &parent->vdic_lock;
+
+       if (!parent) {
+               dev_err(t->dev, "ERR[0x%x]invalid parent\n", t->task_no);
+               return;
+       }
+       mutex_lock(lock);
+       stripe_mode = t->task_no & 0xf;
+       task_no = t->task_no >> 4;
+
+       /* Save both luma and chroma part for interleaved YUV(e.g. YUYV).
+        * Save luma part for non-interleaved and partial-interleaved
+        * YUV format (e.g NV12 and YV12). */
+       if (t->output.format == IPU_PIX_FMT_YUYV ||
+                       t->output.format == IPU_PIX_FMT_UYVY)
+               line_size = t->output.crop.w * fmt_to_bpp(t->output.format)/8;
+       else
+               line_size = t->output.crop.w;
+
+       vdi_save_lines = (t->output.crop.h - t->set.sp_setting.ud_split_line)/2;
+       vdi_size = vdi_save_lines * line_size;
+       if (vdi_save_lines <= 0) {
+               dev_err(t->dev, "[0x%p] vdi_save_line error\n", (void *)t);
+               mutex_unlock(lock);
+               return;
+       }
+
+       /*check vditmpbuf buffer have alloced or buffer size is changed */
+       if ((vdi_save_lines != parent->old_save_lines) ||
+               (vdi_size != parent->old_size)) {
+               if (parent->vditmpbuf[0] != NULL)
+                       kfree(parent->vditmpbuf[0]);
+               if (parent->vditmpbuf[1] != NULL)
+                       kfree(parent->vditmpbuf[1]);
+
+               parent->vditmpbuf[0] = kmalloc(vdi_size, GFP_KERNEL);
+               if (parent->vditmpbuf[0] == NULL) {
+                       dev_err(t->dev,
+                               "[0x%p]Falied Alloc vditmpbuf[0]\n", (void *)t);
+                       mutex_unlock(lock);
+                       return;
+               }
+               memset(parent->vditmpbuf[0], 0, vdi_size);
+
+               parent->vditmpbuf[1] = kmalloc(vdi_size, GFP_KERNEL);
+               if (parent->vditmpbuf[1] == NULL) {
+                       dev_err(t->dev,
+                               "[0x%p]Falied Alloc vditmpbuf[1]\n", (void *)t);
+                       mutex_unlock(lock);
+                       return;
+               }
+               memset(parent->vditmpbuf[1], 0, vdi_size);
+
+               parent->old_save_lines = vdi_save_lines;
+               parent->old_size = vdi_size;
+       }
+
+       if (pfn_valid(t->output.paddr >> PAGE_SHIFT)) {
+               base_off = page_address(pfn_to_page(t->output.paddr >> PAGE_SHIFT));
+               base_off += t->output.paddr & ((1 << PAGE_SHIFT) - 1);
+       } else {
+               base_off = (char *)ioremap_nocache(t->output.paddr,
+                               t->output.width * t->output.height *
+                               fmt_to_bpp(t->output.format)/8);
+       }
+       if (base_off == NULL) {
+               dev_err(t->dev, "ERR[0x%p]Failed get virtual address\n", t);
+               mutex_unlock(lock);
+               return;
+       }
+
+       /* UP stripe or UP&LEFT stripe */
+       if ((stripe_mode == UP_STRIPE) ||
+                       (stripe_mode == (UP_STRIPE | LEFT_STRIPE))) {
+               if (!parent->buf0filled) {
+                       offset_addr = t->set.o_off +
+                               t->set.sp_setting.ud_split_line*t->set.ostride;
+                       dmac_flush_range(base_off + offset_addr,
+                                       base_off + offset_addr + vdi_size);
+                       outer_flush_range(t->output.paddr + offset_addr,
+                               t->output.paddr + offset_addr + vdi_size);
+
+                       for (i = 0; i < vdi_save_lines; i++)
+                               memcpy(parent->vditmpbuf[0] + i*line_size,
+                                       base_off + offset_addr +
+                                       i*t->set.ostride, line_size);
+                       parent->buf0filled = true;
+               } else {
+                       offset_addr = t->set.o_off + (t->output.crop.h -
+                                       vdi_save_lines) * t->set.ostride;
+                       for (i = 0; i < vdi_save_lines; i++)
+                               memcpy(base_off + offset_addr + i*t->set.ostride,
+                                               parent->vditmpbuf[0] + i*line_size, line_size);
+
+                       dmac_flush_range(base_off + offset_addr,
+                                       base_off + offset_addr + i*t->set.ostride);
+                       outer_flush_range(t->output.paddr + offset_addr,
+                                       t->output.paddr + offset_addr + i*t->set.ostride);
+                       parent->buf0filled = false;
+               }
+       }
+       /*Down stripe or Down&Left stripe*/
+       else if ((stripe_mode == DOWN_STRIPE) ||
+                       (stripe_mode == (DOWN_STRIPE | LEFT_STRIPE))) {
+               if (!parent->buf0filled) {
+                       offset_addr = t->set.o_off + vdi_save_lines*t->set.ostride;
+                       dmac_flush_range(base_off + offset_addr,
+                                       base_off + offset_addr + vdi_size);
+                       outer_flush_range(t->output.paddr + offset_addr,
+                                       t->output.paddr + offset_addr + vdi_size);
+
+                       for (i = 0; i < vdi_save_lines; i++)
+                               memcpy(parent->vditmpbuf[0] + i*line_size,
+                                               base_off + offset_addr + i*t->set.ostride,
+                                               line_size);
+                       parent->buf0filled = true;
+               } else {
+                       offset_addr = t->set.o_off;
+                       for (i = 0; i < vdi_save_lines; i++)
+                               memcpy(base_off + offset_addr + i*t->set.ostride,
+                                               parent->vditmpbuf[0] + i*line_size,
+                                               line_size);
+
+                       dmac_flush_range(base_off + offset_addr,
+                                       base_off + offset_addr + i*t->set.ostride);
+                       outer_flush_range(t->output.paddr + offset_addr,
+                                       t->output.paddr + offset_addr + i*t->set.ostride);
+                       parent->buf0filled = false;
+               }
+       }
+       /*Up&Right stripe*/
+       else if (stripe_mode == (UP_STRIPE | RIGHT_STRIPE)) {
+               if (!parent->buf1filled) {
+                       offset_addr = t->set.o_off +
+                               t->set.sp_setting.ud_split_line*t->set.ostride;
+                       dmac_flush_range(base_off + offset_addr,
+                                       base_off + offset_addr + vdi_size);
+                       outer_flush_range(t->output.paddr + offset_addr,
+                                       t->output.paddr + offset_addr + vdi_size);
+
+                       for (i = 0; i < vdi_save_lines; i++)
+                               memcpy(parent->vditmpbuf[1] + i*line_size,
+                                               base_off + offset_addr + i*t->set.ostride,
+                                               line_size);
+                       parent->buf1filled = true;
+               } else {
+                       offset_addr = t->set.o_off +
+                               (t->output.crop.h - vdi_save_lines)*t->set.ostride;
+                       for (i = 0; i < vdi_save_lines; i++)
+                               memcpy(base_off + offset_addr + i*t->set.ostride,
+                                               parent->vditmpbuf[1] + i*line_size,
+                                               line_size);
+
+                       dmac_flush_range(base_off + offset_addr,
+                                       base_off + offset_addr + i*t->set.ostride);
+                       outer_flush_range(t->output.paddr + offset_addr,
+                                       t->output.paddr + offset_addr + i*t->set.ostride);
+                       parent->buf1filled = false;
+               }
+       }
+       /*Down stripe or Down&Right stript*/
+       else if (stripe_mode == (DOWN_STRIPE | RIGHT_STRIPE)) {
+               if (!parent->buf1filled) {
+                       offset_addr = t->set.o_off + vdi_save_lines*t->set.ostride;
+                       dmac_flush_range(base_off + offset_addr,
+                                       base_off + offset_addr + vdi_save_lines*t->set.ostride);
+                       outer_flush_range(t->output.paddr + offset_addr,
+                                       t->output.paddr + offset_addr + vdi_save_lines*t->set.ostride);
+
+                       for (i = 0; i < vdi_save_lines; i++)
+                               memcpy(parent->vditmpbuf[1] + i*line_size,
+                                               base_off + offset_addr + i*t->set.ostride,
+                                               line_size);
+                       parent->buf1filled = true;
+               } else {
+                       offset_addr = t->set.o_off;
+                       for (i = 0; i < vdi_save_lines; i++)
+                               memcpy(base_off + offset_addr + i*t->set.ostride,
+                                               parent->vditmpbuf[1] + i*line_size,
+                                               line_size);
+
+                       dmac_flush_range(base_off + offset_addr,
+                                       base_off + offset_addr + vdi_save_lines*t->set.ostride);
+                       outer_flush_range(t->output.paddr + offset_addr,
+                                       t->output.paddr + offset_addr + vdi_save_lines*t->set.ostride);
+                       parent->buf1filled = false;
+               }
+       }
+       if (!pfn_valid(t->output.paddr >> PAGE_SHIFT))
+               iounmap(base_off);
+       mutex_unlock(lock);
+}
+
+static void do_task_release(struct ipu_task_entry *t, int fail)
+{
+       int ret;
+       struct ipu_soc *ipu = t->ipu;
+
+       if (t->input.deinterlace.enable && !fail &&
+                       (t->task_no & (UP_STRIPE | DOWN_STRIPE)))
+               vdi_split_process(ipu, t);
+
+       ipu_free_irq(ipu, t->irq, t);
+
+       if (t->vdoa_dma.vaddr)
+               dma_free_coherent(t->dev,
+                       t->vdoa_dma.size,
+                       t->vdoa_dma.vaddr,
+                       t->vdoa_dma.paddr);
+
+       if (only_ic(t->set.mode)) {
+               ret = ipu_disable_channel(ipu, t->set.ic_chan, true);
+               CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch only_ic",
+                               STATE_DISABLE_CHAN_FAIL, ret);
+               if (deinterlace_3_field(t)) {
+                       ret = ipu_disable_channel(ipu, t->set.vdi_ic_p_chan,
+                                                       true);
+                       CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch only_ic_p",
+                                       STATE_DISABLE_CHAN_FAIL, ret);
+                       ret = ipu_disable_channel(ipu, t->set.vdi_ic_n_chan,
+                                                       true);
+                       CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch only_ic_n",
+                                       STATE_DISABLE_CHAN_FAIL, ret);
+               }
+       } else if (only_rot(t->set.mode)) {
+               ret = ipu_disable_channel(ipu, t->set.rot_chan, true);
+               CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch only_rot",
+                               STATE_DISABLE_CHAN_FAIL, ret);
+       } else if (ic_and_rot(t->set.mode)) {
+               ret = ipu_unlink_channels(ipu, t->set.ic_chan, t->set.rot_chan);
+               CHECK_RETCODE_CONT(ret < 0, "ipu_unlink_ch",
+                               STATE_UNLINK_CHAN_FAIL, ret);
+               ret = ipu_disable_channel(ipu, t->set.rot_chan, true);
+               CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch ic_and_rot-rot",
+                               STATE_DISABLE_CHAN_FAIL, ret);
+               ret = ipu_disable_channel(ipu, t->set.ic_chan, true);
+               CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch ic_and_rot-ic",
+                               STATE_DISABLE_CHAN_FAIL, ret);
+               if (deinterlace_3_field(t)) {
+                       ret = ipu_disable_channel(ipu, t->set.vdi_ic_p_chan,
+                                                       true);
+                       CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch icrot-ic-p",
+                                       STATE_DISABLE_CHAN_FAIL, ret);
+                       ret = ipu_disable_channel(ipu, t->set.vdi_ic_n_chan,
+                                                       true);
+                       CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch icrot-ic-n",
+                                       STATE_DISABLE_CHAN_FAIL, ret);
+               }
+       }
+
+       if (only_ic(t->set.mode))
+               uninit_ic(ipu, t);
+       else if (only_rot(t->set.mode))
+               uninit_rot(ipu, t);
+       else if (ic_and_rot(t->set.mode)) {
+               uninit_ic(ipu, t);
+               uninit_rot(ipu, t);
+       }
+
+       t->state = STATE_OK;
+       CHECK_PERF(&t->ts_rel);
+       return;
+}
+
+static void do_task_vdoa_only(struct ipu_task_entry *t)
+{
+       int ret;
+
+       ret = init_tiled_ch_bufs(NULL, t);
+       CHECK_RETCODE(ret < 0, "do_vdoa_only", STATE_ERR, out, ret);
+       ret = vdoa_start(t->vdoa_handle, VDOA_DEF_TIMEOUT_MS);
+       vdoa_stop(t->vdoa_handle);
+       CHECK_RETCODE(ret < 0, "vdoa_wait4complete, do_vdoa_only",
+                       STATE_VDOA_IRQ_TIMEOUT, out, ret);
+
+       t->state = STATE_OK;
+out:
+       return;
+}
+
+static void do_task(struct ipu_task_entry *t)
+{
+       int r_size;
+       int irq;
+       int ret;
+       uint32_t busy;
+       struct ipu_soc *ipu = t->ipu;
+
+       CHECK_PERF(&t->ts_dotask);
+
+       if (!ipu) {
+               t->state = STATE_NO_IPU;
+               return;
+       }
+
+       init_completion(&t->irq_comp);
+       dev_dbg(ipu->dev, "[0x%p]Do task no:0x%x: id %d\n", (void *)t,
+                t->task_no, t->task_id);
+       dump_task_info(t);
+
+       if (t->set.task & IC_PP) {
+               t->set.ic_chan = MEM_PP_MEM;
+               dev_dbg(ipu->dev, "[0x%p]ic channel MEM_PP_MEM\n", (void *)t);
+       } else if (t->set.task & IC_VF) {
+               t->set.ic_chan = MEM_PRP_VF_MEM;
+               dev_dbg(ipu->dev, "[0x%p]ic channel MEM_PRP_VF_MEM\n", (void *)t);
+       } else if (t->set.task & VDI_VF) {
+               if (t->set.mode & VDOA_BAND_MODE) {
+                       t->set.ic_chan = MEM_VDI_MEM;
+                       if (deinterlace_3_field(t)) {
+                               t->set.vdi_ic_p_chan = MEM_VDI_MEM_P;
+                               t->set.vdi_ic_n_chan = MEM_VDI_MEM_N;
+                       }
+                       dev_dbg(ipu->dev, "[0x%p]ic ch MEM_VDI_MEM\n",
+                                        (void *)t);
+               } else {
+                       t->set.ic_chan = MEM_VDI_PRP_VF_MEM;
+                       if (deinterlace_3_field(t)) {
+                               t->set.vdi_ic_p_chan = MEM_VDI_PRP_VF_MEM_P;
+                               t->set.vdi_ic_n_chan = MEM_VDI_PRP_VF_MEM_N;
+                       }
+                       dev_dbg(ipu->dev,
+                               "[0x%p]ic ch MEM_VDI_PRP_VF_MEM\n", t);
+               }
+       }
+
+       if (t->set.task & ROT_PP) {
+               t->set.rot_chan = MEM_ROT_PP_MEM;
+               dev_dbg(ipu->dev, "[0x%p]rot channel MEM_ROT_PP_MEM\n", (void *)t);
+       } else if (t->set.task & ROT_VF) {
+               t->set.rot_chan = MEM_ROT_VF_MEM;
+               dev_dbg(ipu->dev, "[0x%p]rot channel MEM_ROT_VF_MEM\n", (void *)t);
+       }
+
+       if (t->task_id == IPU_TASK_ID_VF)
+               busy = ic_vf_pp_is_busy(ipu, true);
+       else if (t->task_id == IPU_TASK_ID_PP)
+               busy = ic_vf_pp_is_busy(ipu, false);
+       else {
+               dev_err(ipu->dev, "ERR[no:0x%x]ipu task_id:%d invalid!\n",
+                               t->task_no, t->task_id);
+               return;
+       }
+       if (busy) {
+               dev_err(ipu->dev, "ERR[0x%p-no:0x%x]ipu task_id:%d busy!\n",
+                               (void *)t, t->task_no, t->task_id);
+               t->state = STATE_IPU_BUSY;
+               return;
+       }
+
+       irq = get_irq(t);
+       if (irq < 0) {
+               t->state = STATE_NO_IRQ;
+               return;
+       }
+       t->irq = irq;
+
+       /* channel setup */
+       if (only_ic(t->set.mode)) {
+               dev_dbg(t->dev, "[0x%p]only ic mode\n", (void *)t);
+               ret = init_ic(ipu, t);
+               CHECK_RETCODE(ret < 0, "init_ic only_ic",
+                               t->state, chan_setup, ret);
+       } else if (only_rot(t->set.mode)) {
+               dev_dbg(t->dev, "[0x%p]only rot mode\n", (void *)t);
+               ret = init_rot(ipu, t);
+               CHECK_RETCODE(ret < 0, "init_rot only_rot",
+                               t->state, chan_setup, ret);
+       } else if (ic_and_rot(t->set.mode)) {
+               int rot_idx = (t->task_id == IPU_TASK_ID_VF) ? 0 : 1;
+
+               dev_dbg(t->dev, "[0x%p]ic + rot mode\n", (void *)t);
+               t->set.r_fmt = t->output.format;
+               if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
+                       t->set.r_width = t->output.crop.h;
+                       t->set.r_height = t->output.crop.w;
+               } else {
+                       t->set.r_width = t->output.crop.w;
+                       t->set.r_height = t->output.crop.h;
+               }
+               t->set.r_stride = t->set.r_width *
+                       bytes_per_pixel(t->set.r_fmt);
+               r_size = PAGE_ALIGN(t->set.r_width * t->set.r_height
+                       * fmt_to_bpp(t->set.r_fmt)/8);
+
+               if (r_size > ipu->rot_dma[rot_idx].size) {
+                       dev_dbg(t->dev, "[0x%p]realloc rot buffer\n", (void *)t);
+
+                       if (ipu->rot_dma[rot_idx].vaddr)
+                               dma_free_coherent(t->dev,
+                                       ipu->rot_dma[rot_idx].size,
+                                       ipu->rot_dma[rot_idx].vaddr,
+                                       ipu->rot_dma[rot_idx].paddr);
+
+                       ipu->rot_dma[rot_idx].size = r_size;
+                       ipu->rot_dma[rot_idx].vaddr = dma_alloc_coherent(t->dev,
+                                               r_size,
+                                               &ipu->rot_dma[rot_idx].paddr,
+                                               GFP_DMA | GFP_KERNEL);
+                       CHECK_RETCODE(ipu->rot_dma[rot_idx].vaddr == NULL,
+                                       "ic_and_rot", STATE_SYS_NO_MEM,
+                                       chan_setup, -ENOMEM);
+               }
+               t->set.r_paddr = ipu->rot_dma[rot_idx].paddr;
+
+               dev_dbg(t->dev, "[0x%p]rotation:\n", (void *)t);
+               dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->set.r_fmt);
+               dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->set.r_width);
+               dev_dbg(t->dev, "[0x%p]\theight = %d\n", (void *)t, t->set.r_height);
+               dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", (void *)t, t->set.r_paddr);
+               dev_dbg(t->dev, "[0x%p]\trstride = %d\n", (void *)t, t->set.r_stride);
+
+               ret = init_ic(ipu, t);
+               CHECK_RETCODE(ret < 0, "init_ic ic_and_rot",
+                               t->state, chan_setup, ret);
+               ret = init_rot(ipu, t);
+               CHECK_RETCODE(ret < 0, "init_rot ic_and_rot",
+                               t->state, chan_setup, ret);
+               ret = ipu_link_channels(ipu, t->set.ic_chan,
+                               t->set.rot_chan);
+               CHECK_RETCODE(ret < 0, "ipu_link_ch ic_and_rot",
+                               STATE_LINK_CHAN_FAIL, chan_setup, ret);
+       } else {
+               dev_err(t->dev, "ERR [0x%p]do task: should not be here\n", t);
+               t->state = STATE_ERR;
+               return;
+       }
+
+       ret = ipu_request_irq(ipu, irq, task_irq_handler, 0, NULL, t);
+       CHECK_RETCODE(ret < 0, "ipu_req_irq",
+                       STATE_IRQ_FAIL, chan_setup, ret);
+
+       /* enable/start channel */
+       if (only_ic(t->set.mode)) {
+               ret = ipu_enable_channel(ipu, t->set.ic_chan);
+               CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic",
+                               STATE_ENABLE_CHAN_FAIL, chan_en, ret);
+               if (deinterlace_3_field(t)) {
+                       ret = ipu_enable_channel(ipu, t->set.vdi_ic_p_chan);
+                       CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic_p",
+                                       STATE_ENABLE_CHAN_FAIL, chan_en, ret);
+                       ret = ipu_enable_channel(ipu, t->set.vdi_ic_n_chan);
+                       CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic_n",
+                                       STATE_ENABLE_CHAN_FAIL, chan_en, ret);
+               }
+
+               ret = ipu_select_buffer(ipu, t->set.ic_chan, IPU_OUTPUT_BUFFER,
+                                       0);
+               CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic",
+                               STATE_SEL_BUF_FAIL, chan_buf, ret);
+               if (t->overlay_en) {
+                       ret = ipu_select_buffer(ipu, t->set.ic_chan,
+                                               IPU_GRAPH_IN_BUFFER, 0);
+                       CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_g",
+                                       STATE_SEL_BUF_FAIL, chan_buf, ret);
+                       if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) {
+                               ret = ipu_select_buffer(ipu, t->set.ic_chan,
+                                                       IPU_ALPHA_IN_BUFFER, 0);
+                               CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_a",
+                                               STATE_SEL_BUF_FAIL, chan_buf,
+                                               ret);
+                       }
+               }
+               if (!(t->set.mode & VDOA_BAND_MODE)) {
+                       if (deinterlace_3_field(t))
+                               ipu_select_multi_vdi_buffer(ipu, 0);
+                       else {
+                               ret = ipu_select_buffer(ipu, t->set.ic_chan,
+                                                       IPU_INPUT_BUFFER, 0);
+                               CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_i",
+                                       STATE_SEL_BUF_FAIL, chan_buf, ret);
+                       }
+               }
+       } else if (only_rot(t->set.mode)) {
+               ret = ipu_enable_channel(ipu, t->set.rot_chan);
+               CHECK_RETCODE(ret < 0, "ipu_enable_ch only_rot",
+                               STATE_ENABLE_CHAN_FAIL, chan_en, ret);
+               ret = ipu_select_buffer(ipu, t->set.rot_chan,
+                                               IPU_OUTPUT_BUFFER, 0);
+               CHECK_RETCODE(ret < 0, "ipu_sel_buf only_rot_o",
+                               STATE_SEL_BUF_FAIL, chan_buf, ret);
+               ret = ipu_select_buffer(ipu, t->set.rot_chan,
+                                               IPU_INPUT_BUFFER, 0);
+               CHECK_RETCODE(ret < 0, "ipu_sel_buf only_rot_i",
+                               STATE_SEL_BUF_FAIL, chan_buf, ret);
+       } else if (ic_and_rot(t->set.mode)) {
+               ret = ipu_enable_channel(ipu, t->set.rot_chan);
+               CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-rot",
+                               STATE_ENABLE_CHAN_FAIL, chan_en, ret);
+               ret = ipu_enable_channel(ipu, t->set.ic_chan);
+               CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-ic",
+                               STATE_ENABLE_CHAN_FAIL, chan_en, ret);
+               if (deinterlace_3_field(t)) {
+                       ret = ipu_enable_channel(ipu, t->set.vdi_ic_p_chan);
+                       CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-p",
+                                       STATE_ENABLE_CHAN_FAIL, chan_en, ret);
+                       ret = ipu_enable_channel(ipu, t->set.vdi_ic_n_chan);
+                       CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-n",
+                                       STATE_ENABLE_CHAN_FAIL, chan_en, ret);
+               }
+
+               ret = ipu_select_buffer(ipu, t->set.rot_chan,
+                                               IPU_OUTPUT_BUFFER, 0);
+               CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-rot-o",
+                               STATE_SEL_BUF_FAIL, chan_buf, ret);
+               if (t->overlay_en) {
+                       ret = ipu_select_buffer(ipu, t->set.ic_chan,
+                                                       IPU_GRAPH_IN_BUFFER, 0);
+                       CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-g",
+                                       STATE_SEL_BUF_FAIL, chan_buf, ret);
+                       if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) {
+                               ret = ipu_select_buffer(ipu, t->set.ic_chan,
+                                                       IPU_ALPHA_IN_BUFFER, 0);
+                               CHECK_RETCODE(ret < 0, "ipu_sel_buf icrot-ic-a",
+                                               STATE_SEL_BUF_FAIL,
+                                               chan_buf, ret);
+                       }
+               }
+               ret = ipu_select_buffer(ipu, t->set.ic_chan,
+                                               IPU_OUTPUT_BUFFER, 0);
+               CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-o",
+                               STATE_SEL_BUF_FAIL, chan_buf, ret);
+               if (deinterlace_3_field(t))
+                       ipu_select_multi_vdi_buffer(ipu, 0);
+               else {
+                       ret = ipu_select_buffer(ipu, t->set.ic_chan,
+                                                       IPU_INPUT_BUFFER, 0);
+                       CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-i",
+                                       STATE_SEL_BUF_FAIL, chan_buf, ret);
+               }
+       }
+
+       if (need_split(t))
+               t->state = STATE_IN_PROGRESS;
+
+       if (t->set.mode & VDOA_BAND_MODE) {
+               ret = vdoa_start(t->vdoa_handle, VDOA_DEF_TIMEOUT_MS);
+               CHECK_RETCODE(ret < 0, "vdoa_wait4complete, do_vdoa_band",
+                               STATE_VDOA_IRQ_TIMEOUT, chan_rel, ret);
+       }
+
+       CHECK_PERF(&t->ts_waitirq);
+       ret = wait_for_completion_timeout(&t->irq_comp,
+                                msecs_to_jiffies(t->timeout - DEF_DELAY_MS));
+       CHECK_PERF(&t->ts_wakeup);
+       CHECK_RETCODE(ret == 0, "wait_for_comp_timeout",
+                       STATE_IRQ_TIMEOUT, chan_rel, ret);
+       dev_dbg(t->dev, "[0x%p] no-0x%x ipu irq done!", t, t->task_no);
+
+chan_rel:
+chan_buf:
+chan_en:
+chan_setup:
+       if (t->set.mode & VDOA_BAND_MODE)
+               vdoa_stop(t->vdoa_handle);
+       do_task_release(t, t->state >= STATE_ERR);
+       return;
+}
+
+static void do_task_vdoa_vdi(struct ipu_task_entry *t)
+{
+       int i;
+       int ret;
+       u32 stripe_width;
+
+       /* FIXME: crop mode not support now */
+       stripe_width = t->input.width >> 1;
+       t->input.crop.pos.x = 0;
+       t->input.crop.pos.y = 0;
+       t->input.crop.w = stripe_width;
+       t->input.crop.h = t->input.height;
+       t->output.crop.w = stripe_width;
+       t->output.crop.h = t->input.height;
+
+       for (i = 0; i < 2; i++) {
+               t->input.crop.pos.x = t->input.crop.pos.x + i * stripe_width;
+               t->output.crop.pos.x = t->output.crop.pos.x + i * stripe_width;
+               /* check input */
+               ret = set_crop(&t->input.crop, t->input.width, t->input.height,
+                       t->input.format);
+               if (ret < 0) {
+                       ret = STATE_ERR;
+                       goto done;
+               } else
+                       update_offset(t->input.format,
+                                       t->input.width, t->input.height,
+                                       t->input.crop.pos.x,
+                                       t->input.crop.pos.y,
+                                       &t->set.i_off, &t->set.i_uoff,
+                                       &t->set.i_voff, &t->set.istride);
+               dev_dbg(t->dev, "i_off:0x%x, i_uoff:0x%x, istride:%d.\n",
+                       t->set.i_off, t->set.i_uoff, t->set.istride);
+               /* check output */
+               ret = set_crop(&t->output.crop, t->input.width,
+                                       t->output.height, t->output.format);
+               if (ret < 0) {
+                       ret = STATE_ERR;
+                       goto done;
+               } else
+                       update_offset(t->output.format,
+                                       t->output.width, t->output.height,
+                                       t->output.crop.pos.x,
+                                       t->output.crop.pos.y,
+                                       &t->set.o_off, &t->set.o_uoff,
+                                       &t->set.o_voff, &t->set.ostride);
+
+               dev_dbg(t->dev, "o_off:0x%x, o_uoff:0x%x, ostride:%d.\n",
+                               t->set.o_off, t->set.o_uoff, t->set.ostride);
+
+               do_task(t);
+       }
+
+       return;
+done:
+       dev_err(t->dev, "ERR %s set_crop.\n", __func__);
+       t->state = ret;
+       return;
+}
+
+static void get_res_do_task(struct ipu_task_entry *t)
+{
+       uint32_t        found;
+       uint32_t        split_child;
+       struct mutex    *lock;
+
+       found = get_vdoa_ipu_res(t);
+       if (!found) {
+               dev_err(t->dev, "ERR:[0x%p] no-0x%x can not get res\n",
+                       t, t->task_no);
+               return;
+       } else {
+               if (t->set.task & VDOA_ONLY)
+                       do_task_vdoa_only(t);
+               else if ((IPU_PIX_FMT_TILED_NV12F == t->input.format) &&
+                               (t->set.mode & VDOA_BAND_MODE) &&
+                               (t->input.crop.w >
+                                soc_max_vdi_in_width(t->ipu)))
+                       do_task_vdoa_vdi(t);
+               else
+                       do_task(t);
+               put_vdoa_ipu_res(t, 0);
+       }
+       if (t->state != STATE_OK) {
+               dev_err(t->dev, "ERR:[0x%p] no-0x%x state: %s\n",
+                       t, t->task_no, state_msg[t->state].msg);
+       }
+
+       split_child = need_split(t) && t->parent;
+       if (split_child) {
+               lock = &t->parent->split_lock;
+               mutex_lock(lock);
+               t->split_done = 1;
+               mutex_unlock(lock);
+               wake_up(&t->parent->split_waitq);
+       }
+
+       return;
+}
+
+static void wait_split_task_complete(struct ipu_task_entry *parent,
+                               struct ipu_split_task *sp_task, uint32_t size)
+{
+       struct ipu_task_entry *tsk = NULL;
+       int ret = 0, rc;
+       int j, idx = -1;
+       unsigned long flags;
+       struct mutex *lock = &parent->split_lock;
+       int k, busy_vf, busy_pp;
+       struct ipu_soc *ipu;
+       DECLARE_PERF_VAR;
+
+       for (j = 0; j < size; j++) {
+               rc = wait_event_timeout(
+                       parent->split_waitq,
+                       sp_task_check_done(sp_task, parent, size, &idx),
+                       msecs_to_jiffies(parent->timeout - DEF_DELAY_MS));
+               if (!rc) {
+                       dev_err(parent->dev,
+                               "ERR:[0x%p] no-0x%x, split_task timeout,j:%d,"
+                               "size:%d.\n",
+                                parent, parent->task_no, j, size);
+                       ret = -ETIMEDOUT;
+                       goto out;
+               } else {
+                       if (idx < 0) {
+                               dev_err(parent->dev,
+                               "ERR:[0x%p] no-0x%x, invalid task idx:%d\n",
+                                parent, parent->task_no, idx);
+                               continue;
+                       }
+                       tsk = sp_task[idx].child_task;
+                       mutex_lock(lock);
+                       if (!tsk->split_done || !tsk->ipu)
+                               dev_err(tsk->dev,
+                               "ERR:no-0x%x,split not done:%d/null ipu:0x%p\n",
+                                tsk->task_no, tsk->split_done, tsk->ipu);
+                       tsk->split_done = 0;
+                       mutex_unlock(lock);
+
+                       dev_dbg(tsk->dev,
+                               "[0x%p] no-0x%x sp_tsk[%d] done,state:%d.\n",
+                                tsk, tsk->task_no, idx, tsk->state);
+                       #ifdef DBG_IPU_PERF
+                               CHECK_PERF(&tsk->ts_rel);
+                               PRINT_TASK_STATISTICS;
+                       #endif
+               }
+       }
+
+out:
+       if (ret == -ETIMEDOUT) {
+               /* debug */
+               for (k = 0; k < max_ipu_no; k++) {
+                       ipu = ipu_get_soc(k);
+                       if (IS_ERR(ipu)) {
+                               dev_err(parent->dev, "no:0x%x, null ipu:%d\n",
+                                parent->task_no, k);
+                       } else {
+                               busy_vf = ic_vf_pp_is_busy(ipu, true);
+                               busy_pp = ic_vf_pp_is_busy(ipu, false);
+                               dev_err(parent->dev,
+                                       "ERR:ipu[%d] busy_vf:%d, busy_pp:%d.\n",
+                                       k, busy_vf, busy_pp);
+                       }
+               }
+               for (k = 0; k < size; k++) {
+                       tsk = sp_task[k].child_task;
+                       if (!tsk)
+                               continue;
+                       dev_err(parent->dev,
+                               "ERR: sp_task[%d][0x%p] no-0x%x done:%d,"
+                                "state:%s,on_list:%d, ipu:0x%p,timeout!\n",
+                                k, tsk, tsk->task_no, tsk->split_done,
+                                state_msg[tsk->state].msg, tsk->task_in_list,
+                                tsk->ipu);
+               }
+       }
+
+       for (j = 0; j < size; j++) {
+               tsk = sp_task[j].child_task;
+               if (!tsk)
+                       continue;
+               spin_lock_irqsave(&ipu_task_list_lock, flags);
+               if (tsk->task_in_list) {
+                       list_del(&tsk->node);
+                       tsk->task_in_list = 0;
+                       dev_dbg(tsk->dev,
+                               "[0x%p] no-0x%x,id:%d sp_tsk timeout list_del.\n",
+                                tsk, tsk->task_no, tsk->task_id);
+               }
+               spin_unlock_irqrestore(&ipu_task_list_lock, flags);
+               if (!tsk->ipu)
+                       continue;
+               if (tsk->state != STATE_OK) {
+                       dev_err(tsk->dev,
+                               "ERR:[0x%p] no-0x%x,id:%d, sp_tsk state: %s\n",
+                                       tsk, tsk->task_no, tsk->task_id,
+                                       state_msg[tsk->state].msg);
+               }
+               kref_put(&tsk->refcount, task_mem_free);
+       }
+
+       kfree(parent->vditmpbuf[0]);
+       kfree(parent->vditmpbuf[1]);
+
+       if (ret < 0)
+               parent->state = STATE_TIMEOUT;
+       else
+               parent->state = STATE_OK;
+       return;
+}
+
+static inline int find_task(struct ipu_task_entry **t, int thread_id)
+{
+       int found;
+       unsigned long flags;
+       struct ipu_task_entry *tsk;
+       struct list_head *task_list = &ipu_task_list;
+
+       *t = NULL;
+       spin_lock_irqsave(&ipu_task_list_lock, flags);
+       found = !list_empty(task_list);
+       if (found) {
+               tsk = list_first_entry(task_list, struct ipu_task_entry, node);
+               if (tsk->task_in_list) {
+                       list_del(&tsk->node);
+                       tsk->task_in_list = 0;
+                       *t = tsk;
+                       kref_get(&tsk->refcount);
+                       dev_dbg(tsk->dev,
+                       "thread_id:%d,[0x%p] task_no:0x%x,mode:0x%x list_del\n",
+                       thread_id, tsk, tsk->task_no, tsk->set.mode);
+               } else
+                       dev_err(tsk->dev,
+                       "thread_id:%d,task_no:0x%x,mode:0x%x not on list_del\n",
+                       thread_id, tsk->task_no, tsk->set.mode);
+       }
+       spin_unlock_irqrestore(&ipu_task_list_lock, flags);
+
+       return found;
+}
+
+static int ipu_task_thread(void *argv)
+{
+       struct ipu_task_entry *tsk;
+       struct ipu_task_entry *sp_tsk0;
+       struct ipu_split_task sp_task[4];
+       /* priority lower than irq_thread */
+       const struct sched_param param = {
+               .sched_priority = MAX_USER_RT_PRIO/2 - 1,
+       };
+       int ret;
+       int curr_thread_id;
+       uint32_t size;
+       unsigned long flags;
+       unsigned int cpu;
+       struct cpumask cpu_mask;
+       struct ipu_thread_data *data = (struct ipu_thread_data *)argv;
+
+       thread_id++;
+       curr_thread_id = thread_id;
+       sched_setscheduler(current, SCHED_FIFO, &param);
+
+       if (!data->is_vdoa) {
+               cpu = cpumask_first(cpu_online_mask);
+               cpumask_set_cpu(cpu, &cpu_mask);
+               ret = sched_setaffinity(data->ipu->thread[data->id]->pid,
+                       &cpu_mask);
+               if (ret < 0) {
+                       pr_err("%s: sched_setaffinity fail:%d.\n", __func__, ret);
+               }
+               pr_debug("%s: sched_setaffinity cpu:%d.\n", __func__, cpu);
+       }
+
+       while (!kthread_should_stop()) {
+               int split_fail = 0;
+               int split_parent;
+               int split_child;
+
+               wait_event_interruptible(thread_waitq, find_task(&tsk, curr_thread_id));
+
+               if (!tsk) {
+                       pr_err("thread:%d can not find task.\n",
+                               curr_thread_id);
+                       continue;
+               }
+
+               /* note: other threads run split child task */
+               split_parent = need_split(tsk) && !tsk->parent;
+               split_child = need_split(tsk) && tsk->parent;
+               if (split_parent) {
+                       if ((tsk->set.split_mode == RL_SPLIT) ||
+                                (tsk->set.split_mode == UD_SPLIT))
+                               size = 2;
+                       else
+                               size = 4;
+                       ret = queue_split_task(tsk, sp_task, size);
+                       if (ret < 0) {
+                               split_fail = 1;
+                       } else {
+                               struct list_head *pos;
+
+                               spin_lock_irqsave(&ipu_task_list_lock, flags);
+
+                               sp_tsk0 = list_first_entry(&tsk->split_list,
+                                               struct ipu_task_entry, node);
+                               list_del(&sp_tsk0->node);
+
+                               list_for_each(pos, &tsk->split_list) {
+                                       struct ipu_task_entry *tmp;
+
+                                       tmp = list_entry(pos,
+                                               struct ipu_task_entry, node);
+                                       tmp->task_in_list = 1;
+                                       dev_dbg(tmp->dev,
+                                               "[0x%p] no-0x%x,id:%d sp_tsk "
+                                               "add_to_list.\n", tmp,
+                                               tmp->task_no, tmp->task_id);
+                               }
+                               /* add to global list */
+                               list_splice(&tsk->split_list, &ipu_task_list);
+
+                               spin_unlock_irqrestore(&ipu_task_list_lock,
+                                                                       flags);
+                               /* let the parent thread do the first sp_task */
+                               /* FIXME: ensure the correct sequence for split
+                                       4size: 5/6->9/a*/
+                               if (!sp_tsk0)
+                                       dev_err(tsk->dev,
+                                       "ERR: no-0x%x,can not get split_tsk0\n",
+                                       tsk->task_no);
+                               wake_up_interruptible(&thread_waitq);
+                               get_res_do_task(sp_tsk0);
+                               dev_dbg(sp_tsk0->dev,
+                                       "thread:%d complete tsk no:0x%x.\n",
+                                       curr_thread_id, sp_tsk0->task_no);
+                               ret = atomic_read(&req_cnt);
+                               if (ret > 0) {
+                                       wake_up(&res_waitq);
+                                       dev_dbg(sp_tsk0->dev,
+                                       "sp_tsk0 sche thread:%d no:0x%x,"
+                                       "req_cnt:%d\n", curr_thread_id,
+                                       sp_tsk0->task_no, ret);
+                                       /* For other threads to get_res */
+                                       schedule();
+                               }
+                       }
+               } else
+                       get_res_do_task(tsk);
+
+               /* wait for all 4 sp_task finished here or timeout
+                       and then release all resources */
+               if (split_parent && !split_fail)
+                       wait_split_task_complete(tsk, sp_task, size);
+
+               if (!split_child) {
+                       atomic_inc(&tsk->done);
+                       wake_up(&tsk->task_waitq);
+               }
+
+               dev_dbg(tsk->dev, "thread:%d complete tsk no:0x%x-[0x%p].\n",
+                               curr_thread_id, tsk->task_no, tsk);
+               ret = atomic_read(&req_cnt);
+               if (ret > 0) {
+                       wake_up(&res_waitq);
+                       dev_dbg(tsk->dev, "sche thread:%d no:0x%x,req_cnt:%d\n",
+                               curr_thread_id, tsk->task_no, ret);
+                       /* note: give cpu to other threads to get_res */
+                       schedule();
+               }
+
+               kref_put(&tsk->refcount, task_mem_free);
+       }
+
+       pr_info("ERR %s exit.\n", __func__);
+       return 0;
+}
+
+int ipu_check_task(struct ipu_task *task)
+{
+       struct ipu_task_entry *tsk;
+       int ret = 0;
+
+       tsk = create_task_entry(task);
+       if (IS_ERR(tsk))
+               return PTR_ERR(tsk);
+
+       ret = check_task(tsk);
+
+       task->input = tsk->input;
+       task->output = tsk->output;
+       task->overlay = tsk->overlay;
+       dump_task_info(tsk);
+
+       kref_put(&tsk->refcount, task_mem_free);
+       if (ret != 0)
+               pr_debug("%s ret:%d.\n", __func__, ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_check_task);
+
+int ipu_queue_task(struct ipu_task *task)
+{
+       struct ipu_task_entry *tsk;
+       unsigned long flags;
+       int ret;
+       u32 tmp_task_no;
+       DECLARE_PERF_VAR;
+
+       tsk = create_task_entry(task);
+       if (IS_ERR(tsk))
+               return PTR_ERR(tsk);
+
+       CHECK_PERF(&tsk->ts_queue);
+       ret = prepare_task(tsk);
+       if (ret < 0)
+               goto done;
+
+       if (need_split(tsk)) {
+               CHECK_PERF(&tsk->ts_dotask);
+               CHECK_PERF(&tsk->ts_waitirq);
+               CHECK_PERF(&tsk->ts_inirq);
+               CHECK_PERF(&tsk->ts_wakeup);
+       }
+
+       /* task_no last four bits for split task type*/
+       tmp_task_no = atomic_inc_return(&frame_no);
+       tsk->task_no = tmp_task_no << 4;
+       init_waitqueue_head(&tsk->task_waitq);
+
+       spin_lock_irqsave(&ipu_task_list_lock, flags);
+       list_add_tail(&tsk->node, &ipu_task_list);
+       tsk->task_in_list = 1;
+       dev_dbg(tsk->dev, "[0x%p,no-0x%x] list_add_tail\n", tsk, tsk->task_no);
+       spin_unlock_irqrestore(&ipu_task_list_lock, flags);
+       wake_up_interruptible(&thread_waitq);
+
+       ret = wait_event_timeout(tsk->task_waitq, atomic_read(&tsk->done),
+                                               msecs_to_jiffies(tsk->timeout));
+       if (0 == ret) {
+               /* note: the timeout should larger than the internal timeout!*/
+               ret = -ETIMEDOUT;
+               dev_err(tsk->dev, "ERR: [0x%p] no-0x%x, timeout:%dms!\n",
+                               tsk, tsk->task_no, tsk->timeout);
+       } else {
+               if (STATE_OK != tsk->state) {
+                       dev_err(tsk->dev, "ERR: [0x%p] no-0x%x,state %d: %s\n",
+                               tsk, tsk->task_no, tsk->state,
+                               state_msg[tsk->state].msg);
+                       ret = -ECANCELED;
+               } else
+                       ret = 0;
+       }
+
+       spin_lock_irqsave(&ipu_task_list_lock, flags);
+       if (tsk->task_in_list) {
+               list_del(&tsk->node);
+               tsk->task_in_list = 0;
+               dev_dbg(tsk->dev, "[0x%p] no:0x%x list_del\n",
+                               tsk, tsk->task_no);
+       }
+       spin_unlock_irqrestore(&ipu_task_list_lock, flags);
+
+#ifdef DBG_IPU_PERF
+       CHECK_PERF(&tsk->ts_rel);
+       PRINT_TASK_STATISTICS;
+       if (ts_frame_avg == 0)
+               ts_frame_avg = ts_frame.tv_nsec / NSEC_PER_USEC +
+                               ts_frame.tv_sec * USEC_PER_SEC;
+       else
+               ts_frame_avg = (ts_frame_avg + ts_frame.tv_nsec / NSEC_PER_USEC
+                               + ts_frame.tv_sec * USEC_PER_SEC)/2;
+       if (timespec_compare(&ts_frame, &ts_frame_max) > 0)
+               ts_frame_max = ts_frame;
+
+       atomic_inc(&frame_cnt);
+
+       if ((atomic_read(&frame_cnt) %  1000) == 0)
+               pr_debug("ipu_dev: max frame time:%ldus, avg frame time:%dus,"
+                       "frame_cnt:%d\n", ts_frame_max.tv_nsec / NSEC_PER_USEC
+                       + ts_frame_max.tv_sec * USEC_PER_SEC,
+                       ts_frame_avg, atomic_read(&frame_cnt));
+#endif
+done:
+       if (ret < 0)
+               dev_err(tsk->dev, "ERR: no-0x%x,ipu_queue_task err:%d\n",
+                               tsk->task_no, ret);
+
+       kref_put(&tsk->refcount, task_mem_free);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_queue_task);
+
+static int mxc_ipu_open(struct inode *inode, struct file *file)
+{
+       file->private_data = (void *)atomic_inc_return(&file_index);
+       return 0;
+}
+
+static long mxc_ipu_ioctl(struct file *file,
+               unsigned int cmd, unsigned long arg)
+{
+       int __user *argp = (void __user *)arg;
+       int ret = 0;
+
+       switch (cmd) {
+       case IPU_CHECK_TASK:
+               {
+                       struct ipu_task task;
+
+                       if (copy_from_user
+                                       (&task, (struct ipu_task *) arg,
+                                        sizeof(struct ipu_task)))
+                               return -EFAULT;
+                       ret = ipu_check_task(&task);
+                       if (copy_to_user((struct ipu_task *) arg,
+                               &task, sizeof(struct ipu_task)))
+                               return -EFAULT;
+                       break;
+               }
+       case IPU_QUEUE_TASK:
+               {
+                       struct ipu_task task;
+
+                       if (copy_from_user
+                                       (&task, (struct ipu_task *) arg,
+                                        sizeof(struct ipu_task)))
+                               return -EFAULT;
+                       ret = ipu_queue_task(&task);
+                       break;
+               }
+       case IPU_ALLOC:
+               {
+                       int size;
+                       struct ipu_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(ipu_dev, size,
+                                                          &mem->phy_addr,
+                                                          GFP_DMA | GFP_KERNEL);
+                       if (mem->cpu_addr == NULL) {
+                               kfree(mem);
+                               return -ENOMEM;
+                       }
+                       mem->file_index = file->private_data;
+                       mutex_lock(&ipu_alloc_lock);
+                       list_add(&mem->list, &ipu_alloc_list);
+                       mutex_unlock(&ipu_alloc_lock);
+
+                       dev_dbg(ipu_dev, "allocated %d bytes @ 0x%08X\n",
+                               mem->size, mem->phy_addr);
+
+                       if (put_user(mem->phy_addr, argp))
+                               return -EFAULT;
+
+                       break;
+               }
+       case IPU_FREE:
+               {
+                       unsigned long offset;
+                       struct ipu_alloc_list *mem;
+
+                       if (get_user(offset, argp))
+                               return -EFAULT;
+
+                       ret = -EINVAL;
+                       mutex_lock(&ipu_alloc_lock);
+                       list_for_each_entry(mem, &ipu_alloc_list, list) {
+                               if (mem->phy_addr == offset) {
+                                       list_del(&mem->list);
+                                       dma_free_coherent(ipu_dev,
+                                                         mem->size,
+                                                         mem->cpu_addr,
+                                                         mem->phy_addr);
+                                       kfree(mem);
+                                       ret = 0;
+                                       break;
+                               }
+                       }
+                       mutex_unlock(&ipu_alloc_lock);
+                       if (0 == ret)
+                               dev_dbg(ipu_dev, "free %d bytes @ 0x%08X\n",
+                                       mem->size, mem->phy_addr);
+
+                       break;
+               }
+       default:
+               break;
+       }
+       return ret;
+}
+
+static int mxc_ipu_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       bool found = false;
+       u32 len;
+       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+       struct ipu_alloc_list *mem;
+
+       mutex_lock(&ipu_alloc_lock);
+       list_for_each_entry(mem, &ipu_alloc_list, list) {
+               if (offset == mem->phy_addr) {
+                       found = true;
+                       len = mem->size;
+                       break;
+               }
+       }
+       mutex_unlock(&ipu_alloc_lock);
+       if (!found)
+               return -EINVAL;
+
+       if (vma->vm_end - vma->vm_start > len)
+               return -EINVAL;
+
+       vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+       if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+                               vma->vm_end - vma->vm_start,
+                               vma->vm_page_prot)) {
+               printk(KERN_ERR
+                               "mmap failed!\n");
+               return -ENOBUFS;
+       }
+       return 0;
+}
+
+static int mxc_ipu_release(struct inode *inode, struct file *file)
+{
+       struct ipu_alloc_list *mem;
+       struct ipu_alloc_list *n;
+
+       mutex_lock(&ipu_alloc_lock);
+       list_for_each_entry_safe(mem, n, &ipu_alloc_list, list) {
+               if ((mem->cpu_addr != 0) &&
+                       (file->private_data == mem->file_index)) {
+                       list_del(&mem->list);
+                       dma_free_coherent(ipu_dev,
+                                         mem->size,
+                                         mem->cpu_addr,
+                                         mem->phy_addr);
+                       dev_dbg(ipu_dev, "rel-free %d bytes @ 0x%08X\n",
+                               mem->size, mem->phy_addr);
+                       kfree(mem);
+               }
+       }
+       mutex_unlock(&ipu_alloc_lock);
+       atomic_dec(&file_index);
+
+       return 0;
+}
+
+static struct file_operations mxc_ipu_fops = {
+       .owner = THIS_MODULE,
+       .open = mxc_ipu_open,
+       .mmap = mxc_ipu_mmap,
+       .release = mxc_ipu_release,
+       .unlocked_ioctl = mxc_ipu_ioctl,
+};
+
+int register_ipu_device(struct ipu_soc *ipu, int id)
+{
+       int ret = 0;
+       static int idx;
+       static struct ipu_thread_data thread_data[5];
+
+       if (!major) {
+               major = register_chrdev(0, "mxc_ipu", &mxc_ipu_fops);
+               if (major < 0) {
+                       printk(KERN_ERR "Unable to register mxc_ipu as a char device\n");
+                       ret = major;
+                       goto register_cdev_fail;
+               }
+
+               ipu_class = class_create(THIS_MODULE, "mxc_ipu");
+               if (IS_ERR(ipu_class)) {
+                       ret = PTR_ERR(ipu_class);
+                       goto ipu_class_fail;
+               }
+
+               ipu_dev = device_create(ipu_class, NULL, MKDEV(major, 0),
+                               NULL, "mxc_ipu");
+               if (IS_ERR(ipu_dev)) {
+                       ret = PTR_ERR(ipu_dev);
+                       goto dev_create_fail;
+               }
+               ipu_dev->dma_mask = kmalloc(sizeof(*ipu_dev->dma_mask), GFP_KERNEL);
+               *ipu_dev->dma_mask = DMA_BIT_MASK(32);
+               ipu_dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+               mutex_init(&ipu_ch_tbl.lock);
+       }
+       max_ipu_no = ++id;
+       ipu->rot_dma[0].size = 0;
+       ipu->rot_dma[1].size = 0;
+
+       thread_data[idx].ipu = ipu;
+       thread_data[idx].id = 0;
+       thread_data[idx].is_vdoa = 0;
+       ipu->thread[0] = kthread_run(ipu_task_thread, &thread_data[idx++],
+                                       "ipu%d_task", id);
+       if (IS_ERR(ipu->thread[0])) {
+               ret = PTR_ERR(ipu->thread[0]);
+               goto kthread0_fail;
+       }
+
+       thread_data[idx].ipu = ipu;
+       thread_data[idx].id = 1;
+       thread_data[idx].is_vdoa = 0;
+       ipu->thread[1] = kthread_run(ipu_task_thread, &thread_data[idx++],
+                               "ipu%d_task", id);
+       if (IS_ERR(ipu->thread[1])) {
+               ret = PTR_ERR(ipu->thread[1]);
+               goto kthread1_fail;
+       }
+
+
+       return ret;
+
+kthread1_fail:
+       kthread_stop(ipu->thread[0]);
+kthread0_fail:
+       if (id == 0)
+               device_destroy(ipu_class, MKDEV(major, 0));
+dev_create_fail:
+       if (id == 0) {
+               class_destroy(ipu_class);
+       }
+ipu_class_fail:
+       if (id == 0)
+               unregister_chrdev(major, "mxc_ipu");
+register_cdev_fail:
+       return ret;
+}
+
+void unregister_ipu_device(struct ipu_soc *ipu, int id)
+{
+       int i;
+
+       kthread_stop(ipu->thread[0]);
+       kthread_stop(ipu->thread[1]);
+       for (i = 0; i < 2; i++) {
+               if (ipu->rot_dma[i].vaddr)
+                       dma_free_coherent(ipu_dev,
+                               ipu->rot_dma[i].size,
+                               ipu->rot_dma[i].vaddr,
+                               ipu->rot_dma[i].paddr);
+       }
+
+       if (major) {
+               device_destroy(ipu_class, MKDEV(major, 0));
+               class_destroy(ipu_class);
+               unregister_chrdev(major, "mxc_ipu");
+               major = 0;
+       }
+}
diff --git a/drivers/mxc/ipu3/ipu_disp.c b/drivers/mxc/ipu3/ipu_disp.c
new file mode 100644 (file)
index 0000000..f7044c2
--- /dev/null
@@ -0,0 +1,1959 @@
+/*
+ * Copyright 2005-2015 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
+ */
+
+/*!
+ * @file ipu_disp.c
+ *
+ * @brief IPU display submodule API functions
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/ipu-v3.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/atomic.h>
+
+#include "ipu_param_mem.h"
+#include "ipu_regs.h"
+
+struct dp_csc_param_t {
+       int mode;
+       void *coeff;
+};
+
+#define SYNC_WAVE 0
+#define NULL_WAVE (-1)
+#define ASYNC_SER_WAVE 6
+
+/* DC display ID assignments */
+#define DC_DISP_ID_SYNC(di)    (di)
+#define DC_DISP_ID_SERIAL      2
+#define DC_DISP_ID_ASYNC       3
+
+int dmfc_type_setup;
+
+void _ipu_dmfc_init(struct ipu_soc *ipu, int dmfc_type, int first)
+{
+       u32 dmfc_wr_chan, dmfc_dp_chan;
+
+       if (first) {
+               if (dmfc_type_setup > dmfc_type)
+                       dmfc_type = dmfc_type_setup;
+               else
+                       dmfc_type_setup = dmfc_type;
+
+               /* disable DMFC-IC channel*/
+               ipu_dmfc_write(ipu, 0x2, DMFC_IC_CTRL);
+       } else if (dmfc_type_setup >= DMFC_HIGH_RESOLUTION_DC) {
+               dev_dbg(ipu->dev, "DMFC high resolution has set, will not change\n");
+               return;
+       } else
+               dmfc_type_setup = dmfc_type;
+
+       if (dmfc_type == DMFC_HIGH_RESOLUTION_DC) {
+               /* 1 - segment 0~3;
+                * 5B - segement 4, 5;
+                * 5F - segement 6, 7;
+                * 1C, 2C and 6B, 6F unused;
+                */
+               dev_info(ipu->dev, "IPU DMFC DC HIGH RESOLUTION: 1(0~3), 5B(4,5), 5F(6,7)\n");
+               dmfc_wr_chan = 0x00000088;
+               dmfc_dp_chan = 0x00009694;
+               ipu->dmfc_size_28 = 256*4;
+               ipu->dmfc_size_29 = 0;
+               ipu->dmfc_size_24 = 0;
+               ipu->dmfc_size_27 = 128*4;
+               ipu->dmfc_size_23 = 128*4;
+       } else if (dmfc_type == DMFC_HIGH_RESOLUTION_DP) {
+               /* 1 - segment 0, 1;
+                * 5B - segement 2~5;
+                * 5F - segement 6,7;
+                * 1C, 2C and 6B, 6F unused;
+                */
+               dev_info(ipu->dev, "IPU DMFC DP HIGH RESOLUTION: 1(0,1), 5B(2~5), 5F(6,7)\n");
+               dmfc_wr_chan = 0x00000090;
+               dmfc_dp_chan = 0x0000968a;
+               ipu->dmfc_size_28 = 128*4;
+               ipu->dmfc_size_29 = 0;
+               ipu->dmfc_size_24 = 0;
+               ipu->dmfc_size_27 = 128*4;
+               ipu->dmfc_size_23 = 256*4;
+       } else if (dmfc_type == DMFC_HIGH_RESOLUTION_ONLY_DP) {
+               /* 5B - segement 0~3;
+                * 5F - segement 4~7;
+                * 1, 1C, 2C and 6B, 6F unused;
+                */
+               dev_info(ipu->dev, "IPU DMFC ONLY-DP HIGH RESOLUTION: 5B(0~3), 5F(4~7)\n");
+               dmfc_wr_chan = 0x00000000;
+               dmfc_dp_chan = 0x00008c88;
+               ipu->dmfc_size_28 = 0;
+               ipu->dmfc_size_29 = 0;
+               ipu->dmfc_size_24 = 0;
+               ipu->dmfc_size_27 = 256*4;
+               ipu->dmfc_size_23 = 256*4;
+       } else {
+               /* 1 - segment 0, 1;
+                * 5B - segement 4, 5;
+                * 5F - segement 6, 7;
+                * 1C, 2C and 6B, 6F unused;
+                */
+               dev_info(ipu->dev, "IPU DMFC NORMAL mode: 1(0~1), 5B(4,5), 5F(6,7)\n");
+               dmfc_wr_chan = 0x00000090;
+               dmfc_dp_chan = 0x00009694;
+               ipu->dmfc_size_28 = 128*4;
+               ipu->dmfc_size_29 = 0;
+               ipu->dmfc_size_24 = 0;
+               ipu->dmfc_size_27 = 128*4;
+               ipu->dmfc_size_23 = 128*4;
+       }
+       ipu_dmfc_write(ipu, dmfc_wr_chan, DMFC_WR_CHAN);
+       ipu_dmfc_write(ipu, 0x202020F6, DMFC_WR_CHAN_DEF);
+       ipu_dmfc_write(ipu, dmfc_dp_chan, DMFC_DP_CHAN);
+       /* Enable chan 5 watermark set at 5 bursts and clear at 7 bursts */
+       ipu_dmfc_write(ipu, 0x2020F6F6, DMFC_DP_CHAN_DEF);
+}
+
+static int __init dmfc_setup(char *options)
+{
+       get_option(&options, &dmfc_type_setup);
+       if (dmfc_type_setup > DMFC_HIGH_RESOLUTION_ONLY_DP)
+               dmfc_type_setup = DMFC_HIGH_RESOLUTION_ONLY_DP;
+       return 1;
+}
+__setup("dmfc=", dmfc_setup);
+
+void _ipu_dmfc_set_wait4eot(struct ipu_soc *ipu, int dma_chan, int width)
+{
+       u32 dmfc_gen1 = ipu_dmfc_read(ipu, DMFC_GENERAL1);
+
+       if (width >= HIGH_RESOLUTION_WIDTH) {
+               if (dma_chan == 23)
+                       _ipu_dmfc_init(ipu, DMFC_HIGH_RESOLUTION_DP, 0);
+               else if (dma_chan == 28)
+                       _ipu_dmfc_init(ipu, DMFC_HIGH_RESOLUTION_DC, 0);
+       }
+
+       if (dma_chan == 23) { /*5B*/
+               if (ipu->dmfc_size_23/width > 3)
+                       dmfc_gen1 |= 1UL << 20;
+               else
+                       dmfc_gen1 &= ~(1UL << 20);
+       } else if (dma_chan == 24) { /*6B*/
+               if (ipu->dmfc_size_24/width > 1)
+                       dmfc_gen1 |= 1UL << 22;
+               else
+                       dmfc_gen1 &= ~(1UL << 22);
+       } else if (dma_chan == 27) { /*5F*/
+               if (ipu->dmfc_size_27/width > 2)
+                       dmfc_gen1 |= 1UL << 21;
+               else
+                       dmfc_gen1 &= ~(1UL << 21);
+       } else if (dma_chan == 28) { /*1*/
+               if (ipu->dmfc_size_28/width > 2)
+                       dmfc_gen1 |= 1UL << 16;
+               else
+                       dmfc_gen1 &= ~(1UL << 16);
+       } else if (dma_chan == 29) { /*6F*/
+               if (ipu->dmfc_size_29/width > 1)
+                       dmfc_gen1 |= 1UL << 23;
+               else
+                       dmfc_gen1 &= ~(1UL << 23);
+       }
+
+       ipu_dmfc_write(ipu, dmfc_gen1, DMFC_GENERAL1);
+}
+
+void _ipu_dmfc_set_burst_size(struct ipu_soc *ipu, int dma_chan, int burst_size)
+{
+       u32 dmfc_wr_chan = ipu_dmfc_read(ipu, DMFC_WR_CHAN);
+       u32 dmfc_dp_chan = ipu_dmfc_read(ipu, DMFC_DP_CHAN);
+       int dmfc_bs = 0;
+
+       switch (burst_size) {
+       case 64:
+               dmfc_bs = 0x40;
+               break;
+       case 32:
+       case 20:
+               dmfc_bs = 0x80;
+               break;
+       case 16:
+               dmfc_bs = 0xc0;
+               break;
+       default:
+               dev_err(ipu->dev, "Unsupported burst size %d\n",
+                       burst_size);
+               return;
+       }
+
+       if (dma_chan == 23) { /*5B*/
+               dmfc_dp_chan &= ~(0xc0);
+               dmfc_dp_chan |= dmfc_bs;
+       } else if (dma_chan == 27) { /*5F*/
+               dmfc_dp_chan &= ~(0xc000);
+               dmfc_dp_chan |= (dmfc_bs << 8);
+       } else if (dma_chan == 28) { /*1*/
+               dmfc_wr_chan &= ~(0xc0);
+               dmfc_wr_chan |= dmfc_bs;
+       }
+
+       ipu_dmfc_write(ipu, dmfc_wr_chan, DMFC_WR_CHAN);
+       ipu_dmfc_write(ipu, dmfc_dp_chan, DMFC_DP_CHAN);
+}
+
+static void _ipu_di_data_wave_config(struct ipu_soc *ipu,
+                               int di, int wave_gen,
+                               int access_size, int component_size)
+{
+       u32 reg;
+       reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) |
+           (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET);
+       ipu_di_write(ipu, di, reg, DI_DW_GEN(wave_gen));
+}
+
+static void _ipu_di_data_pin_config(struct ipu_soc *ipu,
+                       int di, int wave_gen, int di_pin, int set,
+                       int up, int down)
+{
+       u32 reg;
+
+       reg = ipu_di_read(ipu, di, DI_DW_GEN(wave_gen));
+       reg &= ~(0x3 << (di_pin * 2));
+       reg |= set << (di_pin * 2);
+       ipu_di_write(ipu, di, reg, DI_DW_GEN(wave_gen));
+
+       ipu_di_write(ipu, di, (down << 16) | up, DI_DW_SET(wave_gen, set));
+}
+
+static void _ipu_di_sync_config(struct ipu_soc *ipu,
+                               int di, int wave_gen,
+                               int run_count, int run_src,
+                               int offset_count, int offset_src,
+                               int repeat_count, int cnt_clr_src,
+                               int cnt_polarity_gen_en,
+                               int cnt_polarity_clr_src,
+                               int cnt_polarity_trigger_src,
+                               int cnt_up, int cnt_down)
+{
+       u32 reg;
+
+       if ((run_count >= 0x1000) || (offset_count >= 0x1000) || (repeat_count >= 0x1000) ||
+               (cnt_up >= 0x400) || (cnt_down >= 0x400)) {
+               dev_err(ipu->dev, "DI%d counters out of range.\n", di);
+               return;
+       }
+
+       reg = (run_count << 19) | (++run_src << 16) |
+           (offset_count << 3) | ++offset_src;
+       ipu_di_write(ipu, di, reg, DI_SW_GEN0(wave_gen));
+       reg = (cnt_polarity_gen_en << 29) | (++cnt_clr_src << 25) |
+           (++cnt_polarity_trigger_src << 12) | (++cnt_polarity_clr_src << 9);
+       reg |= (cnt_down << 16) | cnt_up;
+       if (repeat_count == 0) {
+               /* Enable auto reload */
+               reg |= 0x10000000;
+       }
+       ipu_di_write(ipu, di, reg, DI_SW_GEN1(wave_gen));
+       reg = ipu_di_read(ipu, di, DI_STP_REP(wave_gen));
+       reg &= ~(0xFFFF << (16 * ((wave_gen - 1) & 0x1)));
+       reg |= repeat_count << (16 * ((wave_gen - 1) & 0x1));
+       ipu_di_write(ipu, di, reg, DI_STP_REP(wave_gen));
+}
+
+static void _ipu_dc_map_link(struct ipu_soc *ipu,
+               int current_map,
+               int base_map_0, int buf_num_0,
+               int base_map_1, int buf_num_1,
+               int base_map_2, int buf_num_2)
+{
+       int ptr_0 = base_map_0 * 3 + buf_num_0;
+       int ptr_1 = base_map_1 * 3 + buf_num_1;
+       int ptr_2 = base_map_2 * 3 + buf_num_2;
+       int ptr;
+       u32 reg;
+       ptr = (ptr_2 << 10) +  (ptr_1 << 5) + ptr_0;
+
+       reg = ipu_dc_read(ipu, DC_MAP_CONF_PTR(current_map));
+       reg &= ~(0x1F << ((16 * (current_map & 0x1))));
+       reg |= ptr << ((16 * (current_map & 0x1)));
+       ipu_dc_write(ipu, reg, DC_MAP_CONF_PTR(current_map));
+}
+
+static void _ipu_dc_map_config(struct ipu_soc *ipu,
+               int map, int byte_num, int offset, int mask)
+{
+       int ptr = map * 3 + byte_num;
+       u32 reg;
+
+       reg = ipu_dc_read(ipu, DC_MAP_CONF_VAL(ptr));
+       reg &= ~(0xFFFF << (16 * (ptr & 0x1)));
+       reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
+       ipu_dc_write(ipu, reg, DC_MAP_CONF_VAL(ptr));
+
+       reg = ipu_dc_read(ipu, DC_MAP_CONF_PTR(map));
+       reg &= ~(0x1F << ((16 * (map & 0x1)) + (5 * byte_num)));
+       reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
+       ipu_dc_write(ipu, reg, DC_MAP_CONF_PTR(map));
+}
+
+static void _ipu_dc_map_clear(struct ipu_soc *ipu, int map)
+{
+       u32 reg = ipu_dc_read(ipu, DC_MAP_CONF_PTR(map));
+       ipu_dc_write(ipu, reg & ~(0xFFFF << (16 * (map & 0x1))),
+                    DC_MAP_CONF_PTR(map));
+}
+
+static void _ipu_dc_write_tmpl(struct ipu_soc *ipu,
+                       int word, u32 opcode, u32 operand, int map,
+                       int wave, int glue, int sync, int stop)
+{
+       u32 reg;
+
+       if (opcode == WRG) {
+               reg = sync;
+               reg |= (glue << 4);
+               reg |= (++wave << 11);
+               reg |= ((operand & 0x1FFFF) << 15);
+               ipu_dc_tmpl_write(ipu, reg, word * 8);
+
+               reg = (operand >> 17);
+               reg |= opcode << 7;
+               reg |= (stop << 9);
+               ipu_dc_tmpl_write(ipu, reg, word * 8 + 4);
+       } else {
+               reg = sync;
+               reg |= (glue << 4);
+               reg |= (++wave << 11);
+               reg |= (++map << 15);
+               reg |= (operand << 20) & 0xFFF00000;
+               ipu_dc_tmpl_write(ipu, reg, word * 8);
+
+               reg = (operand >> 12);
+               reg |= opcode << 4;
+               reg |= (stop << 9);
+               ipu_dc_tmpl_write(ipu, reg, word * 8 + 4);
+       }
+}
+
+static void _ipu_dc_link_event(struct ipu_soc *ipu,
+               int chan, int event, int addr, int priority)
+{
+       u32 reg;
+       u32 address_shift;
+       if (event < DC_EVEN_UGDE0) {
+               reg = ipu_dc_read(ipu, DC_RL_CH(chan, event));
+               reg &= ~(0xFFFF << (16 * (event & 0x1)));
+               reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
+               ipu_dc_write(ipu, reg, DC_RL_CH(chan, event));
+       } else {
+               reg = ipu_dc_read(ipu, DC_UGDE_0((event - DC_EVEN_UGDE0) / 2));
+               if ((event - DC_EVEN_UGDE0) & 0x1) {
+                       reg &= ~(0x2FF << 16);
+                       reg |= (addr << 16);
+                       reg |= priority ? (2 << 24) : 0x0;
+               } else {
+                       reg &= ~0xFC00FFFF;
+                       if (priority)
+                               chan = (chan >> 1) +
+                                       ((((chan & 0x1) + ((chan & 0x2) >> 1))) | (chan >> 3));
+                       else
+                               chan = 0x7;
+                       address_shift = ((event - DC_EVEN_UGDE0) >> 1) ? 7 : 8;
+                       reg |= (addr << address_shift) | (priority << 3) | chan;
+               }
+               ipu_dc_write(ipu, reg, DC_UGDE_0((event - DC_EVEN_UGDE0) / 2));
+       }
+}
+
+/*     Y = R *  1.200 + G *  2.343 + B *  .453 + 0.250;
+       U = R * -.672 + G * -1.328 + B *  2.000 + 512.250.;
+       V = R *  2.000 + G * -1.672 + B * -.328 + 512.250.;*/
+static const int rgb2ycbcr_coeff[5][3] = {
+       {0x4D, 0x96, 0x1D},
+       {-0x2B, -0x55, 0x80},
+       {0x80, -0x6B, -0x15},
+       {0x0000, 0x0200, 0x0200},       /* B0, B1, B2 */
+       {0x2, 0x2, 0x2},        /* S0, S1, S2 */
+};
+
+/*     R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
+       G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
+       B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */
+static const int ycbcr2rgb_coeff[5][3] = {
+       {0x095, 0x000, 0x0CC},
+       {0x095, 0x3CE, 0x398},
+       {0x095, 0x0FF, 0x000},
+       {0x3E42, 0x010A, 0x3DD6},       /*B0,B1,B2 */
+       {0x1, 0x1, 0x1},        /*S0,S1,S2 */
+};
+
+#define mask_a(a) ((u32)(a) & 0x3FF)
+#define mask_b(b) ((u32)(b) & 0x3FFF)
+
+/* Pls keep S0, S1 and S2 as 0x2 by using this convertion */
+static int _rgb_to_yuv(int n, int red, int green, int blue)
+{
+       int c;
+       c = red * rgb2ycbcr_coeff[n][0];
+       c += green * rgb2ycbcr_coeff[n][1];
+       c += blue * rgb2ycbcr_coeff[n][2];
+       c /= 16;
+       c += rgb2ycbcr_coeff[3][n] * 4;
+       c += 8;
+       c /= 16;
+       if (c < 0)
+               c = 0;
+       if (c > 255)
+               c = 255;
+       return c;
+}
+
+/*
+ * Row is for BG:      RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE
+ * Column is for FG:   RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE
+ */
+static struct dp_csc_param_t dp_csc_array[CSC_NUM][CSC_NUM] = {
+{{DP_COM_CONF_CSC_DEF_BOTH, &rgb2ycbcr_coeff}, {0, 0}, {0, 0}, {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff}, {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff} },
+{{0, 0}, {DP_COM_CONF_CSC_DEF_BOTH, &ycbcr2rgb_coeff}, {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff}, {0, 0}, {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff} },
+{{0, 0}, {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, {0, 0}, {0, 0}, {0, 0} },
+{{DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, {0, 0}, {0, 0}, {0, 0}, {0, 0} },
+{{DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, {0, 0}, {0, 0}, {0, 0} }
+};
+
+void __ipu_dp_csc_setup(struct ipu_soc *ipu,
+               int dp, struct dp_csc_param_t dp_csc_param,
+               bool srm_mode_update)
+{
+       u32 reg;
+       const int (*coeff)[5][3];
+
+       if (dp_csc_param.mode >= 0) {
+               reg = ipu_dp_read(ipu, DP_COM_CONF(dp));
+               reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+               reg |= dp_csc_param.mode;
+               ipu_dp_write(ipu, reg, DP_COM_CONF(dp));
+       }
+
+       coeff = dp_csc_param.coeff;
+
+       if (coeff) {
+               ipu_dp_write(ipu, mask_a((*coeff)[0][0]) |
+                               (mask_a((*coeff)[0][1]) << 16), DP_CSC_A_0(dp));
+               ipu_dp_write(ipu, mask_a((*coeff)[0][2]) |
+                               (mask_a((*coeff)[1][0]) << 16), DP_CSC_A_1(dp));
+               ipu_dp_write(ipu, mask_a((*coeff)[1][1]) |
+                               (mask_a((*coeff)[1][2]) << 16), DP_CSC_A_2(dp));
+               ipu_dp_write(ipu, mask_a((*coeff)[2][0]) |
+                               (mask_a((*coeff)[2][1]) << 16), DP_CSC_A_3(dp));
+               ipu_dp_write(ipu, mask_a((*coeff)[2][2]) |
+                               (mask_b((*coeff)[3][0]) << 16) |
+                               ((*coeff)[4][0] << 30), DP_CSC_0(dp));
+               ipu_dp_write(ipu, mask_b((*coeff)[3][1]) | ((*coeff)[4][1] << 14) |
+                               (mask_b((*coeff)[3][2]) << 16) |
+                               ((*coeff)[4][2] << 30), DP_CSC_1(dp));
+       }
+
+       if (srm_mode_update) {
+               reg = ipu_cm_read(ipu, IPU_SRM_PRI2) | 0x8;
+               ipu_cm_write(ipu, reg, IPU_SRM_PRI2);
+       }
+}
+
+int _ipu_dp_init(struct ipu_soc *ipu,
+               ipu_channel_t channel, uint32_t in_pixel_fmt,
+               uint32_t out_pixel_fmt)
+{
+       int in_fmt, out_fmt;
+       int dp;
+       int partial = false;
+       uint32_t reg;
+
+       if (channel == MEM_FG_SYNC) {
+               dp = DP_SYNC;
+               partial = true;
+       } else if (channel == MEM_BG_SYNC) {
+               dp = DP_SYNC;
+               partial = false;
+       } else if (channel == MEM_BG_ASYNC0) {
+               dp = DP_ASYNC0;
+               partial = false;
+       } else {
+               return -EINVAL;
+       }
+
+       in_fmt = format_to_colorspace(in_pixel_fmt);
+       out_fmt = format_to_colorspace(out_pixel_fmt);
+
+       if (partial) {
+               if (in_fmt == RGB) {
+                       if (out_fmt == RGB)
+                               ipu->fg_csc_type = RGB2RGB;
+                       else
+                               ipu->fg_csc_type = RGB2YUV;
+               } else {
+                       if (out_fmt == RGB)
+                               ipu->fg_csc_type = YUV2RGB;
+                       else
+                               ipu->fg_csc_type = YUV2YUV;
+               }
+       } else {
+               if (in_fmt == RGB) {
+                       if (out_fmt == RGB)
+                               ipu->bg_csc_type = RGB2RGB;
+                       else
+                               ipu->bg_csc_type = RGB2YUV;
+               } else {
+                       if (out_fmt == RGB)
+                               ipu->bg_csc_type = YUV2RGB;
+                       else
+                               ipu->bg_csc_type = YUV2YUV;
+               }
+       }
+
+       /* Transform color key from rgb to yuv if CSC is enabled */
+       reg = ipu_dp_read(ipu, DP_COM_CONF(dp));
+       if (ipu->color_key_4rgb && (reg & DP_COM_CONF_GWCKE) &&
+                       (((ipu->fg_csc_type == RGB2YUV) && (ipu->bg_csc_type == YUV2YUV)) ||
+                        ((ipu->fg_csc_type == YUV2YUV) && (ipu->bg_csc_type == RGB2YUV)) ||
+                        ((ipu->fg_csc_type == YUV2YUV) && (ipu->bg_csc_type == YUV2YUV)) ||
+                        ((ipu->fg_csc_type == YUV2RGB) && (ipu->bg_csc_type == YUV2RGB)))) {
+               int red, green, blue;
+               int y, u, v;
+               uint32_t color_key = ipu_dp_read(ipu, DP_GRAPH_WIND_CTRL(dp)) & 0xFFFFFFL;
+
+               dev_dbg(ipu->dev, "_ipu_dp_init color key 0x%x need change to yuv fmt!\n", color_key);
+
+               red = (color_key >> 16) & 0xFF;
+               green = (color_key >> 8) & 0xFF;
+               blue = color_key & 0xFF;
+
+               y = _rgb_to_yuv(0, red, green, blue);
+               u = _rgb_to_yuv(1, red, green, blue);
+               v = _rgb_to_yuv(2, red, green, blue);
+               color_key = (y << 16) | (u << 8) | v;
+
+               reg = ipu_dp_read(ipu, DP_GRAPH_WIND_CTRL(dp)) & 0xFF000000L;
+               ipu_dp_write(ipu, reg | color_key, DP_GRAPH_WIND_CTRL(dp));
+               ipu->color_key_4rgb = false;
+
+               dev_dbg(ipu->dev, "_ipu_dp_init color key change to yuv fmt 0x%x!\n", color_key);
+       }
+
+       __ipu_dp_csc_setup(ipu, dp,
+                          dp_csc_array[ipu->bg_csc_type][ipu->fg_csc_type],
+                          false);
+
+       return 0;
+}
+
+void _ipu_dp_uninit(struct ipu_soc *ipu, ipu_channel_t channel)
+{
+       int dp;
+       int partial = false;
+
+       if (channel == MEM_FG_SYNC) {
+               dp = DP_SYNC;
+               partial = true;
+       } else if (channel == MEM_BG_SYNC) {
+               dp = DP_SYNC;
+               partial = false;
+       } else if (channel == MEM_BG_ASYNC0) {
+               dp = DP_ASYNC0;
+               partial = false;
+       } else {
+               return;
+       }
+
+       if (partial)
+               ipu->fg_csc_type = CSC_NONE;
+       else
+               ipu->bg_csc_type = CSC_NONE;
+
+       __ipu_dp_csc_setup(ipu, dp, dp_csc_array[ipu->bg_csc_type][ipu->fg_csc_type], false);
+}
+
+void _ipu_dc_init(struct ipu_soc *ipu, int dc_chan, int di, bool interlaced, uint32_t pixel_fmt)
+{
+       u32 reg = 0;
+
+       if ((dc_chan == 1) || (dc_chan == 5)) {
+               if (interlaced) {
+                       _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NL, 0, 3);
+                       _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOL, 0, 2);
+                       _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA, 0, 1);
+               } else {
+                       if (di) {
+                               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NL, 2, 3);
+                               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOL, 3, 2);
+                               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA, 1, 1);
+                               if ((pixel_fmt == IPU_PIX_FMT_YUYV) ||
+                               (pixel_fmt == IPU_PIX_FMT_UYVY) ||
+                               (pixel_fmt == IPU_PIX_FMT_YVYU) ||
+                               (pixel_fmt == IPU_PIX_FMT_VYUY)) {
+                                       _ipu_dc_link_event(ipu, dc_chan, DC_ODD_UGDE1, 9, 5);
+                                       _ipu_dc_link_event(ipu, dc_chan, DC_EVEN_UGDE1, 8, 5);
+                               }
+                       } else {
+                               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NL, 5, 3);
+                               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOL, 6, 2);
+                               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA, 12, 1);
+                               if ((pixel_fmt == IPU_PIX_FMT_YUYV) ||
+                               (pixel_fmt == IPU_PIX_FMT_UYVY) ||
+                               (pixel_fmt == IPU_PIX_FMT_YVYU) ||
+                               (pixel_fmt == IPU_PIX_FMT_VYUY)) {
+                                       _ipu_dc_link_event(ipu, dc_chan, DC_ODD_UGDE0, 10, 5);
+                                       _ipu_dc_link_event(ipu, dc_chan, DC_EVEN_UGDE0, 11, 5);
+                               }
+                       }
+               }
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NF, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NFIELD, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOF, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOFIELD, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_CHAN, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_ADDR, 0, 0);
+
+               reg = 0x2;
+               reg |= DC_DISP_ID_SYNC(di) << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET;
+               reg |= di << 2;
+               if (interlaced)
+                       reg |= DC_WR_CH_CONF_FIELD_MODE;
+       } else if ((dc_chan == 8) || (dc_chan == 9)) {
+               /* async channels */
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA_W_0, 0x64, 1);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA_W_1, 0x64, 1);
+
+               reg = 0x3;
+               reg |= DC_DISP_ID_SERIAL << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET;
+       }
+       ipu_dc_write(ipu, reg, DC_WR_CH_CONF(dc_chan));
+
+       ipu_dc_write(ipu, 0x00000000, DC_WR_CH_ADDR(dc_chan));
+
+       ipu_dc_write(ipu, 0x00000084, DC_GEN);
+}
+
+void _ipu_dc_uninit(struct ipu_soc *ipu, int dc_chan)
+{
+       if ((dc_chan == 1) || (dc_chan == 5)) {
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NL, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOL, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NF, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NFIELD, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOF, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOFIELD, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_CHAN, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_ADDR, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_ODD_UGDE0, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVEN_UGDE0, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_ODD_UGDE1, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVEN_UGDE1, 0, 0);
+       } else if ((dc_chan == 8) || (dc_chan == 9)) {
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_ADDR_W_0, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_ADDR_W_1, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_CHAN_W_0, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_CHAN_W_1, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA_W_0, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA_W_1, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_ADDR_R_0, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_ADDR_R_1, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_CHAN_R_0, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_CHAN_R_1, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA_R_0, 0, 0);
+               _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA_R_1, 0, 0);
+       }
+}
+
+int _ipu_disp_chan_is_interlaced(struct ipu_soc *ipu, ipu_channel_t channel)
+{
+       if (channel == MEM_DC_SYNC)
+               return !!(ipu_dc_read(ipu, DC_WR_CH_CONF_1) &
+                         DC_WR_CH_CONF_FIELD_MODE);
+       else if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC))
+               return !!(ipu_dc_read(ipu, DC_WR_CH_CONF_5) &
+                         DC_WR_CH_CONF_FIELD_MODE);
+       return 0;
+}
+
+void _ipu_dp_dc_enable(struct ipu_soc *ipu, ipu_channel_t channel)
+{
+       int di;
+       uint32_t reg;
+       uint32_t dc_chan;
+       int irq = 0;
+
+       if (channel == MEM_FG_SYNC)
+               irq = IPU_IRQ_DP_SF_END;
+       else if (channel == MEM_DC_SYNC)
+               dc_chan = 1;
+       else if (channel == MEM_BG_SYNC)
+               dc_chan = 5;
+       else
+               return;
+
+       if (channel == MEM_FG_SYNC) {
+               /* Enable FG channel */
+               reg = ipu_dp_read(ipu, DP_COM_CONF(DP_SYNC));
+               ipu_dp_write(ipu, reg | DP_COM_CONF_FG_EN, DP_COM_CONF(DP_SYNC));
+
+               reg = ipu_cm_read(ipu, IPU_SRM_PRI2) | 0x8;
+               ipu_cm_write(ipu, reg, IPU_SRM_PRI2);
+               return;
+       } else if (channel == MEM_BG_SYNC) {
+               reg = ipu_cm_read(ipu, IPU_SRM_PRI2) | 0x8;
+               ipu_cm_write(ipu, reg, IPU_SRM_PRI2);
+       }
+
+       di = ipu->dc_di_assignment[dc_chan];
+
+       /* Make sure other DC sync channel is not assigned same DI */
+       reg = ipu_dc_read(ipu, DC_WR_CH_CONF(6 - dc_chan));
+       if ((di << 2) == (reg & DC_WR_CH_CONF_PROG_DI_ID)) {
+               reg &= ~DC_WR_CH_CONF_PROG_DI_ID;
+               reg |= di ? 0 : DC_WR_CH_CONF_PROG_DI_ID;
+               ipu_dc_write(ipu, reg, DC_WR_CH_CONF(6 - dc_chan));
+       }
+
+       reg = ipu_dc_read(ipu, DC_WR_CH_CONF(dc_chan));
+       reg |= 4 << DC_WR_CH_CONF_PROG_TYPE_OFFSET;
+       ipu_dc_write(ipu, reg, DC_WR_CH_CONF(dc_chan));
+
+       clk_prepare_enable(ipu->pixel_clk[di]);
+       ipu->pixel_clk_en[ipu->dc_di_assignment[dc_chan]] = true;
+}
+
+static irqreturn_t dc_irq_handler(int irq, void *dev_id)
+{
+       struct ipu_soc *ipu = dev_id;
+       struct completion *comp = &ipu->dc_comp;
+       uint32_t reg;
+       uint32_t dc_chan;
+
+       if (irq == IPU_IRQ_DC_FC_1)
+               dc_chan = 1;
+       else
+               dc_chan = 5;
+
+       if (!ipu->dc_swap) {
+               reg = ipu_dc_read(ipu, DC_WR_CH_CONF(dc_chan));
+               reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+               ipu_dc_write(ipu, reg, DC_WR_CH_CONF(dc_chan));
+
+               reg = ipu_cm_read(ipu, IPU_DISP_GEN);
+               if (ipu->dc_di_assignment[dc_chan])
+                       reg &= ~DI1_COUNTER_RELEASE;
+               else
+                       reg &= ~DI0_COUNTER_RELEASE;
+               ipu_cm_write(ipu, reg, IPU_DISP_GEN);
+       }
+
+       complete(comp);
+       return IRQ_HANDLED;
+}
+
+void _ipu_dp_dc_disable(struct ipu_soc *ipu, ipu_channel_t channel, bool swap)
+{
+       int ret;
+       uint32_t reg;
+       uint32_t csc;
+       uint32_t dc_chan;
+       int irq = 0;
+       int timeout = 50;
+
+       ipu->dc_swap = swap;
+
+       if (channel == MEM_DC_SYNC) {
+               dc_chan = 1;
+               irq = IPU_IRQ_DC_FC_1;
+       } else if (channel == MEM_BG_SYNC) {
+               dc_chan = 5;
+               irq = IPU_IRQ_DP_SF_END;
+       } else if (channel == MEM_FG_SYNC) {
+               /* Disable FG channel */
+               dc_chan = 5;
+
+               reg = ipu_dp_read(ipu, DP_COM_CONF(DP_SYNC));
+               csc = reg & DP_COM_CONF_CSC_DEF_MASK;
+               if (csc == DP_COM_CONF_CSC_DEF_FG)
+                       reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+
+               reg &= ~DP_COM_CONF_FG_EN;
+               ipu_dp_write(ipu, reg, DP_COM_CONF(DP_SYNC));
+
+               reg = ipu_cm_read(ipu, IPU_SRM_PRI2) | 0x8;
+               ipu_cm_write(ipu, reg, IPU_SRM_PRI2);
+
+               if (ipu_is_channel_busy(ipu, MEM_BG_SYNC)) {
+                       ipu_cm_write(ipu, IPUIRQ_2_MASK(IPU_IRQ_DP_SF_END),
+                               IPUIRQ_2_STATREG(ipu->devtype,
+                                                       IPU_IRQ_DP_SF_END));
+                       while ((ipu_cm_read(ipu,
+                               IPUIRQ_2_STATREG(ipu->devtype,
+                                                       IPU_IRQ_DP_SF_END)) &
+                               IPUIRQ_2_MASK(IPU_IRQ_DP_SF_END)) == 0) {
+                               msleep(2);
+                               timeout -= 2;
+                               if (timeout <= 0)
+                                       break;
+                       }
+               }
+               return;
+       } else {
+               return;
+       }
+
+       init_completion(&ipu->dc_comp);
+       ret = ipu_request_irq(ipu, irq, dc_irq_handler, 0, NULL, ipu);
+       if (ret < 0) {
+               dev_err(ipu->dev, "DC irq %d in use\n", irq);
+               return;
+       }
+       ret = wait_for_completion_timeout(&ipu->dc_comp, msecs_to_jiffies(50));
+       ipu_free_irq(ipu, irq, ipu);
+       dev_dbg(ipu->dev, "DC stop timeout - %d * 10ms\n", 5 - ret);
+
+       if (ipu->dc_swap) {
+               /* Swap DC channel 1 and 5 settings, and disable old dc chan */
+               reg = ipu_dc_read(ipu, DC_WR_CH_CONF(dc_chan));
+               ipu_dc_write(ipu, reg, DC_WR_CH_CONF(6 - dc_chan));
+               reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+               reg ^= DC_WR_CH_CONF_PROG_DI_ID;
+               ipu_dc_write(ipu, reg, DC_WR_CH_CONF(dc_chan));
+       }
+}
+
+void _ipu_init_dc_mappings(struct ipu_soc *ipu)
+{
+       /* IPU_PIX_FMT_RGB24 */
+       _ipu_dc_map_clear(ipu, 0);
+       _ipu_dc_map_config(ipu, 0, 0, 7, 0xFF);
+       _ipu_dc_map_config(ipu, 0, 1, 15, 0xFF);
+       _ipu_dc_map_config(ipu, 0, 2, 23, 0xFF);
+
+       /* IPU_PIX_FMT_RGB666 */
+       _ipu_dc_map_clear(ipu, 1);
+       _ipu_dc_map_config(ipu, 1, 0, 5, 0xFC);
+       _ipu_dc_map_config(ipu, 1, 1, 11, 0xFC);
+       _ipu_dc_map_config(ipu, 1, 2, 17, 0xFC);
+
+       /* IPU_PIX_FMT_YUV444 */
+       _ipu_dc_map_clear(ipu, 2);
+       _ipu_dc_map_config(ipu, 2, 0, 15, 0xFF);
+       _ipu_dc_map_config(ipu, 2, 1, 23, 0xFF);
+       _ipu_dc_map_config(ipu, 2, 2, 7, 0xFF);
+
+       /* IPU_PIX_FMT_RGB565 */
+       _ipu_dc_map_clear(ipu, 3);
+       _ipu_dc_map_config(ipu, 3, 0, 4, 0xF8);
+       _ipu_dc_map_config(ipu, 3, 1, 10, 0xFC);
+       _ipu_dc_map_config(ipu, 3, 2, 15, 0xF8);
+
+       /* IPU_PIX_FMT_LVDS666 */
+       _ipu_dc_map_clear(ipu, 4);
+       _ipu_dc_map_config(ipu, 4, 0, 5, 0xFC);
+       _ipu_dc_map_config(ipu, 4, 1, 13, 0xFC);
+       _ipu_dc_map_config(ipu, 4, 2, 21, 0xFC);
+
+       /* IPU_PIX_FMT_VYUY 16bit width */
+       _ipu_dc_map_clear(ipu, 5);
+       _ipu_dc_map_config(ipu, 5, 0, 7, 0xFF);
+       _ipu_dc_map_config(ipu, 5, 1, 0, 0x0);
+       _ipu_dc_map_config(ipu, 5, 2, 15, 0xFF);
+       _ipu_dc_map_clear(ipu, 6);
+       _ipu_dc_map_config(ipu, 6, 0, 0, 0x0);
+       _ipu_dc_map_config(ipu, 6, 1, 7, 0xFF);
+       _ipu_dc_map_config(ipu, 6, 2, 15, 0xFF);
+
+       /* IPU_PIX_FMT_UYUV 16bit width */
+       _ipu_dc_map_clear(ipu, 7);
+       _ipu_dc_map_link(ipu, 7, 6, 0, 6, 1, 6, 2);
+       _ipu_dc_map_clear(ipu, 8);
+       _ipu_dc_map_link(ipu, 8, 5, 0, 5, 1, 5, 2);
+
+       /* IPU_PIX_FMT_YUYV 16bit width */
+       _ipu_dc_map_clear(ipu, 9);
+       _ipu_dc_map_link(ipu, 9, 5, 2, 5, 1, 5, 0);
+       _ipu_dc_map_clear(ipu, 10);
+       _ipu_dc_map_link(ipu, 10, 5, 1, 5, 2, 5, 0);
+
+       /* IPU_PIX_FMT_YVYU 16bit width */
+       _ipu_dc_map_clear(ipu, 11);
+       _ipu_dc_map_link(ipu, 11, 5, 1, 5, 2, 5, 0);
+       _ipu_dc_map_clear(ipu, 12);
+       _ipu_dc_map_link(ipu, 12, 5, 2, 5, 1, 5, 0);
+
+       /* IPU_PIX_FMT_GBR24 */
+       /* IPU_PIX_FMT_VYU444 */
+       _ipu_dc_map_clear(ipu, 13);
+       _ipu_dc_map_link(ipu, 13, 0, 2, 0, 0, 0, 1);
+
+       /* IPU_PIX_FMT_BGR24 */
+       _ipu_dc_map_clear(ipu, 14);
+       _ipu_dc_map_link(ipu, 14, 0, 2, 0, 1, 0, 0);
+}
+
+int _ipu_pixfmt_to_map(uint32_t fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_GENERIC:
+       case IPU_PIX_FMT_RGB24:
+               return 0;
+       case IPU_PIX_FMT_RGB666:
+               return 1;
+       case IPU_PIX_FMT_YUV444:
+               return 2;
+       case IPU_PIX_FMT_RGB565:
+               return 3;
+       case IPU_PIX_FMT_LVDS666:
+               return 4;
+       case IPU_PIX_FMT_VYUY:
+               return 6;
+       case IPU_PIX_FMT_UYVY:
+               return 8;
+       case IPU_PIX_FMT_YUYV:
+               return 10;
+       case IPU_PIX_FMT_YVYU:
+               return 12;
+       case IPU_PIX_FMT_GBR24:
+       case IPU_PIX_FMT_VYU444:
+               return 13;
+       case IPU_PIX_FMT_BGR24:
+               return 14;
+       }
+
+       return -1;
+}
+
+/*!
+ * This function sets the colorspace for of dp.
+ * modes.
+ *
+ * @param      ipu             ipu handler
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       param          If it's not NULL, update the csc table
+ *                              with this parameter.
+ *
+ * @return      N/A
+ */
+void _ipu_dp_set_csc_coefficients(struct ipu_soc *ipu, ipu_channel_t channel, int32_t param[][3])
+{
+       int dp;
+       struct dp_csc_param_t dp_csc_param;
+
+       if (channel == MEM_FG_SYNC)
+               dp = DP_SYNC;
+       else if (channel == MEM_BG_SYNC)
+               dp = DP_SYNC;
+       else if (channel == MEM_BG_ASYNC0)
+               dp = DP_ASYNC0;
+       else
+               return;
+
+       dp_csc_param.mode = -1;
+       dp_csc_param.coeff = param;
+       __ipu_dp_csc_setup(ipu, dp, dp_csc_param, true);
+}
+
+void ipu_set_csc_coefficients(struct ipu_soc *ipu, ipu_channel_t channel, int32_t param[][3])
+{
+       _ipu_dp_set_csc_coefficients(ipu, channel, param);
+}
+EXPORT_SYMBOL(ipu_set_csc_coefficients);
+
+/*!
+ * This function is called to adapt synchronous LCD panel to IPU restriction.
+ *
+ */
+void adapt_panel_to_ipu_restricitions(struct ipu_soc *ipu, uint16_t *v_start_width,
+                                       uint16_t *v_sync_width,
+                                       uint16_t *v_end_width)
+{
+       if (*v_end_width < 2) {
+               uint16_t diff = 2 - *v_end_width;
+               if (*v_start_width >= diff) {
+                       *v_end_width = 2;
+                       *v_start_width = *v_start_width - diff;
+               } else if (*v_sync_width > diff) {
+                       *v_end_width = 2;
+                       *v_sync_width = *v_sync_width - diff;
+               } else
+                       dev_err(ipu->dev, "WARNING: try to adapt timming, but failed\n");
+               dev_err(ipu->dev, "WARNING: adapt panel end blank lines\n");
+       }
+}
+
+/*!
+ * This function is called to initialize a synchronous LCD panel.
+ *
+ * @param      ipu             ipu handler
+ * @param       disp            The DI the panel is attached to.
+ *
+ * @param       pixel_clk       Desired pixel clock frequency in Hz.
+ *
+ * @param       pixel_fmt       Input parameter for pixel format of buffer.
+ *                              Pixel format is a FOURCC ASCII code.
+ *
+ * @param       width           The width of panel in pixels.
+ *
+ * @param       height          The height of panel in pixels.
+ *
+ * @param       hStartWidth     The number of pixel clocks between the HSYNC
+ *                              signal pulse and the start of valid data.
+ *
+ * @param       hSyncWidth      The width of the HSYNC signal in units of pixel
+ *                              clocks.
+ *
+ * @param       hEndWidth       The number of pixel clocks between the end of
+ *                              valid data and the HSYNC signal for next line.
+ *
+ * @param       vStartWidth     The number of lines between the VSYNC
+ *                              signal pulse and the start of valid data.
+ *
+ * @param       vSyncWidth      The width of the VSYNC signal in units of lines
+ *
+ * @param       vEndWidth       The number of lines between the end of valid
+ *                              data and the VSYNC signal for next frame.
+ *
+ * @param       sig             Bitfield of signal polarities for LCD interface.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_init_sync_panel(struct ipu_soc *ipu, int disp, uint32_t pixel_clk,
+                           uint16_t width, uint16_t height,
+                           uint32_t pixel_fmt,
+                           uint16_t h_start_width, uint16_t h_sync_width,
+                           uint16_t h_end_width, uint16_t v_start_width,
+                           uint16_t v_sync_width, uint16_t v_end_width,
+                           uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig)
+{
+       uint32_t field0_offset = 0;
+       uint32_t field1_offset;
+       uint32_t reg;
+       uint32_t di_gen, vsync_cnt;
+       uint32_t div, rounded_pixel_clk;
+       uint32_t h_total, v_total;
+       int map;
+       int ret;
+       struct clk *ldb_di0_clk, *ldb_di1_clk;
+       struct clk *di_parent;
+
+       dev_dbg(ipu->dev, "panel size = %d x %d\n", width, height);
+
+       if ((v_sync_width == 0) || (h_sync_width == 0))
+               return -EINVAL;
+
+       adapt_panel_to_ipu_restricitions(ipu, &v_start_width, &v_sync_width, &v_end_width);
+       h_total = width + h_sync_width + h_start_width + h_end_width;
+       v_total = height + v_sync_width + v_start_width + v_end_width;
+
+       /* Init clocking */
+       dev_dbg(ipu->dev, "pixel clk = %d\n", pixel_clk);
+
+       di_parent = clk_get_parent(ipu->di_clk_sel[disp]);
+       if (!di_parent) {
+               dev_err(ipu->dev, "get di clk parent fail\n");
+               return -EINVAL;
+       }
+       ldb_di0_clk = clk_get(ipu->dev, "ldb_di0");
+       if (IS_ERR(ldb_di0_clk)) {
+               dev_err(ipu->dev, "clk_get di0 failed");
+               return PTR_ERR(ldb_di0_clk);
+       }
+       ldb_di1_clk = clk_get(ipu->dev, "ldb_di1");
+       if (IS_ERR(ldb_di1_clk)) {
+               dev_err(ipu->dev, "clk_get di1 failed");
+               return PTR_ERR(ldb_di1_clk);
+       }
+
+       if (ldb_di0_clk == di_parent || ldb_di1_clk == di_parent) {
+               /* if di clk parent is tve/ldb, then keep it;*/
+               dev_dbg(ipu->dev, "use special clk parent\n");
+               ret = clk_set_parent(ipu->pixel_clk_sel[disp], ipu->di_clk[disp]);
+               if (ret) {
+                       dev_err(ipu->dev, "set pixel clk error:%d\n", ret);
+                       return ret;
+               }
+               clk_put(ldb_di0_clk);
+               clk_put(ldb_di1_clk);
+       } else {
+               /* try ipu clk first*/
+               dev_dbg(ipu->dev, "try ipu internal clk\n");
+               ret = clk_set_parent(ipu->pixel_clk_sel[disp], ipu->ipu_clk);
+               if (ret) {
+                       dev_err(ipu->dev, "set pixel clk error:%d\n", ret);
+                       return ret;
+               }
+               rounded_pixel_clk = clk_round_rate(ipu->pixel_clk[disp], pixel_clk);
+               dev_dbg(ipu->dev, "rounded pix clk:%d\n", rounded_pixel_clk);
+               /*
+                * we will only use 1/2 fraction for ipu clk,
+                * so if the clk rate is not fit, try ext clk.
+                */
+               if (!sig.int_clk &&
+                       ((rounded_pixel_clk >= pixel_clk + pixel_clk/200) ||
+                       (rounded_pixel_clk <= pixel_clk - pixel_clk/200))) {
+                       dev_dbg(ipu->dev, "try ipu ext di clk\n");
+
+                       rounded_pixel_clk =
+                               clk_round_rate(ipu->di_clk[disp], pixel_clk);
+                       ret = clk_set_rate(ipu->di_clk[disp],
+                                               rounded_pixel_clk);
+                       if (ret) {
+                               dev_err(ipu->dev,
+                                       "set di clk rate error:%d\n", ret);
+                               return ret;
+                       }
+                       dev_dbg(ipu->dev, "di clk:%d\n", rounded_pixel_clk);
+                       ret = clk_set_parent(ipu->pixel_clk_sel[disp],
+                                               ipu->di_clk[disp]);
+                       if (ret) {
+                               dev_err(ipu->dev,
+                                       "set pixel clk parent error:%d\n", ret);
+                               return ret;
+                       }
+               }
+       }
+       rounded_pixel_clk = clk_round_rate(ipu->pixel_clk[disp], pixel_clk);
+       dev_dbg(ipu->dev, "round pixel clk:%d\n", rounded_pixel_clk);
+       ret = clk_set_rate(ipu->pixel_clk[disp], rounded_pixel_clk);
+       if (ret) {
+               dev_err(ipu->dev, "set pixel clk rate error:%d\n", ret);
+               return ret;
+       }
+       msleep(5);
+       /* Get integer portion of divider */
+       div = clk_get_rate(clk_get_parent(ipu->pixel_clk_sel[disp])) / rounded_pixel_clk;
+       dev_dbg(ipu->dev, "div:%d\n", div);
+       if (!div) {
+               dev_err(ipu->dev, "invalid pixel clk div = 0\n");
+               return -EINVAL;
+       }
+
+
+       mutex_lock(&ipu->mutex_lock);
+
+       _ipu_di_data_wave_config(ipu, disp, SYNC_WAVE, div - 1, div - 1);
+       _ipu_di_data_pin_config(ipu, disp, SYNC_WAVE, DI_PIN15, 3, 0, div * 2);
+
+       map = _ipu_pixfmt_to_map(pixel_fmt);
+       if (map < 0) {
+               dev_dbg(ipu->dev, "IPU_DISP: No MAP\n");
+               mutex_unlock(&ipu->mutex_lock);
+               return -EINVAL;
+       }
+
+       /*clear DI*/
+       di_gen = ipu_di_read(ipu, disp, DI_GENERAL);
+       di_gen &= (0x3 << 20);
+       ipu_di_write(ipu, disp, di_gen, DI_GENERAL);
+
+       if (sig.interlaced) {
+               if (ipu->devtype >= IPUv3EX) {
+                       /* Setup internal HSYNC waveform */
+                       _ipu_di_sync_config(ipu,
+                                       disp,           /* display */
+                                       1,              /* counter */
+                                       h_total/2 - 1,  /* run count */
+                                       DI_SYNC_CLK,    /* run_resolution */
+                                       0,              /* offset */
+                                       DI_SYNC_NONE,   /* offset resolution */
+                                       0,              /* repeat count */
+                                       DI_SYNC_NONE,   /* CNT_CLR_SEL */
+                                       0,              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,              /* COUNT UP */
+                                       0               /* COUNT DOWN */
+                                       );
+
+                       /* Field 1 VSYNC waveform */
+                       _ipu_di_sync_config(ipu,
+                                       disp,           /* display */
+                                       2,              /* counter */
+                                       h_total - 1,    /* run count */
+                                       DI_SYNC_CLK,    /* run_resolution */
+                                       0,              /* offset */
+                                       DI_SYNC_NONE,   /* offset resolution */
+                                       0,              /* repeat count */
+                                       DI_SYNC_NONE,   /* CNT_CLR_SEL */
+                                       0,              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,              /* COUNT UP */
+                                       2*div           /* COUNT DOWN */
+                                       );
+
+                       /* Setup internal HSYNC waveform */
+                       _ipu_di_sync_config(ipu,
+                                       disp,           /* display */
+                                       3,              /* counter */
+                                       v_total*2 - 1,  /* run count */
+                                       DI_SYNC_INT_HSYNC,      /* run_resolution */
+                                       1,                      /* offset */
+                                       DI_SYNC_INT_HSYNC,      /* offset resolution */
+                                       0,              /* repeat count */
+                                       DI_SYNC_NONE,   /* CNT_CLR_SEL */
+                                       0,              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,              /* COUNT UP */
+                                       2*div           /* COUNT DOWN */
+                                       );
+
+                       /* Active Field ? */
+                       _ipu_di_sync_config(ipu,
+                                       disp,           /* display */
+                                       4,              /* counter */
+                                       v_total/2 - 1,  /* run count */
+                                       DI_SYNC_HSYNC,  /* run_resolution */
+                                       v_start_width,  /*  offset */
+                                       DI_SYNC_HSYNC,  /* offset resolution */
+                                       2,              /* repeat count */
+                                       DI_SYNC_VSYNC,  /* CNT_CLR_SEL */
+                                       0,              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,              /* COUNT UP */
+                                       0               /* COUNT DOWN */
+                                       );
+
+                       /* Active Line */
+                       _ipu_di_sync_config(ipu,
+                                       disp,           /* display */
+                                       5,              /* counter */
+                                       0,              /* run count */
+                                       DI_SYNC_HSYNC,  /* run_resolution */
+                                       0,              /*  offset */
+                                       DI_SYNC_NONE,   /* offset resolution */
+                                       height/2,       /* repeat count */
+                                       4,              /* CNT_CLR_SEL */
+                                       0,              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,              /* COUNT UP */
+                                       0               /* COUNT DOWN */
+                                       );
+
+                       /* Field 0 VSYNC waveform */
+                       _ipu_di_sync_config(ipu,
+                                       disp,           /* display */
+                                       6,              /* counter */
+                                       v_total - 1,    /* run count */
+                                       DI_SYNC_HSYNC,  /* run_resolution */
+                                       0,              /* offset */
+                                       DI_SYNC_NONE,   /* offset resolution */
+                                       0,              /* repeat count */
+                                       DI_SYNC_NONE,   /* CNT_CLR_SEL  */
+                                       0,              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,              /* COUNT UP */
+                                       0               /* COUNT DOWN */
+                                       );
+
+                       /* DC VSYNC waveform */
+                       vsync_cnt = 7;
+                       _ipu_di_sync_config(ipu,
+                                       disp,           /* display */
+                                       7,              /* counter */
+                                       v_total/2 - 1,  /* run count */
+                                       DI_SYNC_HSYNC,  /* run_resolution  */
+                                       9,              /* offset  */
+                                       DI_SYNC_HSYNC,  /* offset resolution */
+                                       2,              /* repeat count */
+                                       DI_SYNC_VSYNC,  /* CNT_CLR_SEL */
+                                       0,              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,              /* COUNT UP */
+                                       0               /* COUNT DOWN */
+                                       );
+
+                       /* active pixel waveform */
+                       _ipu_di_sync_config(ipu,
+                                       disp,           /* display */
+                                       8,              /* counter */
+                                       0,              /* run count  */
+                                       DI_SYNC_CLK,    /* run_resolution */
+                                       h_start_width,  /* offset  */
+                                       DI_SYNC_CLK,    /* offset resolution */
+                                       width,          /* repeat count  */
+                                       5,              /* CNT_CLR_SEL  */
+                                       0,              /* CNT_POLARITY_GEN_EN  */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL  */
+                                       0,              /* COUNT UP  */
+                                       0               /* COUNT DOWN */
+                                       );
+
+                       /* Second VSYNC */
+                       _ipu_di_sync_config(ipu,
+                                       disp,           /* display */
+                                       9,              /* counter */
+                                       v_total - 1,    /* run count */
+                                       DI_SYNC_INT_HSYNC,      /* run_resolution */
+                                       v_total/2,              /* offset  */
+                                       DI_SYNC_INT_HSYNC,      /* offset resolution  */
+                                       0,              /* repeat count */
+                                       DI_SYNC_HSYNC,  /* CNT_CLR_SEL */
+                                       0,              /* CNT_POLARITY_GEN_EN  */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL  */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,              /* COUNT UP */
+                                       2*div           /* COUNT DOWN */
+                                       );
+
+                       /* set gentime select and tag sel */
+                       reg = ipu_di_read(ipu, disp, DI_SW_GEN1(9));
+                       reg &= 0x1FFFFFFF;
+                       reg |= (3-1)<<29 | 0x00008000;
+                       ipu_di_write(ipu, disp, reg, DI_SW_GEN1(9));
+
+                       ipu_di_write(ipu, disp, v_total / 2 - 1, DI_SCR_CONF);
+
+                       /* set y_sel = 1 */
+                       di_gen |= 0x10000000;
+                       di_gen |= DI_GEN_POLARITY_5;
+                       di_gen |= DI_GEN_POLARITY_8;
+               } else {
+                       /* Setup internal HSYNC waveform */
+                       _ipu_di_sync_config(ipu, disp, 1, h_total - 1, DI_SYNC_CLK,
+                                       0, DI_SYNC_NONE, 0, DI_SYNC_NONE, 0, DI_SYNC_NONE,
+                                       DI_SYNC_NONE, 0, 0);
+
+                       field1_offset = v_sync_width + v_start_width + height / 2 +
+                               v_end_width;
+                       if (sig.odd_field_first) {
+                               field0_offset = field1_offset - 1;
+                               field1_offset = 0;
+                       }
+                       v_total += v_start_width + v_end_width;
+
+                       /* Field 1 VSYNC waveform */
+                       _ipu_di_sync_config(ipu, disp, 2, v_total - 1, 1,
+                                       field0_offset,
+                                       field0_offset ? 1 : DI_SYNC_NONE,
+                                       0, DI_SYNC_NONE, 0,
+                                       DI_SYNC_NONE, DI_SYNC_NONE, 0, 4);
+
+                       /* Setup internal HSYNC waveform */
+                       _ipu_di_sync_config(ipu, disp, 3, h_total - 1, DI_SYNC_CLK,
+                                       0, DI_SYNC_NONE, 0, DI_SYNC_NONE, 0,
+                                       DI_SYNC_NONE, DI_SYNC_NONE, 0, 4);
+
+                       /* Active Field ? */
+                       _ipu_di_sync_config(ipu, disp, 4,
+                                       field0_offset ?
+                                       field0_offset : field1_offset - 2,
+                                       1, v_start_width + v_sync_width, 1, 2, 2,
+                                       0, DI_SYNC_NONE, DI_SYNC_NONE, 0, 0);
+
+                       /* Active Line */
+                       _ipu_di_sync_config(ipu, disp, 5, 0, 1,
+                                       0, DI_SYNC_NONE,
+                                       height / 2, 4, 0, DI_SYNC_NONE,
+                                       DI_SYNC_NONE, 0, 0);
+
+                       /* Field 0 VSYNC waveform */
+                       _ipu_di_sync_config(ipu, disp, 6, v_total - 1, 1,
+                                       0, DI_SYNC_NONE,
+                                       0, DI_SYNC_NONE, 0, DI_SYNC_NONE,
+                                       DI_SYNC_NONE, 0, 0);
+
+                       /* DC VSYNC waveform */
+                       vsync_cnt = 7;
+                       _ipu_di_sync_config(ipu, disp, 7, 0, 1,
+                                       field1_offset,
+                                       field1_offset ? 1 : DI_SYNC_NONE,
+                                       1, 2, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, 0);
+
+                       /* active pixel waveform */
+                       _ipu_di_sync_config(ipu, disp, 8, 0, DI_SYNC_CLK,
+                                       h_sync_width + h_start_width, DI_SYNC_CLK,
+                                       width, 5, 0, DI_SYNC_NONE, DI_SYNC_NONE,
+                                       0, 0);
+
+                       /* ??? */
+                       _ipu_di_sync_config(ipu, disp, 9, v_total - 1, 2,
+                                       0, DI_SYNC_NONE,
+                                       0, DI_SYNC_NONE, 6, DI_SYNC_NONE,
+                                       DI_SYNC_NONE, 0, 0);
+
+                       reg = ipu_di_read(ipu, disp, DI_SW_GEN1(9));
+                       reg |= 0x8000;
+                       ipu_di_write(ipu, disp, reg, DI_SW_GEN1(9));
+
+                       ipu_di_write(ipu, disp, v_sync_width + v_start_width +
+                                       v_end_width + height / 2 - 1, DI_SCR_CONF);
+               }
+
+               /* Init template microcode */
+               _ipu_dc_write_tmpl(ipu, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
+
+               if (sig.Hsync_pol)
+                       di_gen |= DI_GEN_POLARITY_3;
+               if (sig.Vsync_pol)
+                       di_gen |= DI_GEN_POLARITY_2;
+       } else {
+               /* Setup internal HSYNC waveform */
+               _ipu_di_sync_config(ipu, disp, 1, h_total - 1, DI_SYNC_CLK,
+                                       0, DI_SYNC_NONE, 0, DI_SYNC_NONE, 0, DI_SYNC_NONE,
+                                       DI_SYNC_NONE, 0, 0);
+
+               /* Setup external (delayed) HSYNC waveform */
+               _ipu_di_sync_config(ipu, disp, DI_SYNC_HSYNC, h_total - 1,
+                                   DI_SYNC_CLK, div * v_to_h_sync, DI_SYNC_CLK,
+                                   0, DI_SYNC_NONE, 1, DI_SYNC_NONE,
+                                   DI_SYNC_CLK, 0, h_sync_width * 2);
+               /* Setup VSYNC waveform */
+               vsync_cnt = DI_SYNC_VSYNC;
+               _ipu_di_sync_config(ipu, disp, DI_SYNC_VSYNC, v_total - 1,
+                                   DI_SYNC_INT_HSYNC, 0, DI_SYNC_NONE, 0,
+                                   DI_SYNC_NONE, 1, DI_SYNC_NONE,
+                                   DI_SYNC_INT_HSYNC, 0, v_sync_width * 2);
+               ipu_di_write(ipu, disp, v_total - 1, DI_SCR_CONF);
+
+               /* Setup active data waveform to sync with DC */
+               _ipu_di_sync_config(ipu, disp, 4, 0, DI_SYNC_HSYNC,
+                                   v_sync_width + v_start_width, DI_SYNC_HSYNC, height,
+                                   DI_SYNC_VSYNC, 0, DI_SYNC_NONE,
+                                   DI_SYNC_NONE, 0, 0);
+               _ipu_di_sync_config(ipu, disp, 5, 0, DI_SYNC_CLK,
+                                   h_sync_width + h_start_width, DI_SYNC_CLK,
+                                   width, 4, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0,
+                                   0);
+
+               /* set VGA delayed hsync/vsync no matter VGA enabled */
+               if (disp) {
+                       /* couter 7 for VGA delay HSYNC */
+                       _ipu_di_sync_config(ipu, disp, 7,
+                                       h_total - 1, DI_SYNC_CLK,
+                                       18, DI_SYNC_CLK,
+                                       0, DI_SYNC_NONE,
+                                       1, DI_SYNC_NONE, DI_SYNC_CLK,
+                                       0, h_sync_width * 2);
+
+                       /* couter 8 for VGA delay VSYNC */
+                       _ipu_di_sync_config(ipu, disp, 8,
+                                       v_total - 1, DI_SYNC_INT_HSYNC,
+                                       1, DI_SYNC_INT_HSYNC,
+                                       0, DI_SYNC_NONE,
+                                       1, DI_SYNC_NONE, DI_SYNC_INT_HSYNC,
+                                       0, v_sync_width * 2);
+               }
+
+               /* reset all unused counters */
+               ipu_di_write(ipu, disp, 0, DI_SW_GEN0(6));
+               ipu_di_write(ipu, disp, 0, DI_SW_GEN1(6));
+               if (!disp) {
+                       ipu_di_write(ipu, disp, 0, DI_SW_GEN0(7));
+                       ipu_di_write(ipu, disp, 0, DI_SW_GEN1(7));
+                       ipu_di_write(ipu, disp, 0, DI_STP_REP(7));
+                       ipu_di_write(ipu, disp, 0, DI_SW_GEN0(8));
+                       ipu_di_write(ipu, disp, 0, DI_SW_GEN1(8));
+                       ipu_di_write(ipu, disp, 0, DI_STP_REP(8));
+               }
+               ipu_di_write(ipu, disp, 0, DI_SW_GEN0(9));
+               ipu_di_write(ipu, disp, 0, DI_SW_GEN1(9));
+               ipu_di_write(ipu, disp, 0, DI_STP_REP(9));
+
+               reg = ipu_di_read(ipu, disp, DI_STP_REP(6));
+               reg &= 0x0000FFFF;
+               ipu_di_write(ipu, disp, reg, DI_STP_REP(6));
+
+               /* Init template microcode */
+               if (disp) {
+                       if ((pixel_fmt == IPU_PIX_FMT_YUYV) ||
+                               (pixel_fmt == IPU_PIX_FMT_UYVY) ||
+                               (pixel_fmt == IPU_PIX_FMT_YVYU) ||
+                               (pixel_fmt == IPU_PIX_FMT_VYUY)) {
+                               _ipu_dc_write_tmpl(ipu, 8, WROD(0), 0, (map - 1), SYNC_WAVE, 0, 5, 1);
+                               _ipu_dc_write_tmpl(ipu, 9, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
+                               /* configure user events according to DISP NUM */
+                               ipu_dc_write(ipu, (width - 1), DC_UGDE_3(disp));
+                       }
+                       _ipu_dc_write_tmpl(ipu, 2, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1);
+                       _ipu_dc_write_tmpl(ipu, 3, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0);
+                       _ipu_dc_write_tmpl(ipu, 4, WRG, 0, map, NULL_WAVE, 0, 0, 1);
+                       _ipu_dc_write_tmpl(ipu, 1, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
+
+               } else {
+                       if ((pixel_fmt == IPU_PIX_FMT_YUYV) ||
+                               (pixel_fmt == IPU_PIX_FMT_UYVY) ||
+                               (pixel_fmt == IPU_PIX_FMT_YVYU) ||
+                               (pixel_fmt == IPU_PIX_FMT_VYUY)) {
+                               _ipu_dc_write_tmpl(ipu, 10, WROD(0), 0, (map - 1), SYNC_WAVE, 0, 5, 1);
+                               _ipu_dc_write_tmpl(ipu, 11, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
+                               /* configure user events according to DISP NUM */
+                               ipu_dc_write(ipu, width - 1, DC_UGDE_3(disp));
+                       }
+                  _ipu_dc_write_tmpl(ipu, 5, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1);
+                  _ipu_dc_write_tmpl(ipu, 6, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0);
+                  _ipu_dc_write_tmpl(ipu, 7, WRG, 0, map, NULL_WAVE, 0, 0, 1);
+                  _ipu_dc_write_tmpl(ipu, 12, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
+               }
+
+               if (sig.Hsync_pol) {
+                       di_gen |= DI_GEN_POLARITY_2;
+                       if (disp)
+                               di_gen |= DI_GEN_POLARITY_7;
+               }
+               if (sig.Vsync_pol) {
+                       di_gen |= DI_GEN_POLARITY_3;
+                       if (disp)
+                               di_gen |= DI_GEN_POLARITY_8;
+               }
+       }
+       /* changinc DISP_CLK polarity: it can be wrong for some applications */
+       if ((pixel_fmt == IPU_PIX_FMT_YUYV) ||
+               (pixel_fmt == IPU_PIX_FMT_UYVY) ||
+               (pixel_fmt == IPU_PIX_FMT_YVYU) ||
+               (pixel_fmt == IPU_PIX_FMT_VYUY))
+                       di_gen |= 0x00020000;
+
+       if (!sig.clk_pol)
+               di_gen |= DI_GEN_POLARITY_DISP_CLK;
+
+       ipu_di_write(ipu, disp, di_gen, DI_GENERAL);
+
+       ipu_di_write(ipu, disp, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) |
+                       0x00000002, DI_SYNC_AS_GEN);
+       reg = ipu_di_read(ipu, disp, DI_POL);
+       reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15);
+       if (sig.enable_pol)
+               reg |= DI_POL_DRDY_POLARITY_15;
+       if (sig.data_pol)
+               reg |= DI_POL_DRDY_DATA_POLARITY;
+       ipu_di_write(ipu, disp, reg, DI_POL);
+
+       ipu_dc_write(ipu, width, DC_DISP_CONF2(DC_DISP_ID_SYNC(disp)));
+
+       mutex_unlock(&ipu->mutex_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_init_sync_panel);
+
+void ipu_uninit_sync_panel(struct ipu_soc *ipu, int disp)
+{
+       uint32_t reg;
+       uint32_t di_gen;
+
+       if ((disp != 0) || (disp != 1))
+               return;
+
+       mutex_lock(&ipu->mutex_lock);
+
+       di_gen = ipu_di_read(ipu, disp, DI_GENERAL);
+       di_gen |= 0x3ff | DI_GEN_POLARITY_DISP_CLK;
+       ipu_di_write(ipu, disp, di_gen, DI_GENERAL);
+
+       reg = ipu_di_read(ipu, disp, DI_POL);
+       reg |= 0x3ffffff;
+       ipu_di_write(ipu, disp, reg, DI_POL);
+
+       mutex_unlock(&ipu->mutex_lock);
+}
+EXPORT_SYMBOL(ipu_uninit_sync_panel);
+
+int ipu_init_async_panel(struct ipu_soc *ipu, int disp, int type, uint32_t cycle_time,
+                        uint32_t pixel_fmt, ipu_adc_sig_cfg_t sig)
+{
+       int map;
+       u32 ser_conf = 0;
+       u32 div;
+       u32 di_clk = clk_get_rate(ipu->ipu_clk);
+
+       /* round up cycle_time, then calcalate the divider using scaled math */
+       cycle_time += (1000000000UL / di_clk) - 1;
+       div = (cycle_time * (di_clk / 256UL)) / (1000000000UL / 256UL);
+
+       map = _ipu_pixfmt_to_map(pixel_fmt);
+       if (map < 0)
+               return -EINVAL;
+
+       mutex_lock(&ipu->mutex_lock);
+
+       if (type == IPU_PANEL_SERIAL) {
+               ipu_di_write(ipu, disp, (div << 24) | ((sig.ifc_width - 1) << 4),
+                            DI_DW_GEN(ASYNC_SER_WAVE));
+
+               _ipu_di_data_pin_config(ipu, disp, ASYNC_SER_WAVE, DI_PIN_CS,
+                                       0, 0, (div * 2) + 1);
+               _ipu_di_data_pin_config(ipu, disp, ASYNC_SER_WAVE, DI_PIN_SER_CLK,
+                                       1, div, div * 2);
+               _ipu_di_data_pin_config(ipu, disp, ASYNC_SER_WAVE, DI_PIN_SER_RS,
+                                       2, 0, 0);
+
+               _ipu_dc_write_tmpl(ipu, 0x64, WROD(0), 0, map, ASYNC_SER_WAVE, 0, 0, 1);
+
+               /* Configure DC for serial panel */
+               ipu_dc_write(ipu, 0x14, DC_DISP_CONF1(DC_DISP_ID_SERIAL));
+
+               if (sig.clk_pol)
+                       ser_conf |= DI_SER_CONF_SERIAL_CLK_POL;
+               if (sig.data_pol)
+                       ser_conf |= DI_SER_CONF_SERIAL_DATA_POL;
+               if (sig.rs_pol)
+                       ser_conf |= DI_SER_CONF_SERIAL_RS_POL;
+               if (sig.cs_pol)
+                       ser_conf |= DI_SER_CONF_SERIAL_CS_POL;
+               ipu_di_write(ipu, disp, ser_conf, DI_SER_CONF);
+       }
+
+       mutex_unlock(&ipu->mutex_lock);
+       return 0;
+}
+EXPORT_SYMBOL(ipu_init_async_panel);
+
+/*!
+ * This function sets the foreground and background plane global alpha blending
+ * modes. This function also sets the DP graphic plane according to the
+ * parameter of IPUv3 DP channel.
+ *
+ * @param      ipu             ipu handler
+ * @param      channel         IPUv3 DP channel
+ *
+ * @param       enable          Boolean to enable or disable global alpha
+ *                              blending. If disabled, local blending is used.
+ *
+ * @param       alpha           Global alpha value.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_disp_set_global_alpha(struct ipu_soc *ipu, ipu_channel_t channel,
+                               bool enable, uint8_t alpha)
+{
+       uint32_t reg;
+       uint32_t flow;
+       bool bg_chan;
+
+       if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC)
+               flow = DP_SYNC;
+       else if (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0)
+               flow = DP_ASYNC0;
+       else if (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1)
+               flow = DP_ASYNC1;
+       else
+               return -EINVAL;
+
+       if (channel == MEM_BG_SYNC || channel == MEM_BG_ASYNC0 ||
+           channel == MEM_BG_ASYNC1)
+               bg_chan = true;
+       else
+               bg_chan = false;
+
+       _ipu_get(ipu);
+
+       mutex_lock(&ipu->mutex_lock);
+
+       if (bg_chan) {
+               reg = ipu_dp_read(ipu, DP_COM_CONF(flow));
+               ipu_dp_write(ipu, reg & ~DP_COM_CONF_GWSEL, DP_COM_CONF(flow));
+       } else {
+               reg = ipu_dp_read(ipu, DP_COM_CONF(flow));
+               ipu_dp_write(ipu, reg | DP_COM_CONF_GWSEL, DP_COM_CONF(flow));
+       }
+
+       if (enable) {
+               reg = ipu_dp_read(ipu, DP_GRAPH_WIND_CTRL(flow)) & 0x00FFFFFFL;
+               ipu_dp_write(ipu, reg | ((uint32_t) alpha << 24),
+                            DP_GRAPH_WIND_CTRL(flow));
+
+               reg = ipu_dp_read(ipu, DP_COM_CONF(flow));
+               ipu_dp_write(ipu, reg | DP_COM_CONF_GWAM, DP_COM_CONF(flow));
+       } else {
+               reg = ipu_dp_read(ipu, DP_COM_CONF(flow));
+               ipu_dp_write(ipu, reg & ~DP_COM_CONF_GWAM, DP_COM_CONF(flow));
+       }
+
+       reg = ipu_cm_read(ipu, IPU_SRM_PRI2) | 0x8;
+       ipu_cm_write(ipu, reg, IPU_SRM_PRI2);
+
+       mutex_unlock(&ipu->mutex_lock);
+
+       _ipu_put(ipu);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_disp_set_global_alpha);
+
+/*!
+ * This function sets the transparent color key for SDC graphic plane.
+ *
+ * @param      ipu             ipu handler
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       enable          Boolean to enable or disable color key
+ *
+ * @param       colorKey        24-bit RGB color for transparent color key.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_disp_set_color_key(struct ipu_soc *ipu, ipu_channel_t channel,
+                               bool enable, uint32_t color_key)
+{
+       uint32_t reg, flow;
+       int y, u, v;
+       int red, green, blue;
+
+       if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC)
+               flow = DP_SYNC;
+       else if (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0)
+               flow = DP_ASYNC0;
+       else if (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1)
+               flow = DP_ASYNC1;
+       else
+               return -EINVAL;
+
+       _ipu_get(ipu);
+
+       mutex_lock(&ipu->mutex_lock);
+
+       ipu->color_key_4rgb = true;
+       /* Transform color key from rgb to yuv if CSC is enabled */
+       if (((ipu->fg_csc_type == RGB2YUV) && (ipu->bg_csc_type == YUV2YUV)) ||
+                       ((ipu->fg_csc_type == YUV2YUV) && (ipu->bg_csc_type == RGB2YUV)) ||
+                       ((ipu->fg_csc_type == YUV2YUV) && (ipu->bg_csc_type == YUV2YUV)) ||
+                       ((ipu->fg_csc_type == YUV2RGB) && (ipu->bg_csc_type == YUV2RGB))) {
+
+               dev_dbg(ipu->dev, "color key 0x%x need change to yuv fmt\n", color_key);
+
+               red = (color_key >> 16) & 0xFF;
+               green = (color_key >> 8) & 0xFF;
+               blue = color_key & 0xFF;
+
+               y = _rgb_to_yuv(0, red, green, blue);
+               u = _rgb_to_yuv(1, red, green, blue);
+               v = _rgb_to_yuv(2, red, green, blue);
+               color_key = (y << 16) | (u << 8) | v;
+
+               ipu->color_key_4rgb = false;
+
+               dev_dbg(ipu->dev, "color key change to yuv fmt 0x%x\n", color_key);
+       }
+
+       if (enable) {
+               reg = ipu_dp_read(ipu, DP_GRAPH_WIND_CTRL(flow)) & 0xFF000000L;
+               ipu_dp_write(ipu, reg | color_key, DP_GRAPH_WIND_CTRL(flow));
+
+               reg = ipu_dp_read(ipu, DP_COM_CONF(flow));
+               ipu_dp_write(ipu, reg | DP_COM_CONF_GWCKE, DP_COM_CONF(flow));
+       } else {
+               reg = ipu_dp_read(ipu, DP_COM_CONF(flow));
+               ipu_dp_write(ipu, reg & ~DP_COM_CONF_GWCKE, DP_COM_CONF(flow));
+       }
+
+       reg = ipu_cm_read(ipu, IPU_SRM_PRI2) | 0x8;
+       ipu_cm_write(ipu, reg, IPU_SRM_PRI2);
+
+       mutex_unlock(&ipu->mutex_lock);
+
+       _ipu_put(ipu);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_disp_set_color_key);
+
+/*!
+ * This function sets the gamma correction for DP output.
+ *
+ * @param      ipu             ipu handler
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       enable          Boolean to enable or disable gamma correction.
+ *
+ * @param       constk         Gamma piecewise linear approximation constk coeff.
+ *
+ * @param       slopek         Gamma piecewise linear approximation slopek coeff.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_disp_set_gamma_correction(struct ipu_soc *ipu, ipu_channel_t channel, bool enable, int constk[], int slopek[])
+{
+       uint32_t reg, flow, i;
+
+       if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC)
+               flow = DP_SYNC;
+       else if (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0)
+               flow = DP_ASYNC0;
+       else if (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1)
+               flow = DP_ASYNC1;
+       else
+               return -EINVAL;
+
+       _ipu_get(ipu);
+
+       mutex_lock(&ipu->mutex_lock);
+
+       for (i = 0; i < 8; i++)
+               ipu_dp_write(ipu, (constk[2*i] & 0x1ff) | ((constk[2*i+1] & 0x1ff) << 16), DP_GAMMA_C(flow, i));
+       for (i = 0; i < 4; i++)
+               ipu_dp_write(ipu, (slopek[4*i] & 0xff) | ((slopek[4*i+1] & 0xff) << 8) |
+                       ((slopek[4*i+2] & 0xff) << 16) | ((slopek[4*i+3] & 0xff) << 24), DP_GAMMA_S(flow, i));
+
+       reg = ipu_dp_read(ipu, DP_COM_CONF(flow));
+       if (enable) {
+               if ((ipu->bg_csc_type == RGB2YUV) || (ipu->bg_csc_type == YUV2YUV))
+                       reg |= DP_COM_CONF_GAMMA_YUV_EN;
+               else
+                       reg &= ~DP_COM_CONF_GAMMA_YUV_EN;
+               ipu_dp_write(ipu, reg | DP_COM_CONF_GAMMA_EN, DP_COM_CONF(flow));
+       } else
+               ipu_dp_write(ipu, reg & ~DP_COM_CONF_GAMMA_EN, DP_COM_CONF(flow));
+
+       reg = ipu_cm_read(ipu, IPU_SRM_PRI2) | 0x8;
+       ipu_cm_write(ipu, reg, IPU_SRM_PRI2);
+
+       mutex_unlock(&ipu->mutex_lock);
+
+       _ipu_put(ipu);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_disp_set_gamma_correction);
+
+/*!
+ * This function sets the window position of the foreground or background plane.
+ * modes.
+ *
+ * @param      ipu             ipu handler
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       x_pos           The X coordinate position to place window at.
+ *                              The position is relative to the top left corner.
+ *
+ * @param       y_pos           The Y coordinate position to place window at.
+ *                              The position is relative to the top left corner.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t _ipu_disp_set_window_pos(struct ipu_soc *ipu, ipu_channel_t channel,
+                               int16_t x_pos, int16_t y_pos)
+{
+       u32 reg;
+       uint32_t flow = 0;
+       uint32_t dp_srm_shift;
+
+       if ((channel == MEM_FG_SYNC) || (channel == MEM_BG_SYNC)) {
+               flow = DP_SYNC;
+               dp_srm_shift = 3;
+       } else if (channel == MEM_FG_ASYNC0) {
+               flow = DP_ASYNC0;
+               dp_srm_shift = 5;
+       } else if (channel == MEM_FG_ASYNC1) {
+               flow = DP_ASYNC1;
+               dp_srm_shift = 7;
+       } else
+               return -EINVAL;
+
+       ipu_dp_write(ipu, (x_pos << 16) | y_pos, DP_FG_POS(flow));
+
+       if (ipu_is_channel_busy(ipu, channel)) {
+               /* controled by FSU if channel enabled */
+               reg = ipu_cm_read(ipu, IPU_SRM_PRI2) & (~(0x3 << dp_srm_shift));
+               reg |= (0x1 << dp_srm_shift);
+               ipu_cm_write(ipu, reg, IPU_SRM_PRI2);
+       } else {
+               /* disable auto swap, controled by MCU if channel disabled */
+               reg = ipu_cm_read(ipu, IPU_SRM_PRI2) & (~(0x3 << dp_srm_shift));
+               ipu_cm_write(ipu, reg, IPU_SRM_PRI2);
+       }
+
+       return 0;
+}
+
+int32_t ipu_disp_set_window_pos(struct ipu_soc *ipu, ipu_channel_t channel,
+                               int16_t x_pos, int16_t y_pos)
+{
+       int ret;
+
+       _ipu_get(ipu);
+       mutex_lock(&ipu->mutex_lock);
+       ret = _ipu_disp_set_window_pos(ipu, channel, x_pos, y_pos);
+       mutex_unlock(&ipu->mutex_lock);
+       _ipu_put(ipu);
+       return ret;
+}
+EXPORT_SYMBOL(ipu_disp_set_window_pos);
+
+int32_t _ipu_disp_get_window_pos(struct ipu_soc *ipu, ipu_channel_t channel,
+                               int16_t *x_pos, int16_t *y_pos)
+{
+       u32 reg;
+       uint32_t flow = 0;
+
+       if (channel == MEM_FG_SYNC)
+               flow = DP_SYNC;
+       else if (channel == MEM_FG_ASYNC0)
+               flow = DP_ASYNC0;
+       else if (channel == MEM_FG_ASYNC1)
+               flow = DP_ASYNC1;
+       else
+               return -EINVAL;
+
+       reg = ipu_dp_read(ipu, DP_FG_POS(flow));
+
+       *x_pos = (reg >> 16) & 0x7FF;
+       *y_pos = reg & 0x7FF;
+
+       return 0;
+}
+int32_t ipu_disp_get_window_pos(struct ipu_soc *ipu, ipu_channel_t channel,
+                               int16_t *x_pos, int16_t *y_pos)
+{
+       int ret;
+
+       _ipu_get(ipu);
+       mutex_lock(&ipu->mutex_lock);
+       ret = _ipu_disp_get_window_pos(ipu, channel, x_pos, y_pos);
+       mutex_unlock(&ipu->mutex_lock);
+       _ipu_put(ipu);
+       return ret;
+}
+EXPORT_SYMBOL(ipu_disp_get_window_pos);
+
+void ipu_reset_disp_panel(struct ipu_soc *ipu)
+{
+       uint32_t tmp;
+
+       tmp = ipu_di_read(ipu, 1, DI_GENERAL);
+       ipu_di_write(ipu, 1, tmp | 0x08, DI_GENERAL);
+       msleep(10); /* tRES >= 100us */
+       tmp = ipu_di_read(ipu, 1, DI_GENERAL);
+       ipu_di_write(ipu, 1, tmp & ~0x08, DI_GENERAL);
+       msleep(60);
+
+       return;
+}
+EXPORT_SYMBOL(ipu_reset_disp_panel);
+
+void ipu_disp_init(struct ipu_soc *ipu)
+{
+       ipu->fg_csc_type = ipu->bg_csc_type = CSC_NONE;
+       ipu->color_key_4rgb = true;
+       _ipu_init_dc_mappings(ipu);
+       _ipu_dmfc_init(ipu, DMFC_NORMAL, 1);
+}
diff --git a/drivers/mxc/ipu3/ipu_ic.c b/drivers/mxc/ipu3/ipu_ic.c
new file mode 100644 (file)
index 0000000..29519bd
--- /dev/null
@@ -0,0 +1,924 @@
+/*
+ * Copyright 2005-2015 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
+ */
+
+/*
+ * @file ipu_ic.c
+ *
+ * @brief IPU IC functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ipu-v3.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include "ipu_param_mem.h"
+#include "ipu_regs.h"
+
+enum {
+       IC_TASK_VIEWFINDER,
+       IC_TASK_ENCODER,
+       IC_TASK_POST_PROCESSOR
+};
+
+static void _init_csc(struct ipu_soc *ipu, uint8_t ic_task, ipu_color_space_t in_format,
+                     ipu_color_space_t out_format, int csc_index);
+
+static int _calc_resize_coeffs(struct ipu_soc *ipu,
+                               uint32_t inSize, uint32_t outSize,
+                               uint32_t *resizeCoeff,
+                               uint32_t *downsizeCoeff);
+
+void _ipu_vdi_set_top_field_man(struct ipu_soc *ipu, bool top_field_0)
+{
+       uint32_t reg;
+
+       reg = ipu_vdi_read(ipu, VDI_C);
+       if (top_field_0)
+               reg &= ~VDI_C_TOP_FIELD_MAN_1;
+       else
+               reg |= VDI_C_TOP_FIELD_MAN_1;
+       ipu_vdi_write(ipu, reg, VDI_C);
+}
+
+void _ipu_vdi_set_motion(struct ipu_soc *ipu, ipu_motion_sel motion_sel)
+{
+       uint32_t reg;
+
+       reg = ipu_vdi_read(ipu, VDI_C);
+       reg &= ~(VDI_C_MOT_SEL_FULL | VDI_C_MOT_SEL_MED | VDI_C_MOT_SEL_LOW);
+       if (motion_sel == HIGH_MOTION)
+               reg |= VDI_C_MOT_SEL_FULL;
+       else if (motion_sel == MED_MOTION)
+               reg |= VDI_C_MOT_SEL_MED;
+       else
+               reg |= VDI_C_MOT_SEL_LOW;
+
+       ipu_vdi_write(ipu, reg, VDI_C);
+       dev_dbg(ipu->dev, "VDI_C = \t0x%08X\n", reg);
+}
+
+void ic_dump_register(struct ipu_soc *ipu)
+{
+       printk(KERN_DEBUG "IC_CONF = \t0x%08X\n", ipu_ic_read(ipu, IC_CONF));
+       printk(KERN_DEBUG "IC_PRP_ENC_RSC = \t0x%08X\n",
+              ipu_ic_read(ipu, IC_PRP_ENC_RSC));
+       printk(KERN_DEBUG "IC_PRP_VF_RSC = \t0x%08X\n",
+              ipu_ic_read(ipu, IC_PRP_VF_RSC));
+       printk(KERN_DEBUG "IC_PP_RSC = \t0x%08X\n", ipu_ic_read(ipu, IC_PP_RSC));
+       printk(KERN_DEBUG "IC_IDMAC_1 = \t0x%08X\n", ipu_ic_read(ipu, IC_IDMAC_1));
+       printk(KERN_DEBUG "IC_IDMAC_2 = \t0x%08X\n", ipu_ic_read(ipu, IC_IDMAC_2));
+       printk(KERN_DEBUG "IC_IDMAC_3 = \t0x%08X\n", ipu_ic_read(ipu, IC_IDMAC_3));
+}
+
+void _ipu_ic_enable_task(struct ipu_soc *ipu, ipu_channel_t channel)
+{
+       uint32_t ic_conf;
+
+       ic_conf = ipu_ic_read(ipu, IC_CONF);
+       switch (channel) {
+       case CSI_PRP_VF_MEM:
+       case MEM_PRP_VF_MEM:
+               ic_conf |= IC_CONF_PRPVF_EN;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               ic_conf |= IC_CONF_PRPVF_EN;
+               break;
+       case MEM_VDI_MEM:
+               ic_conf |= IC_CONF_PRPVF_EN | IC_CONF_RWS_EN ;
+               break;
+       case MEM_ROT_VF_MEM:
+               ic_conf |= IC_CONF_PRPVF_ROT_EN;
+               break;
+       case CSI_PRP_ENC_MEM:
+       case MEM_PRP_ENC_MEM:
+               ic_conf |= IC_CONF_PRPENC_EN;
+               break;
+       case MEM_ROT_ENC_MEM:
+               ic_conf |= IC_CONF_PRPENC_ROT_EN;
+               break;
+       case MEM_PP_MEM:
+               ic_conf |= IC_CONF_PP_EN;
+               break;
+       case MEM_ROT_PP_MEM:
+               ic_conf |= IC_CONF_PP_ROT_EN;
+               break;
+       default:
+               break;
+       }
+       ipu_ic_write(ipu, ic_conf, IC_CONF);
+}
+
+void _ipu_ic_disable_task(struct ipu_soc *ipu, ipu_channel_t channel)
+{
+       uint32_t ic_conf;
+
+       ic_conf = ipu_ic_read(ipu, IC_CONF);
+       switch (channel) {
+       case CSI_PRP_VF_MEM:
+       case MEM_PRP_VF_MEM:
+               ic_conf &= ~IC_CONF_PRPVF_EN;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               ic_conf &= ~IC_CONF_PRPVF_EN;
+               break;
+       case MEM_VDI_MEM:
+               ic_conf &= ~(IC_CONF_PRPVF_EN | IC_CONF_RWS_EN);
+               break;
+       case MEM_ROT_VF_MEM:
+               ic_conf &= ~IC_CONF_PRPVF_ROT_EN;
+               break;
+       case CSI_PRP_ENC_MEM:
+       case MEM_PRP_ENC_MEM:
+               ic_conf &= ~IC_CONF_PRPENC_EN;
+               break;
+       case MEM_ROT_ENC_MEM:
+               ic_conf &= ~IC_CONF_PRPENC_ROT_EN;
+               break;
+       case MEM_PP_MEM:
+               ic_conf &= ~IC_CONF_PP_EN;
+               break;
+       case MEM_ROT_PP_MEM:
+               ic_conf &= ~IC_CONF_PP_ROT_EN;
+               break;
+       default:
+               break;
+       }
+       ipu_ic_write(ipu, ic_conf, IC_CONF);
+}
+
+void _ipu_vdi_init(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params)
+{
+       uint32_t reg;
+       uint32_t pixel_fmt;
+       uint32_t pix_per_burst;
+
+       reg = ((params->mem_prp_vf_mem.in_height-1) << 16) |
+         (params->mem_prp_vf_mem.in_width-1);
+       ipu_vdi_write(ipu, reg, VDI_FSIZE);
+
+       /* Full motion, only vertical filter is used
+          Burst size is 4 accesses */
+       if (params->mem_prp_vf_mem.in_pixel_fmt ==
+            IPU_PIX_FMT_UYVY ||
+            params->mem_prp_vf_mem.in_pixel_fmt ==
+            IPU_PIX_FMT_YUYV) {
+               pixel_fmt = VDI_C_CH_422;
+               pix_per_burst = 32;
+        } else {
+               pixel_fmt = VDI_C_CH_420;
+               pix_per_burst = 64;
+       }
+
+       reg = ipu_vdi_read(ipu, VDI_C);
+       reg |= pixel_fmt;
+       switch (channel) {
+       case MEM_VDI_PRP_VF_MEM:
+               reg |= VDI_C_BURST_SIZE2_4;
+               break;
+       case MEM_VDI_PRP_VF_MEM_P:
+               reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_SET_1 | VDI_C_VWM1_CLR_2;
+               break;
+       case MEM_VDI_PRP_VF_MEM_N:
+               reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_SET_1 | VDI_C_VWM3_CLR_2;
+               break;
+
+       case MEM_VDI_MEM:
+               reg |= (((pix_per_burst >> 2) - 1) & VDI_C_BURST_SIZE_MASK)
+                               << VDI_C_BURST_SIZE2_OFFSET;
+               break;
+       case MEM_VDI_MEM_P:
+               reg |= (((pix_per_burst >> 2) - 1) & VDI_C_BURST_SIZE_MASK)
+                               << VDI_C_BURST_SIZE1_OFFSET;
+               reg |= VDI_C_VWM1_SET_2 | VDI_C_VWM1_CLR_2;
+               break;
+       case MEM_VDI_MEM_N:
+               reg |= (((pix_per_burst >> 2) - 1) & VDI_C_BURST_SIZE_MASK)
+                               << VDI_C_BURST_SIZE3_OFFSET;
+               reg |= VDI_C_VWM3_SET_2 | VDI_C_VWM3_CLR_2;
+               break;
+       default:
+               break;
+       }
+       ipu_vdi_write(ipu, reg, VDI_C);
+
+       if (params->mem_prp_vf_mem.field_fmt == IPU_DEINTERLACE_FIELD_TOP)
+               _ipu_vdi_set_top_field_man(ipu, true);
+       else if (params->mem_prp_vf_mem.field_fmt == IPU_DEINTERLACE_FIELD_BOTTOM)
+               _ipu_vdi_set_top_field_man(ipu, false);
+
+       _ipu_vdi_set_motion(ipu, params->mem_prp_vf_mem.motion_sel);
+
+       reg = ipu_ic_read(ipu, IC_CONF);
+       reg &= ~IC_CONF_RWS_EN;
+       ipu_ic_write(ipu, reg, IC_CONF);
+}
+
+void _ipu_vdi_uninit(struct ipu_soc *ipu)
+{
+       ipu_vdi_write(ipu, 0, VDI_FSIZE);
+       ipu_vdi_write(ipu, 0, VDI_C);
+}
+
+int _ipu_ic_init_prpvf(struct ipu_soc *ipu, ipu_channel_params_t *params,
+                      bool src_is_csi)
+{
+       uint32_t reg, ic_conf;
+       uint32_t downsizeCoeff, resizeCoeff;
+       ipu_color_space_t in_fmt, out_fmt;
+       int ret = 0;
+
+       /* Setup vertical resizing */
+       if (!params->mem_prp_vf_mem.outv_resize_ratio) {
+               ret = _calc_resize_coeffs(ipu, params->mem_prp_vf_mem.in_height,
+                                       params->mem_prp_vf_mem.out_height,
+                                       &resizeCoeff, &downsizeCoeff);
+               if (ret < 0) {
+                       dev_err(ipu->dev, "failed to calculate prpvf height "
+                               "scaling coefficients\n");
+                       return ret;
+               }
+
+               reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+       } else
+               reg = (params->mem_prp_vf_mem.outv_resize_ratio) << 16;
+
+       /* Setup horizontal resizing */
+       if (!params->mem_prp_vf_mem.outh_resize_ratio) {
+               ret = _calc_resize_coeffs(ipu, params->mem_prp_vf_mem.in_width,
+                                       params->mem_prp_vf_mem.out_width,
+                                       &resizeCoeff, &downsizeCoeff);
+               if (ret < 0) {
+                       dev_err(ipu->dev, "failed to calculate prpvf width "
+                               "scaling coefficients\n");
+                       return ret;
+               }
+
+               reg |= (downsizeCoeff << 14) | resizeCoeff;
+       } else
+               reg |= params->mem_prp_vf_mem.outh_resize_ratio;
+
+       ipu_ic_write(ipu, reg, IC_PRP_VF_RSC);
+
+       ic_conf = ipu_ic_read(ipu, IC_CONF);
+
+       /* Setup color space conversion */
+       in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_pixel_fmt);
+       out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt);
+       if (in_fmt == RGB) {
+               if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+                       /* Enable RGB->YCBCR CSC1 */
+                       _init_csc(ipu, IC_TASK_VIEWFINDER, RGB, out_fmt, 1);
+                       ic_conf |= IC_CONF_PRPVF_CSC1;
+               }
+       }
+       if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+               if (out_fmt == RGB) {
+                       /* Enable YCBCR->RGB CSC1 */
+                       _init_csc(ipu, IC_TASK_VIEWFINDER, YCbCr, RGB, 1);
+                       ic_conf |= IC_CONF_PRPVF_CSC1;
+               } else {
+                       /* TODO: Support YUV<->YCbCr conversion? */
+               }
+       }
+
+       if (params->mem_prp_vf_mem.graphics_combine_en) {
+               ic_conf |= IC_CONF_PRPVF_CMB;
+
+               if (!(ic_conf & IC_CONF_PRPVF_CSC1)) {
+                       /* need transparent CSC1 conversion */
+                       _init_csc(ipu, IC_TASK_VIEWFINDER, RGB, RGB, 1);
+                       ic_conf |= IC_CONF_PRPVF_CSC1;  /* Enable RGB->RGB CSC */
+               }
+               in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_g_pixel_fmt);
+               out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt);
+               if (in_fmt == RGB) {
+                       if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+                               /* Enable RGB->YCBCR CSC2 */
+                               _init_csc(ipu, IC_TASK_VIEWFINDER, RGB, out_fmt, 2);
+                               ic_conf |= IC_CONF_PRPVF_CSC2;
+                       }
+               }
+               if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+                       if (out_fmt == RGB) {
+                               /* Enable YCBCR->RGB CSC2 */
+                               _init_csc(ipu, IC_TASK_VIEWFINDER, YCbCr, RGB, 2);
+                               ic_conf |= IC_CONF_PRPVF_CSC2;
+                       } else {
+                               /* TODO: Support YUV<->YCbCr conversion? */
+                       }
+               }
+
+               if (params->mem_prp_vf_mem.global_alpha_en) {
+                       ic_conf |= IC_CONF_IC_GLB_LOC_A;
+                       reg = ipu_ic_read(ipu, IC_CMBP_1);
+                       reg &= ~(0xff);
+                       reg |= params->mem_prp_vf_mem.alpha;
+                       ipu_ic_write(ipu, reg, IC_CMBP_1);
+               } else
+                       ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
+
+               if (params->mem_prp_vf_mem.key_color_en) {
+                       ic_conf |= IC_CONF_KEY_COLOR_EN;
+                       ipu_ic_write(ipu, params->mem_prp_vf_mem.key_color,
+                                       IC_CMBP_2);
+               } else
+                       ic_conf &= ~IC_CONF_KEY_COLOR_EN;
+       } else {
+               ic_conf &= ~IC_CONF_PRPVF_CMB;
+       }
+
+       if (src_is_csi)
+               ic_conf &= ~IC_CONF_RWS_EN;
+       else
+               ic_conf |= IC_CONF_RWS_EN;
+
+       ipu_ic_write(ipu, ic_conf, IC_CONF);
+
+       return ret;
+}
+
+void _ipu_ic_uninit_prpvf(struct ipu_soc *ipu)
+{
+       uint32_t reg;
+
+       reg = ipu_ic_read(ipu, IC_CONF);
+       reg &= ~(IC_CONF_PRPVF_EN | IC_CONF_PRPVF_CMB |
+                IC_CONF_PRPVF_CSC2 | IC_CONF_PRPVF_CSC1);
+       ipu_ic_write(ipu, reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_vf(struct ipu_soc *ipu, ipu_channel_params_t *params)
+{
+}
+
+void _ipu_ic_uninit_rotate_vf(struct ipu_soc *ipu)
+{
+       uint32_t reg;
+       reg = ipu_ic_read(ipu, IC_CONF);
+       reg &= ~IC_CONF_PRPVF_ROT_EN;
+       ipu_ic_write(ipu, reg, IC_CONF);
+}
+
+int _ipu_ic_init_prpenc(struct ipu_soc *ipu, ipu_channel_params_t *params,
+                       bool src_is_csi)
+{
+       uint32_t reg, ic_conf;
+       uint32_t downsizeCoeff, resizeCoeff;
+       ipu_color_space_t in_fmt, out_fmt;
+       int ret = 0;
+
+       /* Setup vertical resizing */
+       if (!params->mem_prp_enc_mem.outv_resize_ratio) {
+               ret = _calc_resize_coeffs(ipu,
+                                       params->mem_prp_enc_mem.in_height,
+                                       params->mem_prp_enc_mem.out_height,
+                                       &resizeCoeff, &downsizeCoeff);
+               if (ret < 0) {
+                       dev_err(ipu->dev, "failed to calculate prpenc height "
+                               "scaling coefficients\n");
+                       return ret;
+               }
+
+               reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+       } else
+               reg = (params->mem_prp_enc_mem.outv_resize_ratio) << 16;
+
+       /* Setup horizontal resizing */
+       if (!params->mem_prp_enc_mem.outh_resize_ratio) {
+               ret = _calc_resize_coeffs(ipu, params->mem_prp_enc_mem.in_width,
+                                       params->mem_prp_enc_mem.out_width,
+                                       &resizeCoeff, &downsizeCoeff);
+               if (ret < 0) {
+                       dev_err(ipu->dev, "failed to calculate prpenc width "
+                               "scaling coefficients\n");
+                       return ret;
+               }
+
+               reg |= (downsizeCoeff << 14) | resizeCoeff;
+       } else
+               reg |= params->mem_prp_enc_mem.outh_resize_ratio;
+
+       ipu_ic_write(ipu, reg, IC_PRP_ENC_RSC);
+
+       ic_conf = ipu_ic_read(ipu, IC_CONF);
+
+       /* Setup color space conversion */
+       in_fmt = format_to_colorspace(params->mem_prp_enc_mem.in_pixel_fmt);
+       out_fmt = format_to_colorspace(params->mem_prp_enc_mem.out_pixel_fmt);
+       if (in_fmt == RGB) {
+               if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+                       /* Enable RGB->YCBCR CSC1 */
+                       _init_csc(ipu, IC_TASK_ENCODER, RGB, out_fmt, 1);
+                       ic_conf |= IC_CONF_PRPENC_CSC1;
+               }
+       }
+       if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+               if (out_fmt == RGB) {
+                       /* Enable YCBCR->RGB CSC1 */
+                       _init_csc(ipu, IC_TASK_ENCODER, YCbCr, RGB, 1);
+                       ic_conf |= IC_CONF_PRPENC_CSC1;
+               } else {
+                       /* TODO: Support YUV<->YCbCr conversion? */
+               }
+       }
+
+       if (src_is_csi)
+               ic_conf &= ~IC_CONF_RWS_EN;
+       else
+               ic_conf |= IC_CONF_RWS_EN;
+
+       ipu_ic_write(ipu, ic_conf, IC_CONF);
+
+       return ret;
+}
+
+void _ipu_ic_uninit_prpenc(struct ipu_soc *ipu)
+{
+       uint32_t reg;
+
+       reg = ipu_ic_read(ipu, IC_CONF);
+       reg &= ~(IC_CONF_PRPENC_EN | IC_CONF_PRPENC_CSC1);
+       ipu_ic_write(ipu, reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_enc(struct ipu_soc *ipu, ipu_channel_params_t *params)
+{
+}
+
+void _ipu_ic_uninit_rotate_enc(struct ipu_soc *ipu)
+{
+       uint32_t reg;
+
+       reg = ipu_ic_read(ipu, IC_CONF);
+       reg &= ~(IC_CONF_PRPENC_ROT_EN);
+       ipu_ic_write(ipu, reg, IC_CONF);
+}
+
+int _ipu_ic_init_pp(struct ipu_soc *ipu, ipu_channel_params_t *params)
+{
+       uint32_t reg, ic_conf;
+       uint32_t downsizeCoeff, resizeCoeff;
+       ipu_color_space_t in_fmt, out_fmt;
+       int ret = 0;
+
+       /* Setup vertical resizing */
+       if (!params->mem_pp_mem.outv_resize_ratio) {
+               ret = _calc_resize_coeffs(ipu, params->mem_pp_mem.in_height,
+                                   params->mem_pp_mem.out_height,
+                                   &resizeCoeff, &downsizeCoeff);
+               if (ret < 0) {
+                       dev_err(ipu->dev, "failed to calculate pp height "
+                               "scaling coefficients\n");
+                       return ret;
+               }
+
+               reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+       } else {
+               reg = (params->mem_pp_mem.outv_resize_ratio) << 16;
+       }
+
+       /* Setup horizontal resizing */
+       if (!params->mem_pp_mem.outh_resize_ratio) {
+               ret = _calc_resize_coeffs(ipu, params->mem_pp_mem.in_width,
+                                       params->mem_pp_mem.out_width,
+                                       &resizeCoeff, &downsizeCoeff);
+               if (ret < 0) {
+                       dev_err(ipu->dev, "failed to calculate pp width "
+                               "scaling coefficients\n");
+                       return ret;
+               }
+
+               reg |= (downsizeCoeff << 14) | resizeCoeff;
+       } else {
+               reg |= params->mem_pp_mem.outh_resize_ratio;
+       }
+
+       ipu_ic_write(ipu, reg, IC_PP_RSC);
+
+       ic_conf = ipu_ic_read(ipu, IC_CONF);
+
+       /* Setup color space conversion */
+       in_fmt = format_to_colorspace(params->mem_pp_mem.in_pixel_fmt);
+       out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt);
+       if (in_fmt == RGB) {
+               if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+                       /* Enable RGB->YCBCR CSC1 */
+                       _init_csc(ipu, IC_TASK_POST_PROCESSOR, RGB, out_fmt, 1);
+                       ic_conf |= IC_CONF_PP_CSC1;
+               }
+       }
+       if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+               if (out_fmt == RGB) {
+                       /* Enable YCBCR->RGB CSC1 */
+                       _init_csc(ipu, IC_TASK_POST_PROCESSOR, YCbCr, RGB, 1);
+                       ic_conf |= IC_CONF_PP_CSC1;
+               } else {
+                       /* TODO: Support YUV<->YCbCr conversion? */
+               }
+       }
+
+       if (params->mem_pp_mem.graphics_combine_en) {
+               ic_conf |= IC_CONF_PP_CMB;
+
+               if (!(ic_conf & IC_CONF_PP_CSC1)) {
+                       /* need transparent CSC1 conversion */
+                       _init_csc(ipu, IC_TASK_POST_PROCESSOR, RGB, RGB, 1);
+                       ic_conf |= IC_CONF_PP_CSC1;  /* Enable RGB->RGB CSC */
+               }
+
+               in_fmt = format_to_colorspace(params->mem_pp_mem.in_g_pixel_fmt);
+               out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt);
+               if (in_fmt == RGB) {
+                       if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+                               /* Enable RGB->YCBCR CSC2 */
+                               _init_csc(ipu, IC_TASK_POST_PROCESSOR, RGB, out_fmt, 2);
+                               ic_conf |= IC_CONF_PP_CSC2;
+                       }
+               }
+               if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+                       if (out_fmt == RGB) {
+                               /* Enable YCBCR->RGB CSC2 */
+                               _init_csc(ipu, IC_TASK_POST_PROCESSOR, YCbCr, RGB, 2);
+                               ic_conf |= IC_CONF_PP_CSC2;
+                       } else {
+                               /* TODO: Support YUV<->YCbCr conversion? */
+                       }
+               }
+
+               if (params->mem_pp_mem.global_alpha_en) {
+                       ic_conf |= IC_CONF_IC_GLB_LOC_A;
+                       reg = ipu_ic_read(ipu, IC_CMBP_1);
+                       reg &= ~(0xff00);
+                       reg |= (params->mem_pp_mem.alpha << 8);
+                       ipu_ic_write(ipu, reg, IC_CMBP_1);
+               } else
+                       ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
+
+               if (params->mem_pp_mem.key_color_en) {
+                       ic_conf |= IC_CONF_KEY_COLOR_EN;
+                       ipu_ic_write(ipu, params->mem_pp_mem.key_color,
+                                       IC_CMBP_2);
+               } else
+                       ic_conf &= ~IC_CONF_KEY_COLOR_EN;
+       } else {
+               ic_conf &= ~IC_CONF_PP_CMB;
+       }
+
+       ipu_ic_write(ipu, ic_conf, IC_CONF);
+
+       return ret;
+}
+
+void _ipu_ic_uninit_pp(struct ipu_soc *ipu)
+{
+       uint32_t reg;
+
+       reg = ipu_ic_read(ipu, IC_CONF);
+       reg &= ~(IC_CONF_PP_EN | IC_CONF_PP_CSC1 | IC_CONF_PP_CSC2 |
+                IC_CONF_PP_CMB);
+       ipu_ic_write(ipu, reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_pp(struct ipu_soc *ipu, ipu_channel_params_t *params)
+{
+}
+
+void _ipu_ic_uninit_rotate_pp(struct ipu_soc *ipu)
+{
+       uint32_t reg;
+       reg = ipu_ic_read(ipu, IC_CONF);
+       reg &= ~IC_CONF_PP_ROT_EN;
+       ipu_ic_write(ipu, reg, IC_CONF);
+}
+
+int _ipu_ic_idma_init(struct ipu_soc *ipu, int dma_chan,
+               uint16_t width, uint16_t height,
+               int burst_size, ipu_rotate_mode_t rot)
+{
+       u32 ic_idmac_1, ic_idmac_2, ic_idmac_3;
+       u32 temp_rot = bitrev8(rot) >> 5;
+       bool need_hor_flip = false;
+
+       if ((burst_size != 8) && (burst_size != 16)) {
+               dev_dbg(ipu->dev, "Illegal burst length for IC\n");
+               return -EINVAL;
+       }
+
+       width--;
+       height--;
+
+       if (temp_rot & 0x2)     /* Need horizontal flip */
+               need_hor_flip = true;
+
+       ic_idmac_1 = ipu_ic_read(ipu, IC_IDMAC_1);
+       ic_idmac_2 = ipu_ic_read(ipu, IC_IDMAC_2);
+       ic_idmac_3 = ipu_ic_read(ipu, IC_IDMAC_3);
+       if (dma_chan == 22) {   /* PP output - CB2 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16;
+
+               if (need_hor_flip)
+                       ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS;
+
+               ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK;
+               ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET;
+
+               ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK;
+               ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET;
+       } else if (dma_chan == 11) {    /* PP Input - CB5 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16;
+       } else if (dma_chan == 47) {    /* PP Rot input */
+               ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK;
+               ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET;
+       }
+
+       if (dma_chan == 12) {   /* PRP Input - CB6 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16;
+       }
+
+       if (dma_chan == 20) {   /* PRP ENC output - CB0 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16;
+
+               if (need_hor_flip)
+                       ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS;
+
+               ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK;
+               ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET;
+
+               ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK;
+               ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET;
+
+       } else if (dma_chan == 45) {    /* PRP ENC Rot input */
+               ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK;
+               ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET;
+       }
+
+       if (dma_chan == 21) {   /* PRP VF output - CB1 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16;
+
+               if (need_hor_flip)
+                       ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS;
+
+               ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK;
+               ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET;
+
+               ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK;
+               ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET;
+
+       } else if (dma_chan == 46) {    /* PRP VF Rot input */
+               ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK;
+               ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET;
+       }
+
+       if (dma_chan == 14) {   /* PRP VF graphics combining input - CB3 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16;
+       } else if (dma_chan == 15) {    /* PP graphics combining input - CB4 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16;
+       } else if (dma_chan == 5) {     /* VDIC OUTPUT - CB7 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB7_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB7_BURST_16;
+       }
+
+       ipu_ic_write(ipu, ic_idmac_1, IC_IDMAC_1);
+       ipu_ic_write(ipu, ic_idmac_2, IC_IDMAC_2);
+       ipu_ic_write(ipu, ic_idmac_3, IC_IDMAC_3);
+       return 0;
+}
+
+static void _init_csc(struct ipu_soc *ipu, uint8_t ic_task, ipu_color_space_t in_format,
+                     ipu_color_space_t out_format, int csc_index)
+{
+       /*
+        * Y =  0.257 * R + 0.504 * G + 0.098 * B +  16;
+        * U = -0.148 * R - 0.291 * G + 0.439 * B + 128;
+        * V =  0.439 * R - 0.368 * G - 0.071 * B + 128;
+        */
+       static const uint32_t rgb2ycbcr_coeff[4][3] = {
+               {0x0042, 0x0081, 0x0019},
+               {0x01DA, 0x01B6, 0x0070},
+               {0x0070, 0x01A2, 0x01EE},
+               {0x0040, 0x0200, 0x0200},       /* A0, A1, A2 */
+       };
+
+       /* transparent RGB->RGB matrix for combining
+        */
+       static const uint32_t rgb2rgb_coeff[4][3] = {
+               {0x0080, 0x0000, 0x0000},
+               {0x0000, 0x0080, 0x0000},
+               {0x0000, 0x0000, 0x0080},
+               {0x0000, 0x0000, 0x0000},       /* A0, A1, A2 */
+       };
+
+/*     R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
+       G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
+       B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */
+       static const uint32_t ycbcr2rgb_coeff[4][3] = {
+               {149, 0, 204},
+               {149, 462, 408},
+               {149, 255, 0},
+               {8192 - 446, 266, 8192 - 554},  /* A0, A1, A2 */
+       };
+
+       uint32_t param;
+       uint32_t *base = NULL;
+
+       if (ic_task == IC_TASK_ENCODER) {
+               base = (uint32_t *)ipu->tpmem_base + 0x2008 / 4;
+       } else if (ic_task == IC_TASK_VIEWFINDER) {
+               if (csc_index == 1)
+                       base = (uint32_t *)ipu->tpmem_base + 0x4028 / 4;
+               else
+                       base = (uint32_t *)ipu->tpmem_base + 0x4040 / 4;
+       } else if (ic_task == IC_TASK_POST_PROCESSOR) {
+               if (csc_index == 1)
+                       base = (uint32_t *)ipu->tpmem_base + 0x6060 / 4;
+               else
+                       base = (uint32_t *)ipu->tpmem_base + 0x6078 / 4;
+       } else {
+               BUG();
+       }
+
+       if ((in_format == YCbCr) && (out_format == RGB)) {
+               /* Init CSC (YCbCr->RGB) */
+               param = (ycbcr2rgb_coeff[3][0] << 27) |
+                       (ycbcr2rgb_coeff[0][0] << 18) |
+                       (ycbcr2rgb_coeff[1][1] << 9) | ycbcr2rgb_coeff[2][2];
+               writel(param, base++);
+               /* scale = 2, sat = 0 */
+               param = (ycbcr2rgb_coeff[3][0] >> 5) | (2L << (40 - 32));
+               writel(param, base++);
+
+               param = (ycbcr2rgb_coeff[3][1] << 27) |
+                       (ycbcr2rgb_coeff[0][1] << 18) |
+                       (ycbcr2rgb_coeff[1][0] << 9) | ycbcr2rgb_coeff[2][0];
+               writel(param, base++);
+               param = (ycbcr2rgb_coeff[3][1] >> 5);
+               writel(param, base++);
+
+               param = (ycbcr2rgb_coeff[3][2] << 27) |
+                       (ycbcr2rgb_coeff[0][2] << 18) |
+                       (ycbcr2rgb_coeff[1][2] << 9) | ycbcr2rgb_coeff[2][1];
+               writel(param, base++);
+               param = (ycbcr2rgb_coeff[3][2] >> 5);
+               writel(param, base++);
+       } else if ((in_format == RGB) && (out_format == YCbCr)) {
+               /* Init CSC (RGB->YCbCr) */
+               param = (rgb2ycbcr_coeff[3][0] << 27) |
+                       (rgb2ycbcr_coeff[0][0] << 18) |
+                       (rgb2ycbcr_coeff[1][1] << 9) | rgb2ycbcr_coeff[2][2];
+               writel(param, base++);
+               /* scale = 1, sat = 0 */
+               param = (rgb2ycbcr_coeff[3][0] >> 5) | (1UL << 8);
+               writel(param, base++);
+
+               param = (rgb2ycbcr_coeff[3][1] << 27) |
+                       (rgb2ycbcr_coeff[0][1] << 18) |
+                       (rgb2ycbcr_coeff[1][0] << 9) | rgb2ycbcr_coeff[2][0];
+               writel(param, base++);
+               param = (rgb2ycbcr_coeff[3][1] >> 5);
+               writel(param, base++);
+
+               param = (rgb2ycbcr_coeff[3][2] << 27) |
+                       (rgb2ycbcr_coeff[0][2] << 18) |
+                       (rgb2ycbcr_coeff[1][2] << 9) | rgb2ycbcr_coeff[2][1];
+               writel(param, base++);
+               param = (rgb2ycbcr_coeff[3][2] >> 5);
+               writel(param, base++);
+       } else if ((in_format == RGB) && (out_format == RGB)) {
+               /* Init CSC */
+               param =
+                   (rgb2rgb_coeff[3][0] << 27) | (rgb2rgb_coeff[0][0] << 18) |
+                   (rgb2rgb_coeff[1][1] << 9) | rgb2rgb_coeff[2][2];
+               writel(param, base++);
+               /* scale = 2, sat = 0 */
+               param = (rgb2rgb_coeff[3][0] >> 5) | (2UL << 8);
+               writel(param, base++);
+
+               param =
+                   (rgb2rgb_coeff[3][1] << 27) | (rgb2rgb_coeff[0][1] << 18) |
+                   (rgb2rgb_coeff[1][0] << 9) | rgb2rgb_coeff[2][0];
+               writel(param, base++);
+               param = (rgb2rgb_coeff[3][1] >> 5);
+               writel(param, base++);
+
+               param =
+                   (rgb2rgb_coeff[3][2] << 27) | (rgb2rgb_coeff[0][2] << 18) |
+                   (rgb2rgb_coeff[1][2] << 9) | rgb2rgb_coeff[2][1];
+               writel(param, base++);
+               param = (rgb2rgb_coeff[3][2] >> 5);
+               writel(param, base++);
+       } else {
+               dev_err(ipu->dev, "Unsupported color space conversion\n");
+       }
+}
+
+static int _calc_resize_coeffs(struct ipu_soc *ipu,
+                               uint32_t inSize, uint32_t outSize,
+                               uint32_t *resizeCoeff,
+                               uint32_t *downsizeCoeff)
+{
+       uint32_t tempSize;
+       uint32_t tempDownsize;
+
+       if (inSize > 4096) {
+               dev_err(ipu->dev, "IC input size(%d) cannot exceed 4096\n",
+                       inSize);
+               return -EINVAL;
+       }
+
+       if (outSize > 1024) {
+               dev_err(ipu->dev, "IC output size(%d) cannot exceed 1024\n",
+                       outSize);
+               return -EINVAL;
+       }
+
+       if ((outSize << 3) < inSize) {
+               dev_err(ipu->dev, "IC cannot downsize more than 8:1\n");
+               return -EINVAL;
+       }
+
+       /* Compute downsizing coefficient */
+       /* Output of downsizing unit cannot be more than 1024 */
+       tempDownsize = 0;
+       tempSize = inSize;
+       while (((tempSize > 1024) || (tempSize >= outSize * 2)) &&
+              (tempDownsize < 2)) {
+               tempSize >>= 1;
+               tempDownsize++;
+       }
+       *downsizeCoeff = tempDownsize;
+
+       /* compute resizing coefficient using the following equation:
+          resizeCoeff = M*(SI -1)/(SO - 1)
+          where M = 2^13, SI - input size, SO - output size    */
+       *resizeCoeff = (8192L * (tempSize - 1)) / (outSize - 1);
+       if (*resizeCoeff >= 16384L) {
+               dev_err(ipu->dev, "Overflow on IC resize coefficient.\n");
+               return -EINVAL;
+       }
+
+       dev_dbg(ipu->dev, "resizing from %u -> %u pixels, "
+               "downsize=%u, resize=%u.%lu (reg=%u)\n", inSize, outSize,
+               *downsizeCoeff, (*resizeCoeff >= 8192L) ? 1 : 0,
+               ((*resizeCoeff & 0x1FFF) * 10000L) / 8192L, *resizeCoeff);
+
+       return 0;
+}
+
+void _ipu_vdi_toggle_top_field_man(struct ipu_soc *ipu)
+{
+       uint32_t reg;
+       uint32_t mask_reg;
+
+       reg = ipu_vdi_read(ipu, VDI_C);
+       mask_reg = reg & VDI_C_TOP_FIELD_MAN_1;
+       if (mask_reg == VDI_C_TOP_FIELD_MAN_1)
+               reg &= ~VDI_C_TOP_FIELD_MAN_1;
+       else
+               reg |= VDI_C_TOP_FIELD_MAN_1;
+
+       ipu_vdi_write(ipu, reg, VDI_C);
+}
diff --git a/drivers/mxc/ipu3/ipu_param_mem.h b/drivers/mxc/ipu3/ipu_param_mem.h
new file mode 100644 (file)
index 0000000..266d4d2
--- /dev/null
@@ -0,0 +1,996 @@
+/*
+ * Copyright 2005-2015 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
+ */
+#ifndef __INCLUDE_IPU_PARAM_MEM_H__
+#define __INCLUDE_IPU_PARAM_MEM_H__
+
+#include <linux/bitrev.h>
+#include <linux/types.h>
+
+#include "ipu_prv.h"
+
+extern u32 *ipu_cpmem_base;
+
+struct ipu_ch_param_word {
+       uint32_t data[5];
+       uint32_t res[3];
+};
+
+struct ipu_ch_param {
+       struct ipu_ch_param_word word[2];
+};
+
+#define ipu_ch_param_addr(ipu, ch) (((struct ipu_ch_param *)ipu->cpmem_base) + (ch))
+
+#define _param_word(base, w) \
+       (((struct ipu_ch_param *)(base))->word[(w)].data)
+
+#define ipu_ch_param_set_field(base, w, bit, size, v) { \
+       int i = (bit) / 32; \
+       int off = (bit) % 32; \
+       _param_word(base, w)[i] |= (v) << off; \
+       if (((bit)+(size)-1)/32 > i) { \
+               _param_word(base, w)[i + 1] |= (v) >> (off ? (32 - off) : 0); \
+       } \
+}
+
+#define ipu_ch_param_set_field_io(base, w, bit, size, v) { \
+       int i = (bit) / 32; \
+       int off = (bit) % 32; \
+       unsigned reg_offset; \
+       u32 temp; \
+       reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; \
+       reg_offset += i; \
+       temp = readl((u32 *)base + reg_offset); \
+       temp |= (v) << off; \
+       writel(temp, (u32 *)base + reg_offset); \
+       if (((bit)+(size)-1)/32 > i) { \
+               reg_offset++; \
+               temp = readl((u32 *)base + reg_offset); \
+               temp |= (v) >> (off ? (32 - off) : 0); \
+               writel(temp, (u32 *)base + reg_offset); \
+       } \
+}
+
+#define ipu_ch_param_mod_field(base, w, bit, size, v) { \
+       int i = (bit) / 32; \
+       int off = (bit) % 32; \
+       u32 mask = (1UL << size) - 1; \
+       u32 temp = _param_word(base, w)[i]; \
+       temp &= ~(mask << off); \
+       _param_word(base, w)[i] = temp | (v) << off; \
+       if (((bit)+(size)-1)/32 > i) { \
+               temp = _param_word(base, w)[i + 1]; \
+               temp &= ~(mask >> (32 - off)); \
+               _param_word(base, w)[i + 1] = \
+                       temp | ((v) >> (off ? (32 - off) : 0)); \
+       } \
+}
+
+#define ipu_ch_param_mod_field_io(base, w, bit, size, v) { \
+       int i = (bit) / 32; \
+       int off = (bit) % 32; \
+       u32 mask = (1UL << size) - 1; \
+       unsigned reg_offset; \
+       u32 temp; \
+       reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; \
+       reg_offset += i; \
+       temp = readl((u32 *)base + reg_offset); \
+       temp &= ~(mask << off); \
+       temp |= (v) << off; \
+       writel(temp, (u32 *)base + reg_offset); \
+       if (((bit)+(size)-1)/32 > i) { \
+               reg_offset++; \
+               temp = readl((u32 *)base + reg_offset); \
+               temp &= ~(mask >> (32 - off)); \
+               temp |= ((v) >> (off ? (32 - off) : 0)); \
+               writel(temp, (u32 *)base + reg_offset); \
+       } \
+}
+
+#define ipu_ch_param_read_field(base, w, bit, size) ({ \
+       u32 temp2; \
+       int i = (bit) / 32; \
+       int off = (bit) % 32; \
+       u32 mask = (1UL << size) - 1; \
+       u32 temp1 = _param_word(base, w)[i]; \
+       temp1 = mask & (temp1 >> off); \
+       if (((bit)+(size)-1)/32 > i) { \
+               temp2 = _param_word(base, w)[i + 1]; \
+               temp2 &= mask >> (off ? (32 - off) : 0); \
+               temp1 |= temp2 << (off ? (32 - off) : 0); \
+       } \
+       temp1; \
+})
+
+#define ipu_ch_param_read_field_io(base, w, bit, size) ({ \
+       u32 temp1, temp2; \
+       int i = (bit) / 32; \
+       int off = (bit) % 32; \
+       u32 mask = (1UL << size) - 1; \
+       unsigned reg_offset; \
+       reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; \
+       reg_offset += i; \
+       temp1 = readl((u32 *)base + reg_offset); \
+       temp1 = mask & (temp1 >> off); \
+       if (((bit)+(size)-1)/32 > i) { \
+               reg_offset++; \
+               temp2 = readl((u32 *)base + reg_offset); \
+               temp2 &= mask >> (off ? (32 - off) : 0); \
+               temp1 |= temp2 << (off ? (32 - off) : 0); \
+       } \
+       temp1; \
+})
+
+static inline int __ipu_ch_get_third_buf_cpmem_num(int ch)
+{
+       switch (ch) {
+       case 8:
+               return 64;
+       case 9:
+               return 65;
+       case 10:
+               return 66;
+       case 13:
+               return 67;
+       case 21:
+               return 68;
+       case 23:
+               return 69;
+       case 27:
+               return 70;
+       case 28:
+               return 71;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static inline void _ipu_ch_params_set_packing(struct ipu_ch_param *p,
+                                             int red_width, int red_offset,
+                                             int green_width, int green_offset,
+                                             int blue_width, int blue_offset,
+                                             int alpha_width, int alpha_offset)
+{
+       /* Setup red width and offset */
+       ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1);
+       ipu_ch_param_set_field(p, 1, 128, 5, red_offset);
+       /* Setup green width and offset */
+       ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1);
+       ipu_ch_param_set_field(p, 1, 133, 5, green_offset);
+       /* Setup blue width and offset */
+       ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1);
+       ipu_ch_param_set_field(p, 1, 138, 5, blue_offset);
+       /* Setup alpha width and offset */
+       ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1);
+       ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset);
+}
+
+static inline void _ipu_ch_param_dump(struct ipu_soc *ipu, int ch)
+{
+       struct ipu_ch_param *p = ipu_ch_param_addr(ipu, ch);
+       dev_dbg(ipu->dev, "ch %d word 0 - %08X %08X %08X %08X %08X\n", ch,
+                p->word[0].data[0], p->word[0].data[1], p->word[0].data[2],
+                p->word[0].data[3], p->word[0].data[4]);
+       dev_dbg(ipu->dev, "ch %d word 1 - %08X %08X %08X %08X %08X\n", ch,
+                p->word[1].data[0], p->word[1].data[1], p->word[1].data[2],
+                p->word[1].data[3], p->word[1].data[4]);
+       dev_dbg(ipu->dev, "PFS 0x%x, ",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 85, 4));
+       dev_dbg(ipu->dev, "BPP 0x%x, ",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 107, 3));
+       dev_dbg(ipu->dev, "NPB 0x%x\n",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7));
+
+       dev_dbg(ipu->dev, "FW %d, ",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 125, 13));
+       dev_dbg(ipu->dev, "FH %d, ",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 138, 12));
+       dev_dbg(ipu->dev, "EBA0 0x%x\n",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 0, 29) << 3);
+       dev_dbg(ipu->dev, "EBA1 0x%x\n",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 29, 29) << 3);
+       dev_dbg(ipu->dev, "Stride %d\n",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14));
+       dev_dbg(ipu->dev, "scan_order %d\n",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 113, 1));
+       dev_dbg(ipu->dev, "uv_stride %d\n",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 128, 14));
+       dev_dbg(ipu->dev, "u_offset 0x%x\n",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22) << 3);
+       dev_dbg(ipu->dev, "v_offset 0x%x\n",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22) << 3);
+
+       dev_dbg(ipu->dev, "Width0 %d+1, ",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 116, 3));
+       dev_dbg(ipu->dev, "Width1 %d+1, ",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 119, 3));
+       dev_dbg(ipu->dev, "Width2 %d+1, ",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 122, 3));
+       dev_dbg(ipu->dev, "Width3 %d+1, ",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 125, 3));
+       dev_dbg(ipu->dev, "Offset0 %d, ",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 128, 5));
+       dev_dbg(ipu->dev, "Offset1 %d, ",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 133, 5));
+       dev_dbg(ipu->dev, "Offset2 %d, ",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 138, 5));
+       dev_dbg(ipu->dev, "Offset3 %d\n",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 143, 5));
+}
+
+static inline void fill_cpmem(struct ipu_soc *ipu, int ch, struct ipu_ch_param *params)
+{
+       int i, w;
+       void *addr = ipu_ch_param_addr(ipu, ch);
+
+       /* 2 words, 5 valid data */
+       for (w = 0; w < 2; w++) {
+               for (i = 0; i < 5; i++) {
+                       writel(params->word[w].data[i], addr);
+                       addr += 4;
+               }
+               addr += 12;
+       }
+}
+
+static inline void _ipu_ch_param_init(struct ipu_soc *ipu, int ch,
+                                     uint32_t pixel_fmt, uint32_t width,
+                                     uint32_t height, uint32_t stride,
+                                     uint32_t u, uint32_t v,
+                                     uint32_t uv_stride, dma_addr_t addr0,
+                                     dma_addr_t addr1, dma_addr_t addr2)
+{
+       uint32_t u_offset = 0;
+       uint32_t v_offset = 0;
+       uint32_t bs = 0;
+       int32_t sub_ch = 0;
+       struct ipu_ch_param params;
+
+       memset(&params, 0, sizeof(params));
+
+       ipu_ch_param_set_field(&params, 0, 125, 13, width - 1);
+
+       if (((ch == 8) || (ch == 9) || (ch == 10)) && !ipu->vdoa_en) {
+               ipu_ch_param_set_field(&params, 0, 138, 12, (height / 2) - 1);
+               ipu_ch_param_set_field(&params, 1, 102, 14, (stride * 2) - 1);
+       } else {
+               /* note: for vdoa+vdi- ch8/9/10, always use band mode */
+               ipu_ch_param_set_field(&params, 0, 138, 12, height - 1);
+               ipu_ch_param_set_field(&params, 1, 102, 14, stride - 1);
+       }
+
+       /* EBA is 8-byte aligned */
+       ipu_ch_param_set_field(&params, 1, 0, 29, addr0 >> 3);
+       ipu_ch_param_set_field(&params, 1, 29, 29, addr1 >> 3);
+       if (addr0%8)
+               dev_warn(ipu->dev,
+                        "IDMAC%d's EBA0 is not 8-byte aligned\n", ch);
+       if (addr1%8)
+               dev_warn(ipu->dev,
+                        "IDMAC%d's EBA1 is not 8-byte aligned\n", ch);
+
+       switch (pixel_fmt) {
+       case IPU_PIX_FMT_GENERIC:
+               /*Represents 8-bit Generic data */
+               ipu_ch_param_set_field(&params, 0, 107, 3, 5);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 6);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 63);  /* burst size */
+
+               break;
+       case IPU_PIX_FMT_GENERIC_16:
+               /* Represents 16-bit generic data */
+               ipu_ch_param_set_field(&params, 0, 107, 3, 3);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 6);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+
+               break;
+       case IPU_PIX_FMT_GENERIC_32:
+               /*Represents 32-bit Generic data */
+               break;
+       case IPU_PIX_FMT_RGB565:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 3);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 5, 0, 6, 5, 5, 11, 8, 16);
+               break;
+       case IPU_PIX_FMT_BGRA4444:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 3);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 4, 4, 4, 8, 4, 12, 4, 0);
+               break;
+       case IPU_PIX_FMT_BGRA5551:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 3);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 5, 1, 5, 6, 5, 11, 1, 0);
+               break;
+       case IPU_PIX_FMT_BGR24:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 1);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 19);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 8, 0, 8, 8, 8, 16, 8, 24);
+               break;
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_YUV444:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 1);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 19);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 8, 16, 8, 8, 8, 0, 8, 24);
+               break;
+       case IPU_PIX_FMT_VYU444:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 1);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 19);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 8, 8, 8, 0, 8, 16, 8, 24);
+               break;
+       case IPU_PIX_FMT_AYUV:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 0);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 8, 8, 8, 16, 8, 24, 8, 0);
+               break;
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_BGR32:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 0);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 8, 8, 8, 16, 8, 24, 8, 0);
+               break;
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_RGB32:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 0);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 8, 24, 8, 16, 8, 8, 8, 0);
+               break;
+       case IPU_PIX_FMT_ABGR32:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 0);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 8, 0, 8, 8, 8, 16, 8, 24);
+               break;
+       case IPU_PIX_FMT_UYVY:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 3);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 0xA); /* pix format */
+               if ((ch == 8) || (ch == 9) || (ch == 10)) {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+               } else {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+               }
+               break;
+       case IPU_PIX_FMT_YUYV:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 3);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 0x8); /* pix format */
+               if ((ch == 8) || (ch == 9) || (ch == 10)) {
+                       if (ipu->vdoa_en) {
+                               ipu_ch_param_set_field(&params, 1, 78, 7, 31);
+                       } else {
+                               ipu_ch_param_set_field(&params, 1, 78, 7, 15);
+                       }
+               } else {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+               }
+               break;
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+               ipu_ch_param_set_field(&params, 1, 85, 4, 2);   /* pix format */
+
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               u_offset = stride * height;
+               v_offset = u_offset + (uv_stride * height / 2);
+               if ((ch == 8) || (ch == 9) || (ch == 10)) {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+                       uv_stride = uv_stride*2;
+               } else {
+                       if (_ipu_is_smfc_chan(ch) &&
+                               ipu->smfc_idmac_12bit_3planar_bs_fixup)
+                               bs = 15;
+                       else
+                               bs = 31;
+                       ipu_ch_param_set_field(&params, 1, 78, 7, bs);  /* burst size */
+               }
+               break;
+       case IPU_PIX_FMT_YVU420P:
+               ipu_ch_param_set_field(&params, 1, 85, 4, 2);   /* pix format */
+
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               v_offset = stride * height;
+               u_offset = v_offset + (uv_stride * height / 2);
+               if ((ch == 8) || (ch == 9) || (ch == 10)) {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+                       uv_stride = uv_stride*2;
+               } else {
+                       if (_ipu_is_smfc_chan(ch) &&
+                               ipu->smfc_idmac_12bit_3planar_bs_fixup)
+                               bs = 15;
+                       else
+                               bs = 31;
+                       ipu_ch_param_set_field(&params, 1, 78, 7, bs);  /* burst size */
+               }
+               break;
+       case IPU_PIX_FMT_YVU422P:
+               /* BPP & pixel format */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 1);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               v_offset = (v == 0) ? stride * height : v;
+               u_offset = (u == 0) ? v_offset + v_offset / 2 : u;
+               break;
+       case IPU_PIX_FMT_YUV422P:
+               /* BPP & pixel format */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 1);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               u_offset = (u == 0) ? stride * height : u;
+               v_offset = (v == 0) ? u_offset + u_offset / 2 : v;
+               break;
+       case IPU_PIX_FMT_YUV444P:
+               /* BPP & pixel format */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 0);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+               uv_stride = stride;
+               u_offset = (u == 0) ? stride * height : u;
+               v_offset = (v == 0) ? u_offset * 2 : v;
+               break;
+       case IPU_PIX_FMT_NV16:
+               ipu_ch_param_set_field(&params, 1, 85, 4, 1);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+               uv_stride = stride;
+               u_offset = (u == 0) ? stride * height : u;
+               break;
+       case IPU_PIX_FMT_NV12:
+               /* BPP & pixel format */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 4);   /* pix format */
+               uv_stride = stride;
+               u_offset = (u == 0) ? stride * height : u;
+               if ((ch == 8) || (ch == 9) || (ch == 10)) {
+                       if (ipu->vdoa_en) {
+                                /* one field buffer, memory width 64bits */
+                               ipu_ch_param_set_field(&params, 1, 78, 7, 63);
+                       } else {
+                               ipu_ch_param_set_field(&params, 1, 78, 7, 15);
+                                /* top/bottom field in one buffer*/
+                               uv_stride = uv_stride*2;
+                       }
+               } else {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+               }
+               break;
+       default:
+               dev_err(ipu->dev, "mxc ipu: unimplemented pixel format\n");
+               break;
+       }
+       /*set burst size to 16*/
+
+
+       if (uv_stride)
+               ipu_ch_param_set_field(&params, 1, 128, 14, uv_stride - 1);
+
+       /* Get the uv offset from user when need cropping */
+       if (u || v) {
+               u_offset = u;
+               v_offset = v;
+       }
+
+       /* UBO and VBO are 22-bit and 8-byte aligned */
+       if (u_offset/8 > 0x3fffff)
+               dev_warn(ipu->dev,
+                        "IDMAC%d's U offset exceeds IPU limitation\n", ch);
+       if (v_offset/8 > 0x3fffff)
+               dev_warn(ipu->dev,
+                        "IDMAC%d's V offset exceeds IPU limitation\n", ch);
+       if (u_offset%8)
+               dev_warn(ipu->dev,
+                        "IDMAC%d's U offset is not 8-byte aligned\n", ch);
+       if (v_offset%8)
+               dev_warn(ipu->dev,
+                        "IDMAC%d's V offset is not 8-byte aligned\n", ch);
+
+       ipu_ch_param_set_field(&params, 0, 46, 22, u_offset / 8);
+       ipu_ch_param_set_field(&params, 0, 68, 22, v_offset / 8);
+
+       dev_dbg(ipu->dev, "initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ipu, ch));
+       fill_cpmem(ipu, ch, &params);
+       if (addr2) {
+               sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
+               if (sub_ch <= 0)
+                       return;
+
+               ipu_ch_param_set_field(&params, 1, 0, 29, addr2 >> 3);
+               ipu_ch_param_set_field(&params, 1, 29, 29, 0);
+               if (addr2%8)
+                       dev_warn(ipu->dev,
+                                "IDMAC%d's sub-CPMEM entry%d EBA0 is not "
+                                "8-byte aligned\n", ch, sub_ch);
+
+               dev_dbg(ipu->dev, "initializing idma ch %d @ %p sub cpmem\n", ch,
+                                       ipu_ch_param_addr(ipu, sub_ch));
+               fill_cpmem(ipu, sub_ch, &params);
+       }
+};
+
+static inline void _ipu_ch_param_set_burst_size(struct ipu_soc *ipu,
+                                               uint32_t ch,
+                                               uint16_t burst_pixels)
+{
+       int32_t sub_ch = 0;
+
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7,
+                              burst_pixels - 1);
+
+       sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
+       if (sub_ch <= 0)
+               return;
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 78, 7,
+                              burst_pixels - 1);
+};
+
+static inline int _ipu_ch_param_get_burst_size(struct ipu_soc *ipu, uint32_t ch)
+{
+       return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7) + 1;
+};
+
+static inline int _ipu_ch_param_get_bpp(struct ipu_soc *ipu, uint32_t ch)
+{
+       return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 107, 3);
+};
+
+static inline void _ipu_ch_param_set_buffer(struct ipu_soc *ipu, uint32_t ch,
+                                       int bufNum, dma_addr_t phyaddr)
+{
+       if (bufNum == 2) {
+               ch = __ipu_ch_get_third_buf_cpmem_num(ch);
+               if (ch <= 0)
+                       return;
+               bufNum = 0;
+       }
+
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 29 * bufNum, 29,
+                              phyaddr / 8);
+};
+
+static inline void _ipu_ch_param_set_rotation(struct ipu_soc *ipu, uint32_t ch,
+                                             ipu_rotate_mode_t rot)
+{
+       u32 temp_rot = bitrev8(rot) >> 5;
+       int32_t sub_ch = 0;
+
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 119, 3, temp_rot);
+
+       sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
+       if (sub_ch <= 0)
+               return;
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 119, 3, temp_rot);
+};
+
+static inline void _ipu_ch_param_set_block_mode(struct ipu_soc *ipu, uint32_t ch)
+{
+       int32_t sub_ch = 0;
+
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 117, 2, 1);
+
+       sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
+       if (sub_ch <= 0)
+               return;
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 117, 2, 1);
+};
+
+static inline void _ipu_ch_param_set_alpha_use_separate_channel(struct ipu_soc *ipu,
+                                                               uint32_t ch,
+                                                               bool option)
+{
+       int32_t sub_ch = 0;
+
+       if (option) {
+               ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 89, 1, 1);
+       } else {
+               ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 89, 1, 0);
+       }
+
+       sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
+       if (sub_ch <= 0)
+               return;
+
+       if (option) {
+               ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 89, 1, 1);
+       } else {
+               ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 89, 1, 0);
+       }
+};
+
+static inline void _ipu_ch_param_set_alpha_condition_read(struct ipu_soc *ipu, uint32_t ch)
+{
+       int32_t sub_ch = 0;
+
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 149, 1, 1);
+
+       sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
+       if (sub_ch <= 0)
+               return;
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 149, 1, 1);
+};
+
+static inline void _ipu_ch_param_set_alpha_buffer_memory(struct ipu_soc *ipu, uint32_t ch)
+{
+       int alp_mem_idx;
+       int32_t sub_ch = 0;
+
+       switch (ch) {
+       case 14: /* PRP graphic */
+               alp_mem_idx = 0;
+               break;
+       case 15: /* PP graphic */
+               alp_mem_idx = 1;
+               break;
+       case 23: /* DP BG SYNC graphic */
+               alp_mem_idx = 4;
+               break;
+       case 27: /* DP FG SYNC graphic */
+               alp_mem_idx = 2;
+               break;
+       default:
+               dev_err(ipu->dev, "unsupported correlative channel of local "
+                       "alpha channel\n");
+               return;
+       }
+
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 90, 3, alp_mem_idx);
+
+       sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
+       if (sub_ch <= 0)
+               return;
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 90, 3, alp_mem_idx);
+};
+
+static inline void _ipu_ch_param_set_interlaced_scan(struct ipu_soc *ipu, uint32_t ch)
+{
+       u32 stride;
+       int32_t sub_ch = 0;
+
+       sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
+
+       ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 0, 113, 1, 1);
+       if (sub_ch > 0)
+               ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 113, 1, 1);
+       stride = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14) + 1;
+       /* ILO is 20-bit and 8-byte aligned */
+       if (stride/8 > 0xfffff)
+               dev_warn(ipu->dev,
+                        "IDMAC%d's ILO exceeds IPU limitation\n", ch);
+       if (stride%8)
+               dev_warn(ipu->dev,
+                        "IDMAC%d's ILO is not 8-byte aligned\n", ch);
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 58, 20, stride / 8);
+       if (sub_ch > 0)
+               ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 58, 20,
+                                      stride / 8);
+       stride *= 2;
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14, stride - 1);
+       if (sub_ch > 0)
+               ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 102, 14,
+                                      stride - 1);
+};
+
+static inline void _ipu_ch_param_set_axi_id(struct ipu_soc *ipu, uint32_t ch, uint32_t id)
+{
+       int32_t sub_ch = 0;
+
+       id %= 4;
+
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 93, 2, id);
+
+       sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
+       if (sub_ch <= 0)
+               return;
+       ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 93, 2, id);
+};
+
+static inline int _ipu_ch_param_get_axi_id(struct ipu_soc *ipu, uint32_t ch)
+{
+       return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 93, 2);
+}
+
+static inline int __ipu_ch_offset_calc(uint32_t pixel_fmt,
+                                      uint32_t width,
+                                      uint32_t height,
+                                      uint32_t stride,
+                                      uint32_t u,
+                                      uint32_t v,
+                                      uint32_t uv_stride,
+                                      uint32_t vertical_offset,
+                                      uint32_t horizontal_offset,
+                                      uint32_t *u_offset,
+                                      uint32_t *v_offset)
+{
+       uint32_t u_fix = 0;
+       uint32_t v_fix = 0;
+
+       switch (pixel_fmt) {
+       case IPU_PIX_FMT_GENERIC:
+       case IPU_PIX_FMT_GENERIC_16:
+       case IPU_PIX_FMT_GENERIC_32:
+       case IPU_PIX_FMT_RGB565:
+       case IPU_PIX_FMT_BGR24:
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_YUV444:
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_RGB32:
+       case IPU_PIX_FMT_ABGR32:
+       case IPU_PIX_FMT_UYVY:
+       case IPU_PIX_FMT_YUYV:
+       case IPU_PIX_FMT_GPU32_SB_ST:
+       case IPU_PIX_FMT_GPU32_SB_SRT:
+       case IPU_PIX_FMT_GPU32_ST:
+       case IPU_PIX_FMT_GPU32_SRT:
+       case IPU_PIX_FMT_GPU16_SB_ST:
+       case IPU_PIX_FMT_GPU16_SB_SRT:
+       case IPU_PIX_FMT_GPU16_ST:
+       case IPU_PIX_FMT_GPU16_SRT:
+               *u_offset = 0;
+               *v_offset = 0;
+               break;
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               *u_offset = stride * (height - vertical_offset - 1) +
+                                       (stride - horizontal_offset) +
+                                       (uv_stride * vertical_offset / 2) +
+                                       horizontal_offset / 2;
+               *v_offset = *u_offset + (uv_stride * height / 2);
+               u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
+                                       (horizontal_offset / 2) -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       *u_offset;
+               v_fix = v ? (v + (uv_stride * vertical_offset / 2) +
+                                       (horizontal_offset / 2) -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       *v_offset;
+               break;
+       case IPU_PIX_FMT_YVU420P:
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               *v_offset = stride * (height - vertical_offset - 1) +
+                                       (stride - horizontal_offset) +
+                                       (uv_stride * vertical_offset / 2) +
+                                       horizontal_offset / 2;
+               *u_offset = *v_offset + (uv_stride * height / 2);
+               u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
+                                       (horizontal_offset / 2) -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       *u_offset;
+               v_fix = v ? (v + (uv_stride * vertical_offset / 2) +
+                                       (horizontal_offset / 2) -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       *v_offset;
+               break;
+       case IPU_PIX_FMT_YVU422P:
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               *v_offset = stride * (height - vertical_offset - 1) +
+                                       (stride - horizontal_offset) +
+                                       (uv_stride * vertical_offset) +
+                                       horizontal_offset / 2;
+               *u_offset = *v_offset + uv_stride * height;
+               u_fix = u ? (u + (uv_stride * vertical_offset) +
+                                       horizontal_offset / 2 -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       *u_offset;
+               v_fix = v ? (v + (uv_stride * vertical_offset) +
+                                       horizontal_offset / 2 -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       *v_offset;
+               break;
+       case IPU_PIX_FMT_YUV422P:
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               *u_offset = stride * (height - vertical_offset - 1) +
+                                       (stride - horizontal_offset) +
+                                       (uv_stride * vertical_offset) +
+                                       horizontal_offset / 2;
+               *v_offset = *u_offset + uv_stride * height;
+               u_fix = u ? (u + (uv_stride * vertical_offset) +
+                                       horizontal_offset / 2 -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       *u_offset;
+               v_fix = v ? (v + (uv_stride * vertical_offset) +
+                                       horizontal_offset / 2 -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       *v_offset;
+               break;
+       case IPU_PIX_FMT_YUV444P:
+               uv_stride = stride;
+               *u_offset = stride * (height - vertical_offset - 1) +
+                                       (stride - horizontal_offset) +
+                                       (uv_stride * vertical_offset) +
+                                       horizontal_offset;
+               *v_offset = *u_offset + uv_stride * height;
+               u_fix = u ? (u + (uv_stride * vertical_offset) +
+                                       horizontal_offset -
+                                       (stride * vertical_offset) -
+                                       (horizontal_offset)) :
+                                       *u_offset;
+               v_fix = v ? (v + (uv_stride * vertical_offset) +
+                                       horizontal_offset -
+                                       (stride * vertical_offset) -
+                                       (horizontal_offset)) :
+                                       *v_offset;
+               break;
+       case IPU_PIX_FMT_NV12:
+       case IPU_PIX_FMT_NV16:
+       case PRE_PIX_FMT_NV21:
+       case PRE_PIX_FMT_NV61:
+               uv_stride = stride;
+               *u_offset = stride * (height - vertical_offset - 1) +
+                                       (stride - horizontal_offset) +
+                                       (uv_stride * vertical_offset / 2) +
+                                       horizontal_offset;
+               *v_offset = 0;
+               u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
+                                       horizontal_offset -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       *u_offset;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (u_fix > *u_offset)
+               *u_offset = u_fix;
+
+       if (v_fix > *v_offset)
+               *v_offset = v_fix;
+
+       return 0;
+}
+
+/* IDMAC U/V offset changing support */
+/* U and V input is not affected, */
+/* the update is done by new calculation according to */
+/* vertical_offset and horizontal_offset */
+static inline void _ipu_ch_offset_update(struct ipu_soc *ipu,
+                                        int ch,
+                                        uint32_t pixel_fmt,
+                                        uint32_t width,
+                                        uint32_t height,
+                                        uint32_t stride,
+                                        uint32_t u,
+                                        uint32_t v,
+                                        uint32_t uv_stride,
+                                        uint32_t vertical_offset,
+                                        uint32_t horizontal_offset)
+{
+       uint32_t u_offset = 0;
+       uint32_t v_offset = 0;
+       uint32_t old_offset = 0;
+       int32_t sub_ch = 0;
+       int ret;
+
+       ret = __ipu_ch_offset_calc(pixel_fmt, width, height, stride,
+                                  u, v, uv_stride,
+                                  vertical_offset, horizontal_offset,
+                                  &u_offset, &v_offset);
+       if (ret) {
+               dev_err(ipu->dev, "mxc ipu: unimplemented pixel format\n");
+               return;
+       }
+
+       /* UBO and VBO are 22-bit and 8-byte aligned */
+       if (u_offset/8 > 0x3fffff)
+               dev_warn(ipu->dev,
+                       "IDMAC%d's U offset exceeds IPU limitation\n", ch);
+       if (v_offset/8 > 0x3fffff)
+               dev_warn(ipu->dev,
+                       "IDMAC%d's V offset exceeds IPU limitation\n", ch);
+       if (u_offset%8)
+               dev_warn(ipu->dev,
+                       "IDMAC%d's U offset is not 8-byte aligned\n", ch);
+       if (v_offset%8)
+               dev_warn(ipu->dev,
+                       "IDMAC%d's V offset is not 8-byte aligned\n", ch);
+
+       old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22);
+       if (old_offset != u_offset / 8)
+               ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22, u_offset / 8);
+       old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22);
+       if (old_offset != v_offset / 8)
+               ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22, v_offset / 8);
+
+       sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
+       if (sub_ch <= 0)
+               return;
+       old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 46, 22);
+       if (old_offset != u_offset / 8)
+               ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 46, 22, u_offset / 8);
+       old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 68, 22);
+       if (old_offset != v_offset / 8)
+               ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 68, 22, v_offset / 8);
+};
+
+static inline void _ipu_ch_params_set_alpha_width(struct ipu_soc *ipu, uint32_t ch, int alpha_width)
+{
+       int32_t sub_ch = 0;
+
+       ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 1, 125, 3, alpha_width - 1);
+
+       sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
+       if (sub_ch <= 0)
+               return;
+       ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 125, 3, alpha_width - 1);
+};
+
+static inline void _ipu_ch_param_set_bandmode(struct ipu_soc *ipu,
+                       uint32_t ch, uint32_t band_height)
+{
+       int32_t sub_ch = 0;
+
+       ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch),
+                                       0, 114, 3, band_height - 1);
+       sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
+       if (sub_ch <= 0)
+               return;
+       ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch),
+                                       0, 114, 3, band_height - 1);
+
+       dev_dbg(ipu->dev, "BNDM 0x%x, ",
+                ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 114, 3));
+}
+
+/*
+ * The IPUv3 IDMAC has a bug to read 32bpp pixels from a graphics plane
+ * whose alpha component is at the most significant 8 bits. The bug only
+ * impacts on cases in which the relevant separate alpha channel is enabled.
+ *
+ * Return true on bad alpha component position, otherwise, return false.
+ */
+static inline bool _ipu_ch_param_bad_alpha_pos(uint32_t pixel_fmt)
+{
+       switch (pixel_fmt) {
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_RGB32:
+               return true;
+       }
+
+       return false;
+}
+#endif
diff --git a/drivers/mxc/ipu3/ipu_pixel_clk.c b/drivers/mxc/ipu3/ipu_pixel_clk.c
new file mode 100644 (file)
index 0000000..4efb6b7
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2013-2015 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
+ */
+
+/*!
+ * @file ipu_pixel_clk.c
+ *
+ * @brief IPU pixel clock implementation
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/ipu-v3.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+
+ /*
+ * muxd clock implementation
+ */
+struct clk_di_mux {
+       struct clk_hw hw;
+       u8              ipu_id;
+       u8              di_id;
+       u8              flags;
+       u8              index;
+};
+#define to_clk_di_mux(_hw) container_of(_hw, struct clk_di_mux, hw)
+
+static int _ipu_pixel_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct clk_di_mux *mux = to_clk_di_mux(hw);
+       struct ipu_soc *ipu = ipu_get_soc(mux->ipu_id);
+       u32 di_gen;
+
+       di_gen = ipu_di_read(ipu, mux->di_id, DI_GENERAL);
+       if (index == 0)
+               /* ipu1_clk or ipu2_clk internal clk */
+               di_gen &= ~DI_GEN_DI_CLK_EXT;
+       else
+               di_gen |= DI_GEN_DI_CLK_EXT;
+
+       ipu_di_write(ipu, mux->di_id, di_gen, DI_GENERAL);
+       mux->index = index;
+       pr_debug("ipu_pixel_clk: di_clk_ext:0x%x, di_gen reg:0x%x.\n",
+                       !(di_gen & DI_GEN_DI_CLK_EXT), di_gen);
+       return 0;
+}
+
+static u8 _ipu_pixel_clk_get_parent(struct clk_hw *hw)
+{
+       struct clk_di_mux *mux = to_clk_di_mux(hw);
+
+       return mux->index;
+}
+
+const struct clk_ops clk_mux_di_ops = {
+       .get_parent = _ipu_pixel_clk_get_parent,
+       .set_parent = _ipu_pixel_clk_set_parent,
+};
+
+struct clk *clk_register_mux_pix_clk(struct device *dev, const char *name,
+               const char **parent_names, u8 num_parents, unsigned long flags,
+               u8 ipu_id, u8 di_id, u8 clk_mux_flags)
+{
+       struct clk_di_mux *mux;
+       struct clk *clk;
+       struct clk_init_data init;
+
+       mux = kzalloc(sizeof(struct clk_di_mux), GFP_KERNEL);
+       if (!mux)
+               return ERR_PTR(-ENOMEM);
+
+       init.name = name;
+       init.ops = &clk_mux_di_ops;
+       init.flags = flags;
+       init.parent_names = parent_names;
+       init.num_parents = num_parents;
+
+       mux->ipu_id = ipu_id;
+       mux->di_id = di_id;
+       mux->flags = clk_mux_flags | CLK_SET_RATE_PARENT;
+       mux->hw.init = &init;
+
+       clk = clk_register(dev, &mux->hw);
+       if (IS_ERR(clk))
+               kfree(mux);
+
+       return clk;
+}
+
+/*
+ * Gated clock implementation
+ */
+struct clk_di_div {
+       struct clk_hw hw;
+       u8              ipu_id;
+       u8              di_id;
+       u8              flags;
+};
+#define to_clk_di_div(_hw) container_of(_hw, struct clk_di_div, hw)
+
+static unsigned long _ipu_pixel_clk_div_recalc_rate(struct clk_hw *hw,
+                                       unsigned long parent_rate)
+{
+       struct clk_di_div *di_div = to_clk_di_div(hw);
+       struct ipu_soc *ipu = ipu_get_soc(di_div->ipu_id);
+       u32 div;
+       u64 final_rate = (unsigned long long)parent_rate * 16;
+
+       _ipu_get(ipu);
+       div = ipu_di_read(ipu, di_div->di_id, DI_BS_CLKGEN0);
+       _ipu_put(ipu);
+       pr_debug("ipu_di%d read BS_CLKGEN0 div:%d, final_rate:%lld, prate:%ld\n",
+                       di_div->di_id, div, final_rate, parent_rate);
+
+       if (div == 0)
+               return 0;
+       do_div(final_rate, div);
+
+       return (unsigned long)final_rate;
+}
+
+static long _ipu_pixel_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
+                              unsigned long *parent_clk_rate)
+{
+       u64 div, final_rate;
+       u32 remainder;
+       u64 parent_rate = (unsigned long long)(*parent_clk_rate) * 16;
+
+       /*
+        * Calculate divider
+        * Fractional part is 4 bits,
+        * so simply multiply by 2^4 to get fractional part.
+        */
+       div = parent_rate;
+       remainder = do_div(div, rate);
+       /* Round the divider value */
+       if (remainder > (rate/2))
+               div++;
+       if (div < 0x10)            /* Min DI disp clock divider is 1 */
+               div = 0x10;
+       if (div & ~0xFEF)
+               div &= 0xFF8;
+       else {
+               /* Round up divider if it gets us closer to desired pix clk */
+               if ((div & 0xC) == 0xC) {
+                       div += 0x10;
+                       div &= ~0xF;
+               }
+       }
+       final_rate = parent_rate;
+       do_div(final_rate, div);
+
+       return final_rate;
+}
+
+static int _ipu_pixel_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
+                           unsigned long parent_clk_rate)
+{
+       struct clk_di_div *di_div = to_clk_di_div(hw);
+       struct ipu_soc *ipu = ipu_get_soc(di_div->ipu_id);
+       u64 div, parent_rate;
+       u32 remainder;
+
+       parent_rate = (unsigned long long)parent_clk_rate * 16;
+       div = parent_rate;
+       remainder = do_div(div, rate);
+       /* Round the divider value */
+       if (remainder > (rate/2))
+               div++;
+
+       /* Round up divider if it gets us closer to desired pix clk */
+       if ((div & 0xC) == 0xC) {
+               div += 0x10;
+               div &= ~0xF;
+       }
+       if (div > 0x1000)
+               pr_err("Overflow, di:%d, DI_BS_CLKGEN0 div:0x%x\n",
+                               di_div->di_id, (u32)div);
+       _ipu_get(ipu);
+       ipu_di_write(ipu, di_div->di_id, (u32)div, DI_BS_CLKGEN0);
+
+       /* Setup pixel clock timing */
+       /* FIXME: needs to be more flexible */
+       /* Down time is half of period */
+       ipu_di_write(ipu, di_div->di_id, ((u32)div / 16) << 16, DI_BS_CLKGEN1);
+       _ipu_put(ipu);
+
+       return 0;
+}
+
+static struct clk_ops clk_div_ops = {
+       .recalc_rate = _ipu_pixel_clk_div_recalc_rate,
+       .round_rate = _ipu_pixel_clk_div_round_rate,
+       .set_rate = _ipu_pixel_clk_div_set_rate,
+};
+
+struct clk *clk_register_div_pix_clk(struct device *dev, const char *name,
+               const char *parent_name, unsigned long flags,
+               u8 ipu_id, u8 di_id, u8 clk_div_flags)
+{
+       struct clk_di_div *di_div;
+       struct clk *clk;
+       struct clk_init_data init;
+
+       di_div = kzalloc(sizeof(struct clk_di_div), GFP_KERNEL);
+       if (!di_div)
+               return ERR_PTR(-ENOMEM);
+
+       /* struct clk_di_div assignments */
+       di_div->ipu_id = ipu_id;
+       di_div->di_id = di_id;
+       di_div->flags = clk_div_flags;
+
+       init.name = name;
+       init.ops = &clk_div_ops;
+       init.flags = flags | CLK_SET_RATE_PARENT;
+       init.parent_names = parent_name ? &parent_name : NULL;
+       init.num_parents = parent_name ? 1 : 0;
+
+       di_div->hw.init = &init;
+
+       clk = clk_register(dev, &di_div->hw);
+       if (IS_ERR(clk))
+               kfree(clk);
+
+       return clk;
+}
+
+/*
+ * Gated clock implementation
+ */
+struct clk_di_gate {
+       struct clk_hw hw;
+       u8              ipu_id;
+       u8              di_id;
+       u8              flags;
+};
+#define to_clk_di_gate(_hw) container_of(_hw, struct clk_di_gate, hw)
+
+static int _ipu_pixel_clk_enable(struct clk_hw *hw)
+{
+       struct clk_di_gate *gate = to_clk_di_gate(hw);
+       struct ipu_soc *ipu = ipu_get_soc(gate->ipu_id);
+       u32 disp_gen;
+
+       disp_gen = ipu_cm_read(ipu, IPU_DISP_GEN);
+       disp_gen |= gate->di_id ? DI1_COUNTER_RELEASE : DI0_COUNTER_RELEASE;
+       ipu_cm_write(ipu, disp_gen, IPU_DISP_GEN);
+
+       return 0;
+}
+
+static void _ipu_pixel_clk_disable(struct clk_hw *hw)
+{
+       struct clk_di_gate *gate = to_clk_di_gate(hw);
+       struct ipu_soc *ipu = ipu_get_soc(gate->ipu_id);
+       u32 disp_gen;
+
+       disp_gen = ipu_cm_read(ipu, IPU_DISP_GEN);
+       disp_gen &= gate->di_id ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE;
+       ipu_cm_write(ipu, disp_gen, IPU_DISP_GEN);
+
+}
+
+
+static struct clk_ops clk_gate_di_ops = {
+       .enable = _ipu_pixel_clk_enable,
+       .disable = _ipu_pixel_clk_disable,
+};
+
+struct clk *clk_register_gate_pix_clk(struct device *dev, const char *name,
+               const char *parent_name, unsigned long flags,
+               u8 ipu_id, u8 di_id, u8 clk_gate_flags)
+{
+       struct clk_di_gate *gate;
+       struct clk *clk;
+       struct clk_init_data init;
+
+       gate = kzalloc(sizeof(struct clk_di_gate), GFP_KERNEL);
+       if (!gate)
+               return ERR_PTR(-ENOMEM);
+
+       gate->ipu_id = ipu_id;
+       gate->di_id = di_id;
+       gate->flags = clk_gate_flags;
+
+       init.name = name;
+       init.ops = &clk_gate_di_ops;
+       init.flags = flags | CLK_SET_RATE_PARENT;
+       init.parent_names = parent_name ? &parent_name : NULL;
+       init.num_parents = parent_name ? 1 : 0;
+
+       gate->hw.init = &init;
+
+       clk = clk_register(dev, &gate->hw);
+       if (IS_ERR(clk))
+               kfree(clk);
+
+       return clk;
+}
diff --git a/drivers/mxc/ipu3/ipu_prv.h b/drivers/mxc/ipu3/ipu_prv.h
new file mode 100644 (file)
index 0000000..0a77b58
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright 2005-2015 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
+ */
+#ifndef __INCLUDE_IPU_PRV_H__
+#define __INCLUDE_IPU_PRV_H__
+
+#include <linux/clkdev.h>
+#include <linux/device.h>
+#include <linux/fsl_devices.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+
+#define MXC_IPU_MAX_NUM                2
+#define MXC_DI_NUM_PER_IPU     2
+
+/* Globals */
+extern int dmfc_type_setup;
+
+#define IDMA_CHAN_INVALID      0xFF
+#define HIGH_RESOLUTION_WIDTH  1024
+
+enum ipuv3_type {
+       IPUv3D,         /* i.MX37 */
+       IPUv3EX,        /* i.MX51 */
+       IPUv3M,         /* i.MX53 */
+       IPUv3H,         /* i.MX6Q/SDL */
+};
+
+#define IPU_MAX_VDI_IN_WIDTH(type)     ({ (type) >= IPUv3M ? 968 : 720; })
+
+struct ipu_irq_node {
+       irqreturn_t(*handler) (int, void *);    /*!< the ISR */
+       const char *name;       /*!< device associated with the interrupt */
+       void *dev_id;           /*!< some unique information for the ISR */
+       __u32 flags;            /*!< not used */
+};
+
+enum csc_type_t {
+       RGB2YUV = 0,
+       YUV2RGB,
+       RGB2RGB,
+       YUV2YUV,
+       CSC_NONE,
+       CSC_NUM
+};
+
+struct ipu_soc {
+       unsigned int id;
+       unsigned int devtype;
+       bool online;
+
+       /*clk*/
+       struct clk *ipu_clk;
+       struct clk *di_clk[2];
+       struct clk *di_clk_sel[2];
+       struct clk *pixel_clk[2];
+       bool pixel_clk_en[2];
+       struct clk *pixel_clk_sel[2];
+       struct clk *csi_clk[2];
+       struct clk *prg_clk;
+
+       /*irq*/
+       int irq_sync;
+       int irq_err;
+       struct ipu_irq_node irq_list[IPU_IRQ_COUNT];
+
+       /*reg*/
+       void __iomem *cm_reg;
+       void __iomem *idmac_reg;
+       void __iomem *dp_reg;
+       void __iomem *ic_reg;
+       void __iomem *dc_reg;
+       void __iomem *dc_tmpl_reg;
+       void __iomem *dmfc_reg;
+       void __iomem *di_reg[2];
+       void __iomem *smfc_reg;
+       void __iomem *csi_reg[2];
+       void __iomem *cpmem_base;
+       void __iomem *tpmem_base;
+       void __iomem *vdi_reg;
+
+       struct device *dev;
+
+       ipu_channel_t csi_channel[2];
+       ipu_channel_t using_ic_dirct_ch;
+       unsigned char dc_di_assignment[10];
+       bool sec_chan_en[24];
+       bool thrd_chan_en[24];
+       bool chan_is_interlaced[52];
+       uint32_t channel_init_mask;
+       uint32_t channel_enable_mask;
+
+       /*use count*/
+       int dc_use_count;
+       int dp_use_count;
+       int dmfc_use_count;
+       int smfc_use_count;
+       int ic_use_count;
+       int rot_use_count;
+       int vdi_use_count;
+       int di_use_count[2];
+       int csi_use_count[2];
+
+       struct mutex mutex_lock;
+       spinlock_t int_reg_spin_lock;
+       spinlock_t rdy_reg_spin_lock;
+
+       int dmfc_size_28;
+       int dmfc_size_29;
+       int dmfc_size_24;
+       int dmfc_size_27;
+       int dmfc_size_23;
+
+       enum csc_type_t fg_csc_type;
+       enum csc_type_t bg_csc_type;
+       bool color_key_4rgb;
+       bool dc_swap;
+       struct completion dc_comp;
+       struct completion csi_comp;
+
+       struct rot_mem {
+               void *vaddr;
+               dma_addr_t paddr;
+               int size;
+       } rot_dma[2];
+
+       int     vdoa_en;
+       struct task_struct *thread[2];
+
+       /*
+        * Bypass reset to avoid display channel being
+        * stopped by probe since it may starts to work
+        * in bootloader.
+        */
+       bool bypass_reset;
+
+       unsigned int ch0123_axi;
+       unsigned int ch23_axi;
+       unsigned int ch27_axi;
+       unsigned int ch28_axi;
+       unsigned int normal_axi;
+
+       bool smfc_idmac_12bit_3planar_bs_fixup; /* workaround little stripes */
+};
+
+struct ipu_channel {
+       u8 video_in_dma;
+       u8 alpha_in_dma;
+       u8 graph_in_dma;
+       u8 out_dma;
+};
+
+enum ipu_dmfc_type {
+       DMFC_NORMAL = 0,
+       DMFC_HIGH_RESOLUTION_DC,
+       DMFC_HIGH_RESOLUTION_DP,
+       DMFC_HIGH_RESOLUTION_ONLY_DP,
+};
+
+static inline int _ipu_is_smfc_chan(uint32_t dma_chan)
+{
+       return ((dma_chan >= 0) && (dma_chan <= 3));
+}
+
+static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
+{
+       return readl(ipu->cm_reg + offset);
+}
+
+static inline void ipu_cm_write(struct ipu_soc *ipu,
+               u32 value, unsigned offset)
+{
+       writel(value, ipu->cm_reg + offset);
+}
+
+static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset)
+{
+       return readl(ipu->idmac_reg + offset);
+}
+
+static inline void ipu_idmac_write(struct ipu_soc *ipu,
+               u32 value, unsigned offset)
+{
+       writel(value, ipu->idmac_reg + offset);
+}
+
+static inline u32 ipu_dc_read(struct ipu_soc *ipu, unsigned offset)
+{
+       return readl(ipu->dc_reg + offset);
+}
+
+static inline void ipu_dc_write(struct ipu_soc *ipu,
+               u32 value, unsigned offset)
+{
+       writel(value, ipu->dc_reg + offset);
+}
+
+static inline u32 ipu_dc_tmpl_read(struct ipu_soc *ipu, unsigned offset)
+{
+       return readl(ipu->dc_tmpl_reg + offset);
+}
+
+static inline void ipu_dc_tmpl_write(struct ipu_soc *ipu,
+               u32 value, unsigned offset)
+{
+       writel(value, ipu->dc_tmpl_reg + offset);
+}
+
+static inline u32 ipu_dmfc_read(struct ipu_soc *ipu, unsigned offset)
+{
+       return readl(ipu->dmfc_reg + offset);
+}
+
+static inline void ipu_dmfc_write(struct ipu_soc *ipu,
+               u32 value, unsigned offset)
+{
+       writel(value, ipu->dmfc_reg + offset);
+}
+
+static inline u32 ipu_dp_read(struct ipu_soc *ipu, unsigned offset)
+{
+       return readl(ipu->dp_reg + offset);
+}
+
+static inline void ipu_dp_write(struct ipu_soc *ipu,
+               u32 value, unsigned offset)
+{
+       writel(value, ipu->dp_reg + offset);
+}
+
+static inline u32 ipu_di_read(struct ipu_soc *ipu, int di, unsigned offset)
+{
+       return readl(ipu->di_reg[di] + offset);
+}
+
+static inline void ipu_di_write(struct ipu_soc *ipu, int di,
+               u32 value, unsigned offset)
+{
+       writel(value, ipu->di_reg[di] + offset);
+}
+
+static inline u32 ipu_csi_read(struct ipu_soc *ipu, int csi, unsigned offset)
+{
+       return readl(ipu->csi_reg[csi] + offset);
+}
+
+static inline void ipu_csi_write(struct ipu_soc *ipu, int csi,
+               u32 value, unsigned offset)
+{
+       writel(value, ipu->csi_reg[csi] + offset);
+}
+
+static inline u32 ipu_smfc_read(struct ipu_soc *ipu, unsigned offset)
+{
+       return readl(ipu->smfc_reg + offset);
+}
+
+static inline void ipu_smfc_write(struct ipu_soc *ipu,
+               u32 value, unsigned offset)
+{
+       writel(value, ipu->smfc_reg + offset);
+}
+
+static inline u32 ipu_vdi_read(struct ipu_soc *ipu, unsigned offset)
+{
+       return readl(ipu->vdi_reg + offset);
+}
+
+static inline void ipu_vdi_write(struct ipu_soc *ipu,
+               u32 value, unsigned offset)
+{
+       writel(value, ipu->vdi_reg + offset);
+}
+
+static inline u32 ipu_ic_read(struct ipu_soc *ipu, unsigned offset)
+{
+       return readl(ipu->ic_reg + offset);
+}
+
+static inline void ipu_ic_write(struct ipu_soc *ipu,
+               u32 value, unsigned offset)
+{
+       writel(value, ipu->ic_reg + offset);
+}
+
+int register_ipu_device(struct ipu_soc *ipu, int id);
+void unregister_ipu_device(struct ipu_soc *ipu, int id);
+ipu_color_space_t format_to_colorspace(uint32_t fmt);
+bool ipu_pixel_format_has_alpha(uint32_t fmt);
+
+void ipu_dump_registers(struct ipu_soc *ipu);
+
+uint32_t _ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel);
+
+void ipu_disp_init(struct ipu_soc *ipu);
+void _ipu_init_dc_mappings(struct ipu_soc *ipu);
+int _ipu_dp_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t in_pixel_fmt,
+                uint32_t out_pixel_fmt);
+void _ipu_dp_uninit(struct ipu_soc *ipu, ipu_channel_t channel);
+void _ipu_dc_init(struct ipu_soc *ipu, int dc_chan, int di, bool interlaced, uint32_t pixel_fmt);
+void _ipu_dc_uninit(struct ipu_soc *ipu, int dc_chan);
+void _ipu_dp_dc_enable(struct ipu_soc *ipu, ipu_channel_t channel);
+void _ipu_dp_dc_disable(struct ipu_soc *ipu, ipu_channel_t channel, bool swap);
+void _ipu_dmfc_init(struct ipu_soc *ipu, int dmfc_type, int first);
+void _ipu_dmfc_set_wait4eot(struct ipu_soc *ipu, int dma_chan, int width);
+void _ipu_dmfc_set_burst_size(struct ipu_soc *ipu, int dma_chan, int burst_size);
+int _ipu_disp_chan_is_interlaced(struct ipu_soc *ipu, ipu_channel_t channel);
+
+void _ipu_ic_enable_task(struct ipu_soc *ipu, ipu_channel_t channel);
+void _ipu_ic_disable_task(struct ipu_soc *ipu, ipu_channel_t channel);
+int  _ipu_ic_init_prpvf(struct ipu_soc *ipu, ipu_channel_params_t *params,
+                       bool src_is_csi);
+void _ipu_vdi_init(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params);
+void _ipu_vdi_uninit(struct ipu_soc *ipu);
+void _ipu_ic_uninit_prpvf(struct ipu_soc *ipu);
+void _ipu_ic_init_rotate_vf(struct ipu_soc *ipu, ipu_channel_params_t *params);
+void _ipu_ic_uninit_rotate_vf(struct ipu_soc *ipu);
+void _ipu_ic_init_csi(struct ipu_soc *ipu, ipu_channel_params_t *params);
+void _ipu_ic_uninit_csi(struct ipu_soc *ipu);
+int  _ipu_ic_init_prpenc(struct ipu_soc *ipu, ipu_channel_params_t *params,
+                        bool src_is_csi);
+void _ipu_ic_uninit_prpenc(struct ipu_soc *ipu);
+void _ipu_ic_init_rotate_enc(struct ipu_soc *ipu, ipu_channel_params_t *params);
+void _ipu_ic_uninit_rotate_enc(struct ipu_soc *ipu);
+int  _ipu_ic_init_pp(struct ipu_soc *ipu, ipu_channel_params_t *params);
+void _ipu_ic_uninit_pp(struct ipu_soc *ipu);
+void _ipu_ic_init_rotate_pp(struct ipu_soc *ipu, ipu_channel_params_t *params);
+void _ipu_ic_uninit_rotate_pp(struct ipu_soc *ipu);
+int _ipu_ic_idma_init(struct ipu_soc *ipu, int dma_chan, uint16_t width, uint16_t height,
+                     int burst_size, ipu_rotate_mode_t rot);
+void _ipu_vdi_toggle_top_field_man(struct ipu_soc *ipu);
+int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi);
+int _ipu_csi_set_mipi_di(struct ipu_soc *ipu, uint32_t num, uint32_t di_val, uint32_t csi);
+void ipu_csi_set_test_generator(struct ipu_soc *ipu, bool active, uint32_t r_value,
+               uint32_t g_value, uint32_t b_value,
+               uint32_t pix_clk, uint32_t csi);
+void _ipu_csi_ccir_err_detection_enable(struct ipu_soc *ipu, uint32_t csi);
+void _ipu_csi_ccir_err_detection_disable(struct ipu_soc *ipu, uint32_t csi);
+void _ipu_csi_wait4eof(struct ipu_soc *ipu, ipu_channel_t channel);
+void _ipu_smfc_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t mipi_id, uint32_t csi);
+void _ipu_smfc_set_burst_size(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t bs);
+void _ipu_dp_set_csc_coefficients(struct ipu_soc *ipu, ipu_channel_t channel, int32_t param[][3]);
+int32_t _ipu_disp_set_window_pos(struct ipu_soc *ipu, ipu_channel_t channel,
+               int16_t x_pos, int16_t y_pos);
+int32_t _ipu_disp_get_window_pos(struct ipu_soc *ipu, ipu_channel_t channel,
+               int16_t *x_pos, int16_t *y_pos);
+void _ipu_get(struct ipu_soc *ipu);
+void _ipu_put(struct ipu_soc *ipu);
+
+struct clk *clk_register_mux_pix_clk(struct device *dev, const char *name,
+               const char **parent_names, u8 num_parents, unsigned long flags,
+               u8 ipu_id, u8 di_id, u8 clk_mux_flags);
+struct clk *clk_register_div_pix_clk(struct device *dev, const char *name,
+               const char *parent_name, unsigned long flags,
+               u8 ipu_id, u8 di_id, u8 clk_div_flags);
+struct clk *clk_register_gate_pix_clk(struct device *dev, const char *name,
+               const char *parent_name, unsigned long flags,
+               u8 ipu_id, u8 di_id, u8 clk_gate_flags);
+#endif                         /* __INCLUDE_IPU_PRV_H__ */
diff --git a/drivers/mxc/ipu3/ipu_regs.h b/drivers/mxc/ipu3/ipu_regs.h
new file mode 100644 (file)
index 0000000..79051cf
--- /dev/null
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2005-2015 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
+ */
+
+/*
+ * @file ipu_regs.h
+ *
+ * @brief IPU Register definitions
+ *
+ * @ingroup IPU
+ */
+#ifndef __IPU_REGS_INCLUDED__
+#define __IPU_REGS_INCLUDED__
+
+#include "ipu_prv.h"
+
+#define IPU_MCU_T_DEFAULT      8
+
+/* Register addresses */
+/* IPU Common registers */
+#define IPU_CM_REG(offset)             (offset)
+
+#define IPU_CONF                       IPU_CM_REG(0)
+#define IPU_SRM_PRI1                   IPU_CM_REG(0x00A0)
+#define IPU_SRM_PRI2                   IPU_CM_REG(0x00A4)
+#define IPU_FS_PROC_FLOW1              IPU_CM_REG(0x00A8)
+#define IPU_FS_PROC_FLOW2              IPU_CM_REG(0x00AC)
+#define IPU_FS_PROC_FLOW3              IPU_CM_REG(0x00B0)
+#define IPU_FS_DISP_FLOW1              IPU_CM_REG(0x00B4)
+#define IPU_FS_DISP_FLOW2              IPU_CM_REG(0x00B8)
+#define IPU_SKIP                       IPU_CM_REG(0x00BC)
+#define IPU_DISP_ALT_CONF              IPU_CM_REG(0x00C0)
+#define IPU_DISP_GEN                   IPU_CM_REG(0x00C4)
+#define IPU_DISP_ALT1                  IPU_CM_REG(0x00C8)
+#define IPU_DISP_ALT2                  IPU_CM_REG(0x00CC)
+#define IPU_DISP_ALT3                  IPU_CM_REG(0x00D0)
+#define IPU_DISP_ALT4                  IPU_CM_REG(0x00D4)
+#define IPU_SNOOP                      IPU_CM_REG(0x00D8)
+#define IPU_MEM_RST                    IPU_CM_REG(0x00DC)
+#define IPU_PM                         IPU_CM_REG(0x00E0)
+#define IPU_GPR                                IPU_CM_REG(0x00E4)
+#define IPU_CHA_DB_MODE_SEL(ch)                IPU_CM_REG(0x0150 + 4 * ((ch) / 32))
+#define IPU_ALT_CHA_DB_MODE_SEL(ch)    IPU_CM_REG(0x0168 + 4 * ((ch) / 32))
+/*
+ * IPUv3D doesn't support triple buffer, so point
+ * IPU_CHA_TRB_MODE_SEL, IPU_CHA_TRIPLE_CUR_BUF and
+ * IPU_CHA_BUF2_RDY to readonly
+ * IPU_ALT_CUR_BUF0 for IPUv3D.
+ */
+#define IPU_CHA_TRB_MODE_SEL(type, ch) IPU_CM_REG({(type) >= IPUv3EX ? \
+                                           (0x0178 + 4 * ((ch) / 32)) : \
+                                           (0x012C); })
+#define IPU_CHA_TRIPLE_CUR_BUF(type, ch) IPU_CM_REG({(type) >= IPUv3EX ? \
+                                             (0x0258 + \
+                                              4 * (((ch) * 2) / 32)) : \
+                                             (0x012C); })
+#define IPU_CHA_BUF2_RDY(type, ch)     IPU_CM_REG({(type) >= IPUv3EX ? \
+                                           (0x0288 + 4 * ((ch) / 32)) : \
+                                           (0x012C); })
+#define IPU_CHA_CUR_BUF(type, ch)      IPU_CM_REG({(type) >= IPUv3EX ? \
+                                           (0x023C + 4 * ((ch) / 32)) : \
+                                           (0x0124 + 4 * ((ch) / 32)); })
+#define IPU_ALT_CUR_BUF0(type) IPU_CM_REG({(type) >= IPUv3EX ? \
+                                           (0x0244) : \
+                                           (0x012C); })
+#define IPU_ALT_CUR_BUF1(type) IPU_CM_REG({(type) >= IPUv3EX ? \
+                                           (0x0248) : \
+                                           (0x0130); })
+#define IPU_SRM_STAT(type)     IPU_CM_REG({(type) >= IPUv3EX ? \
+                                           (0x024C) : \
+                                           (0x0134); })
+#define IPU_PROC_TASK_STAT(type)       IPU_CM_REG({(type) >= IPUv3EX ? \
+                                           (0x0250) : \
+                                           (0x0138); })
+#define IPU_DISP_TASK_STAT(type)       IPU_CM_REG({(type) >= IPUv3EX ? \
+                                           (0x0254) : \
+                                           (0x013C); })
+#define IPU_CHA_BUF0_RDY(type, ch)     IPU_CM_REG({(type) >= IPUv3EX ? \
+                                           (0x0268 + 4 * ((ch) / 32)) : \
+                                           (0x0140 + 4 * ((ch) / 32)); })
+#define IPU_CHA_BUF1_RDY(type, ch)     IPU_CM_REG({(type) >= IPUv3EX ? \
+                                           (0x0270 + 4 * ((ch) / 32)) : \
+                                           (0x0148 + 4 * ((ch) / 32)); })
+#define IPU_ALT_CHA_BUF0_RDY(type, ch) IPU_CM_REG({(type) >= IPUv3EX ? \
+                                            (0x0278 + 4 * ((ch) / 32)) : \
+                                            (0x0158 + 4 * ((ch) / 32)); })
+#define IPU_ALT_CHA_BUF1_RDY(type, ch) IPU_CM_REG({(type) >= IPUv3EX ? \
+                                            (0x0280 + 4 * ((ch) / 32)) : \
+                                            (0x0160 + 4 * ((ch) / 32)); })
+
+#define IPU_INT_CTRL(n)                IPU_CM_REG(0x003C + 4 * ((n) - 1))
+#define IPU_INT_STAT(type, n)          IPU_CM_REG({(type) >= IPUv3EX ? \
+                                           (0x0200 + 4 * ((n) - 1)) : \
+                                           (0x00E8 + 4 * ((n) - 1)); })
+
+#define IPUIRQ_2_STATREG(type, irq)    IPU_CM_REG(IPU_INT_STAT((type), 1) + 4 \
+                                                       * ((irq) / 32))
+#define IPUIRQ_2_CTRLREG(irq)  IPU_CM_REG(IPU_INT_CTRL(1) + 4 * ((irq) / 32))
+#define IPUIRQ_2_MASK(irq)     (1UL << ((irq) & 0x1F))
+
+/* IPU VDI registers */
+#define IPU_VDI_REG(offset)    (offset)
+
+#define VDI_FSIZE              IPU_VDI_REG(0)
+#define VDI_C                  IPU_VDI_REG(0x0004)
+
+/* IPU CSI Registers */
+#define IPU_CSI_REG(offset)    (offset)
+
+#define CSI_SENS_CONF          IPU_CSI_REG(0)
+#define CSI_SENS_FRM_SIZE      IPU_CSI_REG(0x0004)
+#define CSI_ACT_FRM_SIZE       IPU_CSI_REG(0x0008)
+#define CSI_OUT_FRM_CTRL       IPU_CSI_REG(0x000C)
+#define CSI_TST_CTRL           IPU_CSI_REG(0x0010)
+#define CSI_CCIR_CODE_1                IPU_CSI_REG(0x0014)
+#define CSI_CCIR_CODE_2                IPU_CSI_REG(0x0018)
+#define CSI_CCIR_CODE_3                IPU_CSI_REG(0x001C)
+#define CSI_MIPI_DI            IPU_CSI_REG(0x0020)
+#define CSI_SKIP               IPU_CSI_REG(0x0024)
+#define CSI_CPD_CTRL           IPU_CSI_REG(0x0028)
+#define CSI_CPD_RC(n)          IPU_CSI_REG(0x002C + 4 * (n))
+#define CSI_CPD_RS(n)          IPU_CSI_REG(0x004C + 4 * (n))
+#define CSI_CPD_GRC(n)         IPU_CSI_REG(0x005C + 4 * (n))
+#define CSI_CPD_GRS(n)         IPU_CSI_REG(0x007C + 4 * (n))
+#define CSI_CPD_GBC(n)         IPU_CSI_REG(0x008C + 4 * (n))
+#define CSI_CPD_GBS(n)         IPU_CSI_REG(0x00AC + 4 * (n))
+#define CSI_CPD_BC(n)          IPU_CSI_REG(0x00BC + 4 * (n))
+#define CSI_CPD_BS(n)          IPU_CSI_REG(0x00DC + 4 * (n))
+#define CSI_CPD_OFFSET1                IPU_CSI_REG(0x00EC)
+#define CSI_CPD_OFFSET2                IPU_CSI_REG(0x00F0)
+
+/* IPU SMFC Registers */
+#define IPU_SMFC_REG(offset)   (offset)
+
+#define SMFC_MAP               IPU_SMFC_REG(0)
+#define SMFC_WMC               IPU_SMFC_REG(0x0004)
+#define SMFC_BS                        IPU_SMFC_REG(0x0008)
+
+/* IPU IC Registers */
+#define IPU_IC_REG(offset)     (offset)
+
+#define IC_CONF                        IPU_IC_REG(0)
+#define IC_PRP_ENC_RSC         IPU_IC_REG(0x0004)
+#define IC_PRP_VF_RSC          IPU_IC_REG(0x0008)
+#define IC_PP_RSC              IPU_IC_REG(0x000C)
+#define IC_CMBP_1              IPU_IC_REG(0x0010)
+#define IC_CMBP_2              IPU_IC_REG(0x0014)
+#define IC_IDMAC_1             IPU_IC_REG(0x0018)
+#define IC_IDMAC_2             IPU_IC_REG(0x001C)
+#define IC_IDMAC_3             IPU_IC_REG(0x0020)
+#define IC_IDMAC_4             IPU_IC_REG(0x0024)
+
+/* IPU IDMAC Registers */
+#define IPU_IDMAC_REG(offset)  (offset)
+
+#define IDMAC_CONF             IPU_IDMAC_REG(0x0000)
+#define IDMAC_CHA_EN(ch)       IPU_IDMAC_REG(0x0004 + 4 * ((ch) / 32))
+#define IDMAC_SEP_ALPHA                IPU_IDMAC_REG(0x000C)
+#define IDMAC_ALT_SEP_ALPHA    IPU_IDMAC_REG(0x0010)
+#define IDMAC_CHA_PRI(ch)      IPU_IDMAC_REG(0x0014 + 4 * ((ch) / 32))
+#define IDMAC_WM_EN(ch)                IPU_IDMAC_REG(0x001C + 4 * ((ch) / 32))
+#define IDMAC_CH_LOCK_EN_1(type)       IPU_IDMAC_REG({(type) >= IPUv3EX ? \
+                                              (0x0024) : 0; })
+#define IDMAC_CH_LOCK_EN_2(type)       IPU_IDMAC_REG({(type) >= IPUv3EX ? \
+                                              (0x0028) : \
+                                              (0x0024); })
+#define IDMAC_SUB_ADDR_0(type) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
+                                              (0x002C) : \
+                                              (0x0028); })
+#define IDMAC_SUB_ADDR_1(type) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
+                                              (0x0030) : \
+                                              (0x002C); })
+#define IDMAC_SUB_ADDR_2(type) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
+                                              (0x0034) : \
+                                              (0x0030); })
+/*
+ * IPUv3D doesn't support IDMAC_SUB_ADDR_3 and IDMAC_SUB_ADDR_4,
+ * so point them to readonly IDMAC_CHA_BUSY1 for IPUv3D.
+ */
+#define IDMAC_SUB_ADDR_3(type) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
+                                              (0x0038) : \
+                                              (0x0040); })
+#define IDMAC_SUB_ADDR_4(type) IPU_IDMAC_REG({(type) >= IPUv3EX ? \
+                                              (0x003C) : \
+                                              (0x0040); })
+#define IDMAC_BAND_EN(type, ch)        IPU_IDMAC_REG({(type) >= IPUv3EX ? \
+                                              (0x0040 + 4 * ((ch) / 32)) : \
+                                              (0x0034 + 4 * ((ch) / 32)); })
+#define IDMAC_CHA_BUSY(type, ch)       IPU_IDMAC_REG({(type) >= IPUv3EX ? \
+                                              (0x0100 + 4 * ((ch) / 32)) : \
+                                              (0x0040 + 4 * ((ch) / 32)); })
+
+/* IPU DI Registers */
+#define IPU_DI_REG(offset)     (offset)
+
+#define DI_GENERAL             IPU_DI_REG(0)
+#define DI_BS_CLKGEN0          IPU_DI_REG(0x0004)
+#define DI_BS_CLKGEN1          IPU_DI_REG(0x0008)
+#define DI_SW_GEN0(gen)                IPU_DI_REG(0x000C + 4 * ((gen) - 1))
+#define DI_SW_GEN1(gen)                IPU_DI_REG(0x0030 + 4 * ((gen) - 1))
+#define DI_STP_REP(gen)                IPU_DI_REG(0x0148 + 4 * (((gen) - 1) / 2))
+#define DI_SYNC_AS_GEN         IPU_DI_REG(0x0054)
+#define DI_DW_GEN(gen)         IPU_DI_REG(0x0058 + 4 * (gen))
+#define DI_DW_SET(gen, set)    IPU_DI_REG(0x0088 + 4 * ((gen) + 0xC * (set)))
+#define DI_SER_CONF            IPU_DI_REG(0x015C)
+#define DI_SSC                 IPU_DI_REG(0x0160)
+#define DI_POL                 IPU_DI_REG(0x0164)
+#define DI_AW0                 IPU_DI_REG(0x0168)
+#define DI_AW1                 IPU_DI_REG(0x016C)
+#define DI_SCR_CONF            IPU_DI_REG(0x0170)
+#define DI_STAT                        IPU_DI_REG(0x0174)
+
+/* IPU DMFC Registers */
+#define IPU_DMFC_REG(offset)   (offset)
+
+#define DMFC_RD_CHAN           IPU_DMFC_REG(0)
+#define DMFC_WR_CHAN           IPU_DMFC_REG(0x0004)
+#define DMFC_WR_CHAN_DEF       IPU_DMFC_REG(0x0008)
+#define DMFC_DP_CHAN           IPU_DMFC_REG(0x000C)
+#define DMFC_DP_CHAN_DEF       IPU_DMFC_REG(0x0010)
+#define DMFC_GENERAL1          IPU_DMFC_REG(0x0014)
+#define DMFC_GENERAL2          IPU_DMFC_REG(0x0018)
+#define DMFC_IC_CTRL           IPU_DMFC_REG(0x001C)
+#define DMFC_STAT              IPU_DMFC_REG(0x0020)
+
+/* IPU DC Registers */
+#define IPU_DC_REG(offset)     (offset)
+
+#define DC_MAP_CONF_PTR(n)     IPU_DC_REG(0x0108 + ((n) & ~0x1) * 2)
+#define DC_MAP_CONF_VAL(n)     IPU_DC_REG(0x0144 + ((n) & ~0x1) * 2)
+
+#define _RL_CH_2_OFFSET(ch)    (((ch) == 0) ? 8 : ( \
+                                ((ch) == 1) ? 0x24 : ( \
+                                ((ch) == 2) ? 0x40 : ( \
+                                ((ch) == 5) ? 0x64 : ( \
+                                ((ch) == 6) ? 0x80 : ( \
+                                ((ch) == 8) ? 0x9C : ( \
+                                ((ch) == 9) ? 0xBC : (-1))))))))
+#define DC_RL_CH(ch, evt)      IPU_DC_REG(_RL_CH_2_OFFSET(ch) + \
+                                          ((evt) & ~0x1) * 2)
+
+#define DC_EVT_NF              0
+#define DC_EVT_NL              1
+#define DC_EVT_EOF             2
+#define DC_EVT_NFIELD          3
+#define DC_EVT_EOL             4
+#define DC_EVT_EOFIELD         5
+#define DC_EVT_NEW_ADDR                6
+#define DC_EVT_NEW_CHAN                7
+#define DC_EVT_NEW_DATA                8
+
+#define DC_EVT_NEW_ADDR_W_0    0
+#define DC_EVT_NEW_ADDR_W_1    1
+#define DC_EVT_NEW_CHAN_W_0    2
+#define DC_EVT_NEW_CHAN_W_1    3
+#define DC_EVT_NEW_DATA_W_0    4
+#define DC_EVT_NEW_DATA_W_1    5
+#define DC_EVT_NEW_ADDR_R_0    6
+#define DC_EVT_NEW_ADDR_R_1    7
+#define DC_EVT_NEW_CHAN_R_0    8
+#define DC_EVT_NEW_CHAN_R_1    9
+#define DC_EVT_NEW_DATA_R_0    10
+#define DC_EVT_NEW_DATA_R_1    11
+#define DC_EVEN_UGDE0          12
+#define DC_ODD_UGDE0           13
+#define DC_EVEN_UGDE1          14
+#define DC_ODD_UGDE1           15
+#define DC_EVEN_UGDE2          16
+#define DC_ODD_UGDE2           17
+#define DC_EVEN_UGDE3          18
+#define DC_ODD_UGDE3           19
+
+#define dc_ch_offset(ch) \
+({ \
+       const u8 _offset[] = { \
+               0, 0x1C, 0x38, 0x54, 0x58, 0x5C, 0x78, 0, 0x94, 0xB4}; \
+       _offset[ch]; \
+})
+#define DC_WR_CH_CONF(ch)      IPU_DC_REG(dc_ch_offset(ch))
+#define DC_WR_CH_ADDR(ch)      IPU_DC_REG(dc_ch_offset(ch) + 4)
+
+#define DC_WR_CH_CONF_1                IPU_DC_REG(0x001C)
+#define DC_WR_CH_ADDR_1                IPU_DC_REG(0x0020)
+#define DC_WR_CH_CONF_5                IPU_DC_REG(0x005C)
+#define DC_WR_CH_ADDR_5                IPU_DC_REG(0x0060)
+#define DC_GEN                 IPU_DC_REG(0x00D4)
+#define DC_DISP_CONF1(disp)    IPU_DC_REG(0x00D8 + 4 * (disp))
+#define DC_DISP_CONF2(disp)    IPU_DC_REG(0x00E8 + 4 * (disp))
+#define DC_STAT                        IPU_DC_REG(0x01C8)
+#define DC_UGDE_0(evt)         IPU_DC_REG(0x0174 + 16 * (evt))
+#define DC_UGDE_1(evt)         IPU_DC_REG(0x0178 + 16 * (evt))
+#define DC_UGDE_2(evt)         IPU_DC_REG(0x017C + 16 * (evt))
+#define DC_UGDE_3(evt)         IPU_DC_REG(0x0180 + 16 * (evt))
+
+/* IPU DP Registers */
+#define IPU_DP_REG(offset)             (offset)
+
+#define DP_SYNC                                0
+#define DP_ASYNC0                      0x60
+#define DP_ASYNC1                      0xBC
+#define DP_COM_CONF(flow)              IPU_DP_REG(flow)
+#define DP_GRAPH_WIND_CTRL(flow)       IPU_DP_REG(0x0004 + (flow))
+#define DP_FG_POS(flow)                        IPU_DP_REG(0x0008 + (flow))
+#define DP_GAMMA_C(flow, i)            IPU_DP_REG(0x0014 + (flow) + 4 * (i))
+#define DP_GAMMA_S(flow, i)            IPU_DP_REG(0x0034 + (flow) + 4 * (i))
+#define DP_CSC_A_0(flow)               IPU_DP_REG(0x0044 + (flow))
+#define DP_CSC_A_1(flow)               IPU_DP_REG(0x0048 + (flow))
+#define DP_CSC_A_2(flow)               IPU_DP_REG(0x004C + (flow))
+#define DP_CSC_A_3(flow)               IPU_DP_REG(0x0050 + (flow))
+#define DP_CSC_0(flow)                 IPU_DP_REG(0x0054 + (flow))
+#define DP_CSC_1(flow)                 IPU_DP_REG(0x0058 + (flow))
+
+enum {
+       IPU_CONF_CSI0_EN = 0x00000001,
+       IPU_CONF_CSI1_EN = 0x00000002,
+       IPU_CONF_IC_EN = 0x00000004,
+       IPU_CONF_ROT_EN = 0x00000008,
+       IPU_CONF_ISP_EN = 0x00000010,
+       IPU_CONF_DP_EN = 0x00000020,
+       IPU_CONF_DI0_EN = 0x00000040,
+       IPU_CONF_DI1_EN = 0x00000080,
+       IPU_CONF_DMFC_EN = 0x00000400,
+       IPU_CONF_SMFC_EN = 0x00000100,
+       IPU_CONF_DC_EN = 0x00000200,
+       IPU_CONF_VDI_EN = 0x00001000,
+       IPU_CONF_IDMAC_DIS = 0x00400000,
+       IPU_CONF_IC_DMFC_SEL = 0x02000000,
+       IPU_CONF_IC_DMFC_SYNC = 0x04000000,
+       IPU_CONF_VDI_DMFC_SYNC = 0x08000000,
+       IPU_CONF_CSI0_DATA_SOURCE = 0x10000000,
+       IPU_CONF_CSI0_DATA_SOURCE_OFFSET = 28,
+       IPU_CONF_CSI1_DATA_SOURCE = 0x20000000,
+       IPU_CONF_IC_INPUT = 0x40000000,
+       IPU_CONF_CSI_SEL = 0x80000000,
+
+       DI0_COUNTER_RELEASE = 0x01000000,
+       DI1_COUNTER_RELEASE = 0x02000000,
+
+       FS_PRPVF_ROT_SRC_SEL_MASK = 0x00000F00,
+       FS_PRPVF_ROT_SRC_SEL_OFFSET = 8,
+       FS_PRPENC_ROT_SRC_SEL_MASK = 0x0000000F,
+       FS_PRPENC_ROT_SRC_SEL_OFFSET = 0,
+       FS_PP_ROT_SRC_SEL_MASK = 0x000F0000,
+       FS_PP_ROT_SRC_SEL_OFFSET = 16,
+       FS_PP_SRC_SEL_MASK = 0x0000F000,
+       FS_PP_SRC_SEL_VDOA = 0x00008000,
+       FS_PP_SRC_SEL_OFFSET = 12,
+       FS_PRP_SRC_SEL_MASK = 0x0F000000,
+       FS_PRP_SRC_SEL_OFFSET = 24,
+       FS_VF_IN_VALID = 0x80000000,
+       FS_ENC_IN_VALID = 0x40000000,
+       FS_VDI_SRC_SEL_MASK = 0x30000000,
+       FS_VDI_SRC_SEL_VDOA = 0x20000000,
+       FS_VDOA_DEST_SEL_MASK = 0x00030000,
+       FS_VDOA_DEST_SEL_VDI = 0x00020000,
+       FS_VDOA_DEST_SEL_IC = 0x00010000,
+       FS_VDI_SRC_SEL_OFFSET = 28,
+
+
+       FS_PRPENC_DEST_SEL_MASK = 0x0000000F,
+       FS_PRPENC_DEST_SEL_OFFSET = 0,
+       FS_PRPVF_DEST_SEL_MASK = 0x000000F0,
+       FS_PRPVF_DEST_SEL_OFFSET = 4,
+       FS_PRPVF_ROT_DEST_SEL_MASK = 0x00000F00,
+       FS_PRPVF_ROT_DEST_SEL_OFFSET = 8,
+       FS_PP_DEST_SEL_MASK = 0x0000F000,
+       FS_PP_DEST_SEL_OFFSET = 12,
+       FS_PP_ROT_DEST_SEL_MASK = 0x000F0000,
+       FS_PP_ROT_DEST_SEL_OFFSET = 16,
+       FS_PRPENC_ROT_DEST_SEL_MASK = 0x00F00000,
+       FS_PRPENC_ROT_DEST_SEL_OFFSET = 20,
+
+       FS_SMFC0_DEST_SEL_MASK = 0x0000000F,
+       FS_SMFC0_DEST_SEL_OFFSET = 0,
+       FS_SMFC1_DEST_SEL_MASK = 0x00000070,
+       FS_SMFC1_DEST_SEL_OFFSET = 4,
+       FS_SMFC2_DEST_SEL_MASK = 0x00000780,
+       FS_SMFC2_DEST_SEL_OFFSET = 7,
+       FS_SMFC3_DEST_SEL_MASK = 0x00003800,
+       FS_SMFC3_DEST_SEL_OFFSET = 11,
+
+       FS_DC1_SRC_SEL_MASK = 0x00F00000,
+       FS_DC1_SRC_SEL_OFFSET = 20,
+       FS_DC2_SRC_SEL_MASK = 0x000F0000,
+       FS_DC2_SRC_SEL_OFFSET = 16,
+       FS_DP_SYNC0_SRC_SEL_MASK = 0x0000000F,
+       FS_DP_SYNC0_SRC_SEL_OFFSET = 0,
+       FS_DP_SYNC1_SRC_SEL_MASK = 0x000000F0,
+       FS_DP_SYNC1_SRC_SEL_OFFSET = 4,
+       FS_DP_ASYNC0_SRC_SEL_MASK = 0x00000F00,
+       FS_DP_ASYNC0_SRC_SEL_OFFSET = 8,
+       FS_DP_ASYNC1_SRC_SEL_MASK = 0x0000F000,
+       FS_DP_ASYNC1_SRC_SEL_OFFSET = 12,
+
+       FS_AUTO_REF_PER_MASK = 0,
+       FS_AUTO_REF_PER_OFFSET = 16,
+
+       TSTAT_VF_MASK = 0x0000000C,
+       TSTAT_VF_OFFSET = 2,
+       TSTAT_VF_ROT_MASK = 0x00000300,
+       TSTAT_VF_ROT_OFFSET = 8,
+       TSTAT_ENC_MASK = 0x00000003,
+       TSTAT_ENC_OFFSET = 0,
+       TSTAT_ENC_ROT_MASK = 0x000000C0,
+       TSTAT_ENC_ROT_OFFSET = 6,
+       TSTAT_PP_MASK = 0x00000030,
+       TSTAT_PP_OFFSET = 4,
+       TSTAT_PP_ROT_MASK = 0x00000C00,
+       TSTAT_PP_ROT_OFFSET = 10,
+
+       TASK_STAT_IDLE = 0,
+       TASK_STAT_ACTIVE = 1,
+       TASK_STAT_WAIT4READY = 2,
+
+       /* IDMAC register bits */
+       IDMAC_CONF_USED_BUFS_EN_R = 0x02000000,
+       IDMAC_CONF_USED_BUFS_MAX_R_MASK = 0x01E00000,
+       IDMAC_CONF_USED_BUFS_MAX_R_OFFSET = 21,
+       IDMAC_CONF_USED_BUFS_EN_W = 0x00100000,
+       IDMAC_CONF_USED_BUFS_MAX_W_MASK = 0x000E0000,
+       IDMAC_CONF_USED_BUFS_MAX_W_OFFSET = 17,
+
+       /* Image Converter Register bits */
+       IC_CONF_PRPENC_EN = 0x00000001,
+       IC_CONF_PRPENC_CSC1 = 0x00000002,
+       IC_CONF_PRPENC_ROT_EN = 0x00000004,
+       IC_CONF_PRPVF_EN = 0x00000100,
+       IC_CONF_PRPVF_CSC1 = 0x00000200,
+       IC_CONF_PRPVF_CSC2 = 0x00000400,
+       IC_CONF_PRPVF_CMB = 0x00000800,
+       IC_CONF_PRPVF_ROT_EN = 0x00001000,
+       IC_CONF_PP_EN = 0x00010000,
+       IC_CONF_PP_CSC1 = 0x00020000,
+       IC_CONF_PP_CSC2 = 0x00040000,
+       IC_CONF_PP_CMB = 0x00080000,
+       IC_CONF_PP_ROT_EN = 0x00100000,
+       IC_CONF_IC_GLB_LOC_A = 0x10000000,
+       IC_CONF_KEY_COLOR_EN = 0x20000000,
+       IC_CONF_RWS_EN = 0x40000000,
+       IC_CONF_CSI_MEM_WR_EN = 0x80000000,
+
+       IC_RSZ_MAX_RESIZE_RATIO = 0x00004000,
+
+       IC_IDMAC_1_CB0_BURST_16 = 0x00000001,
+       IC_IDMAC_1_CB1_BURST_16 = 0x00000002,
+       IC_IDMAC_1_CB2_BURST_16 = 0x00000004,
+       IC_IDMAC_1_CB3_BURST_16 = 0x00000008,
+       IC_IDMAC_1_CB4_BURST_16 = 0x00000010,
+       IC_IDMAC_1_CB5_BURST_16 = 0x00000020,
+       IC_IDMAC_1_CB6_BURST_16 = 0x00000040,
+       IC_IDMAC_1_CB7_BURST_16 = 0x00000080,
+       IC_IDMAC_1_PRPENC_ROT_MASK = 0x00003800,
+       IC_IDMAC_1_PRPENC_ROT_OFFSET = 11,
+       IC_IDMAC_1_PRPVF_ROT_MASK = 0x0001C000,
+       IC_IDMAC_1_PRPVF_ROT_OFFSET = 14,
+       IC_IDMAC_1_PP_ROT_MASK = 0x000E0000,
+       IC_IDMAC_1_PP_ROT_OFFSET = 17,
+       IC_IDMAC_1_PP_FLIP_RS = 0x00400000,
+       IC_IDMAC_1_PRPVF_FLIP_RS = 0x00200000,
+       IC_IDMAC_1_PRPENC_FLIP_RS = 0x00100000,
+
+       IC_IDMAC_2_PRPENC_HEIGHT_MASK = 0x000003FF,
+       IC_IDMAC_2_PRPENC_HEIGHT_OFFSET = 0,
+       IC_IDMAC_2_PRPVF_HEIGHT_MASK = 0x000FFC00,
+       IC_IDMAC_2_PRPVF_HEIGHT_OFFSET = 10,
+       IC_IDMAC_2_PP_HEIGHT_MASK = 0x3FF00000,
+       IC_IDMAC_2_PP_HEIGHT_OFFSET = 20,
+
+       IC_IDMAC_3_PRPENC_WIDTH_MASK = 0x000003FF,
+       IC_IDMAC_3_PRPENC_WIDTH_OFFSET = 0,
+       IC_IDMAC_3_PRPVF_WIDTH_MASK = 0x000FFC00,
+       IC_IDMAC_3_PRPVF_WIDTH_OFFSET = 10,
+       IC_IDMAC_3_PP_WIDTH_MASK = 0x3FF00000,
+       IC_IDMAC_3_PP_WIDTH_OFFSET = 20,
+
+       CSI_SENS_CONF_DATA_FMT_SHIFT = 8,
+       CSI_SENS_CONF_DATA_FMT_MASK = 0x00000700,
+       CSI_SENS_CONF_DATA_FMT_RGB_YUV444 = 0L,
+       CSI_SENS_CONF_DATA_FMT_YUV422_YUYV = 1L,
+       CSI_SENS_CONF_DATA_FMT_YUV422_UYVY = 2L,
+       CSI_SENS_CONF_DATA_FMT_BAYER = 3L,
+       CSI_SENS_CONF_DATA_FMT_RGB565 = 4L,
+       CSI_SENS_CONF_DATA_FMT_RGB555 = 5L,
+       CSI_SENS_CONF_DATA_FMT_RGB444 = 6L,
+       CSI_SENS_CONF_DATA_FMT_JPEG = 7L,
+
+       CSI_SENS_CONF_VSYNC_POL_SHIFT = 0,
+       CSI_SENS_CONF_HSYNC_POL_SHIFT = 1,
+       CSI_SENS_CONF_DATA_POL_SHIFT = 2,
+       CSI_SENS_CONF_PIX_CLK_POL_SHIFT = 3,
+       CSI_SENS_CONF_SENS_PRTCL_MASK = 0x00000070L,
+       CSI_SENS_CONF_SENS_PRTCL_SHIFT = 4,
+       CSI_SENS_CONF_PACK_TIGHT_SHIFT = 7,
+       CSI_SENS_CONF_DATA_WIDTH_SHIFT = 11,
+       CSI_SENS_CONF_EXT_VSYNC_SHIFT = 15,
+       CSI_SENS_CONF_DIVRATIO_SHIFT = 16,
+
+       CSI_SENS_CONF_DIVRATIO_MASK = 0x00FF0000L,
+       CSI_SENS_CONF_DATA_DEST_SHIFT = 24,
+       CSI_SENS_CONF_DATA_DEST_MASK = 0x07000000L,
+       CSI_SENS_CONF_JPEG8_EN_SHIFT = 27,
+       CSI_SENS_CONF_JPEG_EN_SHIFT = 28,
+       CSI_SENS_CONF_FORCE_EOF_SHIFT = 29,
+       CSI_SENS_CONF_DATA_EN_POL_SHIFT = 31,
+
+       CSI_DATA_DEST_ISP = 1L,
+       CSI_DATA_DEST_IC = 2L,
+       CSI_DATA_DEST_IDMAC = 4L,
+
+       CSI_CCIR_ERR_DET_EN = 0x01000000L,
+       CSI_HORI_DOWNSIZE_EN = 0x80000000L,
+       CSI_VERT_DOWNSIZE_EN = 0x40000000L,
+       CSI_TEST_GEN_MODE_EN = 0x01000000L,
+
+       CSI_HSC_MASK = 0x1FFF0000,
+       CSI_HSC_SHIFT = 16,
+       CSI_VSC_MASK = 0x00000FFF,
+       CSI_VSC_SHIFT = 0,
+
+       CSI_TEST_GEN_R_MASK = 0x000000FFL,
+       CSI_TEST_GEN_R_SHIFT = 0,
+       CSI_TEST_GEN_G_MASK = 0x0000FF00L,
+       CSI_TEST_GEN_G_SHIFT = 8,
+       CSI_TEST_GEN_B_MASK = 0x00FF0000L,
+       CSI_TEST_GEN_B_SHIFT = 16,
+
+       CSI_MIPI_DI0_MASK = 0x000000FFL,
+       CSI_MIPI_DI0_SHIFT = 0,
+       CSI_MIPI_DI1_MASK = 0x0000FF00L,
+       CSI_MIPI_DI1_SHIFT = 8,
+       CSI_MIPI_DI2_MASK = 0x00FF0000L,
+       CSI_MIPI_DI2_SHIFT = 16,
+       CSI_MIPI_DI3_MASK = 0xFF000000L,
+       CSI_MIPI_DI3_SHIFT = 24,
+
+       CSI_MAX_RATIO_SKIP_ISP_MASK = 0x00070000L,
+       CSI_MAX_RATIO_SKIP_ISP_SHIFT = 16,
+       CSI_SKIP_ISP_MASK = 0x00F80000L,
+       CSI_SKIP_ISP_SHIFT = 19,
+       CSI_MAX_RATIO_SKIP_SMFC_MASK = 0x00000007L,
+       CSI_MAX_RATIO_SKIP_SMFC_SHIFT = 0,
+       CSI_SKIP_SMFC_MASK = 0x000000F8L,
+       CSI_SKIP_SMFC_SHIFT = 3,
+       CSI_ID_2_SKIP_MASK = 0x00000300L,
+       CSI_ID_2_SKIP_SHIFT = 8,
+
+       CSI_COLOR_FIRST_ROW_MASK = 0x00000002L,
+       CSI_COLOR_FIRST_COMP_MASK = 0x00000001L,
+
+       SMFC_MAP_CH0_MASK = 0x00000007L,
+       SMFC_MAP_CH0_SHIFT = 0,
+       SMFC_MAP_CH1_MASK = 0x00000038L,
+       SMFC_MAP_CH1_SHIFT = 3,
+       SMFC_MAP_CH2_MASK = 0x000001C0L,
+       SMFC_MAP_CH2_SHIFT = 6,
+       SMFC_MAP_CH3_MASK = 0x00000E00L,
+       SMFC_MAP_CH3_SHIFT = 9,
+
+       SMFC_WM0_SET_MASK = 0x00000007L,
+       SMFC_WM0_SET_SHIFT = 0,
+       SMFC_WM1_SET_MASK = 0x000001C0L,
+       SMFC_WM1_SET_SHIFT = 6,
+       SMFC_WM2_SET_MASK = 0x00070000L,
+       SMFC_WM2_SET_SHIFT = 16,
+       SMFC_WM3_SET_MASK = 0x01C00000L,
+       SMFC_WM3_SET_SHIFT = 22,
+
+       SMFC_WM0_CLR_MASK = 0x00000038L,
+       SMFC_WM0_CLR_SHIFT = 3,
+       SMFC_WM1_CLR_MASK = 0x00000E00L,
+       SMFC_WM1_CLR_SHIFT = 9,
+       SMFC_WM2_CLR_MASK = 0x00380000L,
+       SMFC_WM2_CLR_SHIFT = 19,
+       SMFC_WM3_CLR_MASK = 0x0E000000L,
+       SMFC_WM3_CLR_SHIFT = 25,
+
+       SMFC_BS0_MASK = 0x0000000FL,
+       SMFC_BS0_SHIFT = 0,
+       SMFC_BS1_MASK = 0x000000F0L,
+       SMFC_BS1_SHIFT = 4,
+       SMFC_BS2_MASK = 0x00000F00L,
+       SMFC_BS2_SHIFT = 8,
+       SMFC_BS3_MASK = 0x0000F000L,
+       SMFC_BS3_SHIFT = 12,
+
+       PF_CONF_TYPE_MASK = 0x00000007,
+       PF_CONF_TYPE_SHIFT = 0,
+       PF_CONF_PAUSE_EN = 0x00000010,
+       PF_CONF_RESET = 0x00008000,
+       PF_CONF_PAUSE_ROW_MASK = 0x00FF0000,
+       PF_CONF_PAUSE_ROW_SHIFT = 16,
+
+       DI_DW_GEN_ACCESS_SIZE_OFFSET = 24,
+       DI_DW_GEN_COMPONENT_SIZE_OFFSET = 16,
+
+       DI_GEN_DI_CLK_EXT = 0x100000,
+       DI_GEN_POLARITY_DISP_CLK = 0x00020000,
+       DI_GEN_POLARITY_1 = 0x00000001,
+       DI_GEN_POLARITY_2 = 0x00000002,
+       DI_GEN_POLARITY_3 = 0x00000004,
+       DI_GEN_POLARITY_4 = 0x00000008,
+       DI_GEN_POLARITY_5 = 0x00000010,
+       DI_GEN_POLARITY_6 = 0x00000020,
+       DI_GEN_POLARITY_7 = 0x00000040,
+       DI_GEN_POLARITY_8 = 0x00000080,
+
+       DI_POL_DRDY_DATA_POLARITY = 0x00000080,
+       DI_POL_DRDY_POLARITY_15 = 0x00000010,
+
+       DI_VSYNC_SEL_OFFSET = 13,
+
+       DC_WR_CH_CONF_FIELD_MODE = 0x00000200,
+       DC_WR_CH_CONF_PROG_TYPE_OFFSET = 5,
+       DC_WR_CH_CONF_PROG_TYPE_MASK = 0x000000E0,
+       DC_WR_CH_CONF_PROG_DI_ID = 0x00000004,
+       DC_WR_CH_CONF_PROG_DISP_ID_OFFSET = 3,
+       DC_WR_CH_CONF_PROG_DISP_ID_MASK = 0x00000018,
+
+       DC_UGDE_0_ODD_EN = 0x02000000,
+       DC_UGDE_0_ID_CODED_MASK = 0x00000007,
+       DC_UGDE_0_ID_CODED_OFFSET = 0,
+       DC_UGDE_0_EV_PRIORITY_MASK = 0x00000078,
+       DC_UGDE_0_EV_PRIORITY_OFFSET = 3,
+
+       DP_COM_CONF_FG_EN = 0x00000001,
+       DP_COM_CONF_GWSEL = 0x00000002,
+       DP_COM_CONF_GWAM = 0x00000004,
+       DP_COM_CONF_GWCKE = 0x00000008,
+       DP_COM_CONF_CSC_DEF_MASK = 0x00000300,
+       DP_COM_CONF_CSC_DEF_OFFSET = 8,
+       DP_COM_CONF_CSC_DEF_FG = 0x00000300,
+       DP_COM_CONF_CSC_DEF_BG = 0x00000200,
+       DP_COM_CONF_CSC_DEF_BOTH = 0x00000100,
+       DP_COM_CONF_GAMMA_EN = 0x00001000,
+       DP_COM_CONF_GAMMA_YUV_EN = 0x00002000,
+
+       DI_SER_CONF_LLA_SER_ACCESS = 0x00000020,
+       DI_SER_CONF_SERIAL_CLK_POL = 0x00000010,
+       DI_SER_CONF_SERIAL_DATA_POL = 0x00000008,
+       DI_SER_CONF_SERIAL_RS_POL = 0x00000004,
+       DI_SER_CONF_SERIAL_CS_POL = 0x00000002,
+       DI_SER_CONF_WAIT4SERIAL = 0x00000001,
+
+       VDI_C_CH_420 = 0x00000000,
+       VDI_C_CH_422 = 0x00000002,
+       VDI_C_MOT_SEL_FULL = 0x00000008,
+       VDI_C_MOT_SEL_LOW = 0x00000004,
+       VDI_C_MOT_SEL_MED = 0x00000000,
+       VDI_C_BURST_SIZE1_4 = 0x00000030,
+       VDI_C_BURST_SIZE2_4 = 0x00000300,
+       VDI_C_BURST_SIZE3_4 = 0x00003000,
+       VDI_C_BURST_SIZE_MASK = 0xF,
+       VDI_C_BURST_SIZE1_OFFSET = 4,
+       VDI_C_BURST_SIZE2_OFFSET = 8,
+       VDI_C_BURST_SIZE3_OFFSET = 12,
+       VDI_C_VWM1_SET_1 = 0x00000000,
+       VDI_C_VWM1_SET_2 = 0x00010000,
+       VDI_C_VWM1_CLR_2 = 0x00080000,
+       VDI_C_VWM3_SET_1 = 0x00000000,
+       VDI_C_VWM3_SET_2 = 0x00400000,
+       VDI_C_VWM3_CLR_2 = 0x02000000,
+       VDI_C_TOP_FIELD_MAN_1 = 0x40000000,
+       VDI_C_TOP_FIELD_AUTO_1 = 0x80000000,
+};
+
+enum di_pins {
+       DI_PIN11 = 0,
+       DI_PIN12 = 1,
+       DI_PIN13 = 2,
+       DI_PIN14 = 3,
+       DI_PIN15 = 4,
+       DI_PIN16 = 5,
+       DI_PIN17 = 6,
+       DI_PIN_CS = 7,
+
+       DI_PIN_SER_CLK = 0,
+       DI_PIN_SER_RS = 1,
+};
+
+enum di_sync_wave {
+       DI_SYNC_NONE = -1,
+       DI_SYNC_CLK = 0,
+       DI_SYNC_INT_HSYNC = 1,
+       DI_SYNC_HSYNC = 2,
+       DI_SYNC_VSYNC = 3,
+       DI_SYNC_DE = 5,
+};
+
+/* DC template opcodes */
+#define WROD(lf)               (0x18 | (lf << 1))
+#define WRG                    (0x01)
+
+#endif
diff --git a/drivers/mxc/ipu3/pre-regs.h b/drivers/mxc/ipu3/pre-regs.h
new file mode 100644 (file)
index 0000000..06a9b02
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * Freescale PRE Register Definitions
+ *
+ * Copyright 2014-2015 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
+ */
+
+#ifndef __ARCH_ARM___PRE_H
+#define __ARCH_ARM___PRE_H
+
+
+#define HW_PRE_CTRL    (0x00000000)
+#define HW_PRE_CTRL_SET        (0x00000004)
+#define HW_PRE_CTRL_CLR        (0x00000008)
+#define HW_PRE_CTRL_TOG        (0x0000000c)
+
+#define BM_PRE_CTRL_SFTRST 0x80000000
+#define BF_PRE_CTRL_SFTRST(v) \
+       (((v) << 31) & BM_PRE_CTRL_SFTRST)
+#define BM_PRE_CTRL_CLKGATE 0x40000000
+#define BF_PRE_CTRL_CLKGATE(v)  \
+       (((v) << 30) & BM_PRE_CTRL_CLKGATE)
+#define BM_PRE_CTRL_TPR_RESET_SEL 0x20000000
+#define BF_PRE_CTRL_TPR_RESET_SEL(v)  \
+       (((v) << 29) & BM_PRE_CTRL_TPR_RESET_SEL)
+#define BM_PRE_CTRL_EN_REPEAT 0x10000000
+#define BF_PRE_CTRL_EN_REPEAT(v)  \
+       (((v) << 28) & BM_PRE_CTRL_EN_REPEAT)
+#define BP_PRE_CTRL_RSVD2      16
+#define BM_PRE_CTRL_RSVD2 0x0FFF0000
+#define BF_PRE_CTRL_RSVD2(v)  \
+       (((v) << 16) & BM_PRE_CTRL_RSVD2)
+#define BP_PRE_CTRL_RSVD1      12
+#define BM_PRE_CTRL_RSVD1 0x0000F000
+#define BF_PRE_CTRL_RSVD1(v)  \
+       (((v) << 12) & BM_PRE_CTRL_RSVD1)
+#define BM_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN 0x00000800
+#define BF_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN(v)  \
+       (((v) << 11) & BM_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN)
+#define BV_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN__0 0x0
+#define BV_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN__1 0x1
+#define BP_PRE_CTRL_HANDSHAKE_LINE_NUM      9
+#define BM_PRE_CTRL_HANDSHAKE_LINE_NUM 0x00000600
+#define BF_PRE_CTRL_HANDSHAKE_LINE_NUM(v)  \
+       (((v) << 9) & BM_PRE_CTRL_HANDSHAKE_LINE_NUM)
+#define BV_PRE_CTRL_HANDSHAKE_LINE_NUM__0 0x0
+#define BV_PRE_CTRL_HANDSHAKE_LINE_NUM__1 0x1
+#define BV_PRE_CTRL_HANDSHAKE_LINE_NUM__2 0x2
+#define BM_PRE_CTRL_HANDSHAKE_EN 0x00000100
+#define BF_PRE_CTRL_HANDSHAKE_EN(v)  \
+       (((v) << 8) & BM_PRE_CTRL_HANDSHAKE_EN)
+#define BV_PRE_CTRL_HANDSHAKE_EN__0 0x0
+#define BV_PRE_CTRL_HANDSHAKE_EN__1 0x1
+#define BM_PRE_CTRL_INTERLACED_FIELD 0x00000080
+#define BF_PRE_CTRL_INTERLACED_FIELD(v)  \
+       (((v) << 7) & BM_PRE_CTRL_INTERLACED_FIELD)
+#define BM_PRE_CTRL_SO 0x00000040
+#define BF_PRE_CTRL_SO(v)  \
+       (((v) << 6) & BM_PRE_CTRL_SO)
+#define BM_PRE_CTRL_VFLIP 0x00000020
+#define BF_PRE_CTRL_VFLIP(v)  \
+       (((v) << 5) & BM_PRE_CTRL_VFLIP)
+#define BM_PRE_CTRL_SDW_UPDATE 0x00000010
+#define BF_PRE_CTRL_SDW_UPDATE(v)  \
+       (((v) << 4) & BM_PRE_CTRL_SDW_UPDATE)
+#define BM_PRE_CTRL_RSVD0 0x00000008
+#define BF_PRE_CTRL_RSVD0(v)  \
+       (((v) << 3) & BM_PRE_CTRL_RSVD0)
+#define BM_PRE_CTRL_BLOCK_16 0x00000004
+#define BF_PRE_CTRL_BLOCK_16(v)  \
+       (((v) << 2) & BM_PRE_CTRL_BLOCK_16)
+#define BV_PRE_CTRL_BLOCK_16__32x4 0x0
+#define BV_PRE_CTRL_BLOCK_16__16x4 0x1
+#define BM_PRE_CTRL_BLOCK_EN 0x00000002
+#define BF_PRE_CTRL_BLOCK_EN(v)  \
+       (((v) << 1) & BM_PRE_CTRL_BLOCK_EN)
+#define BV_PRE_CTRL_BLOCK_EN__0 0x0
+#define BV_PRE_CTRL_BLOCK_EN__1 0x1
+#define BM_PRE_CTRL_ENABLE 0x00000001
+#define BF_PRE_CTRL_ENABLE(v)  \
+       (((v) << 0) & BM_PRE_CTRL_ENABLE)
+
+#define HW_PRE_IRQ_MASK        (0x00000010)
+#define HW_PRE_IRQ_MASK_SET    (0x00000014)
+#define HW_PRE_IRQ_MASK_CLR    (0x00000018)
+#define HW_PRE_IRQ_MASK_TOG    (0x0000001c)
+
+#define BP_PRE_IRQ_MASK_RSVD1      4
+#define BM_PRE_IRQ_MASK_RSVD1 0xFFFFFFF0
+#define BF_PRE_IRQ_MASK_RSVD1(v) \
+       (((v) << 4) & BM_PRE_IRQ_MASK_RSVD1)
+#define BM_PRE_IRQ_MASK_TPR_RD_NUM_BYTES_OVFL_IRQ_EN 0x00000008
+#define BF_PRE_IRQ_MASK_TPR_RD_NUM_BYTES_OVFL_IRQ_EN(v)  \
+       (((v) << 3) & BM_PRE_IRQ_MASK_TPR_RD_NUM_BYTES_OVFL_IRQ_EN)
+#define BM_PRE_IRQ_MASK_HANDSHAKE_ABORT_IRQ_EN 0x00000004
+#define BF_PRE_IRQ_MASK_HANDSHAKE_ABORT_IRQ_EN(v)  \
+       (((v) << 2) & BM_PRE_IRQ_MASK_HANDSHAKE_ABORT_IRQ_EN)
+#define BM_PRE_IRQ_MASK_STORE_IRQ_EN 0x00000002
+#define BF_PRE_IRQ_MASK_STORE_IRQ_EN(v)  \
+       (((v) << 1) & BM_PRE_IRQ_MASK_STORE_IRQ_EN)
+#define BM_PRE_IRQ_MASK_PREFETCH_IRQ_EN 0x00000001
+#define BF_PRE_IRQ_MASK_PREFETCH_IRQ_EN(v)  \
+       (((v) << 0) & BM_PRE_IRQ_MASK_PREFETCH_IRQ_EN)
+
+#define HW_PRE_IRQ     (0x00000020)
+#define HW_PRE_IRQ_SET (0x00000024)
+#define HW_PRE_IRQ_CLR (0x00000028)
+#define HW_PRE_IRQ_TOG (0x0000002c)
+
+#define BP_PRE_IRQ_RSVD1      14
+#define BM_PRE_IRQ_RSVD1 0xFFFFC000
+#define BF_PRE_IRQ_RSVD1(v) \
+       (((v) << 14) & BM_PRE_IRQ_RSVD1)
+#define BP_PRE_IRQ_AXI_ERROR_ID      10
+#define BM_PRE_IRQ_AXI_ERROR_ID 0x00003C00
+#define BF_PRE_IRQ_AXI_ERROR_ID(v)  \
+       (((v) << 10) & BM_PRE_IRQ_AXI_ERROR_ID)
+#define BM_PRE_IRQ_AXI_READ_ERROR 0x00000200
+#define BF_PRE_IRQ_AXI_READ_ERROR(v)  \
+       (((v) << 9) & BM_PRE_IRQ_AXI_READ_ERROR)
+#define BM_PRE_IRQ_AXI_WRITE_ERROR 0x00000100
+#define BF_PRE_IRQ_AXI_WRITE_ERROR(v)  \
+       (((v) << 8) & BM_PRE_IRQ_AXI_WRITE_ERROR)
+#define BP_PRE_IRQ_RSVD0      5
+#define BM_PRE_IRQ_RSVD0 0x000000E0
+#define BF_PRE_IRQ_RSVD0(v)  \
+       (((v) << 5) & BM_PRE_IRQ_RSVD0)
+#define BM_PRE_IRQ_HANDSHAKE_ERROR_IRQ 0x00000010
+#define BF_PRE_IRQ_HANDSHAKE_ERROR_IRQ(v)  \
+       (((v) << 4) & BM_PRE_IRQ_HANDSHAKE_ERROR_IRQ)
+#define BM_PRE_IRQ_TPR_RD_NUM_BYTES_OVFL_IRQ 0x00000008
+#define BF_PRE_IRQ_TPR_RD_NUM_BYTES_OVFL_IRQ(v)  \
+       (((v) << 3) & BM_PRE_IRQ_TPR_RD_NUM_BYTES_OVFL_IRQ)
+#define BM_PRE_IRQ_HANDSHAKE_ABORT_IRQ 0x00000004
+#define BF_PRE_IRQ_HANDSHAKE_ABORT_IRQ(v)  \
+       (((v) << 2) & BM_PRE_IRQ_HANDSHAKE_ABORT_IRQ)
+#define BM_PRE_IRQ_STORE_IRQ 0x00000002
+#define BF_PRE_IRQ_STORE_IRQ(v)  \
+       (((v) << 1) & BM_PRE_IRQ_STORE_IRQ)
+#define BM_PRE_IRQ_PREFETCH_IRQ 0x00000001
+#define BF_PRE_IRQ_PREFETCH_IRQ(v)  \
+       (((v) << 0) & BM_PRE_IRQ_PREFETCH_IRQ)
+
+#define HW_PRE_CUR_BUF (0x00000030)
+
+#define BP_PRE_CUR_BUF_ADDR      0
+#define BM_PRE_CUR_BUF_ADDR 0xFFFFFFFF
+#define BF_PRE_CUR_BUF_ADDR(v)   (v)
+
+#define HW_PRE_NEXT_BUF        (0x00000040)
+
+#define BP_PRE_NEXT_BUF_ADDR      0
+#define BM_PRE_NEXT_BUF_ADDR 0xFFFFFFFF
+#define BF_PRE_NEXT_BUF_ADDR(v)   (v)
+
+#define HW_PRE_U_BUF_OFFSET    (0x00000050)
+
+#define BP_PRE_U_BUF_OFFSET_RSVD0      25
+#define BM_PRE_U_BUF_OFFSET_RSVD0 0xFE000000
+#define BF_PRE_U_BUF_OFFSET_RSVD0(v) \
+       (((v) << 25) & BM_PRE_U_BUF_OFFSET_RSVD0)
+#define BP_PRE_U_BUF_OFFSET_UBO      0
+#define BM_PRE_U_BUF_OFFSET_UBO 0x01FFFFFF
+#define BF_PRE_U_BUF_OFFSET_UBO(v)  \
+       (((v) << 0) & BM_PRE_U_BUF_OFFSET_UBO)
+
+#define HW_PRE_V_BUF_OFFSET    (0x00000060)
+
+#define BP_PRE_V_BUF_OFFSET_RSVD0      25
+#define BM_PRE_V_BUF_OFFSET_RSVD0 0xFE000000
+#define BF_PRE_V_BUF_OFFSET_RSVD0(v) \
+       (((v) << 25) & BM_PRE_V_BUF_OFFSET_RSVD0)
+#define BP_PRE_V_BUF_OFFSET_VBO      0
+#define BM_PRE_V_BUF_OFFSET_VBO 0x01FFFFFF
+#define BF_PRE_V_BUF_OFFSET_VBO(v)  \
+       (((v) << 0) & BM_PRE_V_BUF_OFFSET_VBO)
+
+#define HW_PRE_TPR_CTRL        (0x00000070)
+#define HW_PRE_TPR_CTRL_SET    (0x00000074)
+#define HW_PRE_TPR_CTRL_CLR    (0x00000078)
+#define HW_PRE_TPR_CTRL_TOG    (0x0000007c)
+
+#define BP_PRE_TPR_CTRL_RSVD      8
+#define BM_PRE_TPR_CTRL_RSVD 0xFFFFFF00
+#define BF_PRE_TPR_CTRL_RSVD(v) \
+       (((v) << 8) & BM_PRE_TPR_CTRL_RSVD)
+#define BP_PRE_TPR_CTRL_TILE_FORMAT      0
+#define BM_PRE_TPR_CTRL_TILE_FORMAT 0x000000FF
+#define BF_PRE_TPR_CTRL_TILE_FORMAT(v)  \
+       (((v) << 0) & BM_PRE_TPR_CTRL_TILE_FORMAT)
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__BYPASS       0x00
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU32_SB_ST  0x10
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU32_SB_SRT 0x50
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU32_ST     0x20
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU32_SRT    0x60
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU32_MST    0xA0
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU32_MSRT   0xE0
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU16_SB_ST  0x11
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU16_SB_SRT 0x51
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU16_ST     0x21
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU16_SRT    0x61
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU16_MST    0xA1
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__GPU16_MSRT   0xE1
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__VPU8_PRO     0x22
+#define BV_PRE_TPR_CTRL_TILE_FORMAT__VPU8_SB_INT  0x13
+
+#define HW_PRE_PREFETCH_ENGINE_CTRL    (0x00000080)
+#define HW_PRE_PREFETCH_ENGINE_CTRL_SET        (0x00000084)
+#define HW_PRE_PREFETCH_ENGINE_CTRL_CLR        (0x00000088)
+#define HW_PRE_PREFETCH_ENGINE_CTRL_TOG        (0x0000008c)
+
+#define BP_PRE_PREFETCH_ENGINE_CTRL_RSVD1      16
+#define BM_PRE_PREFETCH_ENGINE_CTRL_RSVD1 0xFFFF0000
+#define BF_PRE_PREFETCH_ENGINE_CTRL_RSVD1(v) \
+       (((v) << 16) & BM_PRE_PREFETCH_ENGINE_CTRL_RSVD1)
+#define BM_PRE_PREFETCH_ENGINE_CTRL_TPR_COOR_OFFSET_EN 0x00008000
+#define BF_PRE_PREFETCH_ENGINE_CTRL_TPR_COOR_OFFSET_EN(v)  \
+       (((v) << 15) & BM_PRE_PREFETCH_ENGINE_CTRL_TPR_COOR_OFFSET_EN)
+#define BM_PRE_PREFETCH_ENGINE_CTRL_PARTIAL_UV_SWAP 0x00004000
+#define BF_PRE_PREFETCH_ENGINE_CTRL_PARTIAL_UV_SWAP(v)  \
+       (((v) << 14) & BM_PRE_PREFETCH_ENGINE_CTRL_PARTIAL_UV_SWAP)
+#define BM_PRE_PREFETCH_ENGINE_CTRL_CROP_EN 0x00002000
+#define BF_PRE_PREFETCH_ENGINE_CTRL_CROP_EN(v)  \
+       (((v) << 13) & BM_PRE_PREFETCH_ENGINE_CTRL_CROP_EN)
+#define BV_PRE_PREFETCH_ENGINE_CTRL_CROP_EN__0 0x0
+#define BV_PRE_PREFETCH_ENGINE_CTRL_CROP_EN__1 0x1
+#define BM_PRE_PREFETCH_ENGINE_CTRL_FIELD_INVERSE 0x00001000
+#define BF_PRE_PREFETCH_ENGINE_CTRL_FIELD_INVERSE(v)  \
+       (((v) << 12) & BM_PRE_PREFETCH_ENGINE_CTRL_FIELD_INVERSE)
+#define BV_PRE_PREFETCH_ENGINE_CTRL_FIELD_INVERSE__0 0x0
+#define BV_PRE_PREFETCH_ENGINE_CTRL_FIELD_INVERSE__1 0x1
+#define BM_PRE_PREFETCH_ENGINE_CTRL_SHIFT_BYPASS 0x00000800
+#define BF_PRE_PREFETCH_ENGINE_CTRL_SHIFT_BYPASS(v)  \
+       (((v) << 11) & BM_PRE_PREFETCH_ENGINE_CTRL_SHIFT_BYPASS)
+#define BV_PRE_PREFETCH_ENGINE_CTRL_SHIFT_BYPASS__0 0x0
+#define BV_PRE_PREFETCH_ENGINE_CTRL_SHIFT_BYPASS__1 0x1
+#define BP_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT      8
+#define BM_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT 0x00000700
+#define BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(v)  \
+       (((v) << 8) & BM_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT)
+#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT__0 0x0
+#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT__1 0x1
+#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT__2 0x2
+#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT__3 0x3
+#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT__4 0x4
+#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT__5 0x5
+#define BP_PRE_PREFETCH_ENGINE_CTRL_RSVD0      6
+#define BM_PRE_PREFETCH_ENGINE_CTRL_RSVD0 0x000000C0
+#define BF_PRE_PREFETCH_ENGINE_CTRL_RSVD0(v)  \
+       (((v) << 6) & BM_PRE_PREFETCH_ENGINE_CTRL_RSVD0)
+#define BP_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP      4
+#define BM_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP 0x00000030
+#define BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP(v)  \
+       (((v) << 4) & BM_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP)
+#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP__0 0x0
+#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP__1 0x1
+#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP__2 0x2
+#define BV_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP__3 0x3
+#define BP_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES      1
+#define BM_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES 0x0000000E
+#define BF_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES(v)  \
+       (((v) << 1) & BM_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES)
+#define BV_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES__8_bytes   0x0
+#define BV_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES__16_bytes  0x1
+#define BV_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES__32_bytes  0x2
+#define BV_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES__64_bytes  0x3
+#define BV_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES__128_bytes 0x4
+#define BM_PRE_PREFETCH_ENGINE_CTRL_PREFETCH_EN 0x00000001
+#define BF_PRE_PREFETCH_ENGINE_CTRL_PREFETCH_EN(v)  \
+       (((v) << 0) & BM_PRE_PREFETCH_ENGINE_CTRL_PREFETCH_EN)
+#define BV_PRE_PREFETCH_ENGINE_CTRL_PREFETCH_EN__0 0x0
+#define BV_PRE_PREFETCH_ENGINE_CTRL_PREFETCH_EN__1 0x1
+
+#define HW_PRE_PREFETCH_ENGINE_STATUS  (0x00000090)
+
+#define BP_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_Y      16
+#define BM_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_Y 0x3FFF0000
+#define BF_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_Y(v) \
+       (((v) << 16) & BM_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_Y)
+#define BP_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_X      0
+#define BM_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_X 0x0000FFFF
+#define BF_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_X(v)  \
+       (((v) << 0) & BM_PRE_PREFETCH_ENGINE_STATUS_PREFETCH_BLOCK_X)
+
+#define HW_PRE_PREFETCH_ENGINE_INPUT_SIZE      (0x000000a0)
+
+#define BP_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_HEIGHT      16
+#define BM_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_HEIGHT 0xFFFF0000
+#define BF_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_HEIGHT(v) \
+       (((v) << 16) & BM_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_HEIGHT)
+#define BP_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_WIDTH      0
+#define BM_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_WIDTH 0x0000FFFF
+#define BF_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_WIDTH(v)  \
+       (((v) << 0) & BM_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_WIDTH)
+
+#define HW_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC (0x000000b0)
+
+#define BP_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_Y      16
+#define BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_Y 0xFFFF0000
+#define BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_Y(v) \
+       (((v) << 16) & BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_Y)
+#define BP_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_X      0
+#define BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_X 0x0000FFFF
+#define BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_X(v)  \
+       (((v) << 0) & BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_X)
+
+#define HW_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC (0x000000c0)
+
+#define BP_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_Y      16
+#define BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_Y 0xFFFF0000
+#define BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_Y(v) \
+       (((v) << 16) & BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_Y)
+#define BP_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_X      0
+#define BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_X 0x0000FFFF
+#define BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_X(v)  \
+       (((v) << 0) & BM_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_LRC_OUTPUT_SIZE_LRC_X)
+
+#define HW_PRE_PREFETCH_ENGINE_PITCH   (0x000000d0)
+
+#define BP_PRE_PREFETCH_ENGINE_PITCH_INPUT_UV_PITCH      16
+#define BM_PRE_PREFETCH_ENGINE_PITCH_INPUT_UV_PITCH 0xFFFF0000
+#define BF_PRE_PREFETCH_ENGINE_PITCH_INPUT_UV_PITCH(v) \
+       (((v) << 16) & BM_PRE_PREFETCH_ENGINE_PITCH_INPUT_UV_PITCH)
+#define BP_PRE_PREFETCH_ENGINE_PITCH_INPUT_Y_PITCH      0
+#define BM_PRE_PREFETCH_ENGINE_PITCH_INPUT_Y_PITCH 0x0000FFFF
+#define BF_PRE_PREFETCH_ENGINE_PITCH_INPUT_Y_PITCH(v)  \
+       (((v) << 0) & BM_PRE_PREFETCH_ENGINE_PITCH_INPUT_Y_PITCH)
+
+#define HW_PRE_PREFETCH_ENGINE_SHIFT_OFFSET    (0x000000e0)
+#define HW_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_SET        (0x000000e4)
+#define HW_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_CLR        (0x000000e8)
+#define HW_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_TOG        (0x000000ec)
+
+#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD0      29
+#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD0 0xE0000000
+#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD0(v) \
+       (((v) << 29) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD0)
+#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET3      24
+#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET3 0x1F000000
+#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET3(v)  \
+       (((v) << 24) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET3)
+#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD1      21
+#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD1 0x00E00000
+#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD1(v)  \
+       (((v) << 21) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD1)
+#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET2      16
+#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET2 0x001F0000
+#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET2(v)  \
+       (((v) << 16) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET2)
+#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD2      13
+#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD2 0x0000E000
+#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD2(v)  \
+       (((v) << 13) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD2)
+#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET1      8
+#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET1 0x00001F00
+#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET1(v)  \
+       (((v) << 8) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET1)
+#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD3      5
+#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD3 0x000000E0
+#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD3(v)  \
+       (((v) << 5) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_RSVD3)
+#define BP_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET0      0
+#define BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET0 0x0000001F
+#define BF_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET0(v)  \
+       (((v) << 0) & BM_PRE_PREFETCH_ENGINE_SHIFT_OFFSET_OFFSET0)
+
+#define HW_PRE_PREFETCH_ENGINE_SHIFT_WIDTH     (0x000000f0)
+#define HW_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_SET (0x000000f4)
+#define HW_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_CLR (0x000000f8)
+#define HW_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_TOG (0x000000fc)
+
+#define BP_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_RSVD0      16
+#define BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_RSVD0 0xFFFF0000
+#define BF_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_RSVD0(v) \
+       (((v) << 16) & BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_RSVD0)
+#define BP_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH3      12
+#define BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH3 0x0000F000
+#define BF_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH3(v)  \
+       (((v) << 12) & BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH3)
+#define BP_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH2      8
+#define BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH2 0x00000F00
+#define BF_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH2(v)  \
+       (((v) << 8) & BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH2)
+#define BP_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH1      4
+#define BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH1 0x000000F0
+#define BF_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH1(v)  \
+       (((v) << 4) & BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH1)
+#define BP_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH0      0
+#define BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH0 0x0000000F
+#define BF_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH0(v)  \
+       (((v) << 0) & BM_PRE_PREFETCH_ENGINE_SHIFT_WIDTH_WIDTH0)
+
+#define HW_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET        (0x00000100)
+
+#define BP_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_RSVD0      23
+#define BM_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_RSVD0 0xFF800000
+#define BF_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_RSVD0(v) \
+       (((v) << 23) & BM_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_RSVD0)
+#define BP_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_INTERLACE_OFFSET      0
+#define BM_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_INTERLACE_OFFSET 0xFFFFFFFF
+#define BF_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_INTERLACE_OFFSET(v)  \
+       (((v) << 0) & BM_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_INTERLACE_OFFSET)
+
+#define HW_PRE_STORE_ENGINE_CTRL       (0x00000110)
+#define HW_PRE_STORE_ENGINE_CTRL_SET   (0x00000114)
+#define HW_PRE_STORE_ENGINE_CTRL_CLR   (0x00000118)
+#define HW_PRE_STORE_ENGINE_CTRL_TOG   (0x0000011c)
+
+#define BP_PRE_STORE_ENGINE_CTRL_RSVD0      6
+#define BM_PRE_STORE_ENGINE_CTRL_RSVD0 0xFFFFFFC0
+#define BF_PRE_STORE_ENGINE_CTRL_RSVD0(v) \
+       (((v) << 6) & BM_PRE_STORE_ENGINE_CTRL_RSVD0)
+#define BP_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP      4
+#define BM_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP 0x00000030
+#define BF_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP(v)  \
+       (((v) << 4) & BM_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP)
+#define BV_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP__8_bits  0x0
+#define BV_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP__16_bits 0x1
+#define BV_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP__32_bits 0x2
+#define BV_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP__64_bits 0x3
+#define BP_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES      1
+#define BM_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES 0x0000000E
+#define BF_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES(v)  \
+       (((v) << 1) & BM_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES)
+#define BV_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES__8_bytes   0x0
+#define BV_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES__16_bytes  0x1
+#define BV_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES__32_bytes  0x2
+#define BV_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES__64_bytes  0x3
+#define BV_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES__128_bytes 0x4
+#define BM_PRE_STORE_ENGINE_CTRL_STORE_EN 0x00000001
+#define BF_PRE_STORE_ENGINE_CTRL_STORE_EN(v)  \
+       (((v) << 0) & BM_PRE_STORE_ENGINE_CTRL_STORE_EN)
+#define BV_PRE_STORE_ENGINE_CTRL_STORE_EN__0 0x0
+#define BV_PRE_STORE_ENGINE_CTRL_STORE_EN__1 0x1
+
+#define HW_PRE_STORE_ENGINE_STATUS     (0x00000120)
+
+#define BP_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_Y      16
+#define BM_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_Y 0x3FFF0000
+#define BF_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_Y(v) \
+       (((v) << 16) & BM_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_Y)
+#define BP_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_X      0
+#define BM_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_X 0x0000FFFF
+#define BF_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_X(v)  \
+       (((v) << 0) & BM_PRE_STORE_ENGINE_STATUS_STORE_BLOCK_X)
+
+#define HW_PRE_STORE_ENGINE_SIZE       (0x00000130)
+
+#define BP_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_HEIGHT      16
+#define BM_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_HEIGHT 0xFFFF0000
+#define BF_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_HEIGHT(v) \
+       (((v) << 16) & BM_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_HEIGHT)
+#define BP_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_WIDTH      0
+#define BM_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_WIDTH 0x0000FFFF
+#define BF_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_WIDTH(v)  \
+       (((v) << 0) & BM_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_WIDTH)
+
+#define HW_PRE_STORE_ENGINE_PITCH      (0x00000140)
+
+#define BP_PRE_STORE_ENGINE_PITCH_RSVD0      16
+#define BM_PRE_STORE_ENGINE_PITCH_RSVD0 0xFFFF0000
+#define BF_PRE_STORE_ENGINE_PITCH_RSVD0(v) \
+       (((v) << 16) & BM_PRE_STORE_ENGINE_PITCH_RSVD0)
+#define BP_PRE_STORE_ENGINE_PITCH_OUT_PITCH      0
+#define BM_PRE_STORE_ENGINE_PITCH_OUT_PITCH 0x0000FFFF
+#define BF_PRE_STORE_ENGINE_PITCH_OUT_PITCH(v)  \
+       (((v) << 0) & BM_PRE_STORE_ENGINE_PITCH_OUT_PITCH)
+
+#define HW_PRE_STORE_ENGINE_ADDR       (0x00000150)
+
+#define BP_PRE_STORE_ENGINE_ADDR_OUT_BASE_ADDR      0
+#define BM_PRE_STORE_ENGINE_ADDR_OUT_BASE_ADDR 0xFFFFFFFF
+#define BF_PRE_STORE_ENGINE_ADDR_OUT_BASE_ADDR(v)   (v)
+#endif /* __ARCH_ARM___PRE_H */
diff --git a/drivers/mxc/ipu3/pre.c b/drivers/mxc/ipu3/pre.c
new file mode 100644 (file)
index 0000000..a15a60b
--- /dev/null
@@ -0,0 +1,973 @@
+/*
+ * Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
+ *
+ * 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
+ */
+#include <linux/clk.h>
+#include <linux/genalloc.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ipu-v3.h>
+#include <linux/ipu-v3-pre.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "pre-regs.h"
+
+struct ipu_pre_data {
+       unsigned int id;
+       struct device *dev;
+       void __iomem *base;
+       struct clk *clk;
+
+       struct mutex mutex;     /* for in_use */
+       spinlock_t lock;        /* for register access */
+
+       struct list_head list;
+
+       struct gen_pool *iram_pool;
+       unsigned long double_buffer_size;
+       unsigned long double_buffer_base;
+       unsigned long double_buffer_paddr;
+
+       bool in_use;
+       bool enabled;
+};
+
+static LIST_HEAD(pre_list);
+static DEFINE_MUTEX(pre_list_lock);
+
+static inline void pre_write(struct ipu_pre_data *pre,
+                       u32 value, unsigned int offset)
+{
+       writel(value, pre->base + offset);
+}
+
+static inline u32 pre_read(struct ipu_pre_data *pre, unsigned offset)
+{
+       return readl(pre->base + offset);
+}
+
+static struct ipu_pre_data *get_pre(unsigned int id)
+{
+       struct ipu_pre_data *pre;
+
+       mutex_lock(&pre_list_lock);
+       list_for_each_entry(pre, &pre_list, list) {
+               if (pre->id == id) {
+                       mutex_unlock(&pre_list_lock);
+                       return pre;
+               }
+       }
+       mutex_unlock(&pre_list_lock);
+
+       return NULL;
+}
+
+int ipu_pre_alloc(int ipu_id, ipu_channel_t channel)
+{
+       struct ipu_pre_data *pre;
+       int i, fixed;
+
+       if (channel == MEM_BG_SYNC) {
+               fixed = ipu_id ? 3 : 0;
+               pre = get_pre(fixed);
+               if (pre) {
+                       mutex_lock(&pre->mutex);
+                       if (!pre->in_use) {
+                               pre->in_use = true;
+                               mutex_unlock(&pre->mutex);
+                               return pre->id;
+                       }
+                       mutex_unlock(&pre->mutex);
+               }
+               return pre ? -EBUSY : -ENOENT;
+       }
+
+       for (i = 1; i < 3; i++) {
+               pre = get_pre(i);
+               if (!pre)
+                       continue;
+               mutex_lock(&pre->mutex);
+               if (!pre->in_use) {
+                       pre->in_use = true;
+                       mutex_unlock(&pre->mutex);
+                       return pre->id;
+               }
+               mutex_unlock(&pre->mutex);
+       }
+
+       return pre ? -EBUSY : -ENOENT;
+}
+EXPORT_SYMBOL(ipu_pre_alloc);
+
+void ipu_pre_free(unsigned int *id)
+{
+       struct ipu_pre_data *pre;
+
+       pre = get_pre(*id);
+       if (!pre)
+               return;
+
+       mutex_lock(&pre->mutex);
+       pre->in_use = false;
+       mutex_unlock(&pre->mutex);
+
+       *id = -1;
+}
+EXPORT_SYMBOL(ipu_pre_free);
+
+unsigned long ipu_pre_alloc_double_buffer(unsigned int id, unsigned int size)
+{
+       struct ipu_pre_data *pre = get_pre(id);
+
+       if (!pre)
+               return -ENOENT;
+
+       if (!size)
+               return -EINVAL;
+
+       pre->double_buffer_base = gen_pool_alloc(pre->iram_pool, size);
+       if (!pre->double_buffer_base) {
+               dev_err(pre->dev, "double buffer allocate failed\n");
+               return -ENOMEM;
+       }
+       pre->double_buffer_size = size;
+
+       pre->double_buffer_paddr = gen_pool_virt_to_phys(pre->iram_pool,
+                                                        pre->double_buffer_base);
+
+       return pre->double_buffer_paddr;
+}
+EXPORT_SYMBOL(ipu_pre_alloc_double_buffer);
+
+void ipu_pre_free_double_buffer(unsigned int id)
+{
+       struct ipu_pre_data *pre = get_pre(id);
+
+       if (!pre)
+               return;
+
+       if (pre->double_buffer_base) {
+               gen_pool_free(pre->iram_pool,
+                             pre->double_buffer_base,
+                             pre->double_buffer_size);
+               pre->double_buffer_base  = 0;
+               pre->double_buffer_size  = 0;
+               pre->double_buffer_paddr = 0;
+       }
+}
+EXPORT_SYMBOL(ipu_pre_free_double_buffer);
+
+/* PRE register configurations */
+static int ipu_pre_set_ctrl(unsigned int id,
+                           bool repeat,
+                           bool vflip,
+                           bool handshake_en,
+                           bool hsk_abort_en,
+                           unsigned int hsk_line_num,
+                           bool sdw_update,
+                           unsigned int block_size,
+                           unsigned int interlaced,
+                           unsigned int prefetch_mode)
+{
+       struct ipu_pre_data *pre = get_pre(id);
+       unsigned long lock_flags;
+       int ret = 0;
+
+       if (!pre)
+               return -EINVAL;
+
+       spin_lock_irqsave(&pre->lock, lock_flags);
+       pre_write(pre, BF_PRE_CTRL_TPR_RESET_SEL(1), HW_PRE_CTRL_SET);
+
+       if (repeat)
+               pre_write(pre, BF_PRE_CTRL_EN_REPEAT(1), HW_PRE_CTRL_SET);
+       else
+               pre_write(pre, BM_PRE_CTRL_EN_REPEAT, HW_PRE_CTRL_CLR);
+
+       if (vflip)
+               pre_write(pre, BF_PRE_CTRL_VFLIP(1), HW_PRE_CTRL_SET);
+       else
+               pre_write(pre, BM_PRE_CTRL_VFLIP, HW_PRE_CTRL_CLR);
+
+       if (handshake_en) {
+               pre_write(pre, BF_PRE_CTRL_HANDSHAKE_EN(1), HW_PRE_CTRL_SET);
+               if (hsk_abort_en)
+                       pre_write(pre, BF_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN(1),
+                                 HW_PRE_CTRL_SET);
+               else
+                       pre_write(pre, BM_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN,
+                                 HW_PRE_CTRL_CLR);
+
+               switch (hsk_line_num) {
+               case 0 /* 4 lines */:
+                       pre_write(pre, BM_PRE_CTRL_HANDSHAKE_LINE_NUM,
+                                 HW_PRE_CTRL_CLR);
+                       break;
+               case 1 /* 8 lines */:
+                       pre_write(pre, BM_PRE_CTRL_HANDSHAKE_LINE_NUM,
+                                 HW_PRE_CTRL_CLR);
+                       pre_write(pre, BF_PRE_CTRL_HANDSHAKE_LINE_NUM(1),
+                                 HW_PRE_CTRL_SET);
+                       break;
+               case 2 /* 16 lines */:
+                       pre_write(pre, BM_PRE_CTRL_HANDSHAKE_LINE_NUM,
+                                 HW_PRE_CTRL_CLR);
+                       pre_write(pre, BF_PRE_CTRL_HANDSHAKE_LINE_NUM(2),
+                                 HW_PRE_CTRL_SET);
+                       break;
+               default:
+                       dev_err(pre->dev, "invalid hanshake line number\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+       } else
+               pre_write(pre, BM_PRE_CTRL_HANDSHAKE_EN, HW_PRE_CTRL_CLR);
+
+
+       switch (prefetch_mode) {
+       case 0:
+               pre_write(pre, BM_PRE_CTRL_BLOCK_EN, HW_PRE_CTRL_CLR);
+               break;
+       case 1:
+               pre_write(pre, BF_PRE_CTRL_BLOCK_EN(1), HW_PRE_CTRL_SET);
+               switch (block_size) {
+               case 0:
+                       pre_write(pre, BM_PRE_CTRL_BLOCK_16, HW_PRE_CTRL_CLR);
+                       break;
+               case 1:
+                       pre_write(pre, BF_PRE_CTRL_BLOCK_16(1), HW_PRE_CTRL_SET);
+                       break;
+               default:
+                       dev_err(pre->dev, "invalid block size for pre\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+               break;
+       default:
+               dev_err(pre->dev, "invalid prefech mode for pre\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       switch (interlaced) {
+       case 0: /* progressive mode */
+               pre_write(pre, BM_PRE_CTRL_SO, HW_PRE_CTRL_CLR);
+               break;
+       case 2: /* interlaced mode: Pal */
+               pre_write(pre, BF_PRE_CTRL_SO(1), HW_PRE_CTRL_SET);
+               pre_write(pre, BM_PRE_CTRL_INTERLACED_FIELD, HW_PRE_CTRL_CLR);
+               break;
+       case 3: /* interlaced mode: NTSC */
+               pre_write(pre, BF_PRE_CTRL_SO(1), HW_PRE_CTRL_SET);
+               pre_write(pre, BF_PRE_CTRL_INTERLACED_FIELD(1), HW_PRE_CTRL_SET);
+               break;
+       default:
+               dev_err(pre->dev, "invalid interlaced or progressive mode\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (sdw_update)
+               pre_write(pre, BF_PRE_CTRL_SDW_UPDATE(1), HW_PRE_CTRL_SET);
+       else
+               pre_write(pre, BM_PRE_CTRL_SDW_UPDATE, HW_PRE_CTRL_CLR);
+
+err:
+       spin_unlock_irqrestore(&pre->lock, lock_flags);
+
+       return ret;
+}
+
+static void ipu_pre_irq_mask(struct ipu_pre_data *pre,
+                            unsigned long mask, bool clear)
+{
+       if (clear) {
+               pre_write(pre, mask & 0xf, HW_PRE_IRQ_MASK_CLR);
+               return;
+       }
+       pre_write(pre, mask & 0xf, HW_PRE_IRQ_MASK_SET);
+}
+
+static int ipu_pre_buf_set(unsigned int id, unsigned long cur_buf,
+                          unsigned long next_buf)
+{
+       struct ipu_pre_data *pre = get_pre(id);
+       unsigned long lock_flags;
+
+       if (!pre)
+               return -EINVAL;
+
+       spin_lock_irqsave(&pre->lock, lock_flags);
+       pre_write(pre, cur_buf, HW_PRE_CUR_BUF);
+       pre_write(pre, next_buf, HW_PRE_NEXT_BUF);
+       spin_unlock_irqrestore(&pre->lock, lock_flags);
+
+       return 0;
+}
+
+static int ipu_pre_plane_buf_off_set(unsigned int id,
+                                    unsigned long sec_buf_off,
+                                    unsigned long trd_buf_off)
+{
+       struct ipu_pre_data *pre = get_pre(id);
+       unsigned long lock_flags;
+
+       if (!pre || sec_buf_off & BM_PRE_U_BUF_OFFSET_RSVD0 ||
+           trd_buf_off & BM_PRE_V_BUF_OFFSET_RSVD0)
+               return -EINVAL;
+
+       spin_lock_irqsave(&pre->lock, lock_flags);
+       pre_write(pre, sec_buf_off, HW_PRE_U_BUF_OFFSET);
+       pre_write(pre, trd_buf_off, HW_PRE_V_BUF_OFFSET);
+       spin_unlock_irqrestore(&pre->lock, lock_flags);
+
+       return 0;
+}
+
+static int ipu_pre_tpr_set(unsigned int id, unsigned int tile_fmt)
+{
+       struct ipu_pre_data *pre = get_pre(id);
+       unsigned long lock_flags;
+       unsigned int tpr_ctrl, fmt;
+
+       if (!pre)
+               return -EINVAL;
+
+       switch (tile_fmt) {
+       case 0x0: /* Bypass */
+               fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x0);
+               break;
+       case IPU_PIX_FMT_GPU32_SB_ST:
+               fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x10);
+               break;
+       case IPU_PIX_FMT_GPU16_SB_ST:
+               fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x11);
+               break;
+       case IPU_PIX_FMT_GPU32_ST:
+               fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x20);
+               break;
+       case IPU_PIX_FMT_GPU16_ST:
+               fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x21);
+               break;
+       case IPU_PIX_FMT_GPU32_SB_SRT:
+               fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x50);
+               break;
+       case IPU_PIX_FMT_GPU16_SB_SRT:
+               fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x51);
+               break;
+       case IPU_PIX_FMT_GPU32_SRT:
+               fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x60);
+               break;
+       case IPU_PIX_FMT_GPU16_SRT:
+               fmt = BF_PRE_TPR_CTRL_TILE_FORMAT(0x61);
+               break;
+       default:
+               dev_err(pre->dev, "invalid tile fmt for pre\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&pre->lock, lock_flags);
+       tpr_ctrl = pre_read(pre, HW_PRE_TPR_CTRL);
+       tpr_ctrl &= ~BM_PRE_TPR_CTRL_TILE_FORMAT;
+       tpr_ctrl |= fmt;
+       pre_write(pre, tpr_ctrl, HW_PRE_TPR_CTRL);
+       spin_unlock_irqrestore(&pre->lock, lock_flags);
+
+       return 0;
+}
+
+static int ipu_pre_set_shift(int id, unsigned int offset, unsigned int width)
+{
+       struct ipu_pre_data *pre = get_pre(id);
+       unsigned long lock_flags;
+
+       if (!pre)
+               return -EINVAL;
+
+       spin_lock_irqsave(&pre->lock, lock_flags);
+       pre_write(pre, offset, HW_PRE_PREFETCH_ENGINE_SHIFT_OFFSET);
+       pre_write(pre, width,  HW_PRE_PREFETCH_ENGINE_SHIFT_WIDTH);
+       spin_unlock_irqrestore(&pre->lock, lock_flags);
+
+       return 0;
+}
+
+static int ipu_pre_prefetch(unsigned int id,
+                           unsigned int read_burst,
+                           unsigned int input_bpp,
+                           unsigned int input_pixel_fmt,
+                           bool shift_bypass,
+                           bool field_inverse,
+                           bool tpr_coor_offset_en,
+                           struct ipu_rect output_size,
+                           unsigned int input_width,
+                           unsigned int input_height,
+                           unsigned int input_active_width,
+                           unsigned int interlaced,
+                           int interlace_offset)
+{
+       unsigned int prefetch_ctrl = 0;
+       unsigned int input_y_pitch = 0, input_uv_pitch = 0;
+       struct ipu_pre_data *pre = get_pre(id);
+       unsigned long lock_flags;
+
+       if (!pre)
+               return -EINVAL;
+
+       spin_lock_irqsave(&pre->lock, lock_flags);
+       prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_PREFETCH_EN(1);
+       switch (read_burst) {
+       case 0x0:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES(0x0);
+               break;
+       case 0x1:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES(0x1);
+               break;
+       case 0x2:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES(0x2);
+               break;
+       case 0x3:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES(0x3);
+               break;
+       case 0x4:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_RD_NUM_BYTES(0x4);
+               break;
+       default:
+               spin_unlock_irqrestore(&pre->lock, lock_flags);
+               dev_err(pre->dev, "invalid read burst for prefetch engine\n");
+               return -EINVAL;
+       }
+
+       switch (input_bpp) {
+       case 8:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP(0x0);
+               break;
+       case 16:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP(0x1);
+               break;
+       case 32:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP(0x2);
+               break;
+       case 64:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_ACTIVE_BPP(0x3);
+               break;
+       default:
+               spin_unlock_irqrestore(&pre->lock, lock_flags);
+               dev_err(pre->dev, "invalid input bpp for prefetch engine\n");
+               return -EINVAL;
+       }
+
+       switch (input_pixel_fmt) {
+       case 0x1: /* tile */
+       case 0x0: /* generic data */
+       case IPU_PIX_FMT_RGB666:
+       case IPU_PIX_FMT_RGB565:
+       case IPU_PIX_FMT_BGRA4444:
+       case IPU_PIX_FMT_BGRA5551:
+       case IPU_PIX_FMT_BGR24:
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_GBR24:
+       case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_RGB32:
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_ABGR32:
+       case IPU_PIX_FMT_YUYV:
+       case IPU_PIX_FMT_UYVY:
+       case IPU_PIX_FMT_YUV444:
+       case IPU_PIX_FMT_AYUV:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(0x0);
+               input_y_pitch = input_width * (input_bpp >> 3);
+               if (interlaced && input_pixel_fmt != 0x1)
+                       input_y_pitch *= 2;
+               break;
+       case IPU_PIX_FMT_YUV444P:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(0x1);
+               input_y_pitch  = input_width;
+               input_uv_pitch = input_width;
+               break;
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YVU422P:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(0x2);
+               input_y_pitch  = input_width;
+               input_uv_pitch = input_width >> 1;
+               break;
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(0x3);
+               input_y_pitch  = input_width;
+               input_uv_pitch = input_width >> 1;
+               break;
+       case PRE_PIX_FMT_NV61:
+               prefetch_ctrl |= BM_PRE_PREFETCH_ENGINE_CTRL_PARTIAL_UV_SWAP;
+       case IPU_PIX_FMT_NV16:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(0x4);
+               input_y_pitch  = input_width;
+               input_uv_pitch = input_width;
+               break;
+       case PRE_PIX_FMT_NV21:
+               prefetch_ctrl |= BM_PRE_PREFETCH_ENGINE_CTRL_PARTIAL_UV_SWAP;
+       case IPU_PIX_FMT_NV12:
+               prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_INPUT_PIXEL_FORMAT(0x5);
+               input_y_pitch  = input_width;
+               input_uv_pitch = input_width;
+               break;
+       default:
+               spin_unlock_irqrestore(&pre->lock, lock_flags);
+               dev_err(pre->dev, "invalid input pixel format for prefetch engine\n");
+               return -EINVAL;
+       }
+
+       prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_SHIFT_BYPASS(shift_bypass ? 1 : 0);
+       prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_FIELD_INVERSE(field_inverse ? 1 : 0);
+       prefetch_ctrl |= BF_PRE_PREFETCH_ENGINE_CTRL_TPR_COOR_OFFSET_EN(tpr_coor_offset_en ? 1 : 0);
+
+       pre_write(pre, BF_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_WIDTH(input_active_width) |
+                 BF_PRE_PREFETCH_ENGINE_INPUT_SIZE_INPUT_HEIGHT(input_height),
+                 HW_PRE_PREFETCH_ENGINE_INPUT_SIZE);
+
+       if (tpr_coor_offset_en)
+               pre_write(pre, BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_X(output_size.left) |
+                            BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_Y(output_size.top),
+                            HW_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC);
+
+       pre_write(pre, BF_PRE_PREFETCH_ENGINE_PITCH_INPUT_Y_PITCH(input_y_pitch) |
+                    BF_PRE_PREFETCH_ENGINE_PITCH_INPUT_UV_PITCH(input_uv_pitch),
+                    HW_PRE_PREFETCH_ENGINE_PITCH);
+
+       pre_write(pre, BF_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET_INTERLACE_OFFSET(interlace_offset), HW_PRE_PREFETCH_ENGINE_INTERLACE_OFFSET);
+
+       pre_write(pre, prefetch_ctrl, HW_PRE_PREFETCH_ENGINE_CTRL);
+       spin_unlock_irqrestore(&pre->lock, lock_flags);
+
+       return 0;
+}
+
+static int ipu_pre_store(unsigned int id,
+                        bool store_en,
+                        unsigned int write_burst,
+                        unsigned int output_bpp,
+                        /* this means the output
+                         * width by prefetch
+                         */
+                        unsigned int input_width,
+                        unsigned int input_height,
+                        unsigned int out_pitch,
+                        unsigned int output_addr)
+{
+       struct ipu_pre_data *pre = get_pre(id);
+       unsigned int store_ctrl = 0;
+       unsigned long lock_flags;
+
+       if (!pre)
+               return -EINVAL;
+
+       spin_lock_irqsave(&pre->lock, lock_flags);
+       store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_STORE_EN(store_en ? 1 : 0);
+
+       if (store_en) {
+               switch (write_burst) {
+               case 0x0:
+                       store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES(0x0);
+                       break;
+               case 0x1:
+                       store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES(0x1);
+                       break;
+               case 0x2:
+                       store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES(0x2);
+                       break;
+               case 0x3:
+                       store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES(0x3);
+                       break;
+               case 0x4:
+                       store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_WR_NUM_BYTES(0x4);
+                       break;
+               default:
+                       spin_unlock_irqrestore(&pre->lock, lock_flags);
+                       dev_err(pre->dev, "invalid write burst value for store engine\n");
+                       return -EINVAL;
+               }
+
+               switch (output_bpp) {
+               case 8:
+                       store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP(0x0);
+                       break;
+               case 16:
+                       store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP(0x1);
+                       break;
+               case 32:
+                       store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP(0x2);
+                       break;
+               case 64:
+                       store_ctrl |= BF_PRE_STORE_ENGINE_CTRL_OUTPUT_ACTIVE_BPP(0x3);
+                       break;
+               default:
+                       spin_unlock_irqrestore(&pre->lock, lock_flags);
+                       dev_err(pre->dev, "invalid ouput bpp for store engine\n");
+                       return -EINVAL;
+               }
+
+               pre_write(pre, BF_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_WIDTH(input_width) |
+                            BF_PRE_STORE_ENGINE_SIZE_INPUT_TOTAL_HEIGHT(input_height),
+                            HW_PRE_STORE_ENGINE_SIZE);
+
+               pre_write(pre, BF_PRE_STORE_ENGINE_PITCH_OUT_PITCH(out_pitch),
+                            HW_PRE_STORE_ENGINE_PITCH);
+
+               pre_write(pre, BF_PRE_STORE_ENGINE_ADDR_OUT_BASE_ADDR(output_addr),
+                            HW_PRE_STORE_ENGINE_ADDR);
+       }
+
+       pre_write(pre, store_ctrl, HW_PRE_STORE_ENGINE_CTRL);
+       spin_unlock_irqrestore(&pre->lock, lock_flags);
+
+       return 0;
+}
+/* End */
+
+static irqreturn_t ipu_pre_irq_handle(int irq, void *dev_id)
+{
+       struct ipu_pre_data *pre = dev_id;
+       unsigned int irq_stat, axi_id = 0;
+
+       spin_lock(&pre->lock);
+       irq_stat = pre_read(pre, HW_PRE_IRQ);
+
+       if (irq_stat & BM_PRE_IRQ_HANDSHAKE_ABORT_IRQ) {
+               dev_warn(pre->dev, "handshake abort\n");
+               pre_write(pre, BM_PRE_IRQ_HANDSHAKE_ABORT_IRQ, HW_PRE_IRQ_CLR);
+       }
+
+       if (irq_stat & BM_PRE_IRQ_TPR_RD_NUM_BYTES_OVFL_IRQ) {
+               dev_warn(pre->dev, "tpr read num bytes overflow\n");
+               pre_write(pre, BM_PRE_IRQ_TPR_RD_NUM_BYTES_OVFL_IRQ,
+                               HW_PRE_IRQ_CLR);
+       }
+
+       if (irq_stat & BM_PRE_IRQ_HANDSHAKE_ERROR_IRQ) {
+               dev_warn(pre->dev, "handshake error\n");
+               pre_write(pre, BM_PRE_IRQ_HANDSHAKE_ERROR_IRQ, HW_PRE_IRQ_CLR);
+       }
+
+       axi_id = (irq_stat & BM_PRE_IRQ_AXI_ERROR_ID) >>
+                                       BP_PRE_IRQ_AXI_ERROR_ID;
+       if (irq_stat & BM_PRE_IRQ_AXI_WRITE_ERROR) {
+               dev_warn(pre->dev, "AXI%d write error\n", axi_id);
+               pre_write(pre, BM_PRE_IRQ_AXI_WRITE_ERROR, HW_PRE_IRQ_CLR);
+       }
+
+       if (irq_stat & BM_PRE_IRQ_AXI_READ_ERROR) {
+               dev_warn(pre->dev, "AXI%d read error\n", axi_id);
+               pre_write(pre, BM_PRE_IRQ_AXI_READ_ERROR, HW_PRE_IRQ_CLR);
+       }
+       spin_unlock(&pre->lock);
+
+       return IRQ_HANDLED;
+}
+
+static void ipu_pre_out_of_reset(unsigned int id)
+{
+       struct ipu_pre_data *pre = get_pre(id);
+       unsigned long lock_flags;
+
+       if (!pre)
+               return;
+
+       spin_lock_irqsave(&pre->lock, lock_flags);
+       pre_write(pre, BF_PRE_CTRL_SFTRST(1) | BF_PRE_CTRL_CLKGATE(1),
+                 HW_PRE_CTRL_CLR);
+       spin_unlock_irqrestore(&pre->lock, lock_flags);
+}
+
+int ipu_pre_config(int id, struct ipu_pre_context *config)
+{
+       int ret = 0;
+       struct ipu_pre_data *pre = get_pre(id);
+
+       if (!config || !pre)
+               return -EINVAL;
+
+       config->store_addr = pre->double_buffer_paddr;
+
+       if (!pre->enabled)
+               clk_prepare_enable(pre->clk);
+
+       ipu_pre_out_of_reset(id);
+
+       ret = ipu_pre_plane_buf_off_set(id, config->sec_buf_off,
+                                       config->trd_buf_off);
+       if (ret < 0)
+               goto out;
+
+       ret = ipu_pre_tpr_set(id, config->tile_fmt);
+       if (ret < 0)
+               goto out;
+
+       ret = ipu_pre_buf_set(id, config->cur_buf, config->next_buf);
+       if (ret < 0)
+               goto out;
+
+       ret = ipu_pre_set_shift(id, config->prefetch_shift_offset,
+                               config->prefetch_shift_width);
+       if (ret < 0)
+               goto out;
+
+       ret = ipu_pre_prefetch(id, config->read_burst, config->prefetch_input_bpp,
+                       config->prefetch_input_pixel_fmt, config->shift_bypass,
+                       config->field_inverse, config->tpr_coor_offset_en,
+                       config->prefetch_output_size, config->prefetch_input_width,
+                       config->prefetch_input_height,
+                       config->prefetch_input_active_width,
+                       config->interlaced,
+                       config->interlace_offset);
+       if (ret < 0)
+               goto out;
+
+       ret = ipu_pre_store(id, config->store_en,
+                       config->write_burst, config->store_output_bpp,
+                       config->prefetch_output_size.width, config->prefetch_output_size.height,
+                       config->store_pitch,
+                       config->store_addr);
+       if (ret < 0)
+               goto out;
+
+       ret = ipu_pre_set_ctrl(id, config->repeat,
+                       config->vflip, config->handshake_en,
+                       config->hsk_abort_en, config->hsk_line_num,
+                       config->sdw_update, config->block_size,
+                       config->interlaced, config->prefetch_mode);
+
+       ipu_pre_irq_mask(pre, BM_PRE_IRQ_HANDSHAKE_ABORT_IRQ |
+                             BM_PRE_IRQ_TPR_RD_NUM_BYTES_OVFL_IRQ |
+                             BM_PRE_IRQ_HANDSHAKE_ERROR_IRQ, false);
+out:
+       if (!pre->enabled)
+               clk_disable_unprepare(pre->clk);
+
+       return ret;
+}
+EXPORT_SYMBOL(ipu_pre_config);
+
+int ipu_pre_enable(int id)
+{
+       int ret = 0;
+       struct ipu_pre_data *pre = get_pre(id);
+       unsigned long lock_flags;
+
+       if (!pre)
+               return -EINVAL;
+
+       if (pre->enabled)
+               return 0;
+
+       clk_prepare_enable(pre->clk);
+
+       /* start the pre engine */
+       spin_lock_irqsave(&pre->lock, lock_flags);
+       pre_write(pre, BF_PRE_CTRL_ENABLE(1), HW_PRE_CTRL_SET);
+       spin_unlock_irqrestore(&pre->lock, lock_flags);
+
+       pre->enabled = true;
+
+       return ret;
+}
+EXPORT_SYMBOL(ipu_pre_enable);
+
+int ipu_pre_sdw_update(int id)
+{
+       int ret = 0;
+       struct ipu_pre_data *pre = get_pre(id);
+       unsigned long lock_flags;
+
+       if (!pre)
+               return -EINVAL;
+
+       if (!pre->enabled)
+               clk_prepare_enable(pre->clk);
+
+       /* start the pre engine */
+       spin_lock_irqsave(&pre->lock, lock_flags);
+       pre_write(pre, BF_PRE_CTRL_SDW_UPDATE(1), HW_PRE_CTRL_SET);
+       spin_unlock_irqrestore(&pre->lock, lock_flags);
+
+       if (!pre->enabled)
+               clk_disable_unprepare(pre->clk);
+
+       return ret;
+}
+EXPORT_SYMBOL(ipu_pre_sdw_update);
+
+void ipu_pre_disable(int id)
+{
+       struct ipu_pre_data *pre = get_pre(id);
+       unsigned long lock_flags;
+
+       if (!pre)
+               return;
+
+       if (!pre->enabled)
+               return;
+
+       /* stop the pre engine */
+       spin_lock_irqsave(&pre->lock, lock_flags);
+       pre_write(pre, BF_PRE_CTRL_ENABLE(1), HW_PRE_CTRL_CLR);
+       pre_write(pre, BF_PRE_CTRL_SDW_UPDATE(1), HW_PRE_CTRL_SET);
+       pre_write(pre, BF_PRE_CTRL_SFTRST(1), HW_PRE_CTRL_SET);
+       spin_unlock_irqrestore(&pre->lock, lock_flags);
+
+       clk_disable_unprepare(pre->clk);
+
+       pre->enabled = false;
+}
+EXPORT_SYMBOL(ipu_pre_disable);
+
+int ipu_pre_set_fb_buffer(int id, unsigned long fb_paddr,
+                         unsigned int x_crop,
+                         unsigned int y_crop,
+                         unsigned int sec_buf_off,
+                         unsigned int trd_buf_off)
+{
+       struct ipu_pre_data *pre = get_pre(id);
+       unsigned long lock_flags;
+
+       if (!pre)
+               return -EINVAL;
+
+       spin_lock_irqsave(&pre->lock, lock_flags);
+       pre_write(pre, fb_paddr, HW_PRE_NEXT_BUF);
+       pre_write(pre, sec_buf_off, HW_PRE_U_BUF_OFFSET);
+       pre_write(pre, trd_buf_off, HW_PRE_V_BUF_OFFSET);
+       pre_write(pre, BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_X(x_crop) |
+                      BF_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC_OUTPUT_SIZE_ULC_Y(y_crop),
+                 HW_PRE_PREFETCH_ENGINE_OUTPUT_SIZE_ULC);
+       pre_write(pre, BF_PRE_CTRL_SDW_UPDATE(1), HW_PRE_CTRL_SET);
+       spin_unlock_irqrestore(&pre->lock, lock_flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_pre_set_fb_buffer);
+
+static int ipu_pre_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct ipu_pre_data *pre;
+       struct resource *res;
+       int id, irq, err;
+
+       pre = devm_kzalloc(&pdev->dev, sizeof(*pre), GFP_KERNEL);
+       if (!pre)
+               return -ENOMEM;
+       pre->dev = &pdev->dev;
+
+       id = of_alias_get_id(np, "pre");
+       if (id < 0) {
+               dev_err(&pdev->dev, "failed to get PRE id\n");
+               return id;
+       }
+       pre->id = id;
+
+       mutex_init(&pre->mutex);
+       spin_lock_init(&pre->lock);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       pre->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(pre->base))
+               return PTR_ERR(pre->base);
+
+       pre->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(pre->clk)) {
+               dev_err(&pdev->dev, "failed to get the pre clk\n");
+               return PTR_ERR(pre->clk);
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       err = devm_request_irq(&pdev->dev, irq, ipu_pre_irq_handle,
+                              IRQF_TRIGGER_RISING, pdev->name, pre);
+       if (err) {
+               dev_err(&pdev->dev, "failed to request pre irq\n");
+               return err;
+       }
+
+       pre->iram_pool = of_gen_pool_get(pdev->dev.of_node, "ocram", 0);
+       if (!pre->iram_pool) {
+               dev_err(&pdev->dev, "no iram exist for pre\n");
+               return -ENOMEM;
+       }
+
+       mutex_lock(&pre_list_lock);
+       list_add_tail(&pre->list, &pre_list);
+       mutex_unlock(&pre_list_lock);
+
+       ipu_pre_alloc_double_buffer(pre->id, IPU_PRE_MAX_WIDTH * 8 * IPU_PRE_MAX_BPP);
+
+       /* PRE GATE ON */
+       clk_prepare_enable(pre->clk);
+       pre_write(pre, BF_PRE_CTRL_SFTRST(1) | BF_PRE_CTRL_CLKGATE(1),
+                 HW_PRE_CTRL_CLR);
+       pre_write(pre, 0xf, HW_PRE_IRQ_MASK);
+       clk_disable_unprepare(pre->clk);
+
+       platform_set_drvdata(pdev, pre);
+
+       dev_info(&pdev->dev, "driver probed\n");
+
+       return 0;
+}
+
+static int ipu_pre_remove(struct platform_device *pdev)
+{
+       struct ipu_pre_data *pre = platform_get_drvdata(pdev);
+
+       if (pre->iram_pool && pre->double_buffer_base) {
+               gen_pool_free(pre->iram_pool,
+                             pre->double_buffer_base,
+                             pre->double_buffer_size);
+       }
+
+       mutex_lock(&pre_list_lock);
+       list_del(&pre->list);
+       mutex_unlock(&pre_list_lock);
+
+       return 0;
+}
+
+static const struct of_device_id imx_ipu_pre_dt_ids[] = {
+       { .compatible = "fsl,imx6q-pre", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_ipu_pre_dt_ids);
+
+static struct platform_driver ipu_pre_driver = {
+       .driver = {
+                       .name = "imx-pre",
+                       .of_match_table = of_match_ptr(imx_ipu_pre_dt_ids),
+                 },
+       .probe  = ipu_pre_probe,
+       .remove = ipu_pre_remove,
+};
+
+static int __init ipu_pre_init(void)
+{
+       return platform_driver_register(&ipu_pre_driver);
+}
+subsys_initcall(ipu_pre_init);
+
+static void __exit ipu_pre_exit(void)
+{
+       platform_driver_unregister(&ipu_pre_driver);
+}
+module_exit(ipu_pre_exit);
+
+MODULE_DESCRIPTION("i.MX PRE driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/ipu3/prg-regs.h b/drivers/mxc/ipu3/prg-regs.h
new file mode 100644 (file)
index 0000000..bca0e56
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Freescale IPU PRG Register Definitions
+ *
+ * Copyright 2014-2015 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
+ */
+#include <linux/bitops.h>
+
+#ifndef __IPU_PRG_H__
+#define __IPU_PRG_H__
+
+#define IPU_PR_CTRL             0x00
+#define IPU_PR_STATUS           0x04
+#define IPU_PR_QOS              0x08
+#define IPU_PR_REG_UPDATE       0x0c
+#define IPU_PR_STRIDE(ch)      (0x10 + (ch) * 4)
+#define IPU_PR_CROP_LINE        0x1c
+#define IPU_PR_ADDR_THD                 0x20
+#define IPU_PR_CH_BADDR(ch)    (0x24 + (ch) * 4)
+#define IPU_PR_CH_OFFSET(ch)   (0x30 + (ch) * 4)
+#define IPU_PR_CH_ILO(ch)      (0x3c + (ch) * 4)
+#define IPU_PR_CH_HEIGHT(ch)   (0x48 + (ch) * 4)
+
+#define IPU_PR_CTRL_CH_BYPASS(ch)              (0x1 << (ch))
+#define IPU_PR_CTRL_SOFT_CH_ARID(ch, n)                ((n) << ((ch) * 2 + 8))
+#define IPU_PR_CTRL_SOFT_CH_ARID_MASK(ch)      (0x3 << ((ch) * 2 + 8))
+#define IPU_PR_CTRL_CH_SO(ch, interlace)       ((interlace) << ((ch) + 16))
+#define IPU_PR_CTRL_CH_SO_MASK(ch)             (0x1 << ((ch) + 16))
+#define IPU_PR_CTRL_CH_VFLIP(ch, vflip)                ((vflip) << ((ch) + 19))
+#define IPU_PR_CTRL_CH_VFLIP_MASK(ch)          (0x1 << ((ch) + 19))
+#define IPU_PR_CTRL_CH_BLOCK_MODE(ch, mode)    ((mode) << ((ch) + 22))
+#define IPU_PR_CTRL_CH_BLOCK_MODE_MASK(ch)     (0x1 << ((ch) + 22))
+#define IPU_PR_CTRL_CH_CNT_LOAD_EN(ch)         (0x1 << ((ch) + 25))
+#define IPU_PR_CTRL_CH_CNT_LOAD_EN_MASK                (0x7 << 25)
+#define IPU_PR_CTRL_SOFTRST                    BIT(30)
+#define IPU_PR_CTRL_SHADOW_EN                  BIT(31)
+
+#define IPU_PR_STATUS_BUF_RDY(ch, buf)         (1 << ((ch) * 2 + (buf)))
+
+#define IPU_PR_QOS_PRI(id, qos)                        ((qos) << ((id) * 4))
+#define IPU_PR_QOS_MASK(id)                    (0xf << ((id) * 4))
+
+#define IPU_PR_REG_UPDATE_EN                   BIT(0)
+
+#define IPU_PR_STRIDE_MASK                     0x3fff
+
+#define IPU_PR_CROP_LINE_NUM(ch, n)            ((n) << ((ch) * 4))
+#define IPU_PR_CROP_LINE_MASK(ch)              (0xf << ((ch) * 4))
+
+#define IPU_PR_ADDR_THD_MASK                   0xffffffff
+
+#define IPU_PR_CH_BADDR_MASK                   0xffffffff
+
+#define IPU_PR_CH_OFFSET_MASK                  0xffffffff
+
+#define IPU_PR_CH_ILO_MASK                     0x007fffff
+#define IPU_PR_CH_ILO_NUM(ilo)                 ((ilo) & IPU_PR_CH_ILO_MASK)
+
+#define IPU_PR_CH_HEIGHT_MASK                  0x00000fff
+#define IPU_PR_CH_HEIGHT_NUM(fh)               (((fh) - 1) & IPU_PR_CH_HEIGHT_MASK)
+#define IPU_PR_CH_IPU_HEIGHT_MASK              0x0fff0000
+#define IPU_PR_CH_IPU_HEIGHT_NUM(fh)           ((((fh) - 1) << 16) & IPU_PR_CH_IPU_HEIGHT_MASK)
+
+#endif /* __IPU_PRG_H__ */
diff --git a/drivers/mxc/ipu3/prg.c b/drivers/mxc/ipu3/prg.c
new file mode 100644 (file)
index 0000000..45363a8
--- /dev/null
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
+ *
+ * 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
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ipu-v3-prg.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "prg-regs.h"
+
+#define PRG_CHAN_NUM   3
+
+struct prg_chan {
+       unsigned int pre_num;
+       struct mutex mutex;     /* for in_use */
+       bool in_use;
+};
+
+struct ipu_prg_data {
+       unsigned int id;
+       void __iomem *base;
+       unsigned long memory;
+       struct clk *axi_clk;
+       struct clk *apb_clk;
+       struct list_head list;
+       struct device *dev;
+       struct prg_chan chan[PRG_CHAN_NUM];
+       struct regmap *regmap;
+       struct regmap_field *pre_prg_sel[2];
+       spinlock_t lock;
+};
+
+static LIST_HEAD(prg_list);
+static DEFINE_MUTEX(prg_lock);
+
+static inline void prg_write(struct ipu_prg_data *prg,
+                       u32 value, unsigned int offset)
+{
+       writel(value, prg->base + offset);
+}
+
+static inline u32 prg_read(struct ipu_prg_data *prg, unsigned offset)
+{
+       return readl(prg->base + offset);
+}
+
+static struct ipu_prg_data *get_prg(unsigned int ipu_id)
+{
+       struct ipu_prg_data *prg;
+
+       mutex_lock(&prg_lock);
+       list_for_each_entry(prg, &prg_list, list) {
+               if (prg->id == ipu_id) {
+                       mutex_unlock(&prg_lock);
+                       return prg;
+               }
+       }
+       mutex_unlock(&prg_lock);
+
+       return NULL;
+}
+
+static int assign_prg_chan(struct ipu_prg_data *prg, unsigned int pre_num,
+                          ipu_channel_t ipu_ch)
+{
+       int prg_ch;
+
+       if (!prg)
+               return -EINVAL;
+
+       switch (ipu_ch) {
+       case MEM_BG_SYNC:
+               prg_ch = 0;
+               break;
+       case MEM_FG_SYNC:
+               prg_ch = 1;
+               break;
+       case MEM_DC_SYNC:
+               prg_ch = 2;
+               break;
+       default:
+               dev_err(prg->dev, "wrong ipu channel type\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&prg->chan[prg_ch].mutex);
+       if (!prg->chan[prg_ch].in_use) {
+               prg->chan[prg_ch].in_use = true;
+               prg->chan[prg_ch].pre_num = pre_num;
+
+               if (prg_ch != 0) {
+                       unsigned int pmux, psel;        /* primary */
+                       unsigned int smux, ssel;        /* secondary */
+                       struct regmap_field *pfield, *sfield;
+
+                       psel = pre_num - 1;
+                       ssel = psel ? 0 : 1;
+
+                       pfield = prg->pre_prg_sel[psel];
+                       sfield = prg->pre_prg_sel[ssel];
+                       pmux = (prg_ch - 1) + (prg->id << 1);
+
+                       mutex_lock(&prg_lock);
+                       regmap_field_write(pfield, pmux);
+
+                       /*
+                        * PRE1 and PRE2 cannot bind with a same channel of
+                        * one PRG even if one of the two PREs is disabled.
+                        */
+                       regmap_field_read(sfield, &smux);
+                       if (smux == pmux) {
+                               smux = pmux ^ 0x1;
+                               regmap_field_write(sfield, smux);
+                       }
+                       mutex_unlock(&prg_lock);
+               }
+               mutex_unlock(&prg->chan[prg_ch].mutex);
+               dev_dbg(prg->dev, "bind prg%u ch%d with pre%u\n",
+                               prg->id, prg_ch, pre_num);
+               return prg_ch;
+       }
+       mutex_unlock(&prg->chan[prg_ch].mutex);
+       return -EBUSY;
+}
+
+static inline int get_prg_chan(struct ipu_prg_data *prg, unsigned int pre_num)
+{
+       int i;
+
+       if (!prg)
+               return -EINVAL;
+
+       for (i = 0; i < PRG_CHAN_NUM; i++) {
+               mutex_lock(&prg->chan[i].mutex);
+               if (prg->chan[i].in_use &&
+                   prg->chan[i].pre_num == pre_num) {
+                       mutex_unlock(&prg->chan[i].mutex);
+                       return i;
+               }
+               mutex_unlock(&prg->chan[i].mutex);
+       }
+       return -ENOENT;
+}
+
+int ipu_prg_config(struct ipu_prg_config *config)
+{
+       struct ipu_prg_data *prg = get_prg(config->id);
+       struct ipu_soc *ipu = ipu_get_soc(config->id);
+       int prg_ch, axi_id;
+       u32 reg;
+
+       if (!prg || config->crop_line > 3 || !ipu)
+               return -EINVAL;
+
+       if (config->height & ~IPU_PR_CH_HEIGHT_MASK)
+               return -EINVAL;
+
+       prg_ch = assign_prg_chan(prg, config->pre_num, config->ipu_ch);
+       if (prg_ch < 0)
+               return prg_ch;
+
+       axi_id = ipu_ch_param_get_axi_id(ipu, config->ipu_ch, IPU_INPUT_BUFFER);
+
+       clk_prepare_enable(prg->axi_clk);
+       clk_prepare_enable(prg->apb_clk);
+
+       spin_lock(&prg->lock);
+       /* clear all load enable to impact other channels */
+       reg = prg_read(prg, IPU_PR_CTRL);
+       reg &= ~IPU_PR_CTRL_CH_CNT_LOAD_EN_MASK;
+       prg_write(prg, reg, IPU_PR_CTRL);
+
+       /* counter load enable */
+       reg = prg_read(prg, IPU_PR_CTRL);
+       reg |= IPU_PR_CTRL_CH_CNT_LOAD_EN(prg_ch);
+       prg_write(prg, reg, IPU_PR_CTRL);
+
+       /* AXI ID */
+       reg = prg_read(prg, IPU_PR_CTRL);
+       reg &= ~IPU_PR_CTRL_SOFT_CH_ARID_MASK(prg_ch);
+       reg |= IPU_PR_CTRL_SOFT_CH_ARID(prg_ch, axi_id);
+       prg_write(prg, reg, IPU_PR_CTRL);
+
+       /* so */
+       reg = prg_read(prg, IPU_PR_CTRL);
+       reg &= ~IPU_PR_CTRL_CH_SO_MASK(prg_ch);
+       reg |= IPU_PR_CTRL_CH_SO(prg_ch, config->so);
+       prg_write(prg, reg, IPU_PR_CTRL);
+
+       /* vflip */
+       reg = prg_read(prg, IPU_PR_CTRL);
+       reg &= ~IPU_PR_CTRL_CH_VFLIP_MASK(prg_ch);
+       reg |= IPU_PR_CTRL_CH_VFLIP(prg_ch, config->vflip);
+       prg_write(prg, reg, IPU_PR_CTRL);
+
+       /* block mode */
+       reg = prg_read(prg, IPU_PR_CTRL);
+       reg &= ~IPU_PR_CTRL_CH_BLOCK_MODE_MASK(prg_ch);
+       reg |= IPU_PR_CTRL_CH_BLOCK_MODE(prg_ch, config->block_mode);
+       prg_write(prg, reg, IPU_PR_CTRL);
+
+       /* disable bypass */
+       reg = prg_read(prg, IPU_PR_CTRL);
+       reg &= ~IPU_PR_CTRL_CH_BYPASS(prg_ch);
+       prg_write(prg, reg, IPU_PR_CTRL);
+
+       /* stride */
+       reg = prg_read(prg, IPU_PR_STRIDE(prg_ch));
+       reg &= ~IPU_PR_STRIDE_MASK;
+       reg |= config->stride - 1;
+       prg_write(prg, reg, IPU_PR_STRIDE(prg_ch));
+
+       /* ilo */
+       reg = prg_read(prg, IPU_PR_CH_ILO(prg_ch));
+       reg &= ~IPU_PR_CH_ILO_MASK;
+       reg |= IPU_PR_CH_ILO_NUM(config->ilo);
+       prg_write(prg, reg, IPU_PR_CH_ILO(prg_ch));
+
+       /* height */
+       reg = prg_read(prg, IPU_PR_CH_HEIGHT(prg_ch));
+       reg &= ~IPU_PR_CH_HEIGHT_MASK;
+       reg |= IPU_PR_CH_HEIGHT_NUM(config->height);
+       prg_write(prg, reg, IPU_PR_CH_HEIGHT(prg_ch));
+
+       /* ipu height */
+       reg = prg_read(prg, IPU_PR_CH_HEIGHT(prg_ch));
+       reg &= ~IPU_PR_CH_IPU_HEIGHT_MASK;
+       reg |= IPU_PR_CH_IPU_HEIGHT_NUM(config->ipu_height);
+       prg_write(prg, reg, IPU_PR_CH_HEIGHT(prg_ch));
+
+       /* crop */
+       reg = prg_read(prg, IPU_PR_CROP_LINE);
+       reg &= ~IPU_PR_CROP_LINE_MASK(prg_ch);
+       reg |= IPU_PR_CROP_LINE_NUM(prg_ch, config->crop_line);
+       prg_write(prg, reg, IPU_PR_CROP_LINE);
+
+       /* buffer address */
+       reg = prg_read(prg, IPU_PR_CH_BADDR(prg_ch));
+       reg &= ~IPU_PR_CH_BADDR_MASK;
+       reg |= config->baddr;
+       prg_write(prg, reg, IPU_PR_CH_BADDR(prg_ch));
+
+       /* offset */
+       reg = prg_read(prg, IPU_PR_CH_OFFSET(prg_ch));
+       reg &= ~IPU_PR_CH_OFFSET_MASK;
+       reg |= config->offset;
+       prg_write(prg, reg, IPU_PR_CH_OFFSET(prg_ch));
+
+       /* threshold */
+       reg = prg_read(prg, IPU_PR_ADDR_THD);
+       reg &= ~IPU_PR_ADDR_THD_MASK;
+       reg |= prg->memory;
+       prg_write(prg, reg, IPU_PR_ADDR_THD);
+
+       /* shadow enable */
+       reg = prg_read(prg, IPU_PR_CTRL);
+       reg |= IPU_PR_CTRL_SHADOW_EN;
+       prg_write(prg, reg, IPU_PR_CTRL);
+
+       /* register update */
+       reg = prg_read(prg, IPU_PR_REG_UPDATE);
+       reg |= IPU_PR_REG_UPDATE_EN;
+       prg_write(prg, reg, IPU_PR_REG_UPDATE);
+       spin_unlock(&prg->lock);
+
+       clk_disable_unprepare(prg->apb_clk);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_prg_config);
+
+int ipu_prg_disable(unsigned int ipu_id, unsigned int pre_num)
+{
+       struct ipu_prg_data *prg = get_prg(ipu_id);
+       int prg_ch;
+       u32 reg;
+
+       if (!prg)
+               return -EINVAL;
+
+       prg_ch = get_prg_chan(prg, pre_num);
+       if (prg_ch < 0)
+               return prg_ch;
+
+       clk_prepare_enable(prg->apb_clk);
+
+       spin_lock(&prg->lock);
+       /* clear all load enable to impact other channels */
+       reg = prg_read(prg, IPU_PR_CTRL);
+       reg &= ~IPU_PR_CTRL_CH_CNT_LOAD_EN_MASK;
+       prg_write(prg, reg, IPU_PR_CTRL);
+
+       /* counter load enable */
+       reg = prg_read(prg, IPU_PR_CTRL);
+       reg |= IPU_PR_CTRL_CH_CNT_LOAD_EN(prg_ch);
+       prg_write(prg, reg, IPU_PR_CTRL);
+
+       /* enable bypass */
+       reg = prg_read(prg, IPU_PR_CTRL);
+       reg |= IPU_PR_CTRL_CH_BYPASS(prg_ch);
+       prg_write(prg, reg, IPU_PR_CTRL);
+
+       /* shadow enable */
+       reg = prg_read(prg, IPU_PR_CTRL);
+       reg |= IPU_PR_CTRL_SHADOW_EN;
+       prg_write(prg, reg, IPU_PR_CTRL);
+
+       /* register update */
+       reg = prg_read(prg, IPU_PR_REG_UPDATE);
+       reg |= IPU_PR_REG_UPDATE_EN;
+       prg_write(prg, reg, IPU_PR_REG_UPDATE);
+       spin_unlock(&prg->lock);
+
+       clk_disable_unprepare(prg->apb_clk);
+       clk_disable_unprepare(prg->axi_clk);
+
+       mutex_lock(&prg->chan[prg_ch].mutex);
+       prg->chan[prg_ch].in_use = false;
+       mutex_unlock(&prg->chan[prg_ch].mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_prg_disable);
+
+int ipu_prg_wait_buf_ready(unsigned int ipu_id, unsigned int pre_num,
+                          unsigned int hsk_line_num,
+                          int pre_store_out_height)
+{
+       struct ipu_prg_data *prg = get_prg(ipu_id);
+       int prg_ch, timeout = 1000;
+       u32 reg;
+
+       if (!prg)
+               return -EINVAL;
+
+       prg_ch = get_prg_chan(prg, pre_num);
+       if (prg_ch < 0)
+               return prg_ch;
+
+       clk_prepare_enable(prg->apb_clk);
+
+       spin_lock(&prg->lock);
+       if (pre_store_out_height <= (4 << hsk_line_num)) {
+               do {
+                       reg = prg_read(prg, IPU_PR_STATUS);
+                       udelay(1000);
+                       timeout--;
+               } while (!(reg & IPU_PR_STATUS_BUF_RDY(prg_ch, 0)) && timeout);
+       } else {
+               do {
+                       reg = prg_read(prg, IPU_PR_STATUS);
+                       udelay(1000);
+                       timeout--;
+               } while ((!(reg & IPU_PR_STATUS_BUF_RDY(prg_ch, 0)) ||
+                         !(reg & IPU_PR_STATUS_BUF_RDY(prg_ch, 1))) && timeout);
+       }
+       spin_unlock(&prg->lock);
+
+       clk_disable_unprepare(prg->apb_clk);
+
+       if (!timeout)
+               dev_err(prg->dev, "wait for buffer ready timeout\n");
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_prg_wait_buf_ready);
+
+static int ipu_prg_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node, *memory;
+       struct ipu_prg_data *prg;
+       struct resource *res;
+       struct reg_field reg_field0 = REG_FIELD(IOMUXC_GPR5,
+                                               IMX6Q_GPR5_PRE_PRG_SEL0_LSB,
+                                               IMX6Q_GPR5_PRE_PRG_SEL0_MSB);
+       struct reg_field reg_field1 = REG_FIELD(IOMUXC_GPR5,
+                                               IMX6Q_GPR5_PRE_PRG_SEL1_LSB,
+                                               IMX6Q_GPR5_PRE_PRG_SEL1_MSB);
+       int id, i;
+
+       prg = devm_kzalloc(&pdev->dev, sizeof(*prg), GFP_KERNEL);
+       if (!prg)
+               return -ENOMEM;
+       prg->dev = &pdev->dev;
+
+       for (i = 0; i < PRG_CHAN_NUM; i++)
+               mutex_init(&prg->chan[i].mutex);
+
+       spin_lock_init(&prg->lock);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       prg->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(prg->base))
+               return PTR_ERR(prg->base);
+
+       prg->axi_clk = devm_clk_get(&pdev->dev, "axi");
+       if (IS_ERR(prg->axi_clk)) {
+               dev_err(&pdev->dev, "failed to get the axi clk\n");
+               return PTR_ERR(prg->axi_clk);
+       }
+
+       prg->apb_clk = devm_clk_get(&pdev->dev, "apb");
+       if (IS_ERR(prg->apb_clk)) {
+               dev_err(&pdev->dev, "failed to get the apb clk\n");
+               return PTR_ERR(prg->apb_clk);
+       }
+
+       prg->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
+       if (IS_ERR(prg->regmap)) {
+               dev_err(&pdev->dev, "failed to get regmap\n");
+               return PTR_ERR(prg->regmap);
+       }
+
+       prg->pre_prg_sel[0] = devm_regmap_field_alloc(&pdev->dev, prg->regmap,
+                                                       reg_field0);
+       if (IS_ERR(prg->pre_prg_sel[0]))
+               return PTR_ERR(prg->pre_prg_sel[0]);
+
+       prg->pre_prg_sel[1] = devm_regmap_field_alloc(&pdev->dev, prg->regmap,
+                                                       reg_field1);
+       if (IS_ERR(prg->pre_prg_sel[1]))
+               return PTR_ERR(prg->pre_prg_sel[1]);
+
+       memory = of_parse_phandle(np, "memory-region", 0);
+       if (!memory)
+               return -ENODEV;
+
+       prg->memory = of_translate_address(memory,
+                               of_get_address(memory, 0, NULL, NULL));
+
+       id = of_alias_get_id(np, "prg");
+       if (id < 0) {
+               dev_err(&pdev->dev, "failed to get PRG id\n");
+               return id;
+       }
+       prg->id = id;
+
+       mutex_lock(&prg_lock);
+       list_add_tail(&prg->list, &prg_list);
+       mutex_unlock(&prg_lock);
+
+       platform_set_drvdata(pdev, prg);
+
+       dev_info(&pdev->dev, "driver probed\n");
+
+       return 0;
+}
+
+static int ipu_prg_remove(struct platform_device *pdev)
+{
+       struct ipu_prg_data *prg = platform_get_drvdata(pdev);
+
+       mutex_lock(&prg_lock);
+       list_del(&prg->list);
+       mutex_unlock(&prg_lock);
+
+       return 0;
+}
+
+static const struct of_device_id imx_ipu_prg_dt_ids[] = {
+       { .compatible = "fsl,imx6q-prg", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_ipu_prg_dt_ids);
+
+static struct platform_driver ipu_prg_driver = {
+       .driver = {
+                       .name = "imx-prg",
+                       .of_match_table = of_match_ptr(imx_ipu_prg_dt_ids),
+                 },
+       .probe  = ipu_prg_probe,
+       .remove = ipu_prg_remove,
+};
+
+static int __init ipu_prg_init(void)
+{
+       return platform_driver_register(&ipu_prg_driver);
+}
+subsys_initcall(ipu_prg_init);
+
+static void __exit ipu_prg_exit(void)
+{
+       platform_driver_unregister(&ipu_prg_driver);
+}
+module_exit(ipu_prg_exit);
+
+MODULE_DESCRIPTION("i.MX PRG driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/ipu3/vdoa.c b/drivers/mxc/ipu3/vdoa.c
new file mode 100644 (file)
index 0000000..68194c9
--- /dev/null
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2012-2015 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
+ */
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include <linux/genalloc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "vdoa.h"
+/* 6band(3field* double buffer) * (width*2) * bandline(8)
+       = 6x1024x2x8 = 96k or 72k(1.5byte) */
+#define MAX_VDOA_IRAM_SIZE     (1024*96)
+#define VDOA_IRAM_SIZE         (1024*72)
+
+#define VDOAC_BAND_HEIGHT_32LINES      (32)
+#define VDOAC_BAND_HEIGHT_16LINES      (16)
+#define VDOAC_BAND_HEIGHT_8LINES       (8)
+#define VDOAC_THREE_FRAMES             (0x1 << 2)
+#define VDOAC_SYNC_BAND_MODE           (0x1 << 3)
+#define VDOAC_SCAN_ORDER_INTERLACED    (0x1 << 4)
+#define VDOAC_PFS_YUYV                 (0x1 << 5)
+#define VDOAC_IPU_SEL_1                        (0x1 << 6)
+#define VDOAFP_FH_MASK                 (0x1FFF)
+#define VDOAFP_FH_SHIFT                        (16)
+#define VDOAFP_FW_MASK                 (0x3FFF)
+#define VDOAFP_FW_SHIFT                        (0)
+#define VDOASL_VSLY_MASK               (0x3FFF)
+#define VDOASL_VSLY_SHIFT              (16)
+#define VDOASL_ISLY_MASK               (0x7FFF)
+#define VDOASL_ISLY_SHIFT              (0)
+#define VDOASRR_START_XFER             (0x2)
+#define VDOASRR_SWRST                  (0x1)
+#define VDOAIEIST_TRANSFER_ERR         (0x2)
+#define VDOAIEIST_TRANSFER_END         (0x1)
+
+#define        VDOAC           (0x0)   /* Control Register */
+#define        VDOASRR         (0x4)   /* Start and Reset Register */
+#define        VDOAIE          (0x8)   /* Interrupt Enable Register */
+#define        VDOAIST         (0xc)   /* Interrupt Status Register */
+#define        VDOAFP          (0x10)  /* Frame Parameters Register */
+#define        VDOAIEBA00      (0x14)  /* External Buffer n Frame m Address Register */
+#define        VDOAIEBA01      (0x18)  /* External Buffer n Frame m Address Register */
+#define        VDOAIEBA02      (0x1c)  /* External Buffer n Frame m Address Register */
+#define        VDOAIEBA10      (0x20)  /* External Buffer n Frame m Address Register */
+#define        VDOAIEBA11      (0x24)  /* External Buffer n Frame m Address Register */
+#define        VDOAIEBA12      (0x28)  /* External Buffer n Frame m Address Register */
+#define        VDOASL          (0x2c)  /* IPU Stride Line Register */
+#define        VDOAIUBO        (0x30)  /* IPU Chroma Buffer Offset Register */
+#define        VDOAVEBA0       (0x34)  /* External Buffer m Address Register */
+#define        VDOAVEBA1       (0x38)  /* External Buffer m Address Register */
+#define        VDOAVEBA2       (0x3c)  /* External Buffer m Address Register */
+#define        VDOAVUBO        (0x40)  /* VPU Chroma Buffer Offset */
+#define        VDOASR          (0x44)  /* Status Register */
+#define        VDOATD          (0x48)  /* Test Debug Register */
+
+
+enum {
+       VDOA_INIT       = 0x1,
+       VDOA_GET        = 0x2,
+       VDOA_SETUP      = 0x4,
+       VDOA_GET_OBUF   = 0x8,
+       VDOA_START      = 0x10,
+       VDOA_INIRQ      = 0x20,
+       VDOA_STOP       = 0x40,
+       VDOA_PUT        = VDOA_INIT,
+};
+
+enum {
+       VDOA_NULL       = 0,
+       VDOA_FRAME      = 1,
+       VDOA_PREV_FIELD = 2,
+       VDOA_CURR_FIELD = 3,
+       VDOA_NEXT_FIELD = 4,
+};
+
+#define CHECK_STATE(expect, retcode)                                   \
+do {                                                                   \
+       if (!((expect) & vdoa->state)) {                                \
+               dev_err(vdoa->dev, "ERR: %s state:0x%x, expect:0x%x.\n",\
+                               __func__, vdoa->state, (expect));       \
+               retcode;                                                \
+       }                                                               \
+} while (0)
+
+#define CHECK_NULL_PTR(ptr)                                            \
+do {                                                                   \
+       pr_debug("vdoa_ptr:0x%p in %s state:0x%x.\n",                   \
+                       vdoa, __func__, vdoa->state);                   \
+       if (NULL == (ptr)) {                                            \
+               pr_err("ERR vdoa: %s state:0x%x null ptr.\n",           \
+                               __func__, vdoa->state);                 \
+       }                                                               \
+} while (0)
+
+struct vdoa_info {
+       int             state;
+       struct device   *dev;
+       struct clk      *vdoa_clk;
+       void __iomem    *reg_base;
+       struct gen_pool *iram_pool;
+       unsigned long   iram_base;
+       unsigned long   iram_paddr;
+       int             irq;
+       int             field;
+       struct completion comp;
+};
+
+static struct vdoa_info *g_vdoa;
+static unsigned long iram_size;
+static DEFINE_MUTEX(vdoa_lock);
+
+static inline void vdoa_read_register(struct vdoa_info *vdoa,
+                               u32 reg, u32 *val)
+{
+       *val = ioread32(vdoa->reg_base + reg);
+       dev_dbg(vdoa->dev, "read_reg:0x%02x, val:0x%08x.\n", reg, *val);
+}
+
+static inline void vdoa_write_register(struct vdoa_info *vdoa,
+                               u32 reg, u32 val)
+{
+       iowrite32(val, vdoa->reg_base + reg);
+       dev_dbg(vdoa->dev, "\t\twrite_reg:0x%02x, val:0x%08x.\n", reg, val);
+}
+
+static void dump_registers(struct vdoa_info *vdoa)
+{
+       int i;
+       u32 data;
+
+       for (i = VDOAC; i < VDOATD; i += 4)
+               vdoa_read_register(vdoa, i, &data);
+}
+
+int vdoa_setup(vdoa_handle_t handle, struct vdoa_params *params)
+{
+       int     band_size;
+       int     total_band_size = 0;
+       int     ipu_stride;
+       u32     data;
+       struct vdoa_info *vdoa = (struct vdoa_info *)handle;
+
+       CHECK_NULL_PTR(vdoa);
+       CHECK_STATE(VDOA_GET | VDOA_GET_OBUF | VDOA_STOP, return -EINVAL);
+       if (VDOA_GET == vdoa->state) {
+               dev_dbg(vdoa->dev, "w:%d, h:%d.\n",
+                        params->width, params->height);
+               data = (params->band_lines == VDOAC_BAND_HEIGHT_32LINES) ? 2 :
+                       ((params->band_lines == VDOAC_BAND_HEIGHT_16LINES) ?
+                                1 : 0);
+               data |= params->scan_order ? VDOAC_SCAN_ORDER_INTERLACED : 0;
+               data |= params->band_mode ? VDOAC_SYNC_BAND_MODE : 0;
+               data |= params->pfs ? VDOAC_PFS_YUYV : 0;
+               data |= params->ipu_num ? VDOAC_IPU_SEL_1 : 0;
+               vdoa_write_register(vdoa, VDOAC, data);
+
+               data = ((params->width & VDOAFP_FW_MASK) << VDOAFP_FW_SHIFT) |
+                       ((params->height & VDOAFP_FH_MASK) << VDOAFP_FH_SHIFT);
+               vdoa_write_register(vdoa, VDOAFP, data);
+
+               ipu_stride = params->pfs ? params->width << 1 : params->width;
+               data = ((params->vpu_stride & VDOASL_VSLY_MASK) <<
+                                                       VDOASL_VSLY_SHIFT) |
+                       ((ipu_stride & VDOASL_ISLY_MASK) << VDOASL_ISLY_SHIFT);
+               vdoa_write_register(vdoa, VDOASL, data);
+
+               dev_dbg(vdoa->dev, "band_mode:%d, band_line:%d, base:0x%lx.\n",
+               params->band_mode, params->band_lines, vdoa->iram_paddr);
+       }
+       /*
+        * band size    = (luma_per_line + chroma_per_line) * bandLines
+        *              = width * (3/2 or 2) * bandLines
+        * double buffer mode used.
+        */
+       if (params->pfs)
+               band_size = (params->width << 1) * params->band_lines;
+       else
+               band_size = ((params->width * 3) >> 1) *
+                                               params->band_lines;
+       if (params->interlaced) {
+               total_band_size = 6 * band_size; /* 3 frames*double buffer */
+               if (iram_size < total_band_size) {
+                       dev_err(vdoa->dev, "iram_size:0x%lx is smaller than "
+                               "request:0x%x!\n", iram_size, total_band_size);
+                       return -EINVAL;
+               }
+               if (params->vfield_buf.prev_veba) {
+                       if (params->band_mode) {
+                               vdoa_write_register(vdoa, VDOAIEBA00,
+                                                       vdoa->iram_paddr);
+                               vdoa_write_register(vdoa, VDOAIEBA10,
+                                                vdoa->iram_paddr + band_size);
+                       } else
+                               vdoa_write_register(vdoa, VDOAIEBA00,
+                                                       params->ieba0);
+                       vdoa_write_register(vdoa, VDOAVEBA0,
+                                       params->vfield_buf.prev_veba);
+                       vdoa->field = VDOA_PREV_FIELD;
+               }
+               if (params->vfield_buf.cur_veba) {
+                       if (params->band_mode) {
+                               vdoa_write_register(vdoa, VDOAIEBA01,
+                                        vdoa->iram_paddr + band_size * 2);
+                               vdoa_write_register(vdoa, VDOAIEBA11,
+                                        vdoa->iram_paddr + band_size * 3);
+                       } else
+                               vdoa_write_register(vdoa, VDOAIEBA01,
+                                                       params->ieba1);
+                       vdoa_write_register(vdoa, VDOAVEBA1,
+                                       params->vfield_buf.cur_veba);
+                       vdoa->field = VDOA_CURR_FIELD;
+               }
+               if (params->vfield_buf.next_veba) {
+                       if (params->band_mode) {
+                               vdoa_write_register(vdoa, VDOAIEBA02,
+                                        vdoa->iram_paddr + band_size * 4);
+                               vdoa_write_register(vdoa, VDOAIEBA12,
+                                        vdoa->iram_paddr + band_size * 5);
+                       } else
+                               vdoa_write_register(vdoa, VDOAIEBA02,
+                                                       params->ieba2);
+                       vdoa_write_register(vdoa, VDOAVEBA2,
+                                       params->vfield_buf.next_veba);
+                       vdoa->field = VDOA_NEXT_FIELD;
+                       vdoa_read_register(vdoa, VDOAC, &data);
+                       data |= VDOAC_THREE_FRAMES;
+                       vdoa_write_register(vdoa, VDOAC, data);
+               }
+
+               if (!params->pfs)
+                       vdoa_write_register(vdoa, VDOAIUBO,
+                                params->width * params->band_lines);
+               vdoa_write_register(vdoa, VDOAVUBO,
+                                params->vfield_buf.vubo);
+               dev_dbg(vdoa->dev, "total band_size:0x%x.\n", band_size*6);
+       } else if (params->band_mode) {
+               /* used for progressive frame resize on PrP channel */
+               BUG(); /* currently not support */
+               /* progressvie frame: band mode */
+               vdoa_write_register(vdoa, VDOAIEBA00, vdoa->iram_paddr);
+               vdoa_write_register(vdoa, VDOAIEBA10,
+                                        vdoa->iram_paddr + band_size);
+               if (!params->pfs)
+                       vdoa_write_register(vdoa, VDOAIUBO,
+                                       params->width * params->band_lines);
+               dev_dbg(vdoa->dev, "total band_size:0x%x\n", band_size*2);
+       } else {
+               /* progressive frame: mem->mem, non-band mode */
+               vdoa->field = VDOA_FRAME;
+               vdoa_write_register(vdoa, VDOAVEBA0, params->vframe_buf.veba);
+               vdoa_write_register(vdoa, VDOAVUBO, params->vframe_buf.vubo);
+               vdoa_write_register(vdoa, VDOAIEBA00, params->ieba0);
+               if (!params->pfs)
+                       /* note: iubo is relative value, based on ieba0 */
+                       vdoa_write_register(vdoa, VDOAIUBO,
+                                       params->width * params->height);
+       }
+       vdoa->state = VDOA_SETUP;
+       return 0;
+}
+
+void vdoa_get_output_buf(vdoa_handle_t handle, struct vdoa_ipu_buf *buf)
+{
+       u32     data;
+       struct vdoa_info *vdoa = (struct vdoa_info *)handle;
+
+       CHECK_NULL_PTR(vdoa);
+       CHECK_STATE(VDOA_SETUP, return);
+       vdoa->state = VDOA_GET_OBUF;
+       memset(buf, 0, sizeof(*buf));
+
+       vdoa_read_register(vdoa, VDOAC, &data);
+       switch (vdoa->field) {
+       case VDOA_FRAME:
+       case VDOA_PREV_FIELD:
+               vdoa_read_register(vdoa, VDOAIEBA00, &buf->ieba0);
+               if (data & VDOAC_SYNC_BAND_MODE)
+                       vdoa_read_register(vdoa, VDOAIEBA10, &buf->ieba1);
+               break;
+       case VDOA_CURR_FIELD:
+               vdoa_read_register(vdoa, VDOAIEBA01, &buf->ieba0);
+               vdoa_read_register(vdoa, VDOAIEBA11, &buf->ieba1);
+               break;
+       case VDOA_NEXT_FIELD:
+               vdoa_read_register(vdoa, VDOAIEBA02, &buf->ieba0);
+               vdoa_read_register(vdoa, VDOAIEBA12, &buf->ieba1);
+               break;
+       default:
+               BUG();
+               break;
+       }
+       if (!(data & VDOAC_PFS_YUYV))
+               vdoa_read_register(vdoa, VDOAIUBO, &buf->iubo);
+}
+
+int vdoa_start(vdoa_handle_t handle, int timeout_ms)
+{
+       int ret;
+       struct vdoa_info *vdoa = (struct vdoa_info *)handle;
+
+       CHECK_NULL_PTR(vdoa);
+       CHECK_STATE(VDOA_GET_OBUF, return -EINVAL);
+       vdoa->state = VDOA_START;
+       init_completion(&vdoa->comp);
+       vdoa_write_register(vdoa, VDOAIST,
+                       VDOAIEIST_TRANSFER_ERR | VDOAIEIST_TRANSFER_END);
+       vdoa_write_register(vdoa, VDOAIE,
+                       VDOAIEIST_TRANSFER_ERR | VDOAIEIST_TRANSFER_END);
+
+       enable_irq(vdoa->irq);
+       vdoa_write_register(vdoa, VDOASRR, VDOASRR_START_XFER);
+       dump_registers(vdoa);
+
+       ret = wait_for_completion_timeout(&vdoa->comp,
+                       msecs_to_jiffies(timeout_ms));
+
+       return ret > 0 ? 0 : -ETIMEDOUT;
+}
+
+void vdoa_stop(vdoa_handle_t handle)
+{
+       struct vdoa_info *vdoa = (struct vdoa_info *)handle;
+
+       CHECK_NULL_PTR(vdoa);
+       CHECK_STATE(VDOA_GET | VDOA_START | VDOA_INIRQ, return);
+       vdoa->state = VDOA_STOP;
+
+       disable_irq(vdoa->irq);
+
+       vdoa_write_register(vdoa, VDOASRR, VDOASRR_SWRST);
+}
+
+void vdoa_get_handle(vdoa_handle_t *handle)
+{
+       struct vdoa_info *vdoa = g_vdoa;
+
+       CHECK_NULL_PTR(handle);
+       *handle = (vdoa_handle_t *)NULL;
+       CHECK_STATE(VDOA_INIT, return);
+       mutex_lock(&vdoa_lock);
+       clk_prepare_enable(vdoa->vdoa_clk);
+       vdoa->state = VDOA_GET;
+       vdoa->field = VDOA_NULL;
+       vdoa_write_register(vdoa, VDOASRR, VDOASRR_SWRST);
+
+       *handle = (vdoa_handle_t *)vdoa;
+}
+
+void vdoa_put_handle(vdoa_handle_t *handle)
+{
+       struct vdoa_info *vdoa = (struct vdoa_info *)(*handle);
+
+       CHECK_NULL_PTR(vdoa);
+       CHECK_STATE(VDOA_STOP, return);
+       if (vdoa != g_vdoa)
+               BUG();
+
+       clk_disable_unprepare(vdoa->vdoa_clk);
+       vdoa->state = VDOA_PUT;
+       *handle = (vdoa_handle_t *)NULL;
+       mutex_unlock(&vdoa_lock);
+}
+
+static irqreturn_t vdoa_irq_handler(int irq, void *data)
+{
+       u32 status, mask, val;
+       struct vdoa_info *vdoa = data;
+
+       CHECK_NULL_PTR(vdoa);
+       CHECK_STATE(VDOA_START, return IRQ_HANDLED);
+       vdoa->state = VDOA_INIRQ;
+       vdoa_read_register(vdoa, VDOAIST, &status);
+       vdoa_read_register(vdoa, VDOAIE, &mask);
+       val = status & mask;
+       vdoa_write_register(vdoa, VDOAIST, val);
+       if (VDOAIEIST_TRANSFER_ERR & val)
+               dev_err(vdoa->dev, "vdoa Transfer err irq!\n");
+       if (VDOAIEIST_TRANSFER_END & val)
+               dev_dbg(vdoa->dev, "vdoa Transfer end irq!\n");
+       if (0 == val) {
+               dev_err(vdoa->dev, "vdoa unknown irq!\n");
+               BUG();
+       }
+
+       complete(&vdoa->comp);
+       return IRQ_HANDLED;
+}
+
+/* IRAM Size in Kbytes, example:vdoa_iram_size=64, 64KBytes */
+static int __init vdoa_iram_size_setup(char *options)
+{
+       int ret;
+
+       ret = kstrtoul(options, 0, &iram_size);
+       if (ret)
+               iram_size = 0;
+       else
+               iram_size *= SZ_1K;
+
+       return 1;
+}
+__setup("vdoa_iram_size=", vdoa_iram_size_setup);
+
+static const struct of_device_id imx_vdoa_dt_ids[] = {
+       { .compatible = "fsl,imx6q-vdoa", },
+       { /* sentinel */ }
+};
+
+static int vdoa_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct vdoa_info *vdoa;
+       struct resource *res;
+       struct resource *res_irq;
+       struct device   *dev = &pdev->dev;
+       struct device_node *np = pdev->dev.of_node;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "can't get device resources\n");
+               return -ENOENT;
+       }
+
+       res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res_irq) {
+               dev_err(dev, "failed to get irq resource\n");
+               return -ENOENT;
+       }
+
+       vdoa = devm_kzalloc(dev, sizeof(struct vdoa_info), GFP_KERNEL);
+       if (!vdoa)
+               return -ENOMEM;
+       vdoa->dev = dev;
+
+       vdoa->reg_base = devm_ioremap_resource(&pdev->dev, res);
+       if (!vdoa->reg_base)
+               return -EBUSY;
+
+       vdoa->irq = res_irq->start;
+       ret = devm_request_irq(dev, vdoa->irq, vdoa_irq_handler, 0,
+                               "vdoa", vdoa);
+       if (ret) {
+               dev_err(dev, "can't claim irq %d\n", vdoa->irq);
+               return ret;
+       }
+       disable_irq(vdoa->irq);
+
+       vdoa->vdoa_clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(vdoa->vdoa_clk)) {
+               dev_err(dev, "failed to get vdoa_clk\n");
+               return PTR_ERR(vdoa->vdoa_clk);
+       }
+
+       vdoa->iram_pool = of_gen_pool_get(np, "iram", 0);
+       if (!vdoa->iram_pool) {
+               dev_err(&pdev->dev, "iram pool not available\n");
+               return -ENOMEM;
+       }
+
+       if ((iram_size == 0) || (iram_size > MAX_VDOA_IRAM_SIZE))
+               iram_size = VDOA_IRAM_SIZE;
+
+       vdoa->iram_base = gen_pool_alloc(vdoa->iram_pool, iram_size);
+       if (!vdoa->iram_base) {
+               dev_err(&pdev->dev, "unable to alloc iram\n");
+               return -ENOMEM;
+       }
+
+       vdoa->iram_paddr = gen_pool_virt_to_phys(vdoa->iram_pool,
+                                                vdoa->iram_base);
+
+       dev_dbg(dev, "iram_base:0x%lx,iram_paddr:0x%lx,size:0x%lx\n",
+                vdoa->iram_base, vdoa->iram_paddr, iram_size);
+
+       vdoa->state = VDOA_INIT;
+       dev_set_drvdata(dev, vdoa);
+       g_vdoa = vdoa;
+       dev_info(dev, "i.MX Video Data Order Adapter(VDOA) driver probed\n");
+       return 0;
+}
+
+static int vdoa_remove(struct platform_device *pdev)
+{
+       struct vdoa_info *vdoa = dev_get_drvdata(&pdev->dev);
+
+       gen_pool_free(vdoa->iram_pool, vdoa->iram_base, iram_size);
+       kfree(vdoa);
+       dev_set_drvdata(&pdev->dev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver vdoa_driver = {
+       .driver = {
+               .name = "mxc_vdoa",
+               .of_match_table = imx_vdoa_dt_ids,
+       },
+       .probe = vdoa_probe,
+       .remove = vdoa_remove,
+};
+
+static int __init vdoa_init(void)
+{
+       int err;
+
+       err = platform_driver_register(&vdoa_driver);
+       if (err) {
+               pr_err("vdoa_driver register failed\n");
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static void __exit vdoa_cleanup(void)
+{
+       platform_driver_unregister(&vdoa_driver);
+}
+
+module_init(vdoa_init);
+module_exit(vdoa_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX Video Data Order Adapter(VDOA) driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/ipu3/vdoa.h b/drivers/mxc/ipu3/vdoa.h
new file mode 100644 (file)
index 0000000..3e4c735
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012-2015 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
+ */
+
+#ifndef __VDOA_H__
+#define __VDOA_H__
+
+#define VDOA_PFS_YUYV (1)
+#define VDOA_PFS_NV12 (0)
+
+
+struct vfield_buf {
+       u32     prev_veba;
+       u32     cur_veba;
+       u32     next_veba;
+       u32     vubo;
+};
+
+struct vframe_buf {
+       u32     veba;
+       u32     vubo;
+};
+
+struct vdoa_params {
+       u32     width;
+       u32     height;
+       int     vpu_stride;
+       int     interlaced;
+       int     scan_order;
+       int     ipu_num;
+       int     band_lines;
+       int     band_mode;
+       int     pfs;
+       u32     ieba0;
+       u32     ieba1;
+       u32     ieba2;
+       struct  vframe_buf vframe_buf;
+       struct  vfield_buf vfield_buf;
+};
+struct vdoa_ipu_buf {
+       u32     ieba0;
+       u32     ieba1;
+       u32     iubo;
+};
+
+struct vdoa_info;
+typedef void *vdoa_handle_t;
+
+int vdoa_setup(vdoa_handle_t handle, struct vdoa_params *params);
+void vdoa_get_output_buf(vdoa_handle_t handle, struct vdoa_ipu_buf *buf);
+int  vdoa_start(vdoa_handle_t handle, int timeout_ms);
+void vdoa_stop(vdoa_handle_t handle);
+void vdoa_get_handle(vdoa_handle_t *handle);
+void vdoa_put_handle(vdoa_handle_t *handle);
+#endif
index cfdb975..2599628 100644 (file)
@@ -1,29 +1,57 @@
 config FB_MXC
-        tristate "MXC Framebuffer support"
-        depends on FB
-        select FB_CFB_FILLRECT
-        select FB_CFB_COPYAREA
-        select FB_CFB_IMAGEBLIT
-        select FB_MODE_HELPERS
-        default y
-        help
-          This is a framebuffer device for the MXC LCD Controller.
-          See <http://www.linux-fbdev.org/> for information on framebuffer
-          devices.
-
-          If you plan to use the LCD display with your MXC system, say
-          Y here.
+       tristate "MXC Framebuffer support"
+       depends on FB
+       select FB_CFB_FILLRECT
+       select FB_CFB_COPYAREA
+       select FB_CFB_IMAGEBLIT
+       select FB_MODE_HELPERS
+       default y
+       help
+         This is a framebuffer device for the MXC LCD Controller.
+         See <http://www.linux-fbdev.org/> for information on framebuffer
+         devices.
+
+         If you plan to use the LCD display with your MXC system, say
+         Y here.
 
 config FB_MXC_SYNC_PANEL
-        depends on FB_MXC
-        tristate "Synchronous Panel Framebuffer"
+       depends on FB_MXC
+       tristate "Synchronous Panel Framebuffer"
 
 config FB_MXC_MIPI_DSI_SAMSUNG
-        tristate "MXC MIPI_DSI_SAMSUNG"
-        depends on FB_MXC_SYNC_PANEL
-        depends on FB_MXS
+       tristate "MXC MIPI_DSI_SAMSUNG"
+       depends on FB_MXC_SYNC_PANEL
+       depends on FB_MXS
 
 config FB_MXC_TRULY_WVGA_SYNC_PANEL
-        tristate "TRULY WVGA Panel"
-        depends on FB_MXC_SYNC_PANEL
-        depends on FB_MXC_MIPI_DSI_SAMSUNG
+       tristate "TRULY WVGA Panel"
+       depends on FB_MXC_SYNC_PANEL
+       depends on FB_MXC_MIPI_DSI_SAMSUNG
+
+config FB_MXC_LDB
+       tristate "MXC LDB"
+       depends on FB_MXC_SYNC_PANEL
+       depends on MXC_IPU_V3 || FB_MXS
+       select VIDEOMODE_HELPERS
+
+config FB_MXC_HDMI
+       depends on FB_MXC_SYNC_PANEL
+       depends on MXC_IPU_V3
+       depends on I2C
+       tristate "MXC HDMI driver support"
+       select MFD_MXC_HDMI
+       help
+         Driver for the on-chip MXC HDMI controller.
+
+config FB_MXC_EDID
+     depends on FB_MXC && I2C
+     tristate "MXC EDID support"
+     default y
+
+config HANNSTAR_CABC
+       tristate "Hannstar CABC function"
+       help
+         Say yes here to support switching on/off Hannstar CABC
+         function.  This function turns backlight density of a
+         display panel automatically according to the content
+         shown on the panel.
index 5fcecc7..31fa96e 100644 (file)
@@ -1,3 +1,8 @@
-obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_dispdrv.o
 obj-$(CONFIG_FB_MXC_MIPI_DSI_SAMSUNG)           += mipi_dsi_samsung.o
 obj-$(CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL)      += mxcfb_hx8369_wvga.o
+obj-$(CONFIG_FB_MXC_LDB) += ldb.o
+obj-$(CONFIG_FB_MXC_HDMI)                      += mxc_hdmi.o
+obj-$(CONFIG_FB_MXC_EDID)                      += mxc_edid.o
+obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_dispdrv.o mxc_lcdif.o mxc_ipuv3_fb.o
+obj-$(CONFIG_FB_MXS_SII902X) += mxsfb_sii902x.o
+obj-$(CONFIG_HANNSTAR_CABC) += hannstar_cabc.o
diff --git a/drivers/video/fbdev/mxc/hannstar_cabc.c b/drivers/video/fbdev/mxc/hannstar_cabc.c
new file mode 100644 (file)
index 0000000..14528c8
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014-2015 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
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+
+#define DRIVER_NAME "hannstar-cabc"
+
+static const struct of_device_id cabc_dt_ids[] = {
+       { .compatible = "hannstar,cabc", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cabc_dt_ids);
+
+static int cabc_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node, *pp;
+       int cabc_gpio, ret = 0;
+       bool cabc_enable;
+       unsigned long gpio_flag;
+       enum of_gpio_flags flags;
+
+       for_each_child_of_node(np, pp) {
+               if (!of_find_property(pp, "gpios", NULL)) {
+                       dev_warn(&pdev->dev, "Found interface without "
+                                "gpios\n");
+                       continue;
+               }
+
+               cabc_gpio = of_get_gpio_flags(pp, 0, &flags);
+               if (!gpio_is_valid(cabc_gpio)) {
+                       ret = cabc_gpio;
+                       if (ret != -EPROBE_DEFER)
+                               dev_err(&pdev->dev,
+                                       "Failed to get gpio flags, "
+                                       "error: %d\n", ret);
+                       return ret;
+               }
+
+               cabc_enable = of_property_read_bool(pp, "cabc-enable");
+
+               if (flags & OF_GPIO_ACTIVE_LOW)
+                       gpio_flag = cabc_enable ?
+                               GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH;
+               else
+                       gpio_flag = cabc_enable ?
+                               GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+
+               devm_gpio_request_one(&pdev->dev, cabc_gpio,
+                                       gpio_flag, "hannstar-cabc");
+       }
+
+       return ret;
+}
+
+static struct platform_driver cabc_driver = {
+       .probe  = cabc_probe,
+       .driver = {
+               .of_match_table = cabc_dt_ids,
+               .name   = DRIVER_NAME,
+               .owner  = THIS_MODULE,
+       },
+};
+module_platform_driver(cabc_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Hannstar CABC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/video/fbdev/mxc/ldb.c b/drivers/video/fbdev/mxc/ldb.c
new file mode 100644 (file)
index 0000000..e5716cb
--- /dev/null
@@ -0,0 +1,913 @@
+/*
+ * Copyright (C) 2012-2015 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
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+#include "mxc_dispdrv.h"
+
+#define DRIVER_NAME    "ldb"
+
+#define LDB_BGREF_RMODE_INT            (0x1 << 15)
+
+#define LDB_DI1_VS_POL_ACT_LOW         (0x1 << 10)
+#define LDB_DI0_VS_POL_ACT_LOW         (0x1 << 9)
+
+#define LDB_BIT_MAP_CH1_JEIDA          (0x1 << 8)
+#define LDB_BIT_MAP_CH0_JEIDA          (0x1 << 6)
+
+#define LDB_DATA_WIDTH_CH1_24          (0x1 << 7)
+#define LDB_DATA_WIDTH_CH0_24          (0x1 << 5)
+
+#define LDB_CH1_MODE_MASK              (0x3 << 2)
+#define LDB_CH1_MODE_EN_TO_DI1         (0x3 << 2)
+#define LDB_CH1_MODE_EN_TO_DI0         (0x1 << 2)
+#define LDB_CH0_MODE_MASK              (0x3 << 0)
+#define LDB_CH0_MODE_EN_TO_DI1         (0x3 << 0)
+#define LDB_CH0_MODE_EN_TO_DI0         (0x1 << 0)
+
+#define LDB_SPLIT_MODE_EN              (0x1 << 4)
+
+#define INVALID_BUS_REG                        (~0UL)
+
+struct crtc_mux {
+       enum crtc crtc;
+       u32 val;
+};
+
+struct bus_mux {
+       int reg;
+       int shift;
+       int mask;
+       int crtc_mux_num;
+       const struct crtc_mux *crtcs;
+};
+
+struct ldb_info {
+       bool split_cap;
+       bool dual_cap;
+       bool ext_bgref_cap;
+       bool clk_fixup;
+       int ctrl_reg;
+       int bus_mux_num;
+       const struct bus_mux *buses;
+};
+
+struct ldb_data;
+
+struct ldb_chan {
+       struct ldb_data *ldb;
+       struct fb_info *fbi;
+       struct videomode vm;
+       enum crtc crtc;
+       int chno;
+       bool is_used;
+       bool online;
+};
+
+struct ldb_data {
+       struct regmap *regmap;
+       struct device *dev;
+       struct mxc_dispdrv_handle *mddh;
+       struct ldb_chan chan[2];
+       int bus_mux_num;
+       const struct bus_mux *buses;
+       int primary_chno;
+       int ctrl_reg;
+       u32 ctrl;
+       bool spl_mode;
+       bool dual_mode;
+       bool clk_fixup;
+       struct clk *di_clk[4];
+       struct clk *ldb_di_clk[2];
+       struct clk *div_3_5_clk[2];
+       struct clk *div_7_clk[2];
+       struct clk *div_sel_clk[2];
+};
+
+static const struct crtc_mux imx6q_lvds0_crtc_mux[] = {
+       {
+               .crtc = CRTC_IPU1_DI0,
+               .val = IMX6Q_GPR3_LVDS0_MUX_CTL_IPU1_DI0,
+       }, {
+               .crtc = CRTC_IPU1_DI1,
+               .val = IMX6Q_GPR3_LVDS0_MUX_CTL_IPU1_DI1,
+       }, {
+               .crtc = CRTC_IPU2_DI0,
+               .val = IMX6Q_GPR3_LVDS0_MUX_CTL_IPU2_DI0,
+       }, {
+               .crtc = CRTC_IPU2_DI1,
+               .val = IMX6Q_GPR3_LVDS0_MUX_CTL_IPU2_DI1,
+       }
+};
+
+static const struct crtc_mux imx6q_lvds1_crtc_mux[] = {
+       {
+               .crtc = CRTC_IPU1_DI0,
+               .val = IMX6Q_GPR3_LVDS1_MUX_CTL_IPU1_DI0,
+       }, {
+               .crtc = CRTC_IPU1_DI1,
+               .val = IMX6Q_GPR3_LVDS1_MUX_CTL_IPU1_DI1,
+       }, {
+               .crtc = CRTC_IPU2_DI0,
+               .val = IMX6Q_GPR3_LVDS1_MUX_CTL_IPU2_DI0,
+       }, {
+               .crtc = CRTC_IPU2_DI1,
+               .val = IMX6Q_GPR3_LVDS1_MUX_CTL_IPU2_DI1,
+       }
+};
+
+static const struct bus_mux imx6q_ldb_buses[] = {
+       {
+               .reg = IOMUXC_GPR3,
+               .shift = 6,
+               .mask = IMX6Q_GPR3_LVDS0_MUX_CTL_MASK,
+               .crtc_mux_num = ARRAY_SIZE(imx6q_lvds0_crtc_mux),
+               .crtcs = imx6q_lvds0_crtc_mux,
+       }, {
+               .reg = IOMUXC_GPR3,
+               .shift = 8,
+               .mask = IMX6Q_GPR3_LVDS1_MUX_CTL_MASK,
+               .crtc_mux_num = ARRAY_SIZE(imx6q_lvds1_crtc_mux),
+               .crtcs = imx6q_lvds1_crtc_mux,
+       }
+};
+
+static const struct ldb_info imx6q_ldb_info = {
+       .split_cap = true,
+       .dual_cap = true,
+       .ext_bgref_cap = false,
+       .clk_fixup = false,
+       .ctrl_reg = IOMUXC_GPR2,
+       .bus_mux_num = ARRAY_SIZE(imx6q_ldb_buses),
+       .buses = imx6q_ldb_buses,
+};
+
+static const struct ldb_info imx6qp_ldb_info = {
+       .split_cap = true,
+       .dual_cap = true,
+       .ext_bgref_cap = false,
+       .clk_fixup = true,
+       .ctrl_reg = IOMUXC_GPR2,
+       .bus_mux_num = ARRAY_SIZE(imx6q_ldb_buses),
+       .buses = imx6q_ldb_buses,
+};
+
+static const struct crtc_mux imx6dl_lvds0_crtc_mux[] = {
+       {
+               .crtc = CRTC_IPU1_DI0,
+               .val = IMX6DL_GPR3_LVDS0_MUX_CTL_IPU1_DI0,
+       }, {
+               .crtc = CRTC_IPU1_DI1,
+               .val = IMX6DL_GPR3_LVDS0_MUX_CTL_IPU1_DI1,
+       }, {
+               .crtc = CRTC_LCDIF1,
+               .val = IMX6DL_GPR3_LVDS0_MUX_CTL_LCDIF,
+       }
+};
+
+static const struct crtc_mux imx6dl_lvds1_crtc_mux[] = {
+       {
+               .crtc = CRTC_IPU1_DI0,
+               .val = IMX6DL_GPR3_LVDS1_MUX_CTL_IPU1_DI0,
+       }, {
+               .crtc = CRTC_IPU1_DI1,
+               .val = IMX6DL_GPR3_LVDS1_MUX_CTL_IPU1_DI1,
+       }, {
+               .crtc = CRTC_LCDIF1,
+               .val = IMX6DL_GPR3_LVDS1_MUX_CTL_LCDIF,
+       }
+};
+
+static const struct bus_mux imx6dl_ldb_buses[] = {
+       {
+               .reg = IOMUXC_GPR3,
+               .shift = 6,
+               .mask = IMX6DL_GPR3_LVDS0_MUX_CTL_MASK,
+               .crtc_mux_num = ARRAY_SIZE(imx6dl_lvds0_crtc_mux),
+               .crtcs = imx6dl_lvds0_crtc_mux,
+       }, {
+               .reg = IOMUXC_GPR3,
+               .shift = 8,
+               .mask = IMX6DL_GPR3_LVDS1_MUX_CTL_MASK,
+               .crtc_mux_num = ARRAY_SIZE(imx6dl_lvds1_crtc_mux),
+               .crtcs = imx6dl_lvds1_crtc_mux,
+       }
+};
+
+static const struct ldb_info imx6dl_ldb_info = {
+       .split_cap = true,
+       .dual_cap = true,
+       .ext_bgref_cap = false,
+       .clk_fixup = false,
+       .ctrl_reg = IOMUXC_GPR2,
+       .bus_mux_num = ARRAY_SIZE(imx6dl_ldb_buses),
+       .buses = imx6dl_ldb_buses,
+};
+
+static const struct crtc_mux imx6sx_lvds_crtc_mux[] = {
+       {
+               .crtc = CRTC_LCDIF1,
+               .val = IMX6SX_GPR5_DISP_MUX_LDB_CTRL_LCDIF1,
+       }, {
+               .crtc = CRTC_LCDIF2,
+               .val = IMX6SX_GPR5_DISP_MUX_LDB_CTRL_LCDIF2,
+       }
+};
+
+static const struct bus_mux imx6sx_ldb_buses[] = {
+       {
+               .reg = IOMUXC_GPR5,
+               .shift = 3,
+               .mask = IMX6SX_GPR5_DISP_MUX_LDB_CTRL_MASK,
+               .crtc_mux_num = ARRAY_SIZE(imx6sx_lvds_crtc_mux),
+               .crtcs = imx6sx_lvds_crtc_mux,
+       }
+};
+
+static const struct ldb_info imx6sx_ldb_info = {
+       .split_cap = false,
+       .dual_cap = false,
+       .ext_bgref_cap = false,
+       .clk_fixup = false,
+       .ctrl_reg = IOMUXC_GPR6,
+       .bus_mux_num = ARRAY_SIZE(imx6sx_ldb_buses),
+       .buses = imx6sx_ldb_buses,
+};
+
+static const struct crtc_mux imx53_lvds0_crtc_mux[] = {
+       { .crtc = CRTC_IPU1_DI0, },
+};
+
+static const struct crtc_mux imx53_lvds1_crtc_mux[] = {
+       { .crtc = CRTC_IPU1_DI1, }
+};
+
+static const struct bus_mux imx53_ldb_buses[] = {
+       {
+               .reg = INVALID_BUS_REG,
+               .crtc_mux_num = ARRAY_SIZE(imx53_lvds0_crtc_mux),
+               .crtcs = imx53_lvds0_crtc_mux,
+       }, {
+               .reg = INVALID_BUS_REG,
+               .crtc_mux_num = ARRAY_SIZE(imx53_lvds1_crtc_mux),
+               .crtcs = imx53_lvds1_crtc_mux,
+       }
+};
+
+static const struct ldb_info imx53_ldb_info = {
+       .split_cap = true,
+       .dual_cap = false,
+       .ext_bgref_cap = true,
+       .clk_fixup = false,
+       .ctrl_reg = IOMUXC_GPR2,
+       .bus_mux_num = ARRAY_SIZE(imx53_ldb_buses),
+       .buses = imx53_ldb_buses,
+};
+
+static const struct of_device_id ldb_dt_ids[] = {
+       { .compatible = "fsl,imx6qp-ldb", .data = &imx6qp_ldb_info, },
+       { .compatible = "fsl,imx6q-ldb", .data = &imx6q_ldb_info, },
+       { .compatible = "fsl,imx6dl-ldb", .data = &imx6dl_ldb_info, },
+       { .compatible = "fsl,imx6sx-ldb", .data = &imx6sx_ldb_info, },
+       { .compatible = "fsl,imx53-ldb", .data = &imx53_ldb_info, },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ldb_dt_ids);
+
+static int ldb_init(struct mxc_dispdrv_handle *mddh,
+                   struct mxc_dispdrv_setting *setting)
+{
+       struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
+       struct device *dev = ldb->dev;
+       struct fb_info *fbi = setting->fbi;
+       struct ldb_chan *chan;
+       struct fb_videomode fb_vm;
+       int chno;
+
+       chno = ldb->chan[ldb->primary_chno].is_used ?
+               !ldb->primary_chno : ldb->primary_chno;
+
+       chan = &ldb->chan[chno];
+
+       if (chan->is_used) {
+               dev_err(dev, "LVDS channel%d is already used\n", chno);
+               return -EBUSY;
+       }
+       if (!chan->online) {
+               dev_err(dev, "LVDS channel%d is not online\n", chno);
+               return -ENODEV;
+       }
+
+       chan->is_used = true;
+
+       chan->fbi = fbi;
+
+       fb_videomode_from_videomode(&chan->vm, &fb_vm);
+
+       INIT_LIST_HEAD(&fbi->modelist);
+       fb_add_videomode(&fb_vm, &fbi->modelist);
+       fb_videomode_to_var(&fbi->var, &fb_vm);
+
+       setting->crtc = chan->crtc;
+
+       return 0;
+}
+
+static int get_di_clk_id(struct ldb_chan chan, int *id)
+{
+       struct ldb_data *ldb = chan.ldb;
+       int i = 0, chno = chan.chno, mask, shift;
+       enum crtc crtc;
+       u32 val;
+
+       /* no pre-muxing, such as mx53 */
+       if (ldb->buses[chno].reg == INVALID_BUS_REG) {
+               *id = chno;
+               return 0;
+       }
+
+       for (; i < ldb->buses[chno].crtc_mux_num; i++) {
+               crtc = ldb->buses[chno].crtcs[i].crtc;
+               val  = ldb->buses[chno].crtcs[i].val;
+               mask = ldb->buses[chno].mask;
+               shift = ldb->buses[chno].shift;
+               if (chan.crtc == crtc) {
+                       *id = (val & mask) >> shift;
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int get_mux_val(struct bus_mux bus_mux, enum crtc crtc,
+                      u32 *mux_val)
+{
+       int i = 0;
+
+       for (; i < bus_mux.crtc_mux_num; i++)
+               if (bus_mux.crtcs[i].crtc == crtc) {
+                       *mux_val = bus_mux.crtcs[i].val;
+                       return 0;
+               }
+
+       return -EINVAL;
+}
+
+static int find_ldb_chno(struct ldb_data *ldb,
+                        struct fb_info *fbi, int *chno)
+{
+       struct device *dev = ldb->dev;
+       int i = 0;
+
+       for (; i < 2; i++)
+               if (ldb->chan[i].fbi == fbi) {
+                       *chno = ldb->chan[i].chno;
+                       return 0;
+               }
+       dev_err(dev, "failed to find channel number\n");
+       return -EINVAL;
+}
+
+static void ldb_disable(struct mxc_dispdrv_handle *mddh,
+                       struct fb_info *fbi);
+
+static int ldb_setup(struct mxc_dispdrv_handle *mddh,
+                    struct fb_info *fbi)
+{
+       struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
+       struct ldb_chan chan;
+       struct device *dev = ldb->dev;
+       struct clk *ldb_di_parent, *ldb_di_sel, *ldb_di_sel_parent;
+       struct clk *other_ldb_di_sel = NULL;
+       struct bus_mux bus_mux;
+       int ret = 0, id = 0, chno, other_chno;
+       unsigned long serial_clk;
+       u32 mux_val;
+
+       ret = find_ldb_chno(ldb, fbi, &chno);
+       if (ret < 0)
+               return ret;
+
+       other_chno = chno ? 0 : 1;
+
+       chan = ldb->chan[chno];
+
+       bus_mux = ldb->buses[chno];
+
+       ret = get_di_clk_id(chan, &id);
+       if (ret < 0) {
+               dev_err(dev, "failed to get ch%d di clk id\n",
+                       chan.chno);
+               return ret;
+       }
+
+       ret = get_mux_val(bus_mux, chan.crtc, &mux_val);
+       if (ret < 0) {
+               dev_err(dev, "failed to get ch%d mux val\n",
+                       chan.chno);
+               return ret;
+       }
+
+
+       if (ldb->clk_fixup) {
+               /*
+                * ldb_di_sel_parent(plls) -> ldb_di_sel -> ldb_di[chno] ->
+                *
+                *     -> div_3_5[chno] ->
+                * -> |                   |-> div_sel[chno] -> di[id]
+                *     ->  div_7[chno] ->
+                */
+               clk_set_parent(ldb->di_clk[id], ldb->div_sel_clk[chno]);
+       } else {
+               /*
+                * ldb_di_sel_parent(plls) -> ldb_di_sel ->
+                *
+                *     -> div_3_5[chno] ->
+                * -> |                   |-> div_sel[chno] ->
+                *     ->  div_7[chno] ->
+                *
+                * -> ldb_di[chno] -> di[id]
+                */
+               clk_set_parent(ldb->di_clk[id], ldb->ldb_di_clk[chno]);
+       }
+       ldb_di_parent = ldb->spl_mode ? ldb->div_3_5_clk[chno] :
+                       ldb->div_7_clk[chno];
+       clk_set_parent(ldb->div_sel_clk[chno], ldb_di_parent);
+       ldb_di_sel = clk_get_parent(ldb_di_parent);
+       ldb_di_sel_parent = clk_get_parent(ldb_di_sel);
+       serial_clk = ldb->spl_mode ? chan.vm.pixelclock * 7 / 2 :
+                       chan.vm.pixelclock * 7;
+       clk_set_rate(ldb_di_sel_parent, serial_clk);
+
+       /*
+        * split mode or dual mode:
+        * clock tree for the other channel
+        */
+       if (ldb->spl_mode) {
+               clk_set_parent(ldb->div_sel_clk[other_chno],
+                              ldb->div_3_5_clk[other_chno]);
+               other_ldb_di_sel =
+                       clk_get_parent(ldb->div_3_5_clk[other_chno]);;
+       }
+
+       if (ldb->dual_mode) {
+               clk_set_parent(ldb->div_sel_clk[other_chno],
+                              ldb->div_7_clk[other_chno]);
+               other_ldb_di_sel =
+                       clk_get_parent(ldb->div_7_clk[other_chno]);;
+       }
+
+       if (ldb->spl_mode || ldb->dual_mode)
+               clk_set_parent(other_ldb_di_sel, ldb_di_sel_parent);
+
+       if (!(chan.fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)) {
+               if (ldb->spl_mode && bus_mux.reg == INVALID_BUS_REG)
+                       /* no pre-muxing, such as mx53 */
+                       ldb->ctrl |= (id == 0 ? LDB_DI0_VS_POL_ACT_LOW :
+                                       LDB_DI1_VS_POL_ACT_LOW);
+               else
+                       ldb->ctrl |= (chno == 0 ? LDB_DI0_VS_POL_ACT_LOW :
+                                       LDB_DI1_VS_POL_ACT_LOW);
+       }
+
+       if (bus_mux.reg != INVALID_BUS_REG)
+               regmap_update_bits(ldb->regmap, bus_mux.reg,
+                                  bus_mux.mask, mux_val);
+
+       regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ctrl);
+
+       /* disable channel for correct sequence */
+       ldb_disable(mddh, fbi);
+
+       return ret;
+}
+
+static int ldb_enable(struct mxc_dispdrv_handle *mddh,
+                     struct fb_info *fbi)
+{
+       struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
+       struct ldb_chan chan;
+       struct device *dev = ldb->dev;
+       struct bus_mux bus_mux;
+       int ret = 0, id = 0, chno, other_chno;
+
+       ret = find_ldb_chno(ldb, fbi, &chno);
+       if (ret < 0)
+               return ret;
+
+       chan = ldb->chan[chno];
+
+       bus_mux = ldb->buses[chno];
+
+       if (ldb->spl_mode || ldb->dual_mode) {
+               other_chno = chno ? 0 : 1;
+               clk_prepare_enable(ldb->ldb_di_clk[other_chno]);
+       }
+
+       if ((ldb->spl_mode || ldb->dual_mode) &&
+           bus_mux.reg == INVALID_BUS_REG) {
+               /* no pre-muxing, such as mx53 */
+               ret = get_di_clk_id(chan, &id);
+               if (ret < 0) {
+                       dev_err(dev, "failed to get ch%d di clk id\n",
+                               chan.chno);
+                       return ret;
+               }
+
+               ldb->ctrl |= id ?
+                       (LDB_CH0_MODE_EN_TO_DI1 | LDB_CH1_MODE_EN_TO_DI1) :
+                       (LDB_CH0_MODE_EN_TO_DI0 | LDB_CH1_MODE_EN_TO_DI0);
+       } else {
+               if (ldb->spl_mode || ldb->dual_mode)
+                       ldb->ctrl |= LDB_CH0_MODE_EN_TO_DI0 |
+                                    LDB_CH1_MODE_EN_TO_DI0;
+               else
+                       ldb->ctrl |= chno ? LDB_CH1_MODE_EN_TO_DI1 :
+                                           LDB_CH0_MODE_EN_TO_DI0;
+       }
+
+       regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ctrl);
+       return 0;
+}
+
+static void ldb_disable(struct mxc_dispdrv_handle *mddh,
+                      struct fb_info *fbi)
+{
+       struct ldb_data *ldb = mxc_dispdrv_getdata(mddh);
+       int ret, chno, other_chno;
+
+       ret = find_ldb_chno(ldb, fbi, &chno);
+       if (ret < 0)
+               return;
+
+       if (ldb->spl_mode || ldb->dual_mode) {
+               ldb->ctrl &= ~(LDB_CH1_MODE_MASK | LDB_CH0_MODE_MASK);
+               other_chno = chno ? 0 : 1;
+               clk_disable_unprepare(ldb->ldb_di_clk[other_chno]);
+       } else {
+               ldb->ctrl &= ~(chno ? LDB_CH1_MODE_MASK :
+                                     LDB_CH0_MODE_MASK);
+       }
+
+       regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ctrl);
+       return;
+}
+
+static struct mxc_dispdrv_driver ldb_drv = {
+       .name           = DRIVER_NAME,
+       .init           = ldb_init,
+       .setup          = ldb_setup,
+       .enable         = ldb_enable,
+       .disable        = ldb_disable
+};
+
+enum {
+       LVDS_BIT_MAP_SPWG,
+       LVDS_BIT_MAP_JEIDA,
+};
+
+static const char *ldb_bit_mappings[] = {
+       [LVDS_BIT_MAP_SPWG] = "spwg",
+       [LVDS_BIT_MAP_JEIDA] = "jeida",
+};
+
+static int of_get_data_mapping(struct device_node *np)
+{
+       const char *bm;
+       int ret, i;
+
+       ret = of_property_read_string(np, "fsl,data-mapping", &bm);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < ARRAY_SIZE(ldb_bit_mappings); i++)
+               if (!strcasecmp(bm, ldb_bit_mappings[i]))
+                       return i;
+
+       return -EINVAL;
+}
+
+static const char *ldb_crtc_mappings[] = {
+       [CRTC_IPU_DI0] = "ipu-di0",
+       [CRTC_IPU_DI1] = "ipu-di1",
+       [CRTC_IPU1_DI0] = "ipu1-di0",
+       [CRTC_IPU1_DI1] = "ipu1-di1",
+       [CRTC_IPU2_DI0] = "ipu2-di0",
+       [CRTC_IPU2_DI1] = "ipu2-di1",
+       [CRTC_LCDIF] = "lcdif",
+       [CRTC_LCDIF1] = "lcdif1",
+       [CRTC_LCDIF2] = "lcdif2",
+};
+
+static enum crtc of_get_crtc_mapping(struct device_node *np)
+{
+       const char *cm;
+       enum crtc i;
+       int ret;
+
+       ret = of_property_read_string(np, "crtc", &cm);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < ARRAY_SIZE(ldb_crtc_mappings); i++)
+               if (!strcasecmp(cm, ldb_crtc_mappings[i])) {
+                       switch (i) {
+                       case CRTC_IPU_DI0:
+                               i = CRTC_IPU1_DI0;
+                               break;
+                       case CRTC_IPU_DI1:
+                               i = CRTC_IPU1_DI1;
+                               break;
+                       case CRTC_LCDIF:
+                               i = CRTC_LCDIF1;
+                               break;
+                       default:
+                               break;
+                       }
+                       return i;
+               }
+
+       return -EINVAL;
+}
+
+static int mux_count(struct ldb_data *ldb)
+{
+       int i, j, count = 0;
+       bool should_count[CRTC_MAX];
+       enum crtc crtc;
+
+       for (i = 0; i < CRTC_MAX; i++)
+               should_count[i] = true;
+
+       for (i = 0; i < ldb->bus_mux_num; i++) {
+               for (j = 0; j < ldb->buses[i].crtc_mux_num; j++) {
+                       crtc = ldb->buses[i].crtcs[j].crtc;
+                       if (should_count[crtc]) {
+                               count++;
+                               should_count[crtc] = false;
+                       }
+               }
+       }
+
+       return count;
+}
+
+static bool is_valid_crtc(struct ldb_data *ldb, enum crtc crtc,
+                         int chno)
+{
+       int i = 0;
+
+       if (chno > ldb->bus_mux_num - 1)
+               return false;
+
+       for (; i < ldb->buses[chno].crtc_mux_num; i++)
+               if (ldb->buses[chno].crtcs[i].crtc == crtc)
+                       return true;
+
+       return false;
+}
+
+static int ldb_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       const struct of_device_id *of_id =
+                       of_match_device(ldb_dt_ids, dev);
+       const struct ldb_info *ldb_info =
+                       (const struct ldb_info *)of_id->data;
+       struct device_node *np = dev->of_node, *child;
+       struct ldb_data *ldb;
+       bool ext_ref;
+       int i, data_width, mapping, child_count = 0;
+       char clkname[16];
+
+       ldb = devm_kzalloc(dev, sizeof(*ldb), GFP_KERNEL);
+       if (!ldb)
+               return -ENOMEM;
+
+       ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
+       if (IS_ERR(ldb->regmap)) {
+               dev_err(dev, "failed to get parent regmap\n");
+               return PTR_ERR(ldb->regmap);
+       }
+
+       ldb->dev = dev;
+       ldb->bus_mux_num = ldb_info->bus_mux_num;
+       ldb->buses = ldb_info->buses;
+       ldb->ctrl_reg = ldb_info->ctrl_reg;
+       ldb->clk_fixup = ldb_info->clk_fixup;
+       ldb->primary_chno = -1;
+
+       ext_ref = of_property_read_bool(np, "ext-ref");
+       if (!ext_ref && ldb_info->ext_bgref_cap)
+               ldb->ctrl |= LDB_BGREF_RMODE_INT;
+
+       ldb->spl_mode = of_property_read_bool(np, "split-mode");
+       if (ldb->spl_mode) {
+               if (ldb_info->split_cap) {
+                       ldb->ctrl |= LDB_SPLIT_MODE_EN;
+                       dev_info(dev, "split mode\n");
+               } else {
+                       dev_err(dev, "cannot support split mode\n");
+                       return -EINVAL;
+               }
+       }
+
+       ldb->dual_mode = of_property_read_bool(np, "dual-mode");
+       if (ldb->dual_mode) {
+               if (ldb_info->dual_cap) {
+                       dev_info(dev, "dual mode\n");
+               } else {
+                       dev_err(dev, "cannot support dual mode\n");
+                       return -EINVAL;
+               }
+       }
+
+       if (ldb->dual_mode && ldb->spl_mode) {
+               dev_err(dev, "cannot support dual mode and split mode "
+                               "simultaneously\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < mux_count(ldb); i++) {
+               sprintf(clkname, "di%d_sel", i);
+               ldb->di_clk[i] = devm_clk_get(dev, clkname);
+               if (IS_ERR(ldb->di_clk[i])) {
+                       dev_err(dev, "failed to get clk %s\n", clkname);
+                       return PTR_ERR(ldb->di_clk[i]);
+               }
+       }
+
+       for_each_child_of_node(np, child) {
+               struct ldb_chan *chan;
+               enum crtc crtc;
+               bool is_primary;
+               int ret;
+
+               ret = of_property_read_u32(child, "reg", &i);
+               if (ret || i < 0 || i > 1 || i >= ldb->bus_mux_num) {
+                       dev_err(dev, "wrong LVDS channel number\n");
+                       return -EINVAL;
+               }
+
+               if ((ldb->spl_mode || ldb->dual_mode) && i > 0) {
+                       dev_warn(dev, "split mode or dual mode, ignoring "
+                               "second output\n");
+                       continue;
+               }
+
+               if (!of_device_is_available(child))
+                       continue;
+
+               if (++child_count > ldb->bus_mux_num) {
+                       dev_err(dev, "too many LVDS channels\n");
+                       return -EINVAL;
+               }
+
+               chan = &ldb->chan[i];
+               chan->chno = i;
+               chan->ldb = ldb;
+               chan->online = true;
+
+               is_primary = of_property_read_bool(child, "primary");
+
+               if (ldb->bus_mux_num == 1 || (ldb->primary_chno == -1 &&
+                   (is_primary || ldb->spl_mode || ldb->dual_mode)))
+                       ldb->primary_chno = chan->chno;
+
+               ret = of_property_read_u32(child, "fsl,data-width",
+                                          &data_width);
+               if (ret || (data_width != 18 && data_width != 24)) {
+                       dev_err(dev, "data width not specified or invalid\n");
+                       return -EINVAL;
+               }
+
+               mapping = of_get_data_mapping(child);
+               switch (mapping) {
+               case LVDS_BIT_MAP_SPWG:
+                       if (data_width == 24) {
+                               if (i == 0 || ldb->spl_mode || ldb->dual_mode)
+                                       ldb->ctrl |= LDB_DATA_WIDTH_CH0_24;
+                               if (i == 1 || ldb->spl_mode || ldb->dual_mode)
+                                       ldb->ctrl |= LDB_DATA_WIDTH_CH1_24;
+                       }
+                       break;
+               case LVDS_BIT_MAP_JEIDA:
+                       if (data_width == 18) {
+                               dev_err(dev, "JEIDA only support 24bit\n");
+                               return -EINVAL;
+                       }
+                       if (i == 0 || ldb->spl_mode || ldb->dual_mode)
+                               ldb->ctrl |= LDB_DATA_WIDTH_CH0_24 |
+                                            LDB_BIT_MAP_CH0_JEIDA;
+                       if (i == 1 || ldb->spl_mode || ldb->dual_mode)
+                               ldb->ctrl |= LDB_DATA_WIDTH_CH1_24 |
+                                            LDB_BIT_MAP_CH1_JEIDA;
+                       break;
+               default:
+                       dev_err(dev, "data mapping not specified or invalid\n");
+                       return -EINVAL;
+               }
+
+               crtc = of_get_crtc_mapping(child);
+               if (is_valid_crtc(ldb, crtc, chan->chno)) {
+                       ldb->chan[i].crtc = crtc;
+               } else {
+                       dev_err(dev, "crtc not specified or invalid\n");
+                       return -EINVAL;
+               }
+
+               ret = of_get_videomode(child, &chan->vm, 0);
+               if (ret)
+                       return -EINVAL;
+
+               sprintf(clkname, "ldb_di%d", i);
+               ldb->ldb_di_clk[i] = devm_clk_get(dev, clkname);
+               if (IS_ERR(ldb->ldb_di_clk[i])) {
+                       dev_err(dev, "failed to get clk %s\n", clkname);
+                       return PTR_ERR(ldb->ldb_di_clk[i]);
+               }
+
+               sprintf(clkname, "ldb_di%d_div_3_5", i);
+               ldb->div_3_5_clk[i] = devm_clk_get(dev, clkname);
+               if (IS_ERR(ldb->div_3_5_clk[i])) {
+                       dev_err(dev, "failed to get clk %s\n", clkname);
+                       return PTR_ERR(ldb->div_3_5_clk[i]);
+               }
+
+               sprintf(clkname, "ldb_di%d_div_7", i);
+               ldb->div_7_clk[i] = devm_clk_get(dev, clkname);
+               if (IS_ERR(ldb->div_7_clk[i])) {
+                       dev_err(dev, "failed to get clk %s\n", clkname);
+                       return PTR_ERR(ldb->div_7_clk[i]);
+               }
+
+               sprintf(clkname, "ldb_di%d_div_sel", i);
+               ldb->div_sel_clk[i] = devm_clk_get(dev, clkname);
+               if (IS_ERR(ldb->div_sel_clk[i])) {
+                       dev_err(dev, "failed to get clk %s\n", clkname);
+                       return PTR_ERR(ldb->div_sel_clk[i]);
+               }
+       }
+
+       if (child_count == 0) {
+               dev_err(dev, "failed to find valid LVDS channel\n");
+               return -EINVAL;
+       }
+
+       if (ldb->primary_chno == -1) {
+               dev_err(dev, "failed to know primary channel\n");
+               return -EINVAL;
+       }
+
+       ldb->mddh = mxc_dispdrv_register(&ldb_drv);
+       mxc_dispdrv_setdata(ldb->mddh, ldb);
+       dev_set_drvdata(&pdev->dev, ldb);
+
+       return 0;
+}
+
+static int ldb_remove(struct platform_device *pdev)
+{
+       struct ldb_data *ldb = dev_get_drvdata(&pdev->dev);
+
+       mxc_dispdrv_puthandle(ldb->mddh);
+       mxc_dispdrv_unregister(ldb->mddh);
+       return 0;
+}
+
+static struct platform_driver ldb_driver = {
+       .driver = {
+               .name = DRIVER_NAME,
+               .of_match_table = ldb_dt_ids,
+       },
+       .probe = ldb_probe,
+       .remove = ldb_remove,
+};
+
+module_platform_driver(ldb_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("LDB driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
index 805c1c9..d8501eb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2011-2015 Freescale Semiconductor, Inc. All Rights Reserved.
  */
 
 /*
diff --git a/drivers/video/fbdev/mxc/mxc_edid.c b/drivers/video/fbdev/mxc/mxc_edid.c
new file mode 100644 (file)
index 0000000..998b2d4
--- /dev/null
@@ -0,0 +1,769 @@
+/*
+ * Copyright 2009-2015 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 mxc_edid.c
+ *
+ * @brief MXC EDID driver
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <video/mxc_edid.h>
+#include "../edid.h"
+
+#undef DEBUG  /* define this for verbose EDID parsing output */
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) printk(fmt, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+const struct fb_videomode mxc_cea_mode[64] = {
+       /* #1: 640x480p@59.94/60Hz 4:3 */
+       [1] = {
+               NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+       },
+       /* #2: 720x480p@59.94/60Hz 4:3 */
+       [2] = {
+               NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+       },
+       /* #3: 720x480p@59.94/60Hz 16:9 */
+       [3] = {
+               NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #4: 1280x720p@59.94/60Hz 16:9 */
+       [4] = {
+               NULL, 60, 1280, 720, 13468, 220, 110, 20, 5, 40, 5,
+               FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0
+       },
+       /* #5: 1920x1080i@59.94/60Hz 16:9 */
+       [5] = {
+               NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
+               FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+               FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #6: 720(1440)x480iH@59.94/60Hz 4:3 */
+       [6] = {
+               NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
+               FB_VMODE_INTERLACED | FB_VMODE_ASPECT_4_3, 0,
+       },
+       /* #7: 720(1440)x480iH@59.94/60Hz 16:9 */
+       [7] = {
+               NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
+               FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #8: 720(1440)x240pH@59.94/60Hz 4:3 */
+       [8] = {
+               NULL, 60, 1440, 240, 37108, 114, 38, 15, 4, 124, 3, 0,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+       },
+       /* #9: 720(1440)x240pH@59.94/60Hz 16:9 */
+       [9] = {
+               NULL, 60, 1440, 240, 37108, 114, 38, 15, 4, 124, 3, 0,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #14: 1440x480p@59.94/60Hz 4:3 */
+       [14] = {
+               NULL, 60, 1440, 480, 18500, 120, 32, 30, 9, 124, 6, 0,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+       },
+       /* #15: 1440x480p@59.94/60Hz 16:9 */
+       [15] = {
+               NULL, 60, 1440, 480, 18500, 120, 32, 30, 9, 124, 6, 0,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #16: 1920x1080p@60Hz 16:9 */
+       [16] = {
+               NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5,
+               FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #17: 720x576pH@50Hz 4:3 */
+       [17] = {
+               NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+       },
+       /* #18: 720x576pH@50Hz 16:9 */
+       [18] = {
+               NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #19: 1280x720p@50Hz */
+       [19] = {
+               NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
+               FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #20: 1920x1080i@50Hz */
+       [20] = {
+               NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
+               FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+               FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #23: 720(1440)x288pH@50Hz 4:3 */
+       [23] = {
+               NULL, 50, 1440, 288, 37037, 138, 24, 19, 2, 126, 3, 0,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+       },
+       /* #24: 720(1440)x288pH@50Hz 16:9 */
+       [24] = {
+               NULL, 50, 1440, 288, 37037, 138, 24, 19, 2, 126, 3, 0,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #29: 720(1440)x576pH@50Hz 4:3 */
+       [29] = {
+               NULL, 50, 1440, 576, 18518, 136, 24, 39, 5, 128, 5, 0,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+       },
+       /* #30: 720(1440)x576pH@50Hz 16:9 */
+       [30] = {
+               NULL, 50, 1440, 576, 18518, 136, 24, 39, 5, 128, 5, 0,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #31: 1920x1080p@50Hz */
+       [31] = {
+               NULL, 50, 1920, 1080, 6734, 148, 528, 36, 4, 44, 5,
+               FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #32: 1920x1080p@23.98/24Hz */
+       [32] = {
+               NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
+               FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #33: 1920x1080p@25Hz */
+       [33] = {
+               NULL, 25, 1920, 1080, 13468, 148, 528, 36, 4, 44, 5,
+               FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #34: 1920x1080p@30Hz */
+       [34] = {
+               NULL, 30, 1920, 1080, 13468, 148, 88, 36, 4, 44, 5,
+               FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+       },
+       /* #41: 1280x720p@100Hz 16:9 */
+       [41] = {
+               NULL, 100, 1280, 720, 6734, 220, 440, 20, 5, 40, 5,
+               FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0
+       },
+       /* #47: 1280x720p@119.88/120Hz 16:9 */
+       [47] = {
+               NULL, 120, 1280, 720, 6734, 220, 110, 20, 5, 40, 5,
+               FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+               FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0
+       },
+};
+
+/*
+ * We have a special version of fb_mode_is_equal that ignores
+ * pixclock, since for many CEA modes, 2 frequencies are supported
+ * e.g. 640x480 @ 60Hz or 59.94Hz
+ */
+int mxc_edid_fb_mode_is_equal(bool use_aspect,
+                       const struct fb_videomode *mode1,
+                       const struct fb_videomode *mode2)
+{
+       u32 mask;
+
+       if (use_aspect)
+               mask = ~0;
+       else
+               mask = ~FB_VMODE_ASPECT_MASK;
+
+       return (mode1->xres         == mode2->xres &&
+               mode1->yres         == mode2->yres &&
+               mode1->hsync_len    == mode2->hsync_len &&
+               mode1->vsync_len    == mode2->vsync_len &&
+               mode1->left_margin  == mode2->left_margin &&
+               mode1->right_margin == mode2->right_margin &&
+               mode1->upper_margin == mode2->upper_margin &&
+               mode1->lower_margin == mode2->lower_margin &&
+               mode1->sync         == mode2->sync &&
+               /* refresh check, 59.94Hz and 60Hz have the same parameter
+                * in struct of mxc_cea_mode */
+               abs(mode1->refresh - mode2->refresh) <= 1 &&
+               (mode1->vmode & mask) == (mode2->vmode & mask));
+}
+
+static void get_detailed_timing(unsigned char *block,
+                               struct fb_videomode *mode)
+{
+       mode->xres = H_ACTIVE;
+       mode->yres = V_ACTIVE;
+       mode->pixclock = PIXEL_CLOCK;
+       mode->pixclock /= 1000;
+       mode->pixclock = KHZ2PICOS(mode->pixclock);
+       mode->right_margin = H_SYNC_OFFSET;
+       mode->left_margin = (H_ACTIVE + H_BLANKING) -
+               (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
+       mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
+               V_SYNC_WIDTH;
+       mode->lower_margin = V_SYNC_OFFSET;
+       mode->hsync_len = H_SYNC_WIDTH;
+       mode->vsync_len = V_SYNC_WIDTH;
+       if (HSYNC_POSITIVE)
+               mode->sync |= FB_SYNC_HOR_HIGH_ACT;
+       if (VSYNC_POSITIVE)
+               mode->sync |= FB_SYNC_VERT_HIGH_ACT;
+       mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
+                                    (V_ACTIVE + V_BLANKING));
+       if (INTERLACED) {
+               mode->yres *= 2;
+               mode->upper_margin *= 2;
+               mode->lower_margin *= 2;
+               mode->vsync_len *= 2;
+               mode->vmode |= FB_VMODE_INTERLACED;
+       }
+       mode->flag = FB_MODE_IS_DETAILED;
+
+       if ((H_SIZE / 16) == (V_SIZE / 9))
+               mode->vmode |= FB_VMODE_ASPECT_16_9;
+       else if ((H_SIZE / 4) == (V_SIZE / 3))
+               mode->vmode |= FB_VMODE_ASPECT_4_3;
+       else if ((mode->xres / 16) == (mode->yres / 9))
+               mode->vmode |= FB_VMODE_ASPECT_16_9;
+       else if ((mode->xres / 4) == (mode->yres / 3))
+               mode->vmode |= FB_VMODE_ASPECT_4_3;
+
+       if (mode->vmode & FB_VMODE_ASPECT_16_9)
+               DPRINTK("Aspect ratio: 16:9\n");
+       if (mode->vmode & FB_VMODE_ASPECT_4_3)
+               DPRINTK("Aspect ratio: 4:3\n");
+       DPRINTK("      %d MHz ",  PIXEL_CLOCK/1000000);
+       DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
+              H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
+       DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
+              V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
+       DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
+              (VSYNC_POSITIVE) ? "+" : "-");
+}
+
+int mxc_edid_parse_ext_blk(unsigned char *edid,
+               struct mxc_edid_cfg *cfg,
+               struct fb_monspecs *specs)
+{
+       char detail_timing_desc_offset;
+       struct fb_videomode *mode, *m;
+       unsigned char index = 0x0;
+       unsigned char *block;
+       int i, num = 0, revision;
+
+       if (edid[index++] != 0x2) /* only support cea ext block now */
+               return 0;
+       revision = edid[index++];
+       DPRINTK("cea extent revision %d\n", revision);
+       mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
+       if (mode == NULL)
+               return -1;
+
+       detail_timing_desc_offset = edid[index++];
+
+       if (revision >= 2) {
+               cfg->cea_underscan = (edid[index] >> 7) & 0x1;
+               cfg->cea_basicaudio = (edid[index] >> 6) & 0x1;
+               cfg->cea_ycbcr444 = (edid[index] >> 5) & 0x1;
+               cfg->cea_ycbcr422 = (edid[index] >> 4) & 0x1;
+
+               DPRINTK("CEA underscan %d\n", cfg->cea_underscan);
+               DPRINTK("CEA basicaudio %d\n", cfg->cea_basicaudio);
+               DPRINTK("CEA ycbcr444 %d\n", cfg->cea_ycbcr444);
+               DPRINTK("CEA ycbcr422 %d\n", cfg->cea_ycbcr422);
+       }
+
+       if (revision >= 3) {
+               /* short desc */
+               DPRINTK("CEA Short desc timmings\n");
+               index++;
+               while (index < detail_timing_desc_offset) {
+                       unsigned char tagcode, blklen;
+
+                       tagcode = (edid[index] >> 5) & 0x7;
+                       blklen = (edid[index]) & 0x1f;
+
+                       DPRINTK("Tagcode %x Len %d\n", tagcode, blklen);
+
+                       switch (tagcode) {
+                       case 0x2: /*Video data block*/
+                               {
+                                       int cea_idx;
+                                       i = 0;
+                                       while (i < blklen) {
+                                               index++;
+                                               cea_idx = edid[index] & 0x7f;
+                                               if (cea_idx < ARRAY_SIZE(mxc_cea_mode) &&
+                                                               (mxc_cea_mode[cea_idx].xres)) {
+                                                       DPRINTK("Support CEA Format #%d\n", cea_idx);
+                                                       mode[num] = mxc_cea_mode[cea_idx];
+                                                       mode[num].flag |= FB_MODE_IS_STANDARD;
+                                                       num++;
+                                               }
+                                               i++;
+                                       }
+                                       break;
+                               }
+                       case 0x3: /*Vendor specific data*/
+                               {
+                                       unsigned char IEEE_reg_iden[3];
+                                       unsigned char deep_color;
+                                       unsigned char latency_present;
+                                       unsigned char I_latency_present;
+                                       unsigned char hdmi_video_present;
+                                       unsigned char hdmi_3d_present;
+                                       unsigned char hdmi_3d_multi_present;
+                                       unsigned char hdmi_vic_len;
+                                       unsigned char hdmi_3d_len;
+                                       unsigned char index_inc = 0;
+                                       unsigned char vsd_end;
+
+                                       vsd_end = index + blklen;
+
+                                       IEEE_reg_iden[0] = edid[index+1];
+                                       IEEE_reg_iden[1] = edid[index+2];
+                                       IEEE_reg_iden[2] = edid[index+3];
+                                       cfg->physical_address[0] = (edid[index+4] & 0xf0) >> 4;
+                                       cfg->physical_address[1] = (edid[index+4] & 0x0f);
+                                       cfg->physical_address[2] = (edid[index+5] & 0xf0) >> 4;
+                                       cfg->physical_address[3] = (edid[index+5] & 0x0f);
+
+                                       if ((IEEE_reg_iden[0] == 0x03) &&
+                                                       (IEEE_reg_iden[1] == 0x0c) &&
+                                                       (IEEE_reg_iden[2] == 0x00))
+                                               cfg->hdmi_cap = 1;
+
+                                       if (blklen > 5) {
+                                               deep_color = edid[index+6];
+                                               if (deep_color & 0x80)
+                                                       cfg->vsd_support_ai = true;
+                                               if (deep_color & 0x40)
+                                                       cfg->vsd_dc_48bit = true;
+                                               if (deep_color & 0x20)
+                                                       cfg->vsd_dc_36bit = true;
+                                               if (deep_color & 0x10)
+                                                       cfg->vsd_dc_30bit = true;
+                                               if (deep_color & 0x08)
+                                                       cfg->vsd_dc_y444 = true;
+                                               if (deep_color & 0x01)
+                                                       cfg->vsd_dvi_dual = true;
+                                       }
+
+                                       DPRINTK("VSD hdmi capability %d\n", cfg->hdmi_cap);
+                                       DPRINTK("VSD support ai %d\n", cfg->vsd_support_ai);
+                                       DPRINTK("VSD support deep color 48bit %d\n", cfg->vsd_dc_48bit);
+                                       DPRINTK("VSD support deep color 36bit %d\n", cfg->vsd_dc_36bit);
+                                       DPRINTK("VSD support deep color 30bit %d\n", cfg->vsd_dc_30bit);
+                                       DPRINTK("VSD support deep color y444 %d\n", cfg->vsd_dc_y444);
+                                       DPRINTK("VSD support dvi dual %d\n", cfg->vsd_dvi_dual);
+
+                                       if (blklen > 6)
+                                               cfg->vsd_max_tmdsclk_rate = edid[index+7] * 5;
+                                       DPRINTK("VSD MAX TMDS CLOCK RATE %d\n", cfg->vsd_max_tmdsclk_rate);
+
+                                       if (blklen > 7) {
+                                               latency_present = edid[index+8] >> 7;
+                                               I_latency_present =  (edid[index+8] & 0x40) >> 6;
+                                               hdmi_video_present = (edid[index+8] & 0x20) >> 5;
+                                               cfg->vsd_cnc3 = (edid[index+8] & 0x8) >> 3;
+                                               cfg->vsd_cnc2 = (edid[index+8] & 0x4) >> 2;
+                                               cfg->vsd_cnc1 = (edid[index+8] & 0x2) >> 1;
+                                               cfg->vsd_cnc0 = edid[index+8] & 0x1;
+
+                                               DPRINTK("VSD cnc0 %d\n", cfg->vsd_cnc0);
+                                               DPRINTK("VSD cnc1 %d\n", cfg->vsd_cnc1);
+                                               DPRINTK("VSD cnc2 %d\n", cfg->vsd_cnc2);
+                                               DPRINTK("VSD cnc3 %d\n", cfg->vsd_cnc3);
+                                               DPRINTK("latency_present %d\n", latency_present);
+                                               DPRINTK("I_latency_present %d\n", I_latency_present);
+                                               DPRINTK("hdmi_video_present %d\n", hdmi_video_present);
+
+                                       } else {
+                                               index += blklen;
+                                               break;
+                                       }
+
+                                       index += 9;
+
+                                       /*latency present */
+                                       if (latency_present) {
+                                               cfg->vsd_video_latency = edid[index++];
+                                               cfg->vsd_audio_latency = edid[index++];
+
+                                               if (I_latency_present) {
+                                                       cfg->vsd_I_video_latency = edid[index++];
+                                                       cfg->vsd_I_audio_latency = edid[index++];
+                                               } else {
+                                                       cfg->vsd_I_video_latency = cfg->vsd_video_latency;
+                                                       cfg->vsd_I_audio_latency = cfg->vsd_audio_latency;
+                                               }
+
+                                               DPRINTK("VSD latency video_latency  %d\n", cfg->vsd_video_latency);
+                                               DPRINTK("VSD latency audio_latency  %d\n", cfg->vsd_audio_latency);
+                                               DPRINTK("VSD latency I_video_latency  %d\n", cfg->vsd_I_video_latency);
+                                               DPRINTK("VSD latency I_audio_latency  %d\n", cfg->vsd_I_audio_latency);
+                                       }
+
+                                       if (hdmi_video_present) {
+                                               hdmi_3d_present = edid[index] >> 7;
+                                               hdmi_3d_multi_present = (edid[index] & 0x60) >> 5;
+                                               index++;
+                                               hdmi_vic_len = (edid[index] & 0xe0) >> 5;
+                                               hdmi_3d_len = edid[index] & 0x1f;
+                                               index++;
+
+                                               DPRINTK("hdmi_3d_present %d\n", hdmi_3d_present);
+                                               DPRINTK("hdmi_3d_multi_present %d\n", hdmi_3d_multi_present);
+                                               DPRINTK("hdmi_vic_len %d\n", hdmi_vic_len);
+                                               DPRINTK("hdmi_3d_len %d\n", hdmi_3d_len);
+
+                                               if (hdmi_vic_len > 0) {
+                                                       for (i = 0; i < hdmi_vic_len; i++) {
+                                                               cfg->hdmi_vic[i] = edid[index++];
+                                                               DPRINTK("HDMI_vic=%d\n", cfg->hdmi_vic[i]);
+                                                       }
+                                               }
+
+                                               if (hdmi_3d_len > 0) {
+                                                       if (hdmi_3d_present) {
+                                                               if (hdmi_3d_multi_present == 0x1) {
+                                                                       cfg->hdmi_3d_struct_all = (edid[index] << 8) | edid[index+1];
+                                                                       index_inc = 2;
+                                                               } else if (hdmi_3d_multi_present == 0x2) {
+                                                                       cfg->hdmi_3d_struct_all = (edid[index] << 8) | edid[index+1];
+                                                                       cfg->hdmi_3d_mask_all = (edid[index+2] << 8) | edid[index+3];
+                                                                       index_inc = 4;
+                                                               } else
+                                                                       index_inc = 0;
+                                                       }
+
+                                                       DPRINTK("HDMI 3d struct all =0x%x\n", cfg->hdmi_3d_struct_all);
+                                                       DPRINTK("HDMI 3d mask all =0x%x\n", cfg->hdmi_3d_mask_all);
+
+                                                       /* Read 2D vic 3D_struct */
+                                                       if ((hdmi_3d_len - index_inc) > 0) {
+                                                               DPRINTK("Support 3D video format\n");
+                                                               i = 0;
+                                                               while ((hdmi_3d_len - index_inc) > 0) {
+
+                                                                       cfg->hdmi_3d_format[i].vic_order_2d = edid[index+index_inc] >> 4;
+                                                                       cfg->hdmi_3d_format[i].struct_3d = edid[index+index_inc] & 0x0f;
+                                                                       index_inc++;
+
+                                                                       if (cfg->hdmi_3d_format[i].struct_3d ==  8) {
+                                                                               cfg->hdmi_3d_format[i].detail_3d = edid[index+index_inc] >> 4;
+                                                                               index_inc++;
+                                                                       } else if (cfg->hdmi_3d_format[i].struct_3d > 8) {
+                                                                               cfg->hdmi_3d_format[i].detail_3d = 0;
+                                                                               index_inc++;
+                                                                       }
+
+                                                                       DPRINTK("vic_order_2d=%d, 3d_struct=%d, 3d_detail=0x%x\n",
+                                                                                       cfg->hdmi_3d_format[i].vic_order_2d,
+                                                                                       cfg->hdmi_3d_format[i].struct_3d,
+                                                                                       cfg->hdmi_3d_format[i].detail_3d);
+                                                                       i++;
+                                                               }
+                                                       }
+                                                       index += index_inc;
+                                               }
+                                       }
+
+                                       index = vsd_end;
+
+                                       break;
+                               }
+                       case 0x1: /*Audio data block*/
+                               {
+                                       u8 audio_format, max_ch, byte1, byte2, byte3;
+
+                                       i = 0;
+                                       cfg->max_channels = 0;
+                                       cfg->sample_rates = 0;
+                                       cfg->sample_sizes = 0;
+
+                                       while (i < blklen) {
+                                               byte1 = edid[index + 1];
+                                               byte2 = edid[index + 2];
+                                               byte3 = edid[index + 3];
+                                               index += 3;
+                                               i += 3;
+
+                                               audio_format = byte1 >> 3;
+                                               max_ch = (byte1 & 0x07) + 1;
+
+                                               DPRINTK("Audio Format Descriptor : %2d\n", audio_format);
+                                               DPRINTK("Max Number of Channels  : %2d\n", max_ch);
+                                               DPRINTK("Sample Rates            : %02x\n", byte2);
+
+                                               /* ALSA can't specify specific compressed
+                                                * formats, so only care about PCM for now. */
+                                               if (audio_format == AUDIO_CODING_TYPE_LPCM) {
+                                                       if (max_ch > cfg->max_channels)
+                                                               cfg->max_channels = max_ch;
+
+                                                       cfg->sample_rates |= byte2;
+                                                       cfg->sample_sizes |= byte3 & 0x7;
+                                                       DPRINTK("Sample Sizes            : %02x\n",
+                                                               byte3 & 0x7);
+                                               }
+                                       }
+                                       break;
+                               }
+                       case 0x4: /*Speaker allocation block*/
+                               {
+                                       i = 0;
+                                       while (i < blklen) {
+                                               cfg->speaker_alloc = edid[index + 1];
+                                               index += 3;
+                                               i += 3;
+                                               DPRINTK("Speaker Alloc           : %02x\n", cfg->speaker_alloc);
+                                       }
+                                       break;
+                               }
+                       case 0x7: /*User extended block*/
+                       default:
+                               /* skip */
+                               DPRINTK("Not handle block, tagcode = 0x%x\n", tagcode);
+                               index += blklen;
+                               break;
+                       }
+
+                       index++;
+               }
+       }
+
+       /* long desc */
+       DPRINTK("CEA long desc timmings\n");
+       index = detail_timing_desc_offset;
+       block = edid + index;
+       while (index < (EDID_LENGTH - DETAILED_TIMING_DESCRIPTION_SIZE)) {
+               if (!(block[0] == 0x00 && block[1] == 0x00)) {
+                       get_detailed_timing(block, &mode[num]);
+                       num++;
+               }
+               block += DETAILED_TIMING_DESCRIPTION_SIZE;
+               index += DETAILED_TIMING_DESCRIPTION_SIZE;
+       }
+
+       if (!num) {
+               kfree(mode);
+               return 0;
+       }
+
+       m = kmalloc((num + specs->modedb_len) *
+                       sizeof(struct fb_videomode), GFP_KERNEL);
+       if (!m)
+               return 0;
+
+       if (specs->modedb_len) {
+               memmove(m, specs->modedb,
+                       specs->modedb_len * sizeof(struct fb_videomode));
+               kfree(specs->modedb);
+       }
+       memmove(m+specs->modedb_len, mode,
+               num * sizeof(struct fb_videomode));
+       kfree(mode);
+
+       specs->modedb_len += num;
+       specs->modedb = m;
+
+       return 0;
+}
+EXPORT_SYMBOL(mxc_edid_parse_ext_blk);
+
+static int mxc_edid_readblk(struct i2c_adapter *adp,
+               unsigned short addr, unsigned char *edid)
+{
+       int ret = 0, extblknum = 0;
+       unsigned char regaddr = 0x0;
+       struct i2c_msg msg[2] = {
+               {
+               .addr   = addr,
+               .flags  = 0,
+               .len    = 1,
+               .buf    = &regaddr,
+               }, {
+               .addr   = addr,
+               .flags  = I2C_M_RD,
+               .len    = EDID_LENGTH,
+               .buf    = edid,
+               },
+       };
+
+       ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
+       if (ret != ARRAY_SIZE(msg)) {
+               DPRINTK("unable to read EDID block\n");
+               return -EIO;
+       }
+
+       if (edid[1] == 0x00)
+               return -ENOENT;
+
+       extblknum = edid[0x7E];
+
+       if (extblknum) {
+               regaddr = 128;
+               msg[1].buf = edid + EDID_LENGTH;
+
+               ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
+               if (ret != ARRAY_SIZE(msg)) {
+                       DPRINTK("unable to read EDID ext block\n");
+                       return -EIO;
+               }
+       }
+
+       return extblknum;
+}
+
+static int mxc_edid_readsegblk(struct i2c_adapter *adp, unsigned short addr,
+                       unsigned char *edid, int seg_num)
+{
+       int ret = 0;
+       unsigned char segment = 0x1, regaddr = 0;
+       struct i2c_msg msg[3] = {
+               {
+               .addr   = 0x30,
+               .flags  = 0,
+               .len    = 1,
+               .buf    = &segment,
+               }, {
+               .addr   = addr,
+               .flags  = 0,
+               .len    = 1,
+               .buf    = &regaddr,
+               }, {
+               .addr   = addr,
+               .flags  = I2C_M_RD,
+               .len    = EDID_LENGTH,
+               .buf    = edid,
+               },
+       };
+
+       ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
+       if (ret != ARRAY_SIZE(msg)) {
+               DPRINTK("unable to read EDID block\n");
+               return -EIO;
+       }
+
+       if (seg_num == 2) {
+               regaddr = 128;
+               msg[2].buf = edid + EDID_LENGTH;
+
+               ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
+               if (ret != ARRAY_SIZE(msg)) {
+                       DPRINTK("unable to read EDID block\n");
+                       return -EIO;
+               }
+       }
+
+       return ret;
+}
+
+int mxc_edid_var_to_vic(struct fb_var_screeninfo *var)
+{
+       int i;
+       struct fb_videomode m;
+
+       for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
+               fb_var_to_videomode(&m, var);
+               if (mxc_edid_fb_mode_is_equal(false, &m, &mxc_cea_mode[i]))
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(mxc_cea_mode))
+               return 0;
+
+       return i;
+}
+EXPORT_SYMBOL(mxc_edid_var_to_vic);
+
+int mxc_edid_mode_to_vic(const struct fb_videomode *mode)
+{
+       int i;
+       bool use_aspect = (mode->vmode & FB_VMODE_ASPECT_MASK);
+
+       for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
+               if (mxc_edid_fb_mode_is_equal(use_aspect, mode, &mxc_cea_mode[i]))
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(mxc_cea_mode))
+               return 0;
+
+       return i;
+}
+EXPORT_SYMBOL(mxc_edid_mode_to_vic);
+
+/* make sure edid has 512 bytes*/
+int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr,
+       unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi)
+{
+       int ret = 0, extblknum;
+       if (!adp || !edid || !cfg || !fbi)
+               return -EINVAL;
+
+       memset(edid, 0, EDID_LENGTH*4);
+       memset(cfg, 0, sizeof(struct mxc_edid_cfg));
+
+       extblknum = mxc_edid_readblk(adp, addr, edid);
+       if (extblknum < 0)
+               return extblknum;
+
+       /* edid first block parsing */
+       memset(&fbi->monspecs, 0, sizeof(fbi->monspecs));
+       fb_edid_to_monspecs(edid, &fbi->monspecs);
+
+       if (extblknum) {
+               int i;
+
+               /* FIXME: mxc_edid_readsegblk() won't read more than 2 blocks
+                * and the for-loop will read past the end of the buffer! :-( */
+               if (extblknum > 3) {
+                       WARN_ON(true);
+                       return -EINVAL;
+               }
+
+               /* need read segment block? */
+               if (extblknum > 1) {
+                       ret = mxc_edid_readsegblk(adp, addr,
+                               edid + EDID_LENGTH*2, extblknum - 1);
+                       if (ret < 0)
+                               return ret;
+               }
+
+               for (i = 1; i <= extblknum; i++)
+                       /* edid ext block parsing */
+                       mxc_edid_parse_ext_blk(edid + i*EDID_LENGTH,
+                                       cfg, &fbi->monspecs);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(mxc_edid_read);
+
diff --git a/drivers/video/fbdev/mxc/mxc_hdmi.c b/drivers/video/fbdev/mxc/mxc_hdmi.c
new file mode 100644 (file)
index 0000000..ab87b47
--- /dev/null
@@ -0,0 +1,2948 @@
+/*
+ * Copyright (C) 2011-2015 Freescale Semiconductor, Inc.
+ *
+ * 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
+ */
+/*
+ * SH-Mobile High-Definition Multimedia Interface (HDMI) driver
+ * for SLISHDMI13T and SLIPHDMIT IP cores
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/uaccess.h>
+#include <linux/cpufreq.h>
+#include <linux/firmware.h>
+#include <linux/kthread.h>
+#include <linux/regulator/driver.h>
+#include <linux/fsl_devices.h>
+#include <linux/ipu.h>
+#include <linux/regmap.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_device.h>
+
+#include <linux/console.h>
+#include <linux/types.h>
+
+#include "../edid.h"
+#include <video/mxc_edid.h>
+#include <video/mxc_hdmi.h>
+#include "mxc_dispdrv.h"
+
+#include <linux/mfd/mxc-hdmi-core.h>
+
+#define DISPDRV_HDMI   "hdmi"
+#define HDMI_EDID_LEN          512
+
+/* status codes for reading edid */
+#define HDMI_EDID_SUCCESS      0
+#define HDMI_EDID_FAIL         -1
+#define HDMI_EDID_SAME         -2
+#define HDMI_EDID_NO_MODES     -3
+
+#define NUM_CEA_VIDEO_MODES    64
+#define DEFAULT_VIDEO_MODE     16 /* 1080P */
+
+#define RGB                    0
+#define YCBCR444               1
+#define YCBCR422_16BITS                2
+#define YCBCR422_8BITS         3
+#define XVYCC444            4
+
+/*
+ * We follow a flowchart which is in the "Synopsys DesignWare Courses
+ * HDMI Transmitter Controller User Guide, 1.30a", section 3.1
+ * (dwc_hdmi_tx_user.pdf)
+ *
+ * Below are notes that say "HDMI Initialization Step X"
+ * These correspond to the flowchart.
+ */
+
+/*
+ * We are required to configure VGA mode before reading edid
+ * in HDMI Initialization Step B
+ */
+static const struct fb_videomode vga_mode = {
+       /* 640x480 @ 60 Hz, 31.5 kHz hsync */
+       NULL, 60, 640, 480, 39721, 48, 16, 33, 10, 96, 2, 0,
+       FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, FB_MODE_IS_VESA,
+};
+
+enum hdmi_datamap {
+       RGB444_8B = 0x01,
+       RGB444_10B = 0x03,
+       RGB444_12B = 0x05,
+       RGB444_16B = 0x07,
+       YCbCr444_8B = 0x09,
+       YCbCr444_10B = 0x0B,
+       YCbCr444_12B = 0x0D,
+       YCbCr444_16B = 0x0F,
+       YCbCr422_8B = 0x16,
+       YCbCr422_10B = 0x14,
+       YCbCr422_12B = 0x12,
+};
+
+enum hdmi_colorimetry {
+       eITU601,
+       eITU709,
+};
+
+struct hdmi_vmode {
+       bool mDVI;
+       bool mHSyncPolarity;
+       bool mVSyncPolarity;
+       bool mInterlaced;
+       bool mDataEnablePolarity;
+
+       unsigned int mPixelClock;
+       unsigned int mPixelRepetitionInput;
+       unsigned int mPixelRepetitionOutput;
+};
+
+struct hdmi_data_info {
+       unsigned int enc_in_format;
+       unsigned int enc_out_format;
+       unsigned int enc_color_depth;
+       unsigned int colorimetry;
+       unsigned int pix_repet_factor;
+       unsigned int hdcp_enable;
+       unsigned int rgb_out_enable;
+       struct hdmi_vmode video_mode;
+};
+
+struct hdmi_phy_reg_config {
+       /* HDMI PHY register config for pass HCT */
+       u16 reg_vlev;
+       u16 reg_cksymtx;
+};
+
+struct mxc_hdmi {
+       struct platform_device *pdev;
+       struct platform_device *core_pdev;
+       struct mxc_dispdrv_handle *disp_mxc_hdmi;
+       struct fb_info *fbi;
+       struct clk *hdmi_isfr_clk;
+       struct clk *hdmi_iahb_clk;
+       struct clk *mipi_core_clk;
+       struct delayed_work hotplug_work;
+       struct delayed_work hdcp_hdp_work;
+
+       struct notifier_block nb;
+
+       struct hdmi_data_info hdmi_data;
+       int vic;
+       struct mxc_edid_cfg edid_cfg;
+       u8 edid[HDMI_EDID_LEN];
+       bool fb_reg;
+       bool cable_plugin;
+       u8  blank;
+       bool dft_mode_set;
+       char *dft_mode_str;
+       int default_bpp;
+       u8 latest_intr_stat;
+       bool irq_enabled;
+       spinlock_t irq_lock;
+       bool phy_enabled;
+       struct fb_videomode default_mode;
+       struct fb_videomode previous_non_vga_mode;
+       bool requesting_vga_for_initialization;
+
+       int *gpr_base;
+       int *gpr_hdmi_base;
+       int *gpr_sdma_base;
+       int cpu_type;
+       int cpu_version;
+       struct hdmi_phy_reg_config phy_config;
+
+       struct pinctrl *pinctrl;
+};
+
+static int hdmi_major;
+static struct class *hdmi_class;
+
+struct i2c_client *hdmi_i2c;
+struct mxc_hdmi *g_hdmi;
+
+static bool hdmi_inited;
+static bool hdcp_init;
+
+extern const struct fb_videomode mxc_cea_mode[64];
+extern void mxc_hdmi_cec_handle(u16 cec_stat);
+
+static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event);
+static void hdmi_enable_overflow_interrupts(void);
+static void hdmi_disable_overflow_interrupts(void);
+
+static struct platform_device_id imx_hdmi_devtype[] = {
+       {
+               .name = "hdmi-imx6DL",
+               .driver_data = IMX6DL_HDMI,
+       }, {
+               .name = "hdmi-imx6Q",
+               .driver_data = IMX6Q_HDMI,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(platform, imx_hdmi_devtype);
+
+static const struct of_device_id imx_hdmi_dt_ids[] = {
+       { .compatible = "fsl,imx6dl-hdmi-video", .data = &imx_hdmi_devtype[IMX6DL_HDMI], },
+       { .compatible = "fsl,imx6q-hdmi-video", .data = &imx_hdmi_devtype[IMX6Q_HDMI], },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids);
+
+static inline int cpu_is_imx6dl(struct mxc_hdmi *hdmi)
+{
+       return hdmi->cpu_type == IMX6DL_HDMI;
+}
+#ifdef DEBUG
+static void dump_fb_videomode(struct fb_videomode *m)
+{
+       pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+               m->refresh, m->xres, m->yres, m->pixclock, m->left_margin,
+               m->right_margin, m->upper_margin, m->lower_margin,
+               m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag);
+}
+#else
+static void dump_fb_videomode(struct fb_videomode *m)
+{}
+#endif
+
+static ssize_t mxc_hdmi_show_name(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mxc_hdmi *hdmi = dev_get_drvdata(dev);
+
+       strcpy(buf, hdmi->fbi->fix.id);
+       sprintf(buf+strlen(buf), "\n");
+
+       return strlen(buf);
+}
+
+static DEVICE_ATTR(fb_name, S_IRUGO, mxc_hdmi_show_name, NULL);
+
+static ssize_t mxc_hdmi_show_state(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mxc_hdmi *hdmi = dev_get_drvdata(dev);
+
+       if (hdmi->cable_plugin == false)
+               strcpy(buf, "plugout\n");
+       else
+               strcpy(buf, "plugin\n");
+
+       return strlen(buf);
+}
+
+static DEVICE_ATTR(cable_state, S_IRUGO, mxc_hdmi_show_state, NULL);
+
+static ssize_t mxc_hdmi_show_edid(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mxc_hdmi *hdmi = dev_get_drvdata(dev);
+       int i, j, len = 0;
+
+       for (j = 0; j < HDMI_EDID_LEN/16; j++) {
+               for (i = 0; i < 16; i++)
+                       len += sprintf(buf+len, "0x%02X ",
+                                       hdmi->edid[j*16 + i]);
+               len += sprintf(buf+len, "\n");
+       }
+
+       return len;
+}
+
+static DEVICE_ATTR(edid, S_IRUGO, mxc_hdmi_show_edid, NULL);
+
+static ssize_t mxc_hdmi_show_rgb_out_enable(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mxc_hdmi *hdmi = dev_get_drvdata(dev);
+
+       if (hdmi->hdmi_data.rgb_out_enable == true)
+               strcpy(buf, "RGB out\n");
+       else
+               strcpy(buf, "YCbCr out\n");
+
+       return strlen(buf);
+}
+
+static ssize_t mxc_hdmi_store_rgb_out_enable(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct mxc_hdmi *hdmi = dev_get_drvdata(dev);
+       unsigned long value;
+       int ret;
+
+       ret = kstrtoul(buf, 10, &value);
+       if (ret)
+               return ret;
+
+       hdmi->hdmi_data.rgb_out_enable = value;
+
+       /* Reconfig HDMI for output color space change */
+       mxc_hdmi_setup(hdmi, 0);
+
+       return count;
+}
+
+static DEVICE_ATTR(rgb_out_enable, S_IRUGO | S_IWUSR,
+                               mxc_hdmi_show_rgb_out_enable,
+                               mxc_hdmi_store_rgb_out_enable);
+
+static ssize_t mxc_hdmi_show_hdcp_enable(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mxc_hdmi *hdmi = dev_get_drvdata(dev);
+
+       if (hdmi->hdmi_data.hdcp_enable == false)
+               strcpy(buf, "hdcp disable\n");
+       else
+               strcpy(buf, "hdcp enable\n");
+
+       return strlen(buf);
+
+}
+
+static ssize_t mxc_hdmi_store_hdcp_enable(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct mxc_hdmi *hdmi = dev_get_drvdata(dev);
+       char event_string[32];
+       char *envp[] = { event_string, NULL };
+       unsigned long value;
+       int ret;
+
+       ret = kstrtoul(buf, 10, &value);
+       if (ret)
+               return ret;
+
+       hdmi->hdmi_data.hdcp_enable = value;
+
+       /* Reconfig HDMI for HDCP */
+       mxc_hdmi_setup(hdmi, 0);
+
+       if (hdmi->hdmi_data.hdcp_enable == false) {
+               sprintf(event_string, "EVENT=hdcpdisable");
+               kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp);
+       } else {
+               sprintf(event_string, "EVENT=hdcpenable");
+               kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp);
+       }
+
+       return count;
+
+}
+
+static DEVICE_ATTR(hdcp_enable, S_IRUGO | S_IWUSR,
+                       mxc_hdmi_show_hdcp_enable, mxc_hdmi_store_hdcp_enable);
+
+/*!
+ * this submodule is responsible for the video data synchronization.
+ * for example, for RGB 4:4:4 input, the data map is defined as
+ *                     pin{47~40} <==> R[7:0]
+ *                     pin{31~24} <==> G[7:0]
+ *                     pin{15~8}  <==> B[7:0]
+ */
+static void hdmi_video_sample(struct mxc_hdmi *hdmi)
+{
+       int color_format = 0;
+       u8 val;
+
+       if (hdmi->hdmi_data.enc_in_format == RGB) {
+               if (hdmi->hdmi_data.enc_color_depth == 8)
+                       color_format = 0x01;
+               else if (hdmi->hdmi_data.enc_color_depth == 10)
+                       color_format = 0x03;
+               else if (hdmi->hdmi_data.enc_color_depth == 12)
+                       color_format = 0x05;
+               else if (hdmi->hdmi_data.enc_color_depth == 16)
+                       color_format = 0x07;
+               else
+                       return;
+       } else if (hdmi->hdmi_data.enc_in_format == YCBCR444) {
+               if (hdmi->hdmi_data.enc_color_depth == 8)
+                       color_format = 0x09;
+               else if (hdmi->hdmi_data.enc_color_depth == 10)
+                       color_format = 0x0B;
+               else if (hdmi->hdmi_data.enc_color_depth == 12)
+                       color_format = 0x0D;
+               else if (hdmi->hdmi_data.enc_color_depth == 16)
+                       color_format = 0x0F;
+               else
+                       return;
+       } else if (hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) {
+               if (hdmi->hdmi_data.enc_color_depth == 8)
+                       color_format = 0x16;
+               else if (hdmi->hdmi_data.enc_color_depth == 10)
+                       color_format = 0x14;
+               else if (hdmi->hdmi_data.enc_color_depth == 12)
+                       color_format = 0x12;
+               else
+                       return;
+       }
+
+       val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
+               ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
+               HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
+       hdmi_writeb(val, HDMI_TX_INVID0);
+
+       /* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
+       val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
+               HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
+               HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
+       hdmi_writeb(val, HDMI_TX_INSTUFFING);
+       hdmi_writeb(0x0, HDMI_TX_GYDATA0);
+       hdmi_writeb(0x0, HDMI_TX_GYDATA1);
+       hdmi_writeb(0x0, HDMI_TX_RCRDATA0);
+       hdmi_writeb(0x0, HDMI_TX_RCRDATA1);
+       hdmi_writeb(0x0, HDMI_TX_BCBDATA0);
+       hdmi_writeb(0x0, HDMI_TX_BCBDATA1);
+}
+
+static int isColorSpaceConversion(struct mxc_hdmi *hdmi)
+{
+       return (hdmi->hdmi_data.enc_in_format !=
+               hdmi->hdmi_data.enc_out_format);
+}
+
+static int isColorSpaceDecimation(struct mxc_hdmi *hdmi)
+{
+       return ((hdmi->hdmi_data.enc_out_format == YCBCR422_8BITS) &&
+               (hdmi->hdmi_data.enc_in_format == RGB ||
+               hdmi->hdmi_data.enc_in_format == YCBCR444));
+}
+
+static int isColorSpaceInterpolation(struct mxc_hdmi *hdmi)
+{
+       return ((hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) &&
+               (hdmi->hdmi_data.enc_out_format == RGB
+               || hdmi->hdmi_data.enc_out_format == YCBCR444));
+}
+
+/*!
+ * update the color space conversion coefficients.
+ */
+static void update_csc_coeffs(struct mxc_hdmi *hdmi)
+{
+       unsigned short csc_coeff[3][4];
+       unsigned int csc_scale = 1;
+       u8 val;
+       bool coeff_selected = false;
+
+       if (isColorSpaceConversion(hdmi)) { /* csc needed */
+               if (hdmi->hdmi_data.enc_out_format == RGB) {
+                       if (hdmi->hdmi_data.colorimetry == eITU601) {
+                               csc_coeff[0][0] = 0x2000;
+                               csc_coeff[0][1] = 0x6926;
+                               csc_coeff[0][2] = 0x74fd;
+                               csc_coeff[0][3] = 0x010e;
+
+                               csc_coeff[1][0] = 0x2000;
+                               csc_coeff[1][1] = 0x2cdd;
+                               csc_coeff[1][2] = 0x0000;
+                               csc_coeff[1][3] = 0x7e9a;
+
+                               csc_coeff[2][0] = 0x2000;
+                               csc_coeff[2][1] = 0x0000;
+                               csc_coeff[2][2] = 0x38b4;
+                               csc_coeff[2][3] = 0x7e3b;
+
+                               csc_scale = 1;
+                               coeff_selected = true;
+                       } else if (hdmi->hdmi_data.colorimetry == eITU709) {
+                               csc_coeff[0][0] = 0x2000;
+                               csc_coeff[0][1] = 0x7106;
+                               csc_coeff[0][2] = 0x7a02;
+                               csc_coeff[0][3] = 0x00a7;
+
+                               csc_coeff[1][0] = 0x2000;
+                               csc_coeff[1][1] = 0x3264;
+                               csc_coeff[1][2] = 0x0000;
+                               csc_coeff[1][3] = 0x7e6d;
+
+                               csc_coeff[2][0] = 0x2000;
+                               csc_coeff[2][1] = 0x0000;
+                               csc_coeff[2][2] = 0x3b61;
+                               csc_coeff[2][3] = 0x7e25;
+
+                               csc_scale = 1;
+                               coeff_selected = true;
+                       }
+               } else if (hdmi->hdmi_data.enc_in_format == RGB) {
+                       if (hdmi->hdmi_data.colorimetry == eITU601) {
+                               csc_coeff[0][0] = 0x2591;
+                               csc_coeff[0][1] = 0x1322;
+                               csc_coeff[0][2] = 0x074b;
+                               csc_coeff[0][3] = 0x0000;
+
+                               csc_coeff[1][0] = 0x6535;
+                               csc_coeff[1][1] = 0x2000;
+                               csc_coeff[1][2] = 0x7acc;
+                               csc_coeff[1][3] = 0x0200;
+
+                               csc_coeff[2][0] = 0x6acd;
+                               csc_coeff[2][1] = 0x7534;
+                               csc_coeff[2][2] = 0x2000;
+                               csc_coeff[2][3] = 0x0200;
+
+                               csc_scale = 0;
+                               coeff_selected = true;
+                       } else if (hdmi->hdmi_data.colorimetry == eITU709) {
+                               csc_coeff[0][0] = 0x2dc5;
+                               csc_coeff[0][1] = 0x0d9b;
+                               csc_coeff[0][2] = 0x049e;
+                               csc_coeff[0][3] = 0x0000;
+
+                               csc_coeff[1][0] = 0x62f0;
+                               csc_coeff[1][1] = 0x2000;
+                               csc_coeff[1][2] = 0x7d11;
+                               csc_coeff[1][3] = 0x0200;
+
+                               csc_coeff[2][0] = 0x6756;
+                               csc_coeff[2][1] = 0x78ab;
+                               csc_coeff[2][2] = 0x2000;
+                               csc_coeff[2][3] = 0x0200;
+
+                               csc_scale = 0;
+                               coeff_selected = true;
+                       }
+               }
+       }
+
+       if (!coeff_selected) {
+               csc_coeff[0][0] = 0x2000;
+               csc_coeff[0][1] = 0x0000;
+               csc_coeff[0][2] = 0x0000;
+               csc_coeff[0][3] = 0x0000;
+
+               csc_coeff[1][0] = 0x0000;
+               csc_coeff[1][1] = 0x2000;
+               csc_coeff[1][2] = 0x0000;
+               csc_coeff[1][3] = 0x0000;
+
+               csc_coeff[2][0] = 0x0000;
+               csc_coeff[2][1] = 0x0000;
+               csc_coeff[2][2] = 0x2000;
+               csc_coeff[2][3] = 0x0000;
+
+               csc_scale = 1;
+       }
+
+       /* Update CSC parameters in HDMI CSC registers */
+       hdmi_writeb((unsigned char)(csc_coeff[0][0] & 0xFF),
+               HDMI_CSC_COEF_A1_LSB);
+       hdmi_writeb((unsigned char)(csc_coeff[0][0] >> 8),
+               HDMI_CSC_COEF_A1_MSB);
+       hdmi_writeb((unsigned char)(csc_coeff[0][1] & 0xFF),
+               HDMI_CSC_COEF_A2_LSB);
+       hdmi_writeb((unsigned char)(csc_coeff[0][1] >> 8),
+               HDMI_CSC_COEF_A2_MSB);
+       hdmi_writeb((unsigned char)(csc_coeff[0][2] & 0xFF),
+               HDMI_CSC_COEF_A3_LSB);
+       hdmi_writeb((unsigned char)(csc_coeff[0][2] >> 8),
+               HDMI_CSC_COEF_A3_MSB);
+       hdmi_writeb((unsigned char)(csc_coeff[0][3] & 0xFF),
+               HDMI_CSC_COEF_A4_LSB);
+       hdmi_writeb((unsigned char)(csc_coeff[0][3] >> 8),
+               HDMI_CSC_COEF_A4_MSB);
+
+       hdmi_writeb((unsigned char)(csc_coeff[1][0] & 0xFF),
+               HDMI_CSC_COEF_B1_LSB);
+       hdmi_writeb((unsigned char)(csc_coeff[1][0] >> 8),
+               HDMI_CSC_COEF_B1_MSB);
+       hdmi_writeb((unsigned char)(csc_coeff[1][1] & 0xFF),
+               HDMI_CSC_COEF_B2_LSB);
+       hdmi_writeb((unsigned char)(csc_coeff[1][1] >> 8),
+               HDMI_CSC_COEF_B2_MSB);
+       hdmi_writeb((unsigned char)(csc_coeff[1][2] & 0xFF),
+               HDMI_CSC_COEF_B3_LSB);
+       hdmi_writeb((unsigned char)(csc_coeff[1][2] >> 8),
+               HDMI_CSC_COEF_B3_MSB);
+       hdmi_writeb((unsigned char)(csc_coeff[1][3] & 0xFF),
+               HDMI_CSC_COEF_B4_LSB);
+       hdmi_writeb((unsigned char)(csc_coeff[1][3] >> 8),
+               HDMI_CSC_COEF_B4_MSB);
+
+       hdmi_writeb((unsigned char)(csc_coeff[2][0] & 0xFF),
+               HDMI_CSC_COEF_C1_LSB);
+       hdmi_writeb((unsigned char)(csc_coeff[2][0] >> 8),
+               HDMI_CSC_COEF_C1_MSB);
+       hdmi_writeb((unsigned char)(csc_coeff[2][1] & 0xFF),
+               HDMI_CSC_COEF_C2_LSB);
+       hdmi_writeb((unsigned char)(csc_coeff[2][1] >> 8),
+               HDMI_CSC_COEF_C2_MSB);
+       hdmi_writeb((unsigned char)(csc_coeff[2][2] & 0xFF),
+               HDMI_CSC_COEF_C3_LSB);
+       hdmi_writeb((unsigned char)(csc_coeff[2][2] >> 8),
+               HDMI_CSC_COEF_C3_MSB);
+       hdmi_writeb((unsigned char)(csc_coeff[2][3] & 0xFF),
+               HDMI_CSC_COEF_C4_LSB);
+       hdmi_writeb((unsigned char)(csc_coeff[2][3] >> 8),
+               HDMI_CSC_COEF_C4_MSB);
+
+       val = hdmi_readb(HDMI_CSC_SCALE);
+       val &= ~HDMI_CSC_SCALE_CSCSCALE_MASK;
+       val |= csc_scale & HDMI_CSC_SCALE_CSCSCALE_MASK;
+       hdmi_writeb(val, HDMI_CSC_SCALE);
+}
+
+static void hdmi_video_csc(struct mxc_hdmi *hdmi)
+{
+       int color_depth = 0;
+       int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE;
+       int decimation = 0;
+       u8 val;
+
+       /* YCC422 interpolation to 444 mode */
+       if (isColorSpaceInterpolation(hdmi))
+               interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1;
+       else if (isColorSpaceDecimation(hdmi))
+               decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3;
+
+       if (hdmi->hdmi_data.enc_color_depth == 8)
+               color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP;
+       else if (hdmi->hdmi_data.enc_color_depth == 10)
+               color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP;
+       else if (hdmi->hdmi_data.enc_color_depth == 12)
+               color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP;
+       else if (hdmi->hdmi_data.enc_color_depth == 16)
+               color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP;
+       else
+               return;
+
+       /*configure the CSC registers */
+       hdmi_writeb(interpolation | decimation, HDMI_CSC_CFG);
+       val = hdmi_readb(HDMI_CSC_SCALE);
+       val &= ~HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK;
+       val |= color_depth;
+       hdmi_writeb(val, HDMI_CSC_SCALE);
+
+       update_csc_coeffs(hdmi);
+}
+
+/*!
+ * HDMI video packetizer is used to packetize the data.
+ * for example, if input is YCC422 mode or repeater is used,
+ * data should be repacked this module can be bypassed.
+ */
+static void hdmi_video_packetize(struct mxc_hdmi *hdmi)
+{
+       unsigned int color_depth = 0;
+       unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit;
+       unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
+       struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
+       u8 val;
+
+       if (hdmi_data->enc_out_format == RGB
+               || hdmi_data->enc_out_format == YCBCR444) {
+               if (hdmi_data->enc_color_depth == 0)
+                       output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
+               else if (hdmi_data->enc_color_depth == 8) {
+                       color_depth = 4;
+                       output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
+               } else if (hdmi_data->enc_color_depth == 10)
+                       color_depth = 5;
+               else if (hdmi_data->enc_color_depth == 12)
+                       color_depth = 6;
+               else if (hdmi_data->enc_color_depth == 16)
+                       color_depth = 7;
+               else
+                       return;
+       } else if (hdmi_data->enc_out_format == YCBCR422_8BITS) {
+               if (hdmi_data->enc_color_depth == 0 ||
+                       hdmi_data->enc_color_depth == 8)
+                       remap_size = HDMI_VP_REMAP_YCC422_16bit;
+               else if (hdmi_data->enc_color_depth == 10)
+                       remap_size = HDMI_VP_REMAP_YCC422_20bit;
+               else if (hdmi_data->enc_color_depth == 12)
+                       remap_size = HDMI_VP_REMAP_YCC422_24bit;
+               else
+                       return;
+               output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422;
+       } else
+               return;
+
+       /* HDMI not support deep color,
+        * because IPU MAX support color depth is 24bit */
+       color_depth = 0;
+
+       /* set the packetizer registers */
+       val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
+               HDMI_VP_PR_CD_COLOR_DEPTH_MASK) |
+               ((hdmi_data->pix_repet_factor <<
+               HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) &
+               HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
+       hdmi_writeb(val, HDMI_VP_PR_CD);
+
+       val = hdmi_readb(HDMI_VP_STUFF);
+       val &= ~HDMI_VP_STUFF_PR_STUFFING_MASK;
+       val |= HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE;
+       hdmi_writeb(val, HDMI_VP_STUFF);
+
+       /* Data from pixel repeater block */
+       if (hdmi_data->pix_repet_factor > 1) {
+               val = hdmi_readb(HDMI_VP_CONF);
+               val &= ~(HDMI_VP_CONF_PR_EN_MASK |
+                       HDMI_VP_CONF_BYPASS_SELECT_MASK);
+               val |= HDMI_VP_CONF_PR_EN_ENABLE |
+                       HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER;
+               hdmi_writeb(val, HDMI_VP_CONF);
+       } else { /* data from packetizer block */
+               val = hdmi_readb(HDMI_VP_CONF);
+               val &= ~(HDMI_VP_CONF_PR_EN_MASK |
+                       HDMI_VP_CONF_BYPASS_SELECT_MASK);
+               val |= HDMI_VP_CONF_PR_EN_DISABLE |
+                       HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
+               hdmi_writeb(val, HDMI_VP_CONF);
+       }
+
+       val = hdmi_readb(HDMI_VP_STUFF);
+       val &= ~HDMI_VP_STUFF_IDEFAULT_PHASE_MASK;
+       val |= 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET;
+       hdmi_writeb(val, HDMI_VP_STUFF);
+
+       hdmi_writeb(remap_size, HDMI_VP_REMAP);
+
+       if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
+               val = hdmi_readb(HDMI_VP_CONF);
+               val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK |
+                       HDMI_VP_CONF_PP_EN_ENMASK |
+                       HDMI_VP_CONF_YCC422_EN_MASK);
+               val |= HDMI_VP_CONF_BYPASS_EN_DISABLE |
+                       HDMI_VP_CONF_PP_EN_ENABLE |
+                       HDMI_VP_CONF_YCC422_EN_DISABLE;
+               hdmi_writeb(val, HDMI_VP_CONF);
+       } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) {
+               val = hdmi_readb(HDMI_VP_CONF);
+               val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK |
+                       HDMI_VP_CONF_PP_EN_ENMASK |
+                       HDMI_VP_CONF_YCC422_EN_MASK);
+               val |= HDMI_VP_CONF_BYPASS_EN_DISABLE |
+                       HDMI_VP_CONF_PP_EN_DISABLE |
+                       HDMI_VP_CONF_YCC422_EN_ENABLE;
+               hdmi_writeb(val, HDMI_VP_CONF);
+       } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) {
+               val = hdmi_readb(HDMI_VP_CONF);
+               val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK |
+                       HDMI_VP_CONF_PP_EN_ENMASK |
+                       HDMI_VP_CONF_YCC422_EN_MASK);
+               val |= HDMI_VP_CONF_BYPASS_EN_ENABLE |
+                       HDMI_VP_CONF_PP_EN_DISABLE |
+                       HDMI_VP_CONF_YCC422_EN_DISABLE;
+               hdmi_writeb(val, HDMI_VP_CONF);
+       } else {
+               return;
+       }
+
+       val = hdmi_readb(HDMI_VP_STUFF);
+       val &= ~(HDMI_VP_STUFF_PP_STUFFING_MASK |
+               HDMI_VP_STUFF_YCC422_STUFFING_MASK);
+       val |= HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
+               HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE;
+       hdmi_writeb(val, HDMI_VP_STUFF);
+
+       val = hdmi_readb(HDMI_VP_CONF);
+       val &= ~HDMI_VP_CONF_OUTPUT_SELECTOR_MASK;
+       val |= output_select;
+       hdmi_writeb(val, HDMI_VP_CONF);
+}
+
+#if 0
+/* Force a fixed color screen */
+static void hdmi_video_force_output(struct mxc_hdmi *hdmi, unsigned char force)
+{
+       u8 val;
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       if (force) {
+               hdmi_writeb(0x00, HDMI_FC_DBGTMDS2);   /* R */
+               hdmi_writeb(0x00, HDMI_FC_DBGTMDS1);   /* G */
+               hdmi_writeb(0xFF, HDMI_FC_DBGTMDS0);   /* B */
+               val = hdmi_readb(HDMI_FC_DBGFORCE);
+               val |= HDMI_FC_DBGFORCE_FORCEVIDEO;
+               hdmi_writeb(val, HDMI_FC_DBGFORCE);
+       } else {
+               val = hdmi_readb(HDMI_FC_DBGFORCE);
+               val &= ~HDMI_FC_DBGFORCE_FORCEVIDEO;
+               hdmi_writeb(val, HDMI_FC_DBGFORCE);
+               hdmi_writeb(0x00, HDMI_FC_DBGTMDS2);   /* R */
+               hdmi_writeb(0x00, HDMI_FC_DBGTMDS1);   /* G */
+               hdmi_writeb(0x00, HDMI_FC_DBGTMDS0);   /* B */
+       }
+}
+#endif
+
+static inline void hdmi_phy_test_clear(struct mxc_hdmi *hdmi,
+                                               unsigned char bit)
+{
+       u8 val = hdmi_readb(HDMI_PHY_TST0);
+       val &= ~HDMI_PHY_TST0_TSTCLR_MASK;
+       val |= (bit << HDMI_PHY_TST0_TSTCLR_OFFSET) &
+               HDMI_PHY_TST0_TSTCLR_MASK;
+       hdmi_writeb(val, HDMI_PHY_TST0);
+}
+
+static inline void hdmi_phy_test_enable(struct mxc_hdmi *hdmi,
+                                               unsigned char bit)
+{
+       u8 val = hdmi_readb(HDMI_PHY_TST0);
+       val &= ~HDMI_PHY_TST0_TSTEN_MASK;
+       val |= (bit << HDMI_PHY_TST0_TSTEN_OFFSET) &
+               HDMI_PHY_TST0_TSTEN_MASK;
+       hdmi_writeb(val, HDMI_PHY_TST0);
+}
+
+static inline void hdmi_phy_test_clock(struct mxc_hdmi *hdmi,
+                                               unsigned char bit)
+{
+       u8 val = hdmi_readb(HDMI_PHY_TST0);
+       val &= ~HDMI_PHY_TST0_TSTCLK_MASK;
+       val |= (bit << HDMI_PHY_TST0_TSTCLK_OFFSET) &
+               HDMI_PHY_TST0_TSTCLK_MASK;
+       hdmi_writeb(val, HDMI_PHY_TST0);
+}
+
+static inline void hdmi_phy_test_din(struct mxc_hdmi *hdmi,
+                                               unsigned char bit)
+{
+       hdmi_writeb(bit, HDMI_PHY_TST1);
+}
+
+static inline void hdmi_phy_test_dout(struct mxc_hdmi *hdmi,
+                                               unsigned char bit)
+{
+       hdmi_writeb(bit, HDMI_PHY_TST2);
+}
+
+static bool hdmi_phy_wait_i2c_done(struct mxc_hdmi *hdmi, int msec)
+{
+       unsigned char val = 0;
+       val = hdmi_readb(HDMI_IH_I2CMPHY_STAT0) & 0x3;
+       while (val == 0) {
+               udelay(1000);
+               if (msec-- == 0)
+                       return false;
+               val = hdmi_readb(HDMI_IH_I2CMPHY_STAT0) & 0x3;
+       }
+       return true;
+}
+
+static void hdmi_phy_i2c_write(struct mxc_hdmi *hdmi, unsigned short data,
+                             unsigned char addr)
+{
+       hdmi_writeb(0xFF, HDMI_IH_I2CMPHY_STAT0);
+       hdmi_writeb(addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
+       hdmi_writeb((unsigned char)(data >> 8),
+               HDMI_PHY_I2CM_DATAO_1_ADDR);
+       hdmi_writeb((unsigned char)(data >> 0),
+               HDMI_PHY_I2CM_DATAO_0_ADDR);
+       hdmi_writeb(HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
+               HDMI_PHY_I2CM_OPERATION_ADDR);
+       hdmi_phy_wait_i2c_done(hdmi, 1000);
+}
+
+#if 0
+static unsigned short hdmi_phy_i2c_read(struct mxc_hdmi *hdmi,
+                                       unsigned char addr)
+{
+       unsigned short data;
+       unsigned char msb = 0, lsb = 0;
+       hdmi_writeb(0xFF, HDMI_IH_I2CMPHY_STAT0);
+       hdmi_writeb(addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
+       hdmi_writeb(HDMI_PHY_I2CM_OPERATION_ADDR_READ,
+               HDMI_PHY_I2CM_OPERATION_ADDR);
+       hdmi_phy_wait_i2c_done(hdmi, 1000);
+       msb = hdmi_readb(HDMI_PHY_I2CM_DATAI_1_ADDR);
+       lsb = hdmi_readb(HDMI_PHY_I2CM_DATAI_0_ADDR);
+       data = (msb << 8) | lsb;
+       return data;
+}
+
+static int hdmi_phy_i2c_write_verify(struct mxc_hdmi *hdmi, unsigned short data,
+                                    unsigned char addr)
+{
+       unsigned short val = 0;
+       hdmi_phy_i2c_write(hdmi, data, addr);
+       val = hdmi_phy_i2c_read(hdmi, addr);
+       return (val == data);
+}
+#endif
+
+static bool  hdmi_edid_wait_i2c_done(struct mxc_hdmi *hdmi, int msec)
+{
+    unsigned char val = 0;
+    val = hdmi_readb(HDMI_IH_I2CM_STAT0) & 0x2;
+    while (val == 0) {
+
+               udelay(1000);
+               if (msec-- == 0) {
+                       dev_dbg(&hdmi->pdev->dev,
+                                       "HDMI EDID i2c operation time out!!\n");
+                       return false;
+               }
+               val = hdmi_readb(HDMI_IH_I2CM_STAT0) & 0x2;
+       }
+       return true;
+}
+
+static u8 hdmi_edid_i2c_read(struct mxc_hdmi *hdmi,
+                                       u8 addr, u8 blockno)
+{
+       u8 spointer = blockno / 2;
+       u8 edidaddress = ((blockno % 2) * 0x80) + addr;
+       u8 data;
+
+       hdmi_writeb(0xFF, HDMI_IH_I2CM_STAT0);
+       hdmi_writeb(edidaddress, HDMI_I2CM_ADDRESS);
+       hdmi_writeb(spointer, HDMI_I2CM_SEGADDR);
+       if (spointer == 0)
+               hdmi_writeb(HDMI_I2CM_OPERATION_READ,
+                       HDMI_I2CM_OPERATION);
+       else
+               hdmi_writeb(HDMI_I2CM_OPERATION_READ_EXT,
+                       HDMI_I2CM_OPERATION);
+
+       hdmi_edid_wait_i2c_done(hdmi, 30);
+       data = hdmi_readb(HDMI_I2CM_DATAI);
+       hdmi_writeb(0xFF, HDMI_IH_I2CM_STAT0);
+       return data;
+}
+
+
+/* "Power-down enable (active low)"
+ * That mean that power up == 1! */
+static void mxc_hdmi_phy_enable_power(u8 enable)
+{
+       hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+                       HDMI_PHY_CONF0_PDZ_OFFSET,
+                       HDMI_PHY_CONF0_PDZ_MASK);
+}
+
+static void mxc_hdmi_phy_enable_tmds(u8 enable)
+{
+       hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+                       HDMI_PHY_CONF0_ENTMDS_OFFSET,
+                       HDMI_PHY_CONF0_ENTMDS_MASK);
+}
+
+static void mxc_hdmi_phy_gen2_pddq(u8 enable)
+{
+       hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+                       HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
+                       HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
+}
+
+static void mxc_hdmi_phy_gen2_txpwron(u8 enable)
+{
+       hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+                       HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
+                       HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
+}
+
+#if 0
+static void mxc_hdmi_phy_gen2_enhpdrxsense(u8 enable)
+{
+       hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+                       HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET,
+                       HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK);
+}
+#endif
+
+static void mxc_hdmi_phy_sel_data_en_pol(u8 enable)
+{
+       hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+                       HDMI_PHY_CONF0_SELDATAENPOL_OFFSET,
+                       HDMI_PHY_CONF0_SELDATAENPOL_MASK);
+}
+
+static void mxc_hdmi_phy_sel_interface_control(u8 enable)
+{
+       hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+                       HDMI_PHY_CONF0_SELDIPIF_OFFSET,
+                       HDMI_PHY_CONF0_SELDIPIF_MASK);
+}
+
+static int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
+                             unsigned char cRes, int cscOn)
+{
+       u8 val;
+       u8 msec;
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       /* color resolution 0 is 8 bit colour depth */
+       if (cRes == 0)
+               cRes = 8;
+
+       if (pRep != 0)
+               return false;
+       else if (cRes != 8 && cRes != 12)
+               return false;
+
+       /* Enable csc path */
+       if (cscOn)
+               val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH;
+       else
+               val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS;
+
+       hdmi_writeb(val, HDMI_MC_FLOWCTRL);
+
+       /* gen2 tx power off */
+       mxc_hdmi_phy_gen2_txpwron(0);
+
+       /* gen2 pddq */
+       mxc_hdmi_phy_gen2_pddq(1);
+
+       /* PHY reset */
+       hdmi_writeb(HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
+       hdmi_writeb(HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
+
+       hdmi_writeb(HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
+
+       hdmi_phy_test_clear(hdmi, 1);
+       hdmi_writeb(HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
+                       HDMI_PHY_I2CM_SLAVE_ADDR);
+       hdmi_phy_test_clear(hdmi, 0);
+
+       if (hdmi->hdmi_data.video_mode.mPixelClock < 0) {
+               dev_dbg(&hdmi->pdev->dev, "Pixel clock (%d) must be positive\n",
+                       hdmi->hdmi_data.video_mode.mPixelClock);
+               return false;
+       }
+
+       if (hdmi->hdmi_data.video_mode.mPixelClock <= 45250000) {
+               switch (cRes) {
+               case 8:
+                       /* PLL/MPLL Cfg */
+                       hdmi_phy_i2c_write(hdmi, 0x01e0, 0x06);
+                       hdmi_phy_i2c_write(hdmi, 0x0000, 0x15);  /* GMPCTRL */
+                       break;
+               case 10:
+                       hdmi_phy_i2c_write(hdmi, 0x21e1, 0x06);
+                       hdmi_phy_i2c_write(hdmi, 0x0000, 0x15);
+                       break;
+               case 12:
+                       hdmi_phy_i2c_write(hdmi, 0x41e2, 0x06);
+                       hdmi_phy_i2c_write(hdmi, 0x0000, 0x15);
+                       break;
+               default:
+                       return false;
+               }
+       } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 92500000) {
+               switch (cRes) {
+               case 8:
+                       hdmi_phy_i2c_write(hdmi, 0x0140, 0x06);
+                       hdmi_phy_i2c_write(hdmi, 0x0005, 0x15);
+                       break;
+               case 10:
+                       hdmi_phy_i2c_write(hdmi, 0x2141, 0x06);
+                       hdmi_phy_i2c_write(hdmi, 0x0005, 0x15);
+                       break;
+               case 12:
+                       hdmi_phy_i2c_write(hdmi, 0x4142, 0x06);
+                       hdmi_phy_i2c_write(hdmi, 0x0005, 0x15);
+               default:
+                       return false;
+               }
+       } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 148500000) {
+               switch (cRes) {
+               case 8:
+                       hdmi_phy_i2c_write(hdmi, 0x00a0, 0x06);
+                       hdmi_phy_i2c_write(hdmi, 0x000a, 0x15);
+                       break;
+               case 10:
+                       hdmi_phy_i2c_write(hdmi, 0x20a1, 0x06);
+                       hdmi_phy_i2c_write(hdmi, 0x000a, 0x15);
+                       break;
+               case 12:
+                       hdmi_phy_i2c_write(hdmi, 0x40a2, 0x06);
+                       hdmi_phy_i2c_write(hdmi, 0x000a, 0x15);
+               default:
+                       return false;
+               }
+       } else {
+               switch (cRes) {
+               case 8:
+                       hdmi_phy_i2c_write(hdmi, 0x00a0, 0x06);
+                       hdmi_phy_i2c_write(hdmi, 0x000a, 0x15);
+                       break;
+               case 10:
+                       hdmi_phy_i2c_write(hdmi, 0x2001, 0x06);
+                       hdmi_phy_i2c_write(hdmi, 0x000f, 0x15);
+                       break;
+               case 12:
+                       hdmi_phy_i2c_write(hdmi, 0x4002, 0x06);
+                       hdmi_phy_i2c_write(hdmi, 0x000f, 0x15);
+               default:
+                       return false;
+               }
+       }
+
+       if (hdmi->hdmi_data.video_mode.mPixelClock <= 54000000) {
+               switch (cRes) {
+               case 8:
+                       hdmi_phy_i2c_write(hdmi, 0x091c, 0x10);  /* CURRCTRL */
+                       break;
+               case 10:
+                       hdmi_phy_i2c_write(hdmi, 0x091c, 0x10);
+                       break;
+               case 12:
+                       hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10);
+                       break;
+               default:
+                       return false;
+               }
+       } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 58400000) {
+               switch (cRes) {
+               case 8:
+                       hdmi_phy_i2c_write(hdmi, 0x091c, 0x10);
+                       break;
+               case 10:
+                       hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10);
+                       break;
+               case 12:
+                       hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10);
+                       break;
+               default:
+                       return false;
+               }
+       } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 72000000) {
+               switch (cRes) {
+               case 8:
+                       hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10);
+                       break;
+               case 10:
+                       hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10);
+                       break;
+               case 12:
+                       hdmi_phy_i2c_write(hdmi, 0x091c, 0x10);
+                       break;
+               default:
+                       return false;
+               }
+       } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 74250000) {
+               switch (cRes) {
+               case 8:
+                       hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10);
+                       break;
+               case 10:
+                       hdmi_phy_i2c_write(hdmi, 0x0b5c, 0x10);
+                       break;
+               case 12:
+                       hdmi_phy_i2c_write(hdmi, 0x091c, 0x10);
+                       break;
+               default:
+                       return false;
+               }
+       } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 118800000) {
+               switch (cRes) {
+               case 8:
+                       hdmi_phy_i2c_write(hdmi, 0x091c, 0x10);
+                       break;
+               case 10:
+                       hdmi_phy_i2c_write(hdmi, 0x091c, 0x10);
+                       break;
+               case 12:
+                       hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10);
+                       break;
+               default:
+                       return false;
+               }
+       } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 216000000) {
+               switch (cRes) {
+               case 8:
+                       hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10);
+                       break;
+               case 10:
+                       hdmi_phy_i2c_write(hdmi, 0x0b5c, 0x10);
+                       break;
+               case 12:
+                       hdmi_phy_i2c_write(hdmi, 0x091c, 0x10);
+                       break;
+               default:
+                       return false;
+               }
+       } else {
+               dev_err(&hdmi->pdev->dev,
+                               "Pixel clock %d - unsupported by HDMI\n",
+                               hdmi->hdmi_data.video_mode.mPixelClock);
+               return false;
+       }
+
+       hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);  /* PLLPHBYCTRL */
+       hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
+       /* RESISTANCE TERM 133Ohm Cfg */
+       hdmi_phy_i2c_write(hdmi, 0x0005, 0x19);  /* TXTERM */
+       /* PREEMP Cgf 0.00 */
+       hdmi_phy_i2c_write(hdmi, 0x800d, 0x09);  /* CKSYMTXCTRL */
+       /* TX/CK LVL 10 */
+       hdmi_phy_i2c_write(hdmi, 0x01ad, 0x0E);  /* VLEVCTRL */
+
+       /* Board specific setting for PHY register 0x09, 0x0e to pass HCT */
+       if (hdmi->phy_config.reg_cksymtx != 0)
+               hdmi_phy_i2c_write(hdmi, hdmi->phy_config.reg_cksymtx, 0x09);
+
+       if (hdmi->phy_config.reg_vlev != 0)
+               hdmi_phy_i2c_write(hdmi, hdmi->phy_config.reg_vlev, 0x0E);
+
+       /* REMOVE CLK TERM */
+       hdmi_phy_i2c_write(hdmi, 0x8000, 0x05);  /* CKCALCTRL */
+
+       if (hdmi->hdmi_data.video_mode.mPixelClock > 148500000) {
+                       hdmi_phy_i2c_write(hdmi, 0x800b, 0x09);
+                       hdmi_phy_i2c_write(hdmi, 0x0129, 0x0E);
+       }
+
+       mxc_hdmi_phy_enable_power(1);
+
+       /* toggle TMDS enable */
+       mxc_hdmi_phy_enable_tmds(0);
+       mxc_hdmi_phy_enable_tmds(1);
+
+       /* gen2 tx power on */
+       mxc_hdmi_phy_gen2_txpwron(1);
+       mxc_hdmi_phy_gen2_pddq(0);
+
+       /*Wait for PHY PLL lock */
+       msec = 4;
+       val = hdmi_readb(HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
+       while (val == 0) {
+               udelay(1000);
+               if (msec-- == 0) {
+                       dev_dbg(&hdmi->pdev->dev, "PHY PLL not locked\n");
+                       return false;
+               }
+               val = hdmi_readb(HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
+       }
+
+       return true;
+}
+
+static void mxc_hdmi_phy_init(struct mxc_hdmi *hdmi)
+{
+       int i;
+       bool cscon = false;
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       /* Never do phy init if pixel clock is gated.
+        * Otherwise HDMI PHY will get messed up and generate an overflow
+        * interrupt that can't be cleared or detected by accessing the
+        * status register. */
+       if (!hdmi->fb_reg || !hdmi->cable_plugin
+                       || (hdmi->blank != FB_BLANK_UNBLANK))
+               return;
+
+       if (!hdmi->hdmi_data.video_mode.mDVI)
+               hdmi_enable_overflow_interrupts();
+
+       /*check csc whether needed activated in HDMI mode */
+       cscon = (isColorSpaceConversion(hdmi) &&
+                       !hdmi->hdmi_data.video_mode.mDVI);
+
+       /* HDMI Phy spec says to do the phy initialization sequence twice */
+       for (i = 0 ; i < 2 ; i++) {
+               mxc_hdmi_phy_sel_data_en_pol(1);
+               mxc_hdmi_phy_sel_interface_control(0);
+               mxc_hdmi_phy_enable_tmds(0);
+               mxc_hdmi_phy_enable_power(0);
+
+               /* Enable CSC */
+               hdmi_phy_configure(hdmi, 0, 8, cscon);
+       }
+
+       hdmi->phy_enabled = true;
+}
+
+static void hdmi_config_AVI(struct mxc_hdmi *hdmi)
+{
+       u8 val;
+       u8 pix_fmt;
+       u8 under_scan;
+       u8 act_ratio, coded_ratio, colorimetry, ext_colorimetry;
+       struct fb_videomode mode;
+       const struct fb_videomode *edid_mode;
+       bool aspect_16_9;
+
+       dev_dbg(&hdmi->pdev->dev, "set up AVI frame\n");
+
+       fb_var_to_videomode(&mode, &hdmi->fbi->var);
+       /* Use mode from list extracted from EDID to get aspect ratio */
+       if (!list_empty(&hdmi->fbi->modelist)) {
+               edid_mode = fb_find_nearest_mode(&mode, &hdmi->fbi->modelist);
+               if (edid_mode->vmode & FB_VMODE_ASPECT_16_9)
+                       aspect_16_9 = true;
+               else
+                       aspect_16_9 = false;
+       } else
+               aspect_16_9 = false;
+
+       /********************************************
+        * AVI Data Byte 1
+        ********************************************/
+       if (hdmi->hdmi_data.enc_out_format == YCBCR444)
+               pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_YCBCR444;
+       else if (hdmi->hdmi_data.enc_out_format == YCBCR422_8BITS)
+               pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_YCBCR422;
+       else
+               pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_RGB;
+
+       if (hdmi->edid_cfg.cea_underscan)
+               under_scan = HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN;
+       else
+               under_scan =  HDMI_FC_AVICONF0_SCAN_INFO_NODATA;
+
+       /*
+        * Active format identification data is present in the AVI InfoFrame.
+        * Under scan info, no bar data
+        */
+       val = pix_fmt | under_scan |
+               HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT |
+               HDMI_FC_AVICONF0_BAR_DATA_NO_DATA;
+
+       hdmi_writeb(val, HDMI_FC_AVICONF0);
+
+       /********************************************
+        * AVI Data Byte 2
+        ********************************************/
+
+       /*  Set the Aspect Ratio */
+       if (aspect_16_9) {
+               act_ratio = HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9;
+               coded_ratio = HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9;
+       } else {
+               act_ratio = HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3;
+               coded_ratio = HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3;
+       }
+
+       /* Set up colorimetry */
+       if (hdmi->hdmi_data.enc_out_format == XVYCC444) {
+               colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO;
+               if (hdmi->hdmi_data.colorimetry == eITU601)
+                       ext_colorimetry =
+                               HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601;
+               else /* hdmi->hdmi_data.colorimetry == eITU709 */
+                       ext_colorimetry =
+                               HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709;
+       } else if (hdmi->hdmi_data.enc_out_format != RGB) {
+               if (hdmi->hdmi_data.colorimetry == eITU601)
+                       colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_SMPTE;
+               else /* hdmi->hdmi_data.colorimetry == eITU709 */
+                       colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_ITUR;
+               ext_colorimetry = HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601;
+       } else { /* Carries no data */
+               colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA;
+               ext_colorimetry = HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601;
+       }
+
+       val = colorimetry | coded_ratio | act_ratio;
+       hdmi_writeb(val, HDMI_FC_AVICONF1);
+
+       /********************************************
+        * AVI Data Byte 3
+        ********************************************/
+
+       val = HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA | ext_colorimetry |
+               HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT |
+               HDMI_FC_AVICONF2_SCALING_NONE;
+       hdmi_writeb(val, HDMI_FC_AVICONF2);
+
+       /********************************************
+        * AVI Data Byte 4
+        ********************************************/
+       hdmi_writeb(hdmi->vic, HDMI_FC_AVIVID);
+
+       /********************************************
+        * AVI Data Byte 5
+        ********************************************/
+
+       /* Set up input and output pixel repetition */
+       val = (((hdmi->hdmi_data.video_mode.mPixelRepetitionInput + 1) <<
+               HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET) &
+               HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK) |
+               ((hdmi->hdmi_data.video_mode.mPixelRepetitionOutput <<
+               HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) &
+               HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK);
+       hdmi_writeb(val, HDMI_FC_PRCONF);
+
+       /* IT Content and quantization range = don't care */
+       val = HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS |
+               HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED;
+       hdmi_writeb(val, HDMI_FC_AVICONF3);
+
+       /********************************************
+        * AVI Data Bytes 6-13
+        ********************************************/
+       hdmi_writeb(0, HDMI_FC_AVIETB0);
+       hdmi_writeb(0, HDMI_FC_AVIETB1);
+       hdmi_writeb(0, HDMI_FC_AVISBB0);
+       hdmi_writeb(0, HDMI_FC_AVISBB1);
+       hdmi_writeb(0, HDMI_FC_AVIELB0);
+       hdmi_writeb(0, HDMI_FC_AVIELB1);
+       hdmi_writeb(0, HDMI_FC_AVISRB0);
+       hdmi_writeb(0, HDMI_FC_AVISRB1);
+}
+
+/*!
+ * this submodule is responsible for the video/audio data composition.
+ */
+static void hdmi_av_composer(struct mxc_hdmi *hdmi)
+{
+       u8 inv_val;
+       struct fb_info *fbi = hdmi->fbi;
+       struct fb_videomode fb_mode;
+       struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
+       int hblank, vblank;
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       fb_var_to_videomode(&fb_mode, &fbi->var);
+
+       vmode->mHSyncPolarity = ((fb_mode.sync & FB_SYNC_HOR_HIGH_ACT) != 0);
+       vmode->mVSyncPolarity = ((fb_mode.sync & FB_SYNC_VERT_HIGH_ACT) != 0);
+       vmode->mInterlaced = ((fb_mode.vmode & FB_VMODE_INTERLACED) != 0);
+       vmode->mPixelClock = (fb_mode.xres + fb_mode.left_margin +
+               fb_mode.right_margin + fb_mode.hsync_len) * (fb_mode.yres +
+               fb_mode.upper_margin + fb_mode.lower_margin +
+               fb_mode.vsync_len) * fb_mode.refresh;
+
+       dev_dbg(&hdmi->pdev->dev, "final pixclk = %d\n", vmode->mPixelClock);
+
+       /* Set up HDMI_FC_INVIDCONF */
+       inv_val = (vmode->mVSyncPolarity ?
+               HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
+               HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW);
+
+       inv_val |= (vmode->mHSyncPolarity ?
+               HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH :
+               HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW);
+
+       inv_val |= (vmode->mDataEnablePolarity ?
+               HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH :
+               HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW);
+
+       if (hdmi->vic == 39)
+               inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH;
+       else
+               inv_val |= (vmode->mInterlaced ?
+                       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH :
+                       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW);
+
+       inv_val |= (vmode->mInterlaced ?
+               HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
+               HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE);
+
+       inv_val |= (vmode->mDVI ?
+               HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
+               HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
+
+       hdmi_writeb(inv_val, HDMI_FC_INVIDCONF);
+
+       /* Set up horizontal active pixel region width */
+       hdmi_writeb(fb_mode.xres >> 8, HDMI_FC_INHACTV1);
+       hdmi_writeb(fb_mode.xres, HDMI_FC_INHACTV0);
+
+       /* Set up vertical blanking pixel region width */
+       hdmi_writeb(fb_mode.yres >> 8, HDMI_FC_INVACTV1);
+       hdmi_writeb(fb_mode.yres, HDMI_FC_INVACTV0);
+
+       /* Set up horizontal blanking pixel region width */
+       hblank = fb_mode.left_margin + fb_mode.right_margin +
+               fb_mode.hsync_len;
+       hdmi_writeb(hblank >> 8, HDMI_FC_INHBLANK1);
+       hdmi_writeb(hblank, HDMI_FC_INHBLANK0);
+
+       /* Set up vertical blanking pixel region width */
+       vblank = fb_mode.upper_margin + fb_mode.lower_margin +
+               fb_mode.vsync_len;
+       hdmi_writeb(vblank, HDMI_FC_INVBLANK);
+
+       /* Set up HSYNC active edge delay width (in pixel clks) */
+       hdmi_writeb(fb_mode.right_margin >> 8, HDMI_FC_HSYNCINDELAY1);
+       hdmi_writeb(fb_mode.right_margin, HDMI_FC_HSYNCINDELAY0);
+
+       /* Set up VSYNC active edge delay (in pixel clks) */
+       hdmi_writeb(fb_mode.lower_margin, HDMI_FC_VSYNCINDELAY);
+
+       /* Set up HSYNC active pulse width (in pixel clks) */
+       hdmi_writeb(fb_mode.hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
+       hdmi_writeb(fb_mode.hsync_len, HDMI_FC_HSYNCINWIDTH0);
+
+       /* Set up VSYNC active edge delay (in pixel clks) */
+       hdmi_writeb(fb_mode.vsync_len, HDMI_FC_VSYNCINWIDTH);
+
+       dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__);
+}
+
+static int mxc_edid_read_internal(struct mxc_hdmi *hdmi, unsigned char *edid,
+                       struct mxc_edid_cfg *cfg, struct fb_info *fbi)
+{
+       int extblknum;
+       int i, j, ret;
+       unsigned char *ediddata = edid;
+       unsigned char tmpedid[EDID_LENGTH];
+
+       dev_info(&hdmi->pdev->dev, "%s\n", __func__);
+
+       if (!edid || !cfg || !fbi)
+               return -EINVAL;
+
+       /* init HDMI I2CM for read edid*/
+       hdmi_writeb(0x0, HDMI_I2CM_DIV);
+       hdmi_writeb(0x00, HDMI_I2CM_SS_SCL_HCNT_1_ADDR);
+       hdmi_writeb(0x79, HDMI_I2CM_SS_SCL_HCNT_0_ADDR);
+       hdmi_writeb(0x00, HDMI_I2CM_SS_SCL_LCNT_1_ADDR);
+       hdmi_writeb(0x91, HDMI_I2CM_SS_SCL_LCNT_0_ADDR);
+
+       hdmi_writeb(0x00, HDMI_I2CM_FS_SCL_HCNT_1_ADDR);
+       hdmi_writeb(0x0F, HDMI_I2CM_FS_SCL_HCNT_0_ADDR);
+       hdmi_writeb(0x00, HDMI_I2CM_FS_SCL_LCNT_1_ADDR);
+       hdmi_writeb(0x21, HDMI_I2CM_FS_SCL_LCNT_0_ADDR);
+
+       hdmi_writeb(0x50, HDMI_I2CM_SLAVE);
+       hdmi_writeb(0x30, HDMI_I2CM_SEGADDR);
+
+       /* Umask edid interrupt */
+       hdmi_writeb(HDMI_I2CM_INT_DONE_POL,
+                   HDMI_I2CM_INT);
+
+       hdmi_writeb(HDMI_I2CM_CTLINT_NAC_POL |
+                   HDMI_I2CM_CTLINT_ARBITRATION_POL,
+                   HDMI_I2CM_CTLINT);
+
+       /* reset edid data zero */
+       memset(edid, 0, EDID_LENGTH*4);
+       memset(cfg, 0, sizeof(struct mxc_edid_cfg));
+
+       /* Check first three byte of EDID head */
+       if (!(hdmi_edid_i2c_read(hdmi, 0, 0) == 0x00) ||
+               !(hdmi_edid_i2c_read(hdmi, 1, 0) == 0xFF) ||
+               !(hdmi_edid_i2c_read(hdmi, 2, 0) == 0xFF)) {
+               dev_info(&hdmi->pdev->dev, "EDID head check failed!");
+               return -ENOENT;
+       }
+
+       for (i = 0; i < 128; i++) {
+               *ediddata = hdmi_edid_i2c_read(hdmi, i, 0);
+               ediddata++;
+       }
+
+       extblknum = edid[0x7E];
+       if (extblknum == 255)
+               extblknum = 0;
+
+       if (extblknum) {
+               ediddata = edid + EDID_LENGTH;
+               for (i = 0; i < 128; i++) {
+                       *ediddata = hdmi_edid_i2c_read(hdmi, i, 1);
+                       ediddata++;
+               }
+       }
+
+       /* edid first block parsing */
+       memset(&fbi->monspecs, 0, sizeof(fbi->monspecs));
+       fb_edid_to_monspecs(edid, &fbi->monspecs);
+
+       if (extblknum) {
+               ret = mxc_edid_parse_ext_blk(edid + EDID_LENGTH,
+                               cfg, &fbi->monspecs);
+               if (ret < 0)
+                       return -ENOENT;
+       }
+
+       /* need read segment block? */
+       if (extblknum > 1) {
+               for (j = 2; j <= extblknum; j++) {
+                       for (i = 0; i < 128; i++)
+                               tmpedid[i] = hdmi_edid_i2c_read(hdmi, i, j);
+
+                       /* edid ext block parsing */
+                       ret = mxc_edid_parse_ext_blk(tmpedid,
+                                       cfg, &fbi->monspecs);
+                       if (ret < 0)
+                               return -ENOENT;
+               }
+       }
+
+       return 0;
+}
+
+static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi)
+{
+       int ret;
+       u8 edid_old[HDMI_EDID_LEN];
+       u8 clkdis;
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       /* save old edid */
+       memcpy(edid_old, hdmi->edid, HDMI_EDID_LEN);
+
+       /* Read EDID via HDMI DDC when HDCP Enable */
+       if (!hdcp_init)
+               ret = mxc_edid_read(hdmi_i2c->adapter, hdmi_i2c->addr,
+                               hdmi->edid, &hdmi->edid_cfg, hdmi->fbi);
+       else {
+
+               /* Disable HDCP clk */
+               if (hdmi->hdmi_data.hdcp_enable) {
+                       clkdis = hdmi_readb(HDMI_MC_CLKDIS);
+                       clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE;
+                       hdmi_writeb(clkdis, HDMI_MC_CLKDIS);
+               }
+
+               ret = mxc_edid_read_internal(hdmi, hdmi->edid,
+                               &hdmi->edid_cfg, hdmi->fbi);
+
+               /* Enable HDCP clk */
+               if (hdmi->hdmi_data.hdcp_enable) {
+                       clkdis = hdmi_readb(HDMI_MC_CLKDIS);
+                       clkdis &= ~HDMI_MC_CLKDIS_HDCPCLK_DISABLE;
+                       hdmi_writeb(clkdis, HDMI_MC_CLKDIS);
+               }
+
+       }
+       if (ret < 0) {
+               dev_dbg(&hdmi->pdev->dev, "read failed\n");
+               return HDMI_EDID_FAIL;
+       }
+
+       /* Save edid cfg for audio driver */
+       hdmi_set_edid_cfg(&hdmi->edid_cfg);
+
+       if (!memcmp(edid_old, hdmi->edid, HDMI_EDID_LEN)) {
+               dev_info(&hdmi->pdev->dev, "same edid\n");
+               return HDMI_EDID_SAME;
+       }
+
+       if (hdmi->fbi->monspecs.modedb_len == 0) {
+               dev_info(&hdmi->pdev->dev, "No modes read from edid\n");
+               return HDMI_EDID_NO_MODES;
+       }
+
+       return HDMI_EDID_SUCCESS;
+}
+
+static void mxc_hdmi_phy_disable(struct mxc_hdmi *hdmi)
+{
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       if (!hdmi->phy_enabled)
+               return;
+
+       hdmi_disable_overflow_interrupts();
+
+       /* Setting PHY to reset status */
+       hdmi_writeb(HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
+
+       /* Power down PHY */
+       mxc_hdmi_phy_enable_tmds(0);
+       mxc_hdmi_phy_enable_power(0);
+       mxc_hdmi_phy_gen2_txpwron(0);
+       mxc_hdmi_phy_gen2_pddq(1);
+
+       hdmi->phy_enabled = false;
+       dev_dbg(&hdmi->pdev->dev, "%s - exit\n", __func__);
+}
+
+/* HDMI Initialization Step B.4 */
+static void mxc_hdmi_enable_video_path(struct mxc_hdmi *hdmi)
+{
+       u8 clkdis;
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       /* control period minimum duration */
+       hdmi_writeb(12, HDMI_FC_CTRLDUR);
+       hdmi_writeb(32, HDMI_FC_EXCTRLDUR);
+       hdmi_writeb(1, HDMI_FC_EXCTRLSPAC);
+
+       /* Set to fill TMDS data channels */
+       hdmi_writeb(0x0B, HDMI_FC_CH0PREAM);
+       hdmi_writeb(0x16, HDMI_FC_CH1PREAM);
+       hdmi_writeb(0x21, HDMI_FC_CH2PREAM);
+
+       /* Enable pixel clock and tmds data path */
+       clkdis = 0x7F;
+       clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
+       hdmi_writeb(clkdis, HDMI_MC_CLKDIS);
+
+       clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+       hdmi_writeb(clkdis, HDMI_MC_CLKDIS);
+
+       /* Enable csc path */
+       if (isColorSpaceConversion(hdmi)) {
+               clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
+               hdmi_writeb(clkdis, HDMI_MC_CLKDIS);
+       }
+}
+
+static void hdmi_enable_audio_clk(struct mxc_hdmi *hdmi)
+{
+       u8 clkdis;
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       clkdis = hdmi_readb(HDMI_MC_CLKDIS);
+       clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
+       hdmi_writeb(clkdis, HDMI_MC_CLKDIS);
+}
+
+/* Workaround to clear the overflow condition */
+static void mxc_hdmi_clear_overflow(struct mxc_hdmi *hdmi)
+{
+       int count;
+       u8 val;
+
+       /* TMDS software reset */
+       hdmi_writeb((u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
+
+       val = hdmi_readb(HDMI_FC_INVIDCONF);
+
+       if (cpu_is_imx6dl(hdmi)) {
+                hdmi_writeb(val, HDMI_FC_INVIDCONF);
+                return;
+       }
+
+       for (count = 0 ; count < 5 ; count++)
+               hdmi_writeb(val, HDMI_FC_INVIDCONF);
+}
+
+static void hdmi_enable_overflow_interrupts(void)
+{
+       pr_debug("%s\n", __func__);
+       hdmi_writeb(0, HDMI_FC_MASK2);
+       hdmi_writeb(0, HDMI_IH_MUTE_FC_STAT2);
+}
+
+static void hdmi_disable_overflow_interrupts(void)
+{
+       pr_debug("%s\n", __func__);
+       hdmi_writeb(HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
+                   HDMI_IH_MUTE_FC_STAT2);
+       hdmi_writeb(0xff, HDMI_FC_MASK2);
+}
+
+static void mxc_hdmi_notify_fb(struct mxc_hdmi *hdmi)
+{
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       /* Don't notify if we aren't registered yet */
+       WARN_ON(!hdmi->fb_reg);
+
+       /* disable the phy before ipu changes mode */
+       mxc_hdmi_phy_disable(hdmi);
+
+       /*
+        * Note that fb_set_var will block.  During this time,
+        * FB_EVENT_MODE_CHANGE callback will happen.
+        * So by the end of this function, mxc_hdmi_setup()
+        * will be done.
+        */
+       hdmi->fbi->var.activate |= FB_ACTIVATE_FORCE;
+       console_lock();
+       hdmi->fbi->flags |= FBINFO_MISC_USEREVENT;
+       fb_set_var(hdmi->fbi, &hdmi->fbi->var);
+       hdmi->fbi->flags &= ~FBINFO_MISC_USEREVENT;
+       console_unlock();
+
+       dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__);
+}
+
+static void mxc_hdmi_edid_rebuild_modelist(struct mxc_hdmi *hdmi)
+{
+       int i;
+       struct fb_videomode *mode;
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       console_lock();
+
+       fb_destroy_modelist(&hdmi->fbi->modelist);
+       fb_add_videomode(&vga_mode, &hdmi->fbi->modelist);
+
+       for (i = 0; i < hdmi->fbi->monspecs.modedb_len; i++) {
+               /*
+                * We might check here if mode is supported by HDMI.
+                * We do not currently support interlaced modes.
+                * And add CEA modes in the modelist.
+                */
+               mode = &hdmi->fbi->monspecs.modedb[i];
+
+               if (!(mode->vmode & FB_VMODE_INTERLACED) &&
+                               (mxc_edid_mode_to_vic(mode) != 0)) {
+
+                       dev_dbg(&hdmi->pdev->dev, "Added mode %d:", i);
+                       dev_dbg(&hdmi->pdev->dev,
+                               "xres = %d, yres = %d, freq = %d, vmode = %d, flag = %d\n",
+                               hdmi->fbi->monspecs.modedb[i].xres,
+                               hdmi->fbi->monspecs.modedb[i].yres,
+                               hdmi->fbi->monspecs.modedb[i].refresh,
+                               hdmi->fbi->monspecs.modedb[i].vmode,
+                               hdmi->fbi->monspecs.modedb[i].flag);
+
+                       fb_add_videomode(mode, &hdmi->fbi->modelist);
+               }
+       }
+
+       fb_new_modelist(hdmi->fbi);
+
+       console_unlock();
+}
+
+static void  mxc_hdmi_default_edid_cfg(struct mxc_hdmi *hdmi)
+{
+       /* Default setting HDMI working in HDMI mode */
+       hdmi->edid_cfg.hdmi_cap = true;
+}
+
+static void  mxc_hdmi_default_modelist(struct mxc_hdmi *hdmi)
+{
+       u32 i;
+       const struct fb_videomode *mode;
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       /* If not EDID data read, set up default modelist  */
+       dev_info(&hdmi->pdev->dev, "create default modelist\n");
+
+       console_lock();
+
+       fb_destroy_modelist(&hdmi->fbi->modelist);
+
+       /*Add all no interlaced CEA mode to default modelist */
+       for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
+               mode = &mxc_cea_mode[i];
+               if (!(mode->vmode & FB_VMODE_INTERLACED) && (mode->xres != 0))
+                       fb_add_videomode(mode, &hdmi->fbi->modelist);
+       }
+
+       fb_new_modelist(hdmi->fbi);
+
+       console_unlock();
+}
+
+static void mxc_hdmi_set_mode_to_vga_dvi(struct mxc_hdmi *hdmi)
+{
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       hdmi_disable_overflow_interrupts();
+
+       fb_videomode_to_var(&hdmi->fbi->var, &vga_mode);
+
+       hdmi->requesting_vga_for_initialization = true;
+       mxc_hdmi_notify_fb(hdmi);
+       hdmi->requesting_vga_for_initialization = false;
+}
+
+static void mxc_hdmi_set_mode(struct mxc_hdmi *hdmi)
+{
+       const struct fb_videomode *mode;
+       struct fb_videomode m;
+       struct fb_var_screeninfo var;
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       /* Set the default mode only once. */
+       if (!hdmi->dft_mode_set) {
+               fb_videomode_to_var(&var, &hdmi->default_mode);
+               hdmi->dft_mode_set = true;
+       } else
+               fb_videomode_to_var(&var, &hdmi->previous_non_vga_mode);
+
+       fb_var_to_videomode(&m, &var);
+       dump_fb_videomode(&m);
+
+       mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist);
+       if (!mode) {
+               pr_err("%s: could not find mode in modelist\n", __func__);
+               return;
+       }
+
+       /* If both video mode and work mode same as previous,
+        * init HDMI again */
+       if (fb_mode_is_equal(&hdmi->previous_non_vga_mode, mode) &&
+               (hdmi->edid_cfg.hdmi_cap != hdmi->hdmi_data.video_mode.mDVI)) {
+               dev_dbg(&hdmi->pdev->dev,
+                               "%s: Video mode same as previous\n", __func__);
+               /* update fbi mode in case modelist is updated */
+               hdmi->fbi->mode = (struct fb_videomode *)mode;
+               fb_videomode_to_var(&hdmi->fbi->var, mode);
+               /* update hdmi setting in case EDID data updated  */
+               mxc_hdmi_setup(hdmi, 0);
+       } else {
+               dev_dbg(&hdmi->pdev->dev, "%s: New video mode\n", __func__);
+               mxc_hdmi_set_mode_to_vga_dvi(hdmi);
+               fb_videomode_to_var(&hdmi->fbi->var, mode);
+               dump_fb_videomode((struct fb_videomode *)mode);
+               mxc_hdmi_notify_fb(hdmi);
+       }
+
+}
+
+static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi)
+{
+       int edid_status;
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       hdmi->cable_plugin = true;
+
+       /* HDMI Initialization Step C */
+       edid_status = mxc_hdmi_read_edid(hdmi);
+
+       /* Read EDID again if first EDID read failed */
+       if (edid_status == HDMI_EDID_NO_MODES ||
+                       edid_status == HDMI_EDID_FAIL) {
+               int retry_status;
+               dev_info(&hdmi->pdev->dev, "Read EDID again\n");
+               msleep(200);
+               retry_status = mxc_hdmi_read_edid(hdmi);
+               /* If we get NO_MODES on the 1st and SAME on the 2nd attempt we
+                * want NO_MODES as final result. */
+               if (retry_status != HDMI_EDID_SAME)
+                       edid_status = retry_status;
+       }
+
+       /* HDMI Initialization Steps D, E, F */
+       switch (edid_status) {
+       case HDMI_EDID_SUCCESS:
+               mxc_hdmi_edid_rebuild_modelist(hdmi);
+               break;
+
+       /* Nothing to do if EDID same */
+       case HDMI_EDID_SAME:
+               break;
+
+       case HDMI_EDID_FAIL:
+               mxc_hdmi_default_edid_cfg(hdmi);
+               /* No break here  */
+       case HDMI_EDID_NO_MODES:
+       default:
+               mxc_hdmi_default_modelist(hdmi);
+               break;
+       }
+
+       /* Setting video mode */
+       mxc_hdmi_set_mode(hdmi);
+
+       dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__);
+}
+
+static int mxc_hdmi_power_on(struct mxc_dispdrv_handle *disp,
+                            struct fb_info *fbi)
+{
+       struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp);
+       mxc_hdmi_phy_init(hdmi);
+       return 0;
+}
+
+static void mxc_hdmi_power_off(struct mxc_dispdrv_handle *disp,
+                              struct fb_info *fbi)
+{
+       struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp);
+       mxc_hdmi_phy_disable(hdmi);
+}
+
+static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi)
+{
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       /* Disable All HDMI clock */
+       hdmi_writeb(0xff, HDMI_MC_CLKDIS);
+
+       mxc_hdmi_phy_disable(hdmi);
+
+       hdmi_disable_overflow_interrupts();
+
+       hdmi->cable_plugin = false;
+}
+
+static void hotplug_worker(struct work_struct *work)
+{
+       struct delayed_work *delay_work = to_delayed_work(work);
+       struct mxc_hdmi *hdmi =
+               container_of(delay_work, struct mxc_hdmi, hotplug_work);
+       u32 phy_int_stat, phy_int_pol, phy_int_mask;
+       u8 val;
+       unsigned long flags;
+       char event_string[32];
+       char *envp[] = { event_string, NULL };
+
+       phy_int_stat = hdmi->latest_intr_stat;
+       phy_int_pol = hdmi_readb(HDMI_PHY_POL0);
+
+       dev_dbg(&hdmi->pdev->dev, "phy_int_stat=0x%x, phy_int_pol=0x%x\n",
+                       phy_int_stat, phy_int_pol);
+
+       /* check cable status */
+       if (phy_int_stat & HDMI_IH_PHY_STAT0_HPD) {
+               /* cable connection changes */
+               if (phy_int_pol & HDMI_PHY_HPD) {
+                       /* Plugin event */
+                       dev_dbg(&hdmi->pdev->dev, "EVENT=plugin\n");
+                       mxc_hdmi_cable_connected(hdmi);
+
+                       /* Make HPD intr active low to capture unplug event */
+                       val = hdmi_readb(HDMI_PHY_POL0);
+                       val &= ~HDMI_PHY_HPD;
+                       hdmi_writeb(val, HDMI_PHY_POL0);
+
+                       hdmi_set_cable_state(1);
+
+                       sprintf(event_string, "EVENT=plugin");
+                       kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp);
+#ifdef CONFIG_MXC_HDMI_CEC
+                       mxc_hdmi_cec_handle(0x80);
+#endif
+               } else if (!(phy_int_pol & HDMI_PHY_HPD)) {
+                       /* Plugout event */
+                       dev_dbg(&hdmi->pdev->dev, "EVENT=plugout\n");
+                       hdmi_set_cable_state(0);
+                       mxc_hdmi_abort_stream();
+                       mxc_hdmi_cable_disconnected(hdmi);
+
+                       /* Make HPD intr active high to capture plugin event */
+                       val = hdmi_readb(HDMI_PHY_POL0);
+                       val |= HDMI_PHY_HPD;
+                       hdmi_writeb(val, HDMI_PHY_POL0);
+
+                       sprintf(event_string, "EVENT=plugout");
+                       kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp);
+#ifdef CONFIG_MXC_HDMI_CEC
+                       mxc_hdmi_cec_handle(0x100);
+#endif
+
+               } else
+                       dev_dbg(&hdmi->pdev->dev, "EVENT=none?\n");
+       }
+
+       /* Lock here to ensure full powerdown sequence
+        * completed before next interrupt processed */
+       spin_lock_irqsave(&hdmi->irq_lock, flags);
+
+       /* Re-enable HPD interrupts */
+       phy_int_mask = hdmi_readb(HDMI_PHY_MASK0);
+       phy_int_mask &= ~HDMI_PHY_HPD;
+       hdmi_writeb(phy_int_mask, HDMI_PHY_MASK0);
+
+       /* Unmute interrupts */
+       hdmi_writeb(~HDMI_IH_MUTE_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+
+       if (hdmi_readb(HDMI_IH_FC_STAT2) & HDMI_IH_FC_STAT2_OVERFLOW_MASK)
+               mxc_hdmi_clear_overflow(hdmi);
+
+       spin_unlock_irqrestore(&hdmi->irq_lock, flags);
+}
+
+static void hdcp_hdp_worker(struct work_struct *work)
+{
+       struct delayed_work *delay_work = to_delayed_work(work);
+       struct mxc_hdmi *hdmi =
+               container_of(delay_work, struct mxc_hdmi, hdcp_hdp_work);
+       char event_string[32];
+       char *envp[] = { event_string, NULL };
+
+       /* HDCP interrupt */
+       sprintf(event_string, "EVENT=hdcpint");
+       kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp);
+
+       /* Unmute interrupts in HDCP application*/
+}
+
+static irqreturn_t mxc_hdmi_hotplug(int irq, void *data)
+{
+       struct mxc_hdmi *hdmi = data;
+       u8 val, intr_stat;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hdmi->irq_lock, flags);
+
+       /* Check and clean packet overflow interrupt.*/
+       if (hdmi_readb(HDMI_IH_FC_STAT2) &
+                       HDMI_IH_FC_STAT2_OVERFLOW_MASK) {
+               mxc_hdmi_clear_overflow(hdmi);
+
+               dev_dbg(&hdmi->pdev->dev, "Overflow interrupt received\n");
+               /* clear irq status */
+               hdmi_writeb(HDMI_IH_FC_STAT2_OVERFLOW_MASK,
+                           HDMI_IH_FC_STAT2);
+       }
+
+       /*
+        * We could not disable the irq.  Probably the audio driver
+        * has enabled it. Masking off the HDMI interrupts using
+        * HDMI registers.
+        */
+       /* Capture status - used in hotplug_worker ISR */
+       intr_stat = hdmi_readb(HDMI_IH_PHY_STAT0);
+
+       if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
+
+               dev_dbg(&hdmi->pdev->dev, "Hotplug interrupt received\n");
+               hdmi->latest_intr_stat = intr_stat;
+
+               /* Mute interrupts until handled */
+
+               val = hdmi_readb(HDMI_IH_MUTE_PHY_STAT0);
+               val |= HDMI_IH_MUTE_PHY_STAT0_HPD;
+               hdmi_writeb(val, HDMI_IH_MUTE_PHY_STAT0);
+
+               val = hdmi_readb(HDMI_PHY_MASK0);
+               val |= HDMI_PHY_HPD;
+               hdmi_writeb(val, HDMI_PHY_MASK0);
+
+               /* Clear Hotplug interrupts */
+               hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+
+               schedule_delayed_work(&(hdmi->hotplug_work), msecs_to_jiffies(20));
+       }
+
+       /* Check HDCP  interrupt state */
+       if (hdmi->hdmi_data.hdcp_enable) {
+               val = hdmi_readb(HDMI_A_APIINTSTAT);
+               if (val != 0) {
+                       /* Mute interrupts until interrupt handled */
+                       val = 0xFF;
+                       hdmi_writeb(val, HDMI_A_APIINTMSK);
+                       schedule_delayed_work(&(hdmi->hdcp_hdp_work), msecs_to_jiffies(50));
+               }
+       }
+
+       spin_unlock_irqrestore(&hdmi->irq_lock, flags);
+       return IRQ_HANDLED;
+}
+
+static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event)
+{
+       struct fb_videomode m;
+       const struct fb_videomode *edid_mode;
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       fb_var_to_videomode(&m, &hdmi->fbi->var);
+       dump_fb_videomode(&m);
+
+       dev_dbg(&hdmi->pdev->dev, "%s - video mode changed\n", __func__);
+
+       hdmi->vic = 0;
+       if (!hdmi->requesting_vga_for_initialization) {
+               /* Save mode if this isn't the result of requesting
+                * vga default. */
+               memcpy(&hdmi->previous_non_vga_mode, &m,
+                      sizeof(struct fb_videomode));
+               if (!list_empty(&hdmi->fbi->modelist)) {
+                       edid_mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist);
+                       pr_debug("edid mode ");
+                       dump_fb_videomode((struct fb_videomode *)edid_mode);
+                       /* update fbi mode */
+                       hdmi->fbi->mode = (struct fb_videomode *)edid_mode;
+                       hdmi->vic = mxc_edid_mode_to_vic(edid_mode);
+               }
+       }
+
+       hdmi_disable_overflow_interrupts();
+
+       dev_dbg(&hdmi->pdev->dev, "CEA mode used vic=%d\n", hdmi->vic);
+       if (hdmi->edid_cfg.hdmi_cap)
+               hdmi->hdmi_data.video_mode.mDVI = false;
+       else {
+               dev_dbg(&hdmi->pdev->dev, "CEA mode vic=%d work in DVI\n", hdmi->vic);
+               hdmi->hdmi_data.video_mode.mDVI = true;
+       }
+
+       if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
+               (hdmi->vic == 21) || (hdmi->vic == 22) ||
+               (hdmi->vic == 2) || (hdmi->vic == 3) ||
+               (hdmi->vic == 17) || (hdmi->vic == 18))
+               hdmi->hdmi_data.colorimetry = eITU601;
+       else
+               hdmi->hdmi_data.colorimetry = eITU709;
+
+       if ((hdmi->vic == 10) || (hdmi->vic == 11) ||
+               (hdmi->vic == 12) || (hdmi->vic == 13) ||
+               (hdmi->vic == 14) || (hdmi->vic == 15) ||
+               (hdmi->vic == 25) || (hdmi->vic == 26) ||
+               (hdmi->vic == 27) || (hdmi->vic == 28) ||
+               (hdmi->vic == 29) || (hdmi->vic == 30) ||
+               (hdmi->vic == 35) || (hdmi->vic == 36) ||
+               (hdmi->vic == 37) || (hdmi->vic == 38))
+               hdmi->hdmi_data.video_mode.mPixelRepetitionOutput = 1;
+       else
+               hdmi->hdmi_data.video_mode.mPixelRepetitionOutput = 0;
+
+       hdmi->hdmi_data.video_mode.mPixelRepetitionInput = 0;
+
+       /* TODO: Get input format from IPU (via FB driver iface) */
+       hdmi->hdmi_data.enc_in_format = RGB;
+
+       hdmi->hdmi_data.enc_out_format = RGB;
+
+       /* YCbCr only enabled in HDMI mode */
+       if (!hdmi->hdmi_data.video_mode.mDVI &&
+               !hdmi->hdmi_data.rgb_out_enable) {
+               if (hdmi->edid_cfg.cea_ycbcr444)
+                       hdmi->hdmi_data.enc_out_format = YCBCR444;
+               else if (hdmi->edid_cfg.cea_ycbcr422)
+                       hdmi->hdmi_data.enc_out_format = YCBCR422_8BITS;
+       }
+
+       /* IPU not support depth color output */
+       hdmi->hdmi_data.enc_color_depth = 8;
+       hdmi->hdmi_data.pix_repet_factor = 0;
+       hdmi->hdmi_data.video_mode.mDataEnablePolarity = true;
+
+       /* HDMI Initialization Step B.1 */
+       hdmi_av_composer(hdmi);
+
+       /* HDMI Initializateion Step B.2 */
+       mxc_hdmi_phy_init(hdmi);
+
+       /* HDMI Initialization Step B.3 */
+       mxc_hdmi_enable_video_path(hdmi);
+
+       /* not for DVI mode */
+       if (hdmi->hdmi_data.video_mode.mDVI)
+               dev_dbg(&hdmi->pdev->dev, "%s DVI mode\n", __func__);
+       else {
+               dev_dbg(&hdmi->pdev->dev, "%s CEA mode\n", __func__);
+
+               /* HDMI Initialization Step E - Configure audio */
+               hdmi_clk_regenerator_update_pixel_clock(hdmi->fbi->var.pixclock);
+               hdmi_enable_audio_clk(hdmi);
+
+               /* HDMI Initialization Step F - Configure AVI InfoFrame */
+               hdmi_config_AVI(hdmi);
+       }
+
+       hdmi_video_packetize(hdmi);
+       hdmi_video_csc(hdmi);
+       hdmi_video_sample(hdmi);
+
+       mxc_hdmi_clear_overflow(hdmi);
+
+       dev_dbg(&hdmi->pdev->dev, "%s exit\n\n", __func__);
+
+}
+
+/* Wait until we are registered to enable interrupts */
+static void mxc_hdmi_fb_registered(struct mxc_hdmi *hdmi)
+{
+       unsigned long flags;
+
+       if (hdmi->fb_reg)
+               return;
+
+       spin_lock_irqsave(&hdmi->irq_lock, flags);
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       hdmi_writeb(HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+                   HDMI_PHY_I2CM_INT_ADDR);
+
+       hdmi_writeb(HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+                   HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
+                   HDMI_PHY_I2CM_CTLINT_ADDR);
+
+       /* enable cable hot plug irq */
+       hdmi_writeb((u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
+
+       /* Clear Hotplug interrupts */
+       hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+
+       /* Unmute interrupts */
+       hdmi_writeb(~HDMI_IH_MUTE_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+
+       hdmi->fb_reg = true;
+
+       spin_unlock_irqrestore(&hdmi->irq_lock, flags);
+
+}
+
+static int mxc_hdmi_fb_event(struct notifier_block *nb,
+                                       unsigned long val, void *v)
+{
+       struct fb_event *event = v;
+       struct mxc_hdmi *hdmi = container_of(nb, struct mxc_hdmi, nb);
+
+       if (strcmp(event->info->fix.id, hdmi->fbi->fix.id))
+               return 0;
+
+       switch (val) {
+       case FB_EVENT_FB_REGISTERED:
+               dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_FB_REGISTERED\n");
+               mxc_hdmi_fb_registered(hdmi);
+               hdmi_set_registered(1);
+               break;
+
+       case FB_EVENT_FB_UNREGISTERED:
+               dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_FB_UNREGISTERED\n");
+               hdmi->fb_reg = false;
+               hdmi_set_registered(0);
+               break;
+
+       case FB_EVENT_MODE_CHANGE:
+               dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_MODE_CHANGE\n");
+               if (hdmi->fb_reg)
+                       mxc_hdmi_setup(hdmi, val);
+               break;
+
+       case FB_EVENT_BLANK:
+               if ((*((int *)event->data) == FB_BLANK_UNBLANK) &&
+                       (*((int *)event->data) != hdmi->blank)) {
+                       dev_dbg(&hdmi->pdev->dev,
+                               "event=FB_EVENT_BLANK - UNBLANK\n");
+
+                       hdmi->blank = *((int *)event->data);
+
+                       if (hdmi->fb_reg && hdmi->cable_plugin)
+                               mxc_hdmi_setup(hdmi, val);
+                       hdmi_set_blank_state(1);
+
+               } else if (*((int *)event->data) != hdmi->blank) {
+                       dev_dbg(&hdmi->pdev->dev,
+                               "event=FB_EVENT_BLANK - BLANK\n");
+                       hdmi_set_blank_state(0);
+                       mxc_hdmi_abort_stream();
+
+                       mxc_hdmi_phy_disable(hdmi);
+
+                       hdmi->blank = *((int *)event->data);
+               } else
+                       dev_dbg(&hdmi->pdev->dev,
+                               "FB BLANK state no changed!\n");
+
+               break;
+
+       case FB_EVENT_SUSPEND:
+               dev_dbg(&hdmi->pdev->dev,
+                       "event=FB_EVENT_SUSPEND\n");
+
+               if (hdmi->blank == FB_BLANK_UNBLANK) {
+                       mxc_hdmi_phy_disable(hdmi);
+                       clk_disable(hdmi->hdmi_iahb_clk);
+                       clk_disable(hdmi->hdmi_isfr_clk);
+                       clk_disable(hdmi->mipi_core_clk);
+               }
+               break;
+
+       case FB_EVENT_RESUME:
+               dev_dbg(&hdmi->pdev->dev,
+                       "event=FB_EVENT_RESUME\n");
+
+               if (hdmi->blank == FB_BLANK_UNBLANK) {
+                       clk_enable(hdmi->mipi_core_clk);
+                       clk_enable(hdmi->hdmi_iahb_clk);
+                       clk_enable(hdmi->hdmi_isfr_clk);
+                       mxc_hdmi_phy_init(hdmi);
+               }
+               break;
+
+       }
+       return 0;
+}
+
+static void hdmi_init_route(struct mxc_hdmi *hdmi)
+{
+       uint32_t hdmi_mux_setting, reg;
+       int ipu_id, disp_id;
+
+       ipu_id = mxc_hdmi_ipu_id;
+       disp_id = mxc_hdmi_disp_id;
+
+       if ((ipu_id > 1) || (ipu_id < 0)) {
+               pr_err("Invalid IPU select for HDMI: %d. Set to 0\n", ipu_id);
+               ipu_id = 0;
+       }
+
+       if ((disp_id > 1) || (disp_id < 0)) {
+               pr_err("Invalid DI select for HDMI: %d. Set to 0\n", disp_id);
+               disp_id = 0;
+       }
+
+       reg = readl(hdmi->gpr_hdmi_base);
+
+       /* Configure the connection between IPU1/2 and HDMI */
+       hdmi_mux_setting = 2*ipu_id + disp_id;
+
+       /* GPR3, bits 2-3 = HDMI_MUX_CTL */
+       reg &= ~0xd;
+       reg |= hdmi_mux_setting << 2;
+
+       writel(reg, hdmi->gpr_hdmi_base);
+
+       /* Set HDMI event as SDMA event2 for HDMI audio */
+       reg = readl(hdmi->gpr_sdma_base);
+       reg |= 0x1;
+       writel(reg, hdmi->gpr_sdma_base);
+}
+
+static void hdmi_hdcp_get_property(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+
+       /* Check hdcp enable by dts.*/
+       hdcp_init = of_property_read_bool(np, "fsl,hdcp");
+       if (hdcp_init)
+               dev_dbg(&pdev->dev, "hdcp enable\n");
+       else
+               dev_dbg(&pdev->dev, "hdcp disable\n");
+}
+
+static void hdmi_get_of_property(struct mxc_hdmi *hdmi)
+{
+       struct platform_device *pdev = hdmi->pdev;
+       struct device_node *np = pdev->dev.of_node;
+       const struct of_device_id *of_id =
+                       of_match_device(imx_hdmi_dt_ids, &pdev->dev);
+       int ret;
+       u32 phy_reg_vlev = 0, phy_reg_cksymtx = 0;
+
+       if (of_id) {
+               pdev->id_entry = of_id->data;
+               hdmi->cpu_type = pdev->id_entry->driver_data;
+       }
+
+       /* HDMI PHY register vlev and cksymtx preperty is optional.
+        * It is for specific board to pass HCT electrical part.
+        * Default value will been setting in HDMI PHY config function
+        * if it is not define in device tree.
+        */
+       ret = of_property_read_u32(np, "fsl,phy_reg_vlev", &phy_reg_vlev);
+       if (ret)
+               dev_dbg(&pdev->dev, "No board specific HDMI PHY vlev\n");
+
+       ret = of_property_read_u32(np, "fsl,phy_reg_cksymtx", &phy_reg_cksymtx);
+       if (ret)
+               dev_dbg(&pdev->dev, "No board specific HDMI PHY cksymtx\n");
+
+       /* Specific phy config */
+       hdmi->phy_config.reg_cksymtx = phy_reg_cksymtx;
+       hdmi->phy_config.reg_vlev = phy_reg_vlev;
+
+}
+
+/* HDMI Initialization Step A */
+static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp,
+                             struct mxc_dispdrv_setting *setting)
+{
+       int ret = 0;
+       u32 i;
+       const struct fb_videomode *mode;
+       struct fb_videomode m;
+       struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp);
+       int irq = platform_get_irq(hdmi->pdev, 0);
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       /* Check hdmi disp init once */
+       if (hdmi_inited) {
+               dev_err(&hdmi->pdev->dev,
+                               "Error only one HDMI output support now!\n");
+               return -1;
+       }
+
+       hdmi_get_of_property(hdmi);
+
+       if (irq < 0)
+               return -ENODEV;
+
+       /* Setting HDMI default to blank state */
+       hdmi->blank = FB_BLANK_POWERDOWN;
+
+       ret = ipu_di_to_crtc(&hdmi->pdev->dev, mxc_hdmi_ipu_id,
+                            mxc_hdmi_disp_id, &setting->crtc);
+       if (ret < 0)
+               return ret;
+
+       setting->if_fmt = IPU_PIX_FMT_RGB24;
+
+       hdmi->dft_mode_str = setting->dft_mode_str;
+       hdmi->default_bpp = setting->default_bpp;
+       dev_dbg(&hdmi->pdev->dev, "%s - default mode %s bpp=%d\n",
+               __func__, hdmi->dft_mode_str, hdmi->default_bpp);
+
+       hdmi->fbi = setting->fbi;
+
+       hdmi_init_route(hdmi);
+
+       hdmi->mipi_core_clk = clk_get(&hdmi->pdev->dev, "mipi_core");
+       if (IS_ERR(hdmi->mipi_core_clk)) {
+               ret = PTR_ERR(hdmi->mipi_core_clk);
+               dev_err(&hdmi->pdev->dev,
+                       "Unable to get mipi core clk: %d\n", ret);
+               goto egetclk;
+       }
+
+       ret = clk_prepare_enable(hdmi->mipi_core_clk);
+       if (ret < 0) {
+               dev_err(&hdmi->pdev->dev,
+                               "Cannot enable mipi core clock: %d\n", ret);
+               goto erate;
+       }
+
+       hdmi->hdmi_isfr_clk = clk_get(&hdmi->pdev->dev, "hdmi_isfr");
+       if (IS_ERR(hdmi->hdmi_isfr_clk)) {
+               ret = PTR_ERR(hdmi->hdmi_isfr_clk);
+               dev_err(&hdmi->pdev->dev,
+                       "Unable to get HDMI clk: %d\n", ret);
+               goto egetclk1;
+       }
+
+       ret = clk_prepare_enable(hdmi->hdmi_isfr_clk);
+       if (ret < 0) {
+               dev_err(&hdmi->pdev->dev,
+                       "Cannot enable HDMI isfr clock: %d\n", ret);
+               goto erate1;
+       }
+
+       hdmi->hdmi_iahb_clk = clk_get(&hdmi->pdev->dev, "hdmi_iahb");
+       if (IS_ERR(hdmi->hdmi_iahb_clk)) {
+               ret = PTR_ERR(hdmi->hdmi_iahb_clk);
+               dev_err(&hdmi->pdev->dev,
+                       "Unable to get HDMI clk: %d\n", ret);
+               goto egetclk2;
+       }
+
+       ret = clk_prepare_enable(hdmi->hdmi_iahb_clk);
+       if (ret < 0) {
+               dev_err(&hdmi->pdev->dev,
+                       "Cannot enable HDMI iahb clock: %d\n", ret);
+               goto erate2;
+       }
+
+       dev_dbg(&hdmi->pdev->dev, "Enabled HDMI clocks\n");
+
+       /* Init DDC pins for HDCP  */
+       if (hdcp_init) {
+               hdmi->pinctrl = devm_pinctrl_get_select_default(&hdmi->pdev->dev);
+               if (IS_ERR(hdmi->pinctrl)) {
+                       dev_err(&hdmi->pdev->dev, "can't get/select DDC pinctrl\n");
+                       goto erate2;
+               }
+       }
+
+       /* Product and revision IDs */
+       dev_info(&hdmi->pdev->dev,
+               "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n",
+               hdmi_readb(HDMI_DESIGN_ID),
+               hdmi_readb(HDMI_REVISION_ID),
+               hdmi_readb(HDMI_PRODUCT_ID0),
+               hdmi_readb(HDMI_PRODUCT_ID1));
+
+       /* To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
+        * N and cts values before enabling phy */
+       hdmi_init_clk_regenerator();
+
+       INIT_LIST_HEAD(&hdmi->fbi->modelist);
+
+       spin_lock_init(&hdmi->irq_lock);
+
+       /* Set the default mode and modelist when disp init. */
+       fb_find_mode(&hdmi->fbi->var, hdmi->fbi,
+                    hdmi->dft_mode_str, NULL, 0, NULL,
+                    hdmi->default_bpp);
+
+       console_lock();
+
+       fb_destroy_modelist(&hdmi->fbi->modelist);
+
+       /*Add all no interlaced CEA mode to default modelist */
+       for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
+               mode = &mxc_cea_mode[i];
+               if (!(mode->vmode & FB_VMODE_INTERLACED) && (mode->xres != 0))
+                       fb_add_videomode(mode, &hdmi->fbi->modelist);
+       }
+
+       console_unlock();
+
+       /* Find a nearest mode in default modelist */
+       fb_var_to_videomode(&m, &hdmi->fbi->var);
+       dump_fb_videomode(&m);
+
+       hdmi->dft_mode_set = false;
+       /* Save default video mode */
+       memcpy(&hdmi->default_mode, &m, sizeof(struct fb_videomode));
+
+       mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist);
+       if (!mode) {
+               pr_err("%s: could not find mode in modelist\n", __func__);
+               return -1;
+       }
+
+       fb_videomode_to_var(&hdmi->fbi->var, mode);
+
+       /* update fbi mode */
+       hdmi->fbi->mode = (struct fb_videomode *)mode;
+
+       /* Default setting HDMI working in HDMI mode*/
+       hdmi->edid_cfg.hdmi_cap = true;
+
+       INIT_DELAYED_WORK(&hdmi->hotplug_work, hotplug_worker);
+       INIT_DELAYED_WORK(&hdmi->hdcp_hdp_work, hdcp_hdp_worker);
+
+       /* Configure registers related to HDMI interrupt
+        * generation before registering IRQ. */
+       hdmi_writeb(HDMI_PHY_HPD, HDMI_PHY_POL0);
+
+       /* Clear Hotplug interrupts */
+       hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+
+       hdmi->nb.notifier_call = mxc_hdmi_fb_event;
+       ret = fb_register_client(&hdmi->nb);
+       if (ret < 0)
+               goto efbclient;
+
+       memset(&hdmi->hdmi_data, 0, sizeof(struct hdmi_data_info));
+
+       /* Default HDMI working in RGB mode */
+       hdmi->hdmi_data.rgb_out_enable = true;
+
+       ret = devm_request_irq(&hdmi->pdev->dev, irq, mxc_hdmi_hotplug, IRQF_SHARED,
+                       dev_name(&hdmi->pdev->dev), hdmi);
+       if (ret < 0) {
+               dev_err(&hdmi->pdev->dev,
+                       "Unable to request irq: %d\n", ret);
+               goto ereqirq;
+       }
+
+       ret = device_create_file(&hdmi->pdev->dev, &dev_attr_fb_name);
+       if (ret < 0)
+               dev_warn(&hdmi->pdev->dev,
+                       "cound not create sys node for fb name\n");
+       ret = device_create_file(&hdmi->pdev->dev, &dev_attr_cable_state);
+       if (ret < 0)
+               dev_warn(&hdmi->pdev->dev,
+                       "cound not create sys node for cable state\n");
+       ret = device_create_file(&hdmi->pdev->dev, &dev_attr_edid);
+       if (ret < 0)
+               dev_warn(&hdmi->pdev->dev,
+                       "cound not create sys node for edid\n");
+
+       ret = device_create_file(&hdmi->pdev->dev, &dev_attr_rgb_out_enable);
+       if (ret < 0)
+               dev_warn(&hdmi->pdev->dev,
+                       "cound not create sys node for rgb out enable\n");
+
+       ret = device_create_file(&hdmi->pdev->dev, &dev_attr_hdcp_enable);
+       if (ret < 0)
+               dev_warn(&hdmi->pdev->dev,
+                       "cound not create sys node for hdcp enable\n");
+
+       dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__);
+
+       hdmi_inited = true;
+
+       return ret;
+
+efbclient:
+       free_irq(irq, hdmi);
+ereqirq:
+       clk_disable_unprepare(hdmi->hdmi_iahb_clk);
+erate2:
+       clk_put(hdmi->hdmi_iahb_clk);
+egetclk2:
+       clk_disable_unprepare(hdmi->hdmi_isfr_clk);
+erate1:
+       clk_put(hdmi->hdmi_isfr_clk);
+egetclk1:
+       clk_disable_unprepare(hdmi->mipi_core_clk);
+erate:
+       clk_put(hdmi->mipi_core_clk);
+egetclk:
+       dev_dbg(&hdmi->pdev->dev, "%s error exit\n", __func__);
+
+       return ret;
+}
+
+static void mxc_hdmi_disp_deinit(struct mxc_dispdrv_handle *disp)
+{
+       struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp);
+
+       dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+       fb_unregister_client(&hdmi->nb);
+
+       clk_disable_unprepare(hdmi->hdmi_isfr_clk);
+       clk_put(hdmi->hdmi_isfr_clk);
+       clk_disable_unprepare(hdmi->hdmi_iahb_clk);
+       clk_put(hdmi->hdmi_iahb_clk);
+       clk_disable_unprepare(hdmi->mipi_core_clk);
+       clk_put(hdmi->mipi_core_clk);
+
+       platform_device_unregister(hdmi->pdev);
+
+       hdmi_inited = false;
+}
+
+static struct mxc_dispdrv_driver mxc_hdmi_drv = {
+       .name   = DISPDRV_HDMI,
+       .init   = mxc_hdmi_disp_init,
+       .deinit = mxc_hdmi_disp_deinit,
+       .enable = mxc_hdmi_power_on,
+       .disable = mxc_hdmi_power_off,
+};
+
+
+static int mxc_hdmi_open(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static long mxc_hdmi_ioctl(struct file *file,
+               unsigned int cmd, unsigned long arg)
+{
+       int __user *argp = (void __user *)arg;
+       int ret = 0;
+
+       switch (cmd) {
+       case HDMI_IOC_GET_RESOURCE:
+               ret = copy_to_user(argp, &g_hdmi->hdmi_data,
+                               sizeof(g_hdmi->hdmi_data)) ? -EFAULT : 0;
+               break;
+       case HDMI_IOC_GET_CPU_TYPE:
+               *argp = g_hdmi->cpu_type;
+               break;
+       default:
+               pr_debug("Unsupport cmd %d\n", cmd);
+               break;
+     }
+     return ret;
+}
+
+static int mxc_hdmi_release(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static const struct file_operations mxc_hdmi_fops = {
+       .owner = THIS_MODULE,
+       .open = mxc_hdmi_open,
+       .release = mxc_hdmi_release,
+       .unlocked_ioctl = mxc_hdmi_ioctl,
+};
+
+
+static int mxc_hdmi_probe(struct platform_device *pdev)
+{
+       struct mxc_hdmi *hdmi;
+       struct device *temp_class;
+       struct resource *res;
+       int ret = 0;
+
+       /* Check I2C driver is loaded and available
+        * check hdcp function is enable by dts */
+       hdmi_hdcp_get_property(pdev);
+       if (!hdmi_i2c && !hdcp_init)
+               return -ENODEV;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENOENT;
+
+       hdmi = devm_kzalloc(&pdev->dev,
+                               sizeof(struct mxc_hdmi),
+                               GFP_KERNEL);
+       if (!hdmi) {
+               dev_err(&pdev->dev, "Cannot allocate device data\n");
+               ret = -ENOMEM;
+               goto ealloc;
+       }
+       g_hdmi = hdmi;
+
+       hdmi_major = register_chrdev(hdmi_major, "mxc_hdmi", &mxc_hdmi_fops);
+       if (hdmi_major < 0) {
+               printk(KERN_ERR "HDMI: unable to get a major for HDMI\n");
+               ret = -EBUSY;
+               goto ealloc;
+       }
+
+       hdmi_class = class_create(THIS_MODULE, "mxc_hdmi");
+       if (IS_ERR(hdmi_class)) {
+               ret = PTR_ERR(hdmi_class);
+               goto err_out_chrdev;
+       }
+
+       temp_class = device_create(hdmi_class, NULL, MKDEV(hdmi_major, 0),
+                                  NULL, "mxc_hdmi");
+       if (IS_ERR(temp_class)) {
+               ret = PTR_ERR(temp_class);
+               goto err_out_class;
+       }
+
+       hdmi->pdev = pdev;
+
+       hdmi->core_pdev = platform_device_alloc("mxc_hdmi_core", -1);
+       if (!hdmi->core_pdev) {
+               pr_err("%s failed platform_device_alloc for hdmi core\n",
+                       __func__);
+               ret = -ENOMEM;
+               goto ecore;
+       }
+
+       hdmi->gpr_base = ioremap(res->start, resource_size(res));
+       if (!hdmi->gpr_base) {
+               dev_err(&pdev->dev, "ioremap failed\n");
+               ret = -ENOMEM;
+               goto eiomap;
+       }
+
+       hdmi->gpr_hdmi_base = hdmi->gpr_base + 3;
+       hdmi->gpr_sdma_base = hdmi->gpr_base;
+
+       hdmi_inited = false;
+
+       hdmi->disp_mxc_hdmi = mxc_dispdrv_register(&mxc_hdmi_drv);
+       if (IS_ERR(hdmi->disp_mxc_hdmi)) {
+               dev_err(&pdev->dev, "Failed to register dispdrv - 0x%x\n",
+                       (int)hdmi->disp_mxc_hdmi);
+               ret = (int)hdmi->disp_mxc_hdmi;
+               goto edispdrv;
+       }
+       mxc_dispdrv_setdata(hdmi->disp_mxc_hdmi, hdmi);
+
+       platform_set_drvdata(pdev, hdmi);
+
+       return 0;
+edispdrv:
+       iounmap(hdmi->gpr_base);
+eiomap:
+       platform_device_put(hdmi->core_pdev);
+ecore:
+       kfree(hdmi);
+err_out_class:
+       device_destroy(hdmi_class, MKDEV(hdmi_major, 0));
+       class_destroy(hdmi_class);
+err_out_chrdev:
+       unregister_chrdev(hdmi_major, "mxc_hdmi");
+ealloc:
+       return ret;
+}
+
+static int mxc_hdmi_remove(struct platform_device *pdev)
+{
+       struct mxc_hdmi *hdmi = platform_get_drvdata(pdev);
+       int irq = platform_get_irq(pdev, 0);
+
+       fb_unregister_client(&hdmi->nb);
+
+       mxc_dispdrv_puthandle(hdmi->disp_mxc_hdmi);
+       mxc_dispdrv_unregister(hdmi->disp_mxc_hdmi);
+       iounmap(hdmi->gpr_base);
+       /* No new work will be scheduled, wait for running ISR */
+       free_irq(irq, hdmi);
+       kfree(hdmi);
+       g_hdmi = NULL;
+
+       return 0;
+}
+
+static struct platform_driver mxc_hdmi_driver = {
+       .probe = mxc_hdmi_probe,
+       .remove = mxc_hdmi_remove,
+       .driver = {
+               .name = "mxc_hdmi",
+               .of_match_table = imx_hdmi_dt_ids,
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init mxc_hdmi_init(void)
+{
+       return platform_driver_register(&mxc_hdmi_driver);
+}
+module_init(mxc_hdmi_init);
+
+static void __exit mxc_hdmi_exit(void)
+{
+       if (hdmi_major > 0) {
+               device_destroy(hdmi_class, MKDEV(hdmi_major, 0));
+               class_destroy(hdmi_class);
+               unregister_chrdev(hdmi_major, "mxc_hdmi");
+               hdmi_major = 0;
+       }
+
+       platform_driver_unregister(&mxc_hdmi_driver);
+}
+module_exit(mxc_hdmi_exit);
+
+static int mxc_hdmi_i2c_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       if (!i2c_check_functionality(client->adapter,
+                               I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
+               return -ENODEV;
+
+       hdmi_i2c = client;
+
+       return 0;
+}
+
+static int mxc_hdmi_i2c_remove(struct i2c_client *client)
+{
+       hdmi_i2c = NULL;
+       return 0;
+}
+
+static const struct of_device_id imx_hdmi_i2c_match[] = {
+       { .compatible = "fsl,imx6-hdmi-i2c", },
+       { /* sentinel */ }
+};
+
+static const struct i2c_device_id mxc_hdmi_i2c_id[] = {
+       { "mxc_hdmi_i2c", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, mxc_hdmi_i2c_id);
+
+static struct i2c_driver mxc_hdmi_i2c_driver = {
+       .driver = {
+                  .name = "mxc_hdmi_i2c",
+                       .of_match_table = imx_hdmi_i2c_match,
+                  },
+       .probe = mxc_hdmi_i2c_probe,
+       .remove = mxc_hdmi_i2c_remove,
+       .id_table = mxc_hdmi_i2c_id,
+};
+
+static int __init mxc_hdmi_i2c_init(void)
+{
+       return i2c_add_driver(&mxc_hdmi_i2c_driver);
+}
+
+static void __exit mxc_hdmi_i2c_exit(void)
+{
+       i2c_del_driver(&mxc_hdmi_i2c_driver);
+}
+
+module_init(mxc_hdmi_i2c_init);
+module_exit(mxc_hdmi_i2c_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
diff --git a/drivers/video/fbdev/mxc/mxc_ipuv3_fb.c b/drivers/video/fbdev/mxc/mxc_ipuv3_fb.c
new file mode 100644 (file)
index 0000000..a72450c
--- /dev/null
@@ -0,0 +1,3563 @@
+/*
+ * Copyright 2004-2015 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/ipu-v3-pre.h>
+#include <linux/ipu-v3-prg.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/time.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;
+       int pre_num;
+       u32 ipu_di_pix_fmt;
+       bool ipu_int_clk;
+       bool overlay;
+       bool alpha_chan_en;
+       bool late_init;
+       bool first_set_par;
+       bool resolve;
+       bool prefetch;
+       bool on_the_fly;
+       uint32_t final_pfmt;
+       unsigned long gpu_sec_buf_off;
+       dma_addr_t store_addr;
+       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;
+       uint32_t cur_ipu_pfmt;
+       uint32_t cur_fb_pfmt;
+       bool cur_prefetch;
+};
+
+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;
+};
+
+struct mxcfb_tile_block {
+       u32 fb_pix_fmt;
+       int bw; /* in pixel */
+       int bh; /* in pixel */
+};
+
+#define NA     (~0x0UL)
+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_BGRA4444, 16, { 8, 4, 0}, { 4, 4, 0}, { 0, 4, 0}, { 12, 4, 0} },
+       {IPU_PIX_FMT_BGRA5551, 16, {10, 5, 0}, { 5, 5, 0}, { 0, 5, 0}, { 15, 1, 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} },
+       /*     pixel           bpp      red         green          blue         transp */
+       {IPU_PIX_FMT_YUV420P2, 12, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_YUV420P,  12, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_YVU420P,  12, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_NV12,     12, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {PRE_PIX_FMT_NV21,     12, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_NV16,     16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {PRE_PIX_FMT_NV61,     16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_YUV422P,  16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_YVU422P,  16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_UYVY,     16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_YUYV,     16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_YUV444,   24, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_YUV444P,  24, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_AYUV,     32, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       /*     pixel              bpp      red          green          blue         transp */
+       {IPU_PIX_FMT_GPU32_SB_ST,  32, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_GPU32_SB_SRT, 32, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_GPU32_ST,     32, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_GPU32_SRT,    32, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_GPU16_SB_ST,  16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_GPU16_SB_SRT, 16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_GPU16_ST,     16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+       {IPU_PIX_FMT_GPU16_SRT,    16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} },
+};
+
+/* Tile fb alignment */
+static const struct mxcfb_tile_block tas[] = {
+       {IPU_PIX_FMT_GPU32_SB_ST,   16,   8},
+       {IPU_PIX_FMT_GPU32_SB_SRT,  64, 128},
+       {IPU_PIX_FMT_GPU32_ST,      16,   4},
+       {IPU_PIX_FMT_GPU32_SRT,     64,  64},
+       {IPU_PIX_FMT_GPU16_SB_ST,   16,   8},
+       {IPU_PIX_FMT_GPU16_SB_SRT,  64, 128},
+       {IPU_PIX_FMT_GPU16_ST,      16,   4},
+       {IPU_PIX_FMT_GPU16_SRT,     64,  64},
+};
+
+/* The block can be resolved */
+static const struct mxcfb_tile_block trs[] = {
+       /*     pixel                 w    h */
+       {IPU_PIX_FMT_GPU32_SB_ST,   16,   4},
+       {IPU_PIX_FMT_GPU32_SB_SRT,  16,   4},
+       {IPU_PIX_FMT_GPU32_ST,      16,   4},
+       {IPU_PIX_FMT_GPU32_SRT,     16,   4},
+       {IPU_PIX_FMT_GPU16_SB_ST,   16,   4},
+       {IPU_PIX_FMT_GPU16_SB_SRT,  16,   4},
+       {IPU_PIX_FMT_GPU16_ST,      16,   4},
+       {IPU_PIX_FMT_GPU16_SRT,     16,   4},
+};
+
+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;
+
+       if (var->nonstd)
+               return -1;
+
+       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;
+
+       if (var->nonstd) {
+               for (i = 0; i < ARRAY_SIZE(mxcfb_pfmts); i++) {
+                       if (mxcfb_pfmts[i].fb_pix_fmt == var->nonstd) {
+                               var->bits_per_pixel = mxcfb_pfmts[i].bpp;
+                               ret = 0;
+                               break;
+                       }
+               }
+               return ret;
+       }
+
+       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 fb_to_store_pixfmt(uint32_t fb_pixfmt)
+{
+       switch (fb_pixfmt) {
+       case IPU_PIX_FMT_RGB32:
+       case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_ABGR32:
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_BGR24:
+       case IPU_PIX_FMT_RGB565:
+       case IPU_PIX_FMT_BGRA4444:
+       case IPU_PIX_FMT_BGRA5551:
+       case IPU_PIX_FMT_UYVY:
+       case IPU_PIX_FMT_YUYV:
+       case IPU_PIX_FMT_YUV444:
+       case IPU_PIX_FMT_AYUV:
+               return fb_pixfmt;
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YVU422P:
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_NV12:
+       case PRE_PIX_FMT_NV21:
+       case IPU_PIX_FMT_NV16:
+       case PRE_PIX_FMT_NV61:
+       case IPU_PIX_FMT_YUV420P:
+               return IPU_PIX_FMT_UYVY;
+       case IPU_PIX_FMT_YUV444P:
+               return IPU_PIX_FMT_AYUV;
+       default:
+               return 0;
+       }
+}
+
+static uint32_t fbi_to_pixfmt(struct fb_info *fbi, bool original_fb)
+{
+       struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+       int i;
+       uint32_t pixfmt = 0;
+
+       if (fbi->var.nonstd) {
+               if (mxc_fbi->prefetch && !original_fb) {
+                       if (ipu_pixel_format_is_gpu_tile(fbi->var.nonstd))
+                               goto next;
+
+                       return fb_to_store_pixfmt(fbi->var.nonstd);
+               } else {
+                       return fbi->var.nonstd;
+               }
+       }
+
+next:
+       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 void fmt_to_tile_alignment(uint32_t fmt, int *bw, int *bh)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(tas); i++) {
+               if (tas[i].fb_pix_fmt == fmt) {
+                       *bw = tas[i].bw;
+                       *bh = tas[i].bh;
+               }
+       }
+
+       BUG_ON(!(*bw) || !(*bh));
+}
+
+static void fmt_to_tile_block(uint32_t fmt, int *bw, int *bh)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(trs); i++) {
+               if (trs[i].fb_pix_fmt == fmt) {
+                       *bw = trs[i].bw;
+                       *bh = trs[i].bh;
+               }
+       }
+
+       BUG_ON(!(*bw) || !(*bh));
+}
+
+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 =
+                       ((struct mxcfb_info *)(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 = (struct mxcfb_info *)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 = mxc_fbi->on_the_fly ?
+                                                 mxc_fbi->final_pfmt :
+                                                 fbi_to_pixfmt(fbi, false);
+       } 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 = mxc_fbi->on_the_fly ?
+                                                    mxc_fbi->final_pfmt :
+                                                    fbi_to_pixfmt(fbi, false);
+               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 = (struct mxcfb_info *)fbi->par;
+       int fb_stride, ipu_stride, bw = 0, bh = 0;
+       unsigned long base, ipu_base;
+       unsigned int fr_xoff, fr_yoff, fr_w, fr_h;
+       unsigned int prg_width;
+       struct ipu_pre_context pre;
+       bool post_pre_disable = false;
+
+       switch (fbi_to_pixfmt(fbi, true)) {
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_NV12:
+       case PRE_PIX_FMT_NV21:
+       case IPU_PIX_FMT_NV16:
+       case PRE_PIX_FMT_NV61:
+       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);
+               if (ipu_pixel_format_is_split_gpu_tile(fbi->var.nonstd))
+                       base += (mxc_fbi->gpu_sec_buf_off -
+                                fbi->fix.line_length * fbi->var.yres / 2) *
+                               (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;
+       }
+
+       /* pixel block alignment for resolving cases */
+       if (mxc_fbi->resolve) {
+               fmt_to_tile_block(fbi->var.nonstd, &bw, &bh);
+       } else {
+               base += fr_yoff * fb_stride + fr_xoff *
+                       bytes_per_pixel(fbi_to_pixfmt(fbi, true));
+       }
+
+       if (!mxc_fbi->on_the_fly)
+               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);
+       }
+
+       if (mxc_fbi->prefetch) {
+               struct ipu_prg_config prg;
+               struct fb_var_screeninfo from_var, to_var;
+
+               if (mxc_fbi->pre_num < 0) {
+                       mxc_fbi->pre_num = ipu_pre_alloc(mxc_fbi->ipu_id,
+                                                        mxc_fbi->ipu_ch);
+                       if (mxc_fbi->pre_num < 0) {
+                               dev_dbg(fbi->device, "failed to alloc PRE\n");
+                               mxc_fbi->prefetch = mxc_fbi->cur_prefetch;
+                               mxc_fbi->resolve = false;
+                               if (!mxc_fbi->on_the_fly)
+                                       mxc_fbi->cur_blank = FB_BLANK_POWERDOWN;
+                               return mxc_fbi->pre_num;
+                       }
+               }
+               pre.repeat = true;
+               pre.vflip = fbi->var.rotate ? true : false;
+               pre.handshake_en = true;
+               pre.hsk_abort_en = true;
+               pre.hsk_line_num = 0;
+               pre.sdw_update = false;
+               pre.cur_buf = base;
+               pre.next_buf = pre.cur_buf;
+               if (fbi->var.vmode & FB_VMODE_INTERLACED) {
+                       pre.interlaced = 2;
+                       if (mxc_fbi->resolve) {
+                               pre.field_inverse = fbi->var.rotate;
+                               pre.interlace_offset = 0;
+                       } else {
+                               pre.field_inverse = 0;
+                               if (fbi->var.rotate) {
+                                       pre.interlace_offset = ~(fbi->var.xres_virtual *
+                                                         bytes_per_pixel(fbi_to_pixfmt(fbi, true))) + 1;
+                                       pre.cur_buf += fbi->var.xres_virtual * bytes_per_pixel(fbi_to_pixfmt(fbi, true));
+                                       pre.next_buf = pre.cur_buf;
+                               } else {
+                                       pre.interlace_offset = fbi->var.xres_virtual *
+                                                         bytes_per_pixel(fbi_to_pixfmt(fbi, true));
+                               }
+                       }
+               } else {
+                       pre.interlaced = 0;
+                       pre.interlace_offset = 0;
+               }
+               pre.prefetch_mode = mxc_fbi->resolve ? 1 : 0;
+               pre.tile_fmt = mxc_fbi->resolve ? fbi->var.nonstd : 0;
+               pre.read_burst = mxc_fbi->resolve ? 0x4 : 0x3;
+               if (fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_RGB24 ||
+                   fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_BGR24 ||
+                   fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_YUV444) {
+                       if ((fbi->var.xres * 3) % 8 == 0 &&
+                           (fbi->var.xres_virtual * 3) % 8 == 0)
+                               pre.prefetch_input_bpp = 64;
+                       else if ((fbi->var.xres * 3) % 4 == 0 &&
+                                (fbi->var.xres_virtual * 3) % 4 == 0)
+                               pre.prefetch_input_bpp = 32;
+                       else if ((fbi->var.xres * 3) % 2 == 0 &&
+                                (fbi->var.xres_virtual * 3) % 2 == 0)
+                               pre.prefetch_input_bpp = 16;
+                       else
+                               pre.prefetch_input_bpp = 8;
+               } else {
+                       pre.prefetch_input_bpp =
+                                       8 * bytes_per_pixel(fbi_to_pixfmt(fbi, true));
+               }
+               pre.prefetch_input_pixel_fmt = mxc_fbi->resolve ?
+                                       0x1 : (fbi->var.nonstd ? fbi->var.nonstd : 0);
+               pre.shift_bypass = (mxc_fbi->on_the_fly &&
+                                   mxc_fbi->final_pfmt != fbi_to_pixfmt(fbi, false)) ?
+                                       false : true;
+               pixfmt_to_var(fbi_to_pixfmt(fbi, false), &from_var);
+               pixfmt_to_var(mxc_fbi->final_pfmt, &to_var);
+               if (mxc_fbi->on_the_fly &&
+                   (format_to_colorspace(fbi_to_pixfmt(fbi, true)) == RGB) &&
+                   (bytes_per_pixel(fbi_to_pixfmt(fbi, true)) == 4)) {
+                       pre.prefetch_shift_offset = (from_var.red.offset    << to_var.red.offset)   |
+                                                   (from_var.green.offset  << to_var.green.offset) |
+                                                   (from_var.blue.offset   << to_var.blue.offset)  |
+                                                   (from_var.transp.offset << to_var.transp.offset);
+                       pre.prefetch_shift_width =  (to_var.red.length    << (to_var.red.offset/2))   |
+                                                   (to_var.green.length  << (to_var.green.offset/2)) |
+                                                   (to_var.blue.length   << (to_var.blue.offset/2))  |
+                                                   (to_var.transp.length << (to_var.transp.offset/2));
+               } else {
+                       pre.prefetch_shift_offset = 0;
+                       pre.prefetch_shift_width = 0;
+               }
+               pre.tpr_coor_offset_en = mxc_fbi->resolve ? true : false;
+               pre.prefetch_output_size.left = mxc_fbi->resolve ? (fr_xoff & ~(bw - 1)) : 0;
+               pre.prefetch_output_size.top = mxc_fbi->resolve ? (fr_yoff & ~(bh - 1)) : 0;
+               if (fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_RGB24 ||
+                   fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_BGR24 ||
+                   fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_YUV444) {
+                       pre.prefetch_output_size.width = (fbi->var.xres * 3) /
+                                               (pre.prefetch_input_bpp / 8);
+                       pre.store_output_bpp = pre.prefetch_input_bpp;
+               } else {
+                       pre.prefetch_output_size.width = fbi->var.xres;
+                       pre.store_output_bpp = 8 *
+                                       bytes_per_pixel(fbi_to_pixfmt(fbi, false));
+               }
+               pre.prefetch_output_size.height = fbi->var.yres;
+               pre.prefetch_input_active_width = pre.prefetch_output_size.width;
+               if (fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_RGB24 ||
+                   fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_BGR24 ||
+                   fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_YUV444)
+                       pre.prefetch_input_width = (fbi->var.xres_virtual * 3) /
+                                                  (pre.prefetch_input_bpp / 8);
+               else
+                       pre.prefetch_input_width = fbi->var.xres_virtual;
+               pre.prefetch_input_height = fbi->var.yres;
+               prg_width = pre.prefetch_output_size.width;
+               if (!(pre.prefetch_input_active_width % 32))
+                       pre.block_size = 0;
+               else if (!(pre.prefetch_input_active_width % 16))
+                       pre.block_size = 1;
+               else
+                       pre.block_size = 0;
+               if (mxc_fbi->resolve) {
+                       int bs = pre.block_size ? 16 : 32;
+                       pre.prefetch_input_active_width += fr_xoff % bw;
+                       if (((fr_xoff % bw) + pre.prefetch_input_active_width) % bs)
+                               pre.prefetch_input_active_width =
+                                       ALIGN(pre.prefetch_input_active_width, bs);
+                       pre.prefetch_output_size.width =
+                               pre.prefetch_input_active_width;
+                       prg_width = pre.prefetch_output_size.width;
+                       pre.prefetch_input_height += fr_yoff % bh;
+                       if (((fr_yoff % bh) + fbi->var.yres) % 4) {
+                               pre.prefetch_input_height =
+                                       (fbi->var.vmode & FB_VMODE_INTERLACED) ?
+                                                       ALIGN(pre.prefetch_input_height, 8) :
+                                                       ALIGN(pre.prefetch_input_height, 4);
+                       } else {
+                               if (fbi->var.vmode & FB_VMODE_INTERLACED)
+                                       pre.prefetch_input_height =
+                                                       ALIGN(pre.prefetch_input_height, 8);
+                       }
+                       pre.prefetch_output_size.height = pre.prefetch_input_height;
+               }
+
+               /* store output pitch 8-byte aligned */
+               while ((pre.store_output_bpp * prg_width) % 64)
+                       prg_width++;
+
+               pre.store_pitch = (pre.store_output_bpp * prg_width) / 8;
+
+               pre.store_en = true;
+               pre.write_burst = 0x3;
+               ipu_get_channel_offset(fbi_to_pixfmt(fbi, true),
+                                      fbi->var.xres,
+                                      fr_h,
+                                      fr_w,
+                                      0, 0,
+                                      fr_yoff,
+                                      fr_xoff,
+                                      &pre.sec_buf_off,
+                                      &pre.trd_buf_off);
+               if (mxc_fbi->resolve)
+                       pre.sec_buf_off = mxc_fbi->gpu_sec_buf_off;
+
+               ipu_pre_config(mxc_fbi->pre_num, &pre);
+               ipu_stride = pre.store_pitch;
+               ipu_base = pre.store_addr;
+               mxc_fbi->store_addr = ipu_base;
+
+               retval = ipu_pre_sdw_update(mxc_fbi->pre_num);
+               if (retval < 0) {
+                       dev_err(fbi->device,
+                               "failed to update PRE shadow reg %d\n", retval);
+                       return retval;
+               }
+
+               if (!mxc_fbi->on_the_fly || !mxc_fbi->cur_prefetch) {
+                       prg.id = mxc_fbi->ipu_id;
+                       prg.pre_num = mxc_fbi->pre_num;
+                       prg.ipu_ch = mxc_fbi->ipu_ch;
+                       prg.so = (fbi->var.vmode & FB_VMODE_INTERLACED) ?
+                                 PRG_SO_INTERLACE : PRG_SO_PROGRESSIVE;
+                       prg.vflip = fbi->var.rotate ? true : false;
+                       prg.block_mode = mxc_fbi->resolve ? PRG_BLOCK_MODE : PRG_SCAN_MODE;
+                       prg.stride = (fbi->var.vmode & FB_VMODE_INTERLACED) ?
+                                    ipu_stride * 2 : ipu_stride;
+                       prg.ilo = (fbi->var.vmode & FB_VMODE_INTERLACED) ?
+                                  ipu_stride : 0;
+                       prg.height = mxc_fbi->resolve ?
+                                       pre.prefetch_output_size.height : fbi->var.yres;
+                       prg.ipu_height = fbi->var.yres;
+                       prg.crop_line = mxc_fbi->resolve ?
+                                       ((fbi->var.vmode & FB_VMODE_INTERLACED) ? (fr_yoff % bh) / 2 : fr_yoff % bh) : 0;
+                       prg.baddr = pre.store_addr;
+                       prg.offset = mxc_fbi->resolve ? (prg.crop_line * prg.stride +
+                                    (fr_xoff % bw) *
+                                    bytes_per_pixel(fbi_to_pixfmt(fbi, false))) : 0;
+                       ipu_base += prg.offset;
+                       if (ipu_base % 8) {
+                               dev_err(fbi->device,
+                                       "IPU base address is not 8byte aligned\n");
+                               return -EINVAL;
+                       }
+                       mxc_fbi->store_addr = ipu_base;
+
+                       if (!mxc_fbi->on_the_fly) {
+                               retval = ipu_init_channel_buffer(mxc_fbi->ipu,
+                                                                mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+                                                                fbi_to_pixfmt(fbi, false),
+                                                                fbi->var.xres, fbi->var.yres,
+                                                                ipu_stride,
+                                                                fbi->var.rotate,
+                                                                ipu_base,
+                                                                ipu_base,
+                                                                fbi->var.accel_flags &
+                                                                       FB_ACCEL_DOUBLE_FLAG ? 0 : ipu_base,
+                                                                0, 0);
+                               if (retval) {
+                                       dev_err(fbi->device,
+                                               "ipu_init_channel_buffer error %d\n", retval);
+                                       return retval;
+                               }
+                       }
+
+                       retval = ipu_prg_config(&prg);
+                       if (retval < 0) {
+                               dev_err(fbi->device,
+                                       "failed to configure PRG %d\n", retval);
+                               return retval;
+                       }
+
+                       retval = ipu_pre_enable(mxc_fbi->pre_num);
+                       if (retval < 0) {
+                               ipu_prg_disable(mxc_fbi->ipu_id, mxc_fbi->pre_num);
+                               dev_err(fbi->device,
+                                       "failed to enable PRE %d\n", retval);
+                               return retval;
+                       }
+
+                       retval = ipu_prg_wait_buf_ready(mxc_fbi->ipu_id,
+                                                       mxc_fbi->pre_num,
+                                                       pre.hsk_line_num,
+                                                       pre.prefetch_output_size.height);
+                       if (retval < 0) {
+                               ipu_prg_disable(mxc_fbi->ipu_id, mxc_fbi->pre_num);
+                               ipu_pre_disable(mxc_fbi->pre_num);
+                               ipu_pre_free(&mxc_fbi->pre_num);
+                               dev_err(fbi->device, "failed to wait PRG ready %d\n", retval);
+                               return retval;
+                       }
+               }
+       } else {
+               ipu_stride = fb_stride;
+               ipu_base = base;
+               if (mxc_fbi->on_the_fly)
+                       post_pre_disable = true;
+       }
+
+       if (mxc_fbi->on_the_fly && ((mxc_fbi->cur_prefetch && !mxc_fbi->prefetch) ||
+           (!mxc_fbi->cur_prefetch && mxc_fbi->prefetch))) {
+               int htotal = fbi->var.xres + fbi->var.right_margin +
+                            fbi->var.hsync_len + fbi->var.left_margin;
+               int vtotal = fbi->var.yres + fbi->var.lower_margin +
+                            fbi->var.vsync_len + fbi->var.upper_margin;
+               int timeout = ((htotal * vtotal) / PICOS2KHZ(fbi->var.pixclock)) * 2 ;
+               int cur_buf = 0;
+
+               BUG_ON(timeout <= 0);
+
+               ++mxc_fbi->cur_ipu_buf;
+               mxc_fbi->cur_ipu_buf %= 3;
+               if (ipu_update_channel_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                                             IPU_INPUT_BUFFER,
+                                             mxc_fbi->cur_ipu_buf,
+                                             ipu_base) == 0) {
+                       if (!mxc_fbi->prefetch)
+                               ipu_update_channel_offset(mxc_fbi->ipu,
+                                               mxc_fbi->ipu_ch,
+                                               IPU_INPUT_BUFFER,
+                                               fbi_to_pixfmt(fbi, true),
+                                               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);
+                       for (; timeout > 0; timeout--) {
+                               cur_buf = ipu_get_cur_buffer_idx(mxc_fbi->ipu,
+                                                       mxc_fbi->ipu_ch,
+                                                       IPU_INPUT_BUFFER);
+                               if (cur_buf == mxc_fbi->cur_ipu_buf)
+                                       break;
+
+                               udelay(1000);
+                       }
+                       if (!timeout)
+                               dev_err(fbi->device, "Timeout for switch to buf %d "
+                                       "to address=0x%08lX, current buf %d, "
+                                       "buf0 ready %d, buf1 ready %d, buf2 ready "
+                                       "%d\n", mxc_fbi->cur_ipu_buf, ipu_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));
+               }
+       } else if (!mxc_fbi->on_the_fly && !mxc_fbi->prefetch) {
+               retval = ipu_init_channel_buffer(mxc_fbi->ipu,
+                                                mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+                                                mxc_fbi->on_the_fly ? mxc_fbi->final_pfmt :
+                                                fbi_to_pixfmt(fbi, false),
+                                                fbi->var.xres, fbi->var.yres,
+                                                ipu_stride,
+                                                fbi->var.rotate,
+                                                ipu_base,
+                                                ipu_base,
+                                                fbi->var.accel_flags &
+                                                       FB_ACCEL_DOUBLE_FLAG ? 0 : ipu_base,
+                                                0, 0);
+               if (retval) {
+                       dev_err(fbi->device,
+                               "ipu_init_channel_buffer error %d\n", retval);
+                       return retval;
+               }
+               /* update u/v offset */
+               if (!mxc_fbi->prefetch)
+                       ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                                       IPU_INPUT_BUFFER,
+                                       fbi_to_pixfmt(fbi, true),
+                                       fr_w,
+                                       fr_h,
+                                       fr_w,
+                                       0, 0,
+                                       fr_yoff,
+                                       fr_xoff);
+       }
+
+       if (post_pre_disable) {
+               ipu_prg_disable(mxc_fbi->ipu_id, mxc_fbi->pre_num);
+               ipu_pre_disable(mxc_fbi->pre_num);
+               ipu_pre_free(&mxc_fbi->pre_num);
+       }
+
+       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));
+}
+
+static bool mxcfb_can_set_par_on_the_fly(struct fb_info *fbi,
+                                        uint32_t *final_pfmt)
+{
+       struct mxcfb_info *mxc_fbi = fbi->par;
+       struct fb_var_screeninfo cur_var = mxc_fbi->cur_var;
+       uint32_t cur_pfmt = mxc_fbi->cur_ipu_pfmt;
+       uint32_t new_pfmt = fbi_to_pixfmt(fbi, false);
+       uint32_t new_fb_pfmt = fbi_to_pixfmt(fbi, true);
+       int cur_bpp, new_bpp, cur_bw, cur_bh, new_bw, new_bh;
+       unsigned int mem_len;
+       ipu_color_space_t cur_space, new_space;
+
+       cur_space = format_to_colorspace(cur_pfmt);
+       new_space = format_to_colorspace(new_pfmt);
+
+       cur_bpp = bytes_per_pixel(cur_pfmt);
+       new_bpp = bytes_per_pixel(new_pfmt);
+
+       if (mxc_fbi->first_set_par || mxc_fbi->cur_blank != FB_BLANK_UNBLANK)
+               return false;
+
+       if (!mxc_fbi->prefetch && !mxc_fbi->cur_prefetch)
+               return false;
+
+       if (!mxc_fbi->prefetch && cur_pfmt != new_pfmt)
+               return false;
+
+       if (cur_space == RGB && (cur_bpp == 2 || cur_bpp == 3) &&
+           cur_pfmt != new_pfmt)
+               return false;
+
+       if (!(mxc_fbi->cur_prefetch && mxc_fbi->prefetch) &&
+           ((cur_var.xres_virtual != fbi->var.xres_virtual) ||
+            (cur_var.xres != cur_var.xres_virtual) ||
+            (fbi->var.xres != fbi->var.xres_virtual)))
+               return false;
+
+       if (cur_space != new_space ||
+           (new_space == RGB && cur_bpp != new_bpp))
+               return false;
+
+       if (new_space == YCbCr)
+               return false;
+
+       mem_len = fbi->var.yres_virtual * fbi->fix.line_length;
+       if (mxc_fbi->resolve && mxc_fbi->gpu_sec_buf_off) {
+               if (fbi->var.vmode & FB_VMODE_YWRAP)
+                       mem_len = mxc_fbi->gpu_sec_buf_off + mem_len / 2;
+               else
+                       mem_len = mxc_fbi->gpu_sec_buf_off *
+                                (fbi->var.yres_virtual / fbi->var.yres) + mem_len / 2;
+       }
+       if (mem_len > fbi->fix.smem_len)
+               return false;
+
+       if (mxc_fbi->resolve && ipu_pixel_format_is_gpu_tile(mxc_fbi->cur_fb_pfmt)) {
+               fmt_to_tile_block(mxc_fbi->cur_fb_pfmt, &cur_bw, &cur_bh);
+               fmt_to_tile_block(new_fb_pfmt, &new_bw, &new_bh);
+
+               if (cur_bw != new_bw || cur_bh != new_bh ||
+                   cur_var.xoffset % cur_bw != fbi->var.xoffset % new_bw ||
+                   cur_var.yoffset % cur_bh != fbi->var.yoffset % new_bh)
+                       return false;
+       } else if (mxc_fbi->resolve && mxc_fbi->cur_prefetch) {
+               fmt_to_tile_block(new_fb_pfmt, &new_bw, &new_bh);
+               if (fbi->var.xoffset % new_bw || fbi->var.yoffset % new_bh ||
+                   fbi->var.xres % 16 || fbi->var.yres %
+                    (fbi->var.vmode & FB_VMODE_INTERLACED ? 8 : 4))
+                       return false;
+       } else if (mxc_fbi->prefetch && ipu_pixel_format_is_gpu_tile(mxc_fbi->cur_fb_pfmt)) {
+               fmt_to_tile_block(mxc_fbi->cur_fb_pfmt, &cur_bw, &cur_bh);
+               if (cur_var.xoffset % cur_bw || cur_var.yoffset % cur_bh ||
+                   cur_var.xres % 16 || cur_var.yres %
+                    (cur_var.vmode & FB_VMODE_INTERLACED ? 8 : 4))
+                       return false;
+       }
+
+       cur_var.xres_virtual = fbi->var.xres_virtual;
+       cur_var.yres_virtual = fbi->var.yres_virtual;
+       cur_var.xoffset      = fbi->var.xoffset;
+       cur_var.yoffset      = fbi->var.yoffset;
+       cur_var.red          = fbi->var.red;
+       cur_var.green        = fbi->var.green;
+       cur_var.blue         = fbi->var.blue;
+       cur_var.transp       = fbi->var.transp;
+       cur_var.nonstd       = fbi->var.nonstd;
+       if (memcmp(&cur_var, &fbi->var,
+                  sizeof(struct fb_var_screeninfo)))
+               return false;
+
+       *final_pfmt = cur_pfmt;
+
+       return true;
+}
+
+static void mxcfb_check_resolve(struct fb_info *fbi)
+{
+       struct mxcfb_info *mxc_fbi = fbi->par;
+
+       switch (fbi->var.nonstd) {
+       case IPU_PIX_FMT_GPU32_ST:
+       case IPU_PIX_FMT_GPU32_SRT:
+       case IPU_PIX_FMT_GPU16_ST:
+       case IPU_PIX_FMT_GPU16_SRT:
+               mxc_fbi->gpu_sec_buf_off = 0;
+       case IPU_PIX_FMT_GPU32_SB_ST:
+       case IPU_PIX_FMT_GPU32_SB_SRT:
+       case IPU_PIX_FMT_GPU16_SB_ST:
+       case IPU_PIX_FMT_GPU16_SB_SRT:
+               mxc_fbi->prefetch = true;
+               mxc_fbi->resolve = true;
+               break;
+       default:
+               mxc_fbi->resolve = false;
+       }
+}
+
+static void mxcfb_check_yuv(struct fb_info *fbi)
+{
+       struct mxcfb_info *mxc_fbi = fbi->par;
+
+       if (fbi->var.vmode & FB_VMODE_INTERLACED) {
+               if (ipu_pixel_format_is_multiplanar_yuv(fbi_to_pixfmt(fbi, true)))
+                       mxc_fbi->prefetch = false;
+       } else {
+               if (fbi->var.nonstd == PRE_PIX_FMT_NV21 ||
+                   fbi->var.nonstd == PRE_PIX_FMT_NV61)
+                       mxc_fbi->prefetch = true;
+       }
+}
+
+/*
+ * 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 = (struct mxcfb_info *)fbi->par;
+       uint32_t final_pfmt = 0;
+       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, on_the_fly;
+
+       if (ipu_ch_param_bad_alpha_pos(fbi_to_pixfmt(fbi, true)) &&
+           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 = (struct mxcfb_info *)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;
+
+       mxcfb_set_fix(fbi);
+
+       mxcfb_check_resolve(fbi);
+
+       mxcfb_check_yuv(fbi);
+
+       on_the_fly = mxcfb_can_set_par_on_the_fly(fbi, &final_pfmt);
+       mxc_fbi->on_the_fly = on_the_fly;
+       mxc_fbi->final_pfmt = final_pfmt;
+
+       if (on_the_fly)
+               dev_dbg(fbi->device, "Reconfiguring framebuffer on the fly\n");
+
+       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);
+
+               if (!on_the_fly) {
+                       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);
+                       if (mxc_fbi_fg->cur_prefetch) {
+                               ipu_prg_disable(mxc_fbi_fg->ipu_id, mxc_fbi_fg->pre_num);
+                               ipu_pre_disable(mxc_fbi_fg->pre_num);
+                               ipu_pre_free(&mxc_fbi_fg->pre_num);
+                       }
+               }
+       }
+
+       if (!on_the_fly) {
+               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);
+               if (mxc_fbi->cur_prefetch) {
+                       ipu_prg_disable(mxc_fbi->ipu_id, mxc_fbi->pre_num);
+                       ipu_pre_disable(mxc_fbi->pre_num);
+                       ipu_pre_free(&mxc_fbi->pre_num);
+               }
+       }
+
+       /*
+        * 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);
+
+       mem_len = fbi->var.yres_virtual * fbi->fix.line_length;
+       if (mxc_fbi->resolve && mxc_fbi->gpu_sec_buf_off) {
+               if (fbi->var.vmode & FB_VMODE_YWRAP)
+                       mem_len = mxc_fbi->gpu_sec_buf_off + mem_len / 2;
+               else
+                       mem_len = mxc_fbi->gpu_sec_buf_off *
+                                (fbi->var.yres_virtual / fbi->var.yres) + mem_len / 2;
+       }
+       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((char *)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 (!on_the_fly && 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;
+               }
+       }
+
+       if (!on_the_fly) {
+               _setup_disp_channel1(fbi);
+               if (ovfbi_enable)
+                       _setup_disp_channel1(mxc_fbi->ovfbi);
+       }
+
+       if (!mxc_fbi->overlay && !on_the_fly) {
+               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) {
+               if (!on_the_fly)
+                       ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch);
+               return retval;
+       }
+
+       if (ovfbi_enable && !on_the_fly) {
+               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;
+               }
+       }
+
+       if (!on_the_fly) {
+               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 (!on_the_fly && mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->enable) {
+               retval = mxc_fbi->dispdrv->drv->enable(mxc_fbi->dispdrv, fbi);
+               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;
+       mxc_fbi->cur_ipu_pfmt = on_the_fly ? mxc_fbi->final_pfmt :
+                                            fbi_to_pixfmt(fbi, false);
+       mxc_fbi->cur_fb_pfmt = fbi_to_pixfmt(fbi, true);
+       mxc_fbi->cur_prefetch = mxc_fbi->prefetch;
+
+       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 = (struct mxcfb_info *)fbi_from->par;
+       struct mxcfb_info *mxc_fbi_to = (struct mxcfb_info *)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 = (struct mxcfb_info *)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 = (struct mxcfb_info *)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 = (struct mxcfb_info *)info->par;
+       struct fb_info tmp_fbi;
+       unsigned int fr_xoff, fr_yoff, fr_w, fr_h, line_length;
+       unsigned long base = 0;
+       int ret, bw = 0, bh = 0;
+       bool triple_buffer = false;
+
+       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 (fbi_tmp->var.vmode & FB_VMODE_INTERLACED)
+                       var->vmode |= FB_VMODE_INTERLACED;
+               else
+                       var->vmode &= ~FB_VMODE_INTERLACED;
+
+               var->pixclock = fbi_tmp->var.pixclock;
+               var->right_margin = fbi_tmp->var.right_margin;
+               var->hsync_len = fbi_tmp->var.hsync_len;
+               var->left_margin = fbi_tmp->var.left_margin +
+                                  fbi_tmp->var.xres - var->xres;
+               var->upper_margin = fbi_tmp->var.upper_margin;
+               var->vsync_len = fbi_tmp->var.vsync_len;
+               var->lower_margin = fbi_tmp->var.lower_margin +
+                                   fbi_tmp->var.yres - var->yres;
+       }
+
+       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;
+               triple_buffer = true;
+       }
+
+       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 */
+               ret = bpp_to_var(var->bits_per_pixel, var);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (ipu_pixel_format_is_gpu_tile(var->nonstd)) {
+               fmt_to_tile_alignment(var->nonstd, &bw, &bh);
+               var->xres_virtual = ALIGN(var->xres_virtual, bw);
+               if (triple_buffer)
+                       var->yres_virtual = 3 * ALIGN(var->yres, bh);
+               else
+                       var->yres_virtual = ALIGN(var->yres_virtual, bh);
+       }
+
+       line_length = var->xres_virtual * var->bits_per_pixel / 8;
+       fr_xoff = var->xoffset;
+       fr_w = var->xres_virtual;
+       if (!(var->vmode & FB_VMODE_YWRAP)) {
+               fr_yoff = var->yoffset % var->yres;
+               fr_h = var->yres;
+               base = line_length * var->yres *
+                      (var->yoffset / var->yres);
+               if (ipu_pixel_format_is_split_gpu_tile(var->nonstd))
+                       base += (mxc_fbi->gpu_sec_buf_off -
+                                line_length * var->yres / 2) *
+                               (var->yoffset / var->yres);
+       } else {
+               fr_yoff = var->yoffset;
+               fr_h = var->yres_virtual;
+       }
+
+       tmp_fbi.var = *var;
+       tmp_fbi.par = mxc_fbi;
+       if (ipu_pixel_format_is_gpu_tile(var->nonstd)) {
+               unsigned int crop_line, prg_width = var->xres, offset;
+               int ipu_stride, prg_stride, bs;
+               bool tmp_prefetch = mxc_fbi->prefetch;
+
+               if (!(var->xres % 32))
+                       bs = 32;
+               else if (!(var->xres % 16))
+                       bs = 16;
+               else
+                       bs = 32;
+
+               prg_width += fr_xoff % bw;
+               if (((fr_xoff % bw) + prg_width) % bs)
+                       prg_width = ALIGN(prg_width, bs);
+
+               mxc_fbi->prefetch = true;
+               ipu_stride = prg_width *
+                               bytes_per_pixel(fbi_to_pixfmt(&tmp_fbi, false));
+
+               if (var->vmode & FB_VMODE_INTERLACED) {
+                       if ((fr_yoff % bh) % 2) {
+                               dev_err(info->device,
+                                       "wrong crop value in interlaced mode\n");
+                               return -EINVAL;
+                       }
+                       crop_line = (fr_yoff % bh) / 2;
+                       prg_stride = ipu_stride * 2;
+               } else {
+                       crop_line = fr_yoff % bh;
+                       prg_stride = ipu_stride;
+               }
+
+               offset = crop_line * prg_stride +
+                        (fr_xoff % bw) *
+                        bytes_per_pixel(fbi_to_pixfmt(&tmp_fbi, false));
+               mxc_fbi->prefetch = tmp_prefetch;
+               if (offset % 8) {
+                       dev_err(info->device,
+                               "IPU base address is not 8byte aligned\n");
+                       return -EINVAL;
+               }
+       } else {
+               unsigned int uoff = 0, voff = 0;
+               int fb_stride;
+
+               switch (fbi_to_pixfmt(&tmp_fbi, true)) {
+               case IPU_PIX_FMT_YUV420P2:
+               case IPU_PIX_FMT_YVU420P:
+               case IPU_PIX_FMT_NV12:
+               case PRE_PIX_FMT_NV21:
+               case IPU_PIX_FMT_NV16:
+               case PRE_PIX_FMT_NV61:
+               case IPU_PIX_FMT_YUV422P:
+               case IPU_PIX_FMT_YVU422P:
+               case IPU_PIX_FMT_YUV420P:
+               case IPU_PIX_FMT_YUV444P:
+                       fb_stride = var->xres_virtual;
+                       break;
+               default:
+                       fb_stride = line_length;
+               }
+               base += fr_yoff * fb_stride +
+                       fr_xoff * var->bits_per_pixel / 8;
+
+               ipu_get_channel_offset(fbi_to_pixfmt(&tmp_fbi, true),
+                                      var->xres,
+                                      fr_h,
+                                      fr_w,
+                                      0, 0,
+                                      fr_yoff,
+                                      fr_xoff,
+                                      &uoff,
+                                      &voff);
+               if (base % 8 || uoff % 8 || voff % 8) {
+                       dev_err(info->device,
+                               "IPU base address is not 8byte aligned\n");
+                       return -EINVAL;
+               }
+       }
+
+       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 = (struct mxcfb_info *)fbi->par;
+
+       switch (cmd) {
+       case MXCFB_SET_GBL_ALPHA:
+               {
+                       struct mxcfb_gbl_alpha ga;
+
+                       if (copy_from_user(&ga, (void *)arg, 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, true));
+
+                       if (copy_from_user(&la, (void *)arg, 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((void *)arg, &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, (void *)arg, 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, (void *)arg, 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_SET_GPU_SPLIT_FMT:
+               {
+                       struct mxcfb_gpu_split_fmt fmt;
+
+                       if (copy_from_user(&fmt, (void *)arg, sizeof(fmt))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       if (fmt.var.nonstd != IPU_PIX_FMT_GPU32_SB_ST &&
+                           fmt.var.nonstd != IPU_PIX_FMT_GPU32_SB_SRT &&
+                           fmt.var.nonstd != IPU_PIX_FMT_GPU16_SB_ST &&
+                           fmt.var.nonstd != IPU_PIX_FMT_GPU16_SB_SRT) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       mxc_fbi->gpu_sec_buf_off = fmt.offset;
+                       fmt.var.activate = (fbi->var.activate & ~FB_ACTIVATE_MASK) |
+                                               FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
+                       console_lock();
+                       fbi->flags |= FBINFO_MISC_USEREVENT;
+                       retval = fb_set_var(fbi, &fmt.var);
+                       fbi->flags &= ~FBINFO_MISC_USEREVENT;
+                       console_unlock();
+                       break;
+               }
+       case MXCFB_SET_PREFETCH:
+               {
+                       int enable;
+
+                       if (copy_from_user(&enable, (void *)arg, sizeof(enable))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       if (!enable) {
+                               if (ipu_pixel_format_is_gpu_tile(fbi_to_pixfmt(fbi, true))) {
+                                       dev_err(fbi->device, "Cannot disable prefetch in "
+                                               "resolving mode\n");
+                                       retval = -EINVAL;
+                                       break;
+                               }
+                               if (ipu_pixel_format_is_pre_yuv(fbi_to_pixfmt(fbi, true))) {
+                                       dev_err(fbi->device, "Cannot disable prefetch when "
+                                               "PRE gets NV61 or NV21\n");
+                                       retval = -EINVAL;
+                                       break;
+                               }
+                       } else {
+                               if ((fbi->var.vmode & FB_VMODE_INTERLACED) &&
+                                   ipu_pixel_format_is_multiplanar_yuv(fbi_to_pixfmt(fbi, true))) {
+                                       dev_err(fbi->device, "Cannot enable prefetch when "
+                                               "PRE gets multiplanar YUV frames\n");
+                                       retval = -EINVAL;
+                                       break;
+                               }
+                       }
+
+                       if (mxc_fbi->cur_prefetch == !!enable)
+                               break;
+
+                       retval = mxcfb_check_var(&fbi->var, fbi);
+                       if (retval)
+                               break;
+
+                       mxc_fbi->prefetch = !!enable;
+
+                       fbi->var.activate = (fbi->var.activate & ~FB_ACTIVATE_MASK) |
+                                               FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
+                       retval = mxcfb_set_par(fbi);
+                       break;
+               }
+       case MXCFB_GET_PREFETCH:
+               {
+                       struct mxcfb_info *mxc_fbi =
+                               (struct mxcfb_info *)fbi->par;
+
+                       if (put_user(mxc_fbi->cur_prefetch, argp))
+                               return -EFAULT;
+                       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, (void *)arg, 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((void *)arg, &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, (void *) arg, 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);
+                       break;
+               }
+       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, info);
+               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);
+               if (mxc_fbi->cur_prefetch) {
+                       ipu_prg_disable(mxc_fbi->ipu_id, mxc_fbi->pre_num);
+                       ipu_pre_disable(mxc_fbi->pre_num);
+                       ipu_pre_free(&mxc_fbi->pre_num);
+               }
+               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 int x_crop = 0, y_crop = 0;
+       unsigned long base, ipu_base = 0, active_alpha_phy_addr = 0;
+       bool loc_alpha_en = false;
+       int fb_stride;
+       int bw = 0, bh = 0;
+       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;
+
+       if (mxc_fbi->resolve) {
+               fmt_to_tile_block(info->var.nonstd, &bw, &bh);
+
+               if (mxc_fbi->cur_var.xoffset % bw != var->xoffset % bw ||
+                   mxc_fbi->cur_var.yoffset % bh != var->yoffset % bh) {
+                       dev_err(info->device, "do not support panning "
+                               "with tile crop settings changed\n");
+                       return -EINVAL;
+               }
+       }
+
+       y_bottom = var->yoffset;
+
+       if (y_bottom > info->var.yres_virtual)
+               return -EINVAL;
+
+       switch (fbi_to_pixfmt(info, true)) {
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_NV12:
+       case PRE_PIX_FMT_NV21:
+       case IPU_PIX_FMT_NV16:
+       case PRE_PIX_FMT_NV61:
+       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);
+               if (ipu_pixel_format_is_split_gpu_tile(var->nonstd))
+                       base += (mxc_fbi->gpu_sec_buf_off -
+                                info->fix.line_length * info->var.yres / 2) *
+                               (var->yoffset / info->var.yres);
+       } else {
+               dev_dbg(info->device, "Y wrap enabled\n");
+               fr_yoff = var->yoffset;
+               fr_h = info->var.yres_virtual;
+       }
+       if (!mxc_fbi->resolve) {
+               base += fr_yoff * fb_stride + fr_xoff *
+                       bytes_per_pixel(fbi_to_pixfmt(info, true));
+
+               if (mxc_fbi->cur_prefetch && (info->var.vmode & FB_VMODE_INTERLACED))
+                       base += info->var.rotate ?
+                               fr_w * bytes_per_pixel(fbi_to_pixfmt(info, true)) : 0;
+       } else {
+               x_crop = fr_xoff & ~(bw - 1);
+               y_crop = fr_yoff & ~(bh - 1);
+       }
+
+       if (mxc_fbi->cur_prefetch) {
+               unsigned int sec_buf_off = 0, trd_buf_off = 0;
+               ipu_get_channel_offset(fbi_to_pixfmt(info, true),
+                                      info->var.xres,
+                                      fr_h,
+                                      fr_w,
+                                      0, 0,
+                                      fr_yoff,
+                                      fr_xoff,
+                                      &sec_buf_off,
+                                      &trd_buf_off);
+               if (mxc_fbi->resolve)
+                       sec_buf_off = mxc_fbi->gpu_sec_buf_off;
+               ipu_pre_set_fb_buffer(mxc_fbi->pre_num, base,
+                                     x_crop, y_crop,
+                                     sec_buf_off, trd_buf_off);
+       } else {
+               ipu_base = base;
+       }
+
+       /* 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;
+       }
+
+       if (!mxc_fbi->cur_prefetch) {
+               ++mxc_fbi->cur_ipu_buf;
+               mxc_fbi->cur_ipu_buf %= 3;
+               dev_dbg(info->device, "Updating SDC %s buf %d address=0x%08lX\n",
+                       info->fix.id, mxc_fbi->cur_ipu_buf, base);
+       }
+       mxc_fbi->cur_ipu_alpha_buf = !mxc_fbi->cur_ipu_alpha_buf;
+
+       if (mxc_fbi->cur_prefetch)
+               goto next;
+
+       if (ipu_update_channel_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+                                     mxc_fbi->cur_ipu_buf, ipu_base) == 0) {
+next:
+               /* 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 */
+               if (!mxc_fbi->cur_prefetch) {
+                       ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+                                       IPU_INPUT_BUFFER,
+                                       fbi_to_pixfmt(info, true),
+                                       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));
+               if (!mxc_fbi->cur_prefetch) {
+                       ++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;
+       mxc_fbi->cur_var.xoffset = var->xoffset;
+       mxc_fbi->cur_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)
+{
+       struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+       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;
+
+       if (mxc_fbi->resolve && mxc_fbi->gpu_sec_buf_off) {
+               if (fbi->var.vmode & FB_VMODE_YWRAP)
+                       fbi->fix.smem_len = mxc_fbi->gpu_sec_buf_off +
+                                       fbi->fix.smem_len / 2;
+               else
+                       fbi->fix.smem_len = mxc_fbi->gpu_sec_buf_off *
+                                       (fbi->var.yres_virtual / fbi->var.yres) +
+                                       fbi->fix.smem_len / 2;
+       }
+
+       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;
+       struct ipuv3_fb_platform_data *plat_data = dev->platform_data;
+
+       /*
+        * 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;
+
+       bpp_to_var(plat_data->default_bpp, &fbi->var);
+
+       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_get_crtc(struct device *dev, struct mxcfb_info *mxcfbi,
+                         enum crtc crtc)
+{
+       int i = 0;
+
+       for (; i < ARRAY_SIZE(ipu_di_crtc_maps); i++)
+               if (ipu_di_crtc_maps[i].crtc == crtc) {
+                       mxcfbi->ipu_id = ipu_di_crtc_maps[i].ipu_id;
+                       mxcfbi->ipu_di = ipu_di_crtc_maps[i].ipu_di;
+                       return 0;
+               }
+
+       dev_err(dev, "failed to get valid crtc\n");
+       return -EINVAL;
+}
+
+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';
+       }
+
+       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;
+
+               ret = mxcfb_get_crtc(&pdev->dev, mxcfbi, setting.crtc);
+               if (ret)
+                       return ret;
+
+               dev_dbg(&pdev->dev, "di_pixfmt:0x%x, bpp:0x%x, di:%d, ipu:%d\n",
+                               setting.if_fmt, setting.default_bpp,
+                               mxcfbi->ipu_di, mxcfbi->ipu_id);
+       }
+
+       dev_info(&pdev->dev, "registered mxc display driver %s\n", disp_dev);
+
+       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;
+                       else if (!strncmp(opt+6, "BGRA4444", 8))
+                               fb_pix_fmt = IPU_PIX_FMT_BGRA4444;
+                       else if (!strncmp(opt+6, "BGRA5551", 8))
+                               fb_pix_fmt = IPU_PIX_FMT_BGRA5551;
+
+                       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 = (struct mxcfb_info *)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;
+       mxcfbi_fg->prefetch = false;
+       mxcfbi_fg->resolve = false;
+       mxcfbi_fg->pre_num = -1;
+
+       /* 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 = (struct mxcfb_info *)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 = NULL;
+       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");
+       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;
+       }
+
+       plat_data->prefetch = of_property_read_bool(np, "prefetch");
+
+       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 = (struct mxcfb_info *)fbi->par;
+       mxcfbi->ipu_int_clk = plat_data->int_clk;
+       mxcfbi->late_init = plat_data->late_init;
+       mxcfbi->first_set_par = true;
+       mxcfbi->prefetch = plat_data->prefetch;
+       mxcfbi->pre_num = -1;
+
+       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;
+       }
+
+       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:
+       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");
diff --git a/drivers/video/fbdev/mxc/mxc_lcdif.c b/drivers/video/fbdev/mxc/mxc_lcdif.c
new file mode 100644 (file)
index 0000000..59d429c
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2011-2015 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
+ */
+
+#include <linux/init.h>
+#include <linux/ipu.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mxcfb.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+
+#include "mxc_dispdrv.h"
+
+struct mxc_lcd_platform_data {
+       u32 default_ifmt;
+       u32 ipu_id;
+       u32 disp_id;
+};
+
+struct mxc_lcdif_data {
+       struct platform_device *pdev;
+       struct mxc_dispdrv_handle *disp_lcdif;
+};
+
+#define DISPDRV_LCD    "lcd"
+
+static struct fb_videomode lcdif_modedb[] = {
+       {
+       /* 800x480 @ 57 Hz , pixel clk @ 27MHz */
+       "CLAA-WVGA", 57, 800, 480, 37037, 40, 60, 10, 10, 20, 10,
+       FB_SYNC_CLK_LAT_FALL,
+       FB_VMODE_NONINTERLACED,
+       0,},
+       {
+       /* 800x480 @ 60 Hz , pixel clk @ 32MHz */
+       "SEIKO-WVGA", 60, 800, 480, 29850, 89, 164, 23, 10, 10, 10,
+       FB_SYNC_CLK_LAT_FALL,
+       FB_VMODE_NONINTERLACED,
+       0,},
+};
+static int lcdif_modedb_sz = ARRAY_SIZE(lcdif_modedb);
+
+static int lcdif_init(struct mxc_dispdrv_handle *disp,
+       struct mxc_dispdrv_setting *setting)
+{
+       int ret, i;
+       struct mxc_lcdif_data *lcdif = mxc_dispdrv_getdata(disp);
+       struct device *dev = &lcdif->pdev->dev;
+       struct mxc_lcd_platform_data *plat_data = dev->platform_data;
+       struct fb_videomode *modedb = lcdif_modedb;
+       int modedb_sz = lcdif_modedb_sz;
+
+       /* use platform defined ipu/di */
+       ret = ipu_di_to_crtc(dev, plat_data->ipu_id,
+                            plat_data->disp_id, &setting->crtc);
+       if (ret < 0)
+               return ret;
+
+       ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str,
+                               modedb, modedb_sz, NULL, setting->default_bpp);
+       if (!ret) {
+               fb_videomode_to_var(&setting->fbi->var, &modedb[0]);
+               setting->if_fmt = plat_data->default_ifmt;
+       }
+
+       INIT_LIST_HEAD(&setting->fbi->modelist);
+       for (i = 0; i < modedb_sz; i++) {
+               struct fb_videomode m;
+               fb_var_to_videomode(&m, &setting->fbi->var);
+               if (fb_mode_is_equal(&m, &modedb[i])) {
+                       fb_add_videomode(&modedb[i],
+                                       &setting->fbi->modelist);
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+void lcdif_deinit(struct mxc_dispdrv_handle *disp)
+{
+       /*TODO*/
+}
+
+static struct mxc_dispdrv_driver lcdif_drv = {
+       .name   = DISPDRV_LCD,
+       .init   = lcdif_init,
+       .deinit = lcdif_deinit,
+};
+
+static int lcd_get_of_property(struct platform_device *pdev,
+                               struct mxc_lcd_platform_data *plat_data)
+{
+       struct device_node *np = pdev->dev.of_node;
+       int err;
+       u32 ipu_id, disp_id;
+       const char *default_ifmt;
+
+       err = of_property_read_string(np, "default_ifmt", &default_ifmt);
+       if (err) {
+               dev_dbg(&pdev->dev, "get of property default_ifmt fail\n");
+               return err;
+       }
+       err = of_property_read_u32(np, "ipu_id", &ipu_id);
+       if (err) {
+               dev_dbg(&pdev->dev, "get of property ipu_id fail\n");
+               return err;
+       }
+       err = of_property_read_u32(np, "disp_id", &disp_id);
+       if (err) {
+               dev_dbg(&pdev->dev, "get of property disp_id fail\n");
+               return err;
+       }
+
+       plat_data->ipu_id = ipu_id;
+       plat_data->disp_id = disp_id;
+       if (!strncmp(default_ifmt, "RGB24", 5))
+               plat_data->default_ifmt = IPU_PIX_FMT_RGB24;
+       else if (!strncmp(default_ifmt, "BGR24", 5))
+               plat_data->default_ifmt = IPU_PIX_FMT_BGR24;
+       else if (!strncmp(default_ifmt, "GBR24", 5))
+               plat_data->default_ifmt = IPU_PIX_FMT_GBR24;
+       else if (!strncmp(default_ifmt, "RGB565", 6))
+               plat_data->default_ifmt = IPU_PIX_FMT_RGB565;
+       else if (!strncmp(default_ifmt, "RGB666", 6))
+               plat_data->default_ifmt = IPU_PIX_FMT_RGB666;
+       else if (!strncmp(default_ifmt, "YUV444", 6))
+               plat_data->default_ifmt = IPU_PIX_FMT_YUV444;
+       else if (!strncmp(default_ifmt, "LVDS666", 7))
+               plat_data->default_ifmt = IPU_PIX_FMT_LVDS666;
+       else if (!strncmp(default_ifmt, "YUYV16", 6))
+               plat_data->default_ifmt = IPU_PIX_FMT_YUYV;
+       else if (!strncmp(default_ifmt, "UYVY16", 6))
+               plat_data->default_ifmt = IPU_PIX_FMT_UYVY;
+       else if (!strncmp(default_ifmt, "YVYU16", 6))
+               plat_data->default_ifmt = IPU_PIX_FMT_YVYU;
+       else if (!strncmp(default_ifmt, "VYUY16", 6))
+                               plat_data->default_ifmt = IPU_PIX_FMT_VYUY;
+       else {
+               dev_err(&pdev->dev, "err default_ifmt!\n");
+               return -ENOENT;
+       }
+
+       return err;
+}
+
+static int mxc_lcdif_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct pinctrl *pinctrl;
+       struct mxc_lcdif_data *lcdif;
+       struct mxc_lcd_platform_data *plat_data;
+
+       dev_dbg(&pdev->dev, "%s enter\n", __func__);
+       lcdif = devm_kzalloc(&pdev->dev, sizeof(struct mxc_lcdif_data),
+                               GFP_KERNEL);
+       if (!lcdif)
+               return -ENOMEM;
+       plat_data = devm_kzalloc(&pdev->dev,
+                               sizeof(struct mxc_lcd_platform_data),
+                               GFP_KERNEL);
+       if (!plat_data)
+               return -ENOMEM;
+       pdev->dev.platform_data = plat_data;
+
+       ret = lcd_get_of_property(pdev, plat_data);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "get lcd of property fail\n");
+               return ret;
+       }
+
+       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(pinctrl)) {
+               dev_err(&pdev->dev, "can't get/select pinctrl\n");
+               return PTR_ERR(pinctrl);
+       }
+
+       lcdif->pdev = pdev;
+       lcdif->disp_lcdif = mxc_dispdrv_register(&lcdif_drv);
+       mxc_dispdrv_setdata(lcdif->disp_lcdif, lcdif);
+
+       dev_set_drvdata(&pdev->dev, lcdif);
+       dev_dbg(&pdev->dev, "%s exit\n", __func__);
+
+       return ret;
+}
+
+static int mxc_lcdif_remove(struct platform_device *pdev)
+{
+       struct mxc_lcdif_data *lcdif = dev_get_drvdata(&pdev->dev);
+
+       mxc_dispdrv_puthandle(lcdif->disp_lcdif);
+       mxc_dispdrv_unregister(lcdif->disp_lcdif);
+       kfree(lcdif);
+       return 0;
+}
+
+static const struct of_device_id imx_lcd_dt_ids[] = {
+       { .compatible = "fsl,lcd"},
+       { /* sentinel */ }
+};
+static struct platform_driver mxc_lcdif_driver = {
+       .driver = {
+               .name = "mxc_lcdif",
+               .of_match_table = imx_lcd_dt_ids,
+       },
+       .probe = mxc_lcdif_probe,
+       .remove = mxc_lcdif_remove,
+};
+
+static int __init mxc_lcdif_init(void)
+{
+       return platform_driver_register(&mxc_lcdif_driver);
+}
+
+static void __exit mxc_lcdif_exit(void)
+{
+       platform_driver_unregister(&mxc_lcdif_driver);
+}
+
+module_init(mxc_lcdif_init);
+module_exit(mxc_lcdif_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX ipuv3 LCD extern port driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/ipu-v3-pre.h b/include/linux/ipu-v3-pre.h
new file mode 100644 (file)
index 0000000..4447c40
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
+ *
+ * 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
+ */
+#ifndef __LINUX_IPU_V3_PRE_H_
+#define __LINUX_IPU_V3_PRE_H_
+
+#define IPU_PRE_MAX_WIDTH      1920
+#define IPU_PRE_MAX_BPP                4
+
+struct ipu_rect {
+       int left;
+       int top;
+       int width;
+       int height;
+};
+
+struct ipu_pre_context {
+       bool repeat;
+       bool vflip;
+       bool handshake_en;
+       bool hsk_abort_en;
+       unsigned int hsk_line_num;
+       bool sdw_update;
+       unsigned int block_size;
+       unsigned int interlaced;
+       unsigned int prefetch_mode;
+
+       unsigned long cur_buf;
+       unsigned long next_buf;
+
+       unsigned int tile_fmt;
+
+       unsigned int read_burst;
+       unsigned int prefetch_input_bpp;
+       unsigned int prefetch_input_pixel_fmt;
+       unsigned int prefetch_shift_offset;
+       unsigned int prefetch_shift_width;
+       bool shift_bypass;
+       bool field_inverse;
+       bool tpr_coor_offset_en;
+       /* the output of prefetch is
+        * also the input of store
+        */
+       struct ipu_rect prefetch_output_size;
+       unsigned int prefetch_input_active_width;
+       unsigned int prefetch_input_width;
+       unsigned int prefetch_input_height;
+       unsigned int store_pitch;
+       int interlace_offset;
+
+       bool store_en;
+       unsigned int write_burst;
+       unsigned int store_output_bpp;
+
+       unsigned int sec_buf_off;
+       unsigned int trd_buf_off;
+
+       /* return for IPU fb caller */
+       unsigned long store_addr;
+};
+
+#ifdef CONFIG_MXC_IPU_V3_PRE
+int ipu_pre_alloc(int ipu_id, ipu_channel_t ipu_ch);
+void ipu_pre_free(unsigned int *id);
+unsigned long ipu_pre_alloc_double_buffer(unsigned int id, unsigned int size);
+void ipu_pre_free_double_buffer(unsigned int id);
+int ipu_pre_config(int id, struct ipu_pre_context *config);
+int ipu_pre_enable(int id);
+void ipu_pre_disable(int id);
+int ipu_pre_set_fb_buffer(int id, unsigned long fb_paddr,
+                         unsigned int x_crop,
+                         unsigned int y_crop,
+                         unsigned int sec_buf_off,
+                         unsigned int trd_buf_off);
+int ipu_pre_sdw_update(int id);
+#else
+int ipu_pre_alloc(int ipu_id, ipu_channel_t channel)
+{
+       return -ENODEV;
+}
+
+void ipu_pre_free(unsigned int *id)
+{
+}
+
+unsigned long ipu_pre_alloc_double_buffer(unsigned int id, unsigned int size)
+{
+       return -ENODEV;
+}
+
+void ipu_pre_free_double_buffer(unsigned int id)
+{
+}
+
+int ipu_pre_config(int id, struct ipu_pre_context *config)
+{
+       return -ENODEV;
+}
+
+int ipu_pre_enable(int id)
+{
+       return -ENODEV;
+}
+
+void ipu_pre_disable(int id)
+{
+       return;
+}
+
+int ipu_pre_set_fb_buffer(int id, unsigned long fb_paddr,
+                         unsigned int x_crop,
+                         unsigned int y_crop,
+                         unsigned int sec_buf_off,
+                         unsigned int trd_buf_off)
+{
+       return -ENODEV;
+}
+int ipu_pre_sdw_update(int id)
+{
+       return -ENODEV;
+}
+#endif
+#endif /* __LINUX_IPU_V3_PRE_H_ */
diff --git a/include/linux/ipu-v3-prg.h b/include/linux/ipu-v3-prg.h
new file mode 100644 (file)
index 0000000..2ab803a
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
+ *
+ * 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
+ */
+#ifndef __LINUX_IPU_V3_PRG_H_
+#define __LINUX_IPU_V3_PRG_H_
+
+#include <linux/ipu-v3.h>
+
+#define PRG_SO_INTERLACE       1
+#define PRG_SO_PROGRESSIVE     0
+#define PRG_BLOCK_MODE         1
+#define PRG_SCAN_MODE          0
+
+struct ipu_prg_config {
+       unsigned int id;
+       unsigned int pre_num;
+       ipu_channel_t ipu_ch;
+       unsigned int stride;
+       unsigned int height;
+       unsigned int ipu_height;
+       unsigned int crop_line;
+       unsigned int so;
+       unsigned int ilo;
+       unsigned int block_mode;
+       bool vflip;
+       u32 baddr;
+       u32 offset;
+};
+
+#ifdef CONFIG_MXC_IPU_V3_PRG
+int ipu_prg_config(struct ipu_prg_config *config);
+int ipu_prg_disable(unsigned int ipu_id, unsigned int pre_num);
+int ipu_prg_wait_buf_ready(unsigned int ipu_id, unsigned int pre_num,
+                          unsigned int hsk_line_num,
+                          int pre_store_out_height);
+#else
+int ipu_prg_config(struct ipu_prg_config *config)
+{
+       return -ENODEV;
+}
+
+int ipu_prg_disable(unsigned int ipu_id, unsigned int pre_num)
+{
+       return -ENODEV;
+}
+
+int ipu_prg_wait_buf_ready(unsigned int ipu_id, unsigned int pre_num,
+                          unsigned int hsk_line_num,
+                          int pre_store_out_height)
+{
+       return -ENODEV;
+}
+#endif
+#endif /* __LINUX_IPU_V3_PRG_H_ */
diff --git a/include/linux/ipu-v3.h b/include/linux/ipu-v3.h
new file mode 100644 (file)
index 0000000..ae09614
--- /dev/null
@@ -0,0 +1,770 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2011-2015 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef __LINUX_IPU_V3_H_
+#define __LINUX_IPU_V3_H_
+
+#include <linux/ipu.h>
+
+/* IPU Driver channels definitions.    */
+/* Note these are different from IDMA channels */
+#define IPU_MAX_CH     32
+#define _MAKE_CHAN(num, v_in, g_in, a_in, out) \
+       ((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out)
+#define _MAKE_ALT_CHAN(ch)             (ch | (IPU_MAX_CH << 24))
+#define IPU_CHAN_ID(ch)                        (ch >> 24)
+#define IPU_CHAN_ALT(ch)               (ch & 0x02000000)
+#define IPU_CHAN_ALPHA_IN_DMA(ch)      ((uint32_t) (ch >> 6) & 0x3F)
+#define IPU_CHAN_GRAPH_IN_DMA(ch)      ((uint32_t) (ch >> 12) & 0x3F)
+#define IPU_CHAN_VIDEO_IN_DMA(ch)      ((uint32_t) (ch >> 18) & 0x3F)
+#define IPU_CHAN_OUT_DMA(ch)           ((uint32_t) (ch & 0x3F))
+#define NO_DMA 0x3F
+#define ALT    1
+/*!
+ * Enumeration of IPU logical channels. An IPU logical channel is defined as a
+ * combination of an input (memory to IPU), output (IPU to memory), and/or
+ * secondary input IDMA channels and in some cases an Image Converter task.
+ * Some channels consist of only an input or output.
+ */
+typedef enum {
+       CHAN_NONE = -1,
+       MEM_ROT_ENC_MEM = _MAKE_CHAN(1, 45, NO_DMA, NO_DMA, 48),
+       MEM_ROT_VF_MEM = _MAKE_CHAN(2, 46, NO_DMA, NO_DMA, 49),
+       MEM_ROT_PP_MEM = _MAKE_CHAN(3, 47, NO_DMA, NO_DMA, 50),
+
+       MEM_PRP_ENC_MEM = _MAKE_CHAN(4, 12, 14, 17, 20),
+       MEM_PRP_VF_MEM = _MAKE_CHAN(5, 12, 14, 17, 21),
+       MEM_PP_MEM = _MAKE_CHAN(6, 11, 15, 18, 22),
+
+       MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA),
+       MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA),
+       MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA),
+       MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA),
+
+       MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA),
+       MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA),
+       MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0),
+       MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0),
+
+       DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
+       DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
+
+       CSI_MEM0 = _MAKE_CHAN(15, NO_DMA, NO_DMA, NO_DMA, 0),
+       CSI_MEM1 = _MAKE_CHAN(16, NO_DMA, NO_DMA, NO_DMA, 1),
+       CSI_MEM2 = _MAKE_CHAN(17, NO_DMA, NO_DMA, NO_DMA, 2),
+       CSI_MEM3 = _MAKE_CHAN(18, NO_DMA, NO_DMA, NO_DMA, 3),
+
+       CSI_MEM = CSI_MEM0,
+
+       CSI_PRP_ENC_MEM = _MAKE_CHAN(19, NO_DMA, NO_DMA, NO_DMA, 20),
+       CSI_PRP_VF_MEM = _MAKE_CHAN(20, NO_DMA, NO_DMA, NO_DMA, 21),
+
+       /* for vdi mem->vdi->ic->mem , add graphics plane and alpha*/
+       MEM_VDI_PRP_VF_MEM_P = _MAKE_CHAN(21, 8, 14, 17, 21),
+       MEM_VDI_PRP_VF_MEM = _MAKE_CHAN(22, 9, 14, 17, 21),
+       MEM_VDI_PRP_VF_MEM_N = _MAKE_CHAN(23, 10, 14, 17, 21),
+
+       /* for vdi mem->vdi->mem */
+       MEM_VDI_MEM_P = _MAKE_CHAN(24, 8, NO_DMA, NO_DMA, 5),
+       MEM_VDI_MEM = _MAKE_CHAN(25, 9, NO_DMA, NO_DMA, 5),
+       MEM_VDI_MEM_N = _MAKE_CHAN(26, 10, NO_DMA, NO_DMA, 5),
+
+       /* fake channel for vdoa to link with IPU */
+       MEM_VDOA_MEM =  _MAKE_CHAN(27, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
+
+       MEM_PP_ADC = CHAN_NONE,
+       ADC_SYS2 = CHAN_NONE,
+
+} ipu_channel_t;
+
+/*!
+ * Enumeration of types of buffers for a logical channel.
+ */
+typedef enum {
+       IPU_OUTPUT_BUFFER = 0,  /*!< Buffer for output from IPU */
+       IPU_ALPHA_IN_BUFFER = 1,        /*!< Buffer for input to IPU */
+       IPU_GRAPH_IN_BUFFER = 2,        /*!< Buffer for input to IPU */
+       IPU_VIDEO_IN_BUFFER = 3,        /*!< Buffer for input to IPU */
+       IPU_INPUT_BUFFER = IPU_VIDEO_IN_BUFFER,
+       IPU_SEC_INPUT_BUFFER = IPU_GRAPH_IN_BUFFER,
+} ipu_buffer_t;
+
+#define IPU_PANEL_SERIAL               1
+#define IPU_PANEL_PARALLEL             2
+
+/*!
+ * Enumeration of ADC channel operation mode.
+ */
+typedef enum {
+       Disable,
+       WriteTemplateNonSeq,
+       ReadTemplateNonSeq,
+       WriteTemplateUnCon,
+       ReadTemplateUnCon,
+       WriteDataWithRS,
+       WriteDataWoRS,
+       WriteCmd
+} mcu_mode_t;
+
+/*!
+ * Enumeration of ADC channel addressing mode.
+ */
+typedef enum {
+       FullWoBE,
+       FullWithBE,
+       XY
+} display_addressing_t;
+
+/*!
+ * Union of initialization parameters for a logical channel.
+ */
+typedef union {
+       struct {
+               uint32_t csi;
+               uint32_t mipi_id;
+               uint32_t mipi_vc;
+               bool mipi_en;
+               bool interlaced;
+       } csi_mem;
+       struct {
+               uint32_t in_width;
+               uint32_t in_height;
+               uint32_t in_pixel_fmt;
+               uint32_t out_width;
+               uint32_t out_height;
+               uint32_t out_pixel_fmt;
+               uint32_t outh_resize_ratio;
+               uint32_t outv_resize_ratio;
+               uint32_t csi;
+               uint32_t mipi_id;
+               uint32_t mipi_vc;
+               bool mipi_en;
+       } csi_prp_enc_mem;
+       struct {
+               uint32_t in_width;
+               uint32_t in_height;
+               uint32_t in_pixel_fmt;
+               uint32_t out_width;
+               uint32_t out_height;
+               uint32_t out_pixel_fmt;
+               uint32_t outh_resize_ratio;
+               uint32_t outv_resize_ratio;
+       } mem_prp_enc_mem;
+       struct {
+               uint32_t in_width;
+               uint32_t in_height;
+               uint32_t in_pixel_fmt;
+               uint32_t out_width;
+               uint32_t out_height;
+               uint32_t out_pixel_fmt;
+       } mem_rot_enc_mem;
+       struct {
+               uint32_t in_width;
+               uint32_t in_height;
+               uint32_t in_pixel_fmt;
+               uint32_t out_width;
+               uint32_t out_height;
+               uint32_t out_pixel_fmt;
+               uint32_t outh_resize_ratio;
+               uint32_t outv_resize_ratio;
+               bool graphics_combine_en;
+               bool global_alpha_en;
+               bool key_color_en;
+               uint32_t in_g_pixel_fmt;
+               uint8_t alpha;
+               uint32_t key_color;
+               bool alpha_chan_en;
+               ipu_motion_sel motion_sel;
+               enum v4l2_field field_fmt;
+               uint32_t csi;
+               uint32_t mipi_id;
+               uint32_t mipi_vc;
+               bool mipi_en;
+       } csi_prp_vf_mem;
+       struct {
+               uint32_t in_width;
+               uint32_t in_height;
+               uint32_t in_pixel_fmt;
+               uint32_t out_width;
+               uint32_t out_height;
+               uint32_t out_pixel_fmt;
+               bool graphics_combine_en;
+               bool global_alpha_en;
+               bool key_color_en;
+               display_port_t disp;
+               uint32_t out_left;
+               uint32_t out_top;
+       } csi_prp_vf_adc;
+       struct {
+               uint32_t in_width;
+               uint32_t in_height;
+               uint32_t in_pixel_fmt;
+               uint32_t out_width;
+               uint32_t out_height;
+               uint32_t out_pixel_fmt;
+               uint32_t outh_resize_ratio;
+               uint32_t outv_resize_ratio;
+               bool graphics_combine_en;
+               bool global_alpha_en;
+               bool key_color_en;
+               uint32_t in_g_pixel_fmt;
+               uint8_t alpha;
+               uint32_t key_color;
+               bool alpha_chan_en;
+               ipu_motion_sel motion_sel;
+               enum v4l2_field field_fmt;
+       } mem_prp_vf_mem;
+       struct {
+               uint32_t temp;
+       } mem_prp_vf_adc;
+       struct {
+               uint32_t temp;
+       } mem_rot_vf_mem;
+       struct {
+               uint32_t in_width;
+               uint32_t in_height;
+               uint32_t in_pixel_fmt;
+               uint32_t out_width;
+               uint32_t out_height;
+               uint32_t out_pixel_fmt;
+               uint32_t outh_resize_ratio;
+               uint32_t outv_resize_ratio;
+               bool graphics_combine_en;
+               bool global_alpha_en;
+               bool key_color_en;
+               uint32_t in_g_pixel_fmt;
+               uint8_t alpha;
+               uint32_t key_color;
+               bool alpha_chan_en;
+       } mem_pp_mem;
+       struct {
+               uint32_t temp;
+       } mem_rot_mem;
+       struct {
+               uint32_t in_width;
+               uint32_t in_height;
+               uint32_t in_pixel_fmt;
+               uint32_t out_width;
+               uint32_t out_height;
+               uint32_t out_pixel_fmt;
+               bool graphics_combine_en;
+               bool global_alpha_en;
+               bool key_color_en;
+               display_port_t disp;
+               uint32_t out_left;
+               uint32_t out_top;
+       } mem_pp_adc;
+       struct {
+               uint32_t di;
+               bool interlaced;
+               uint32_t in_pixel_fmt;
+               uint32_t out_pixel_fmt;
+       } mem_dc_sync;
+       struct {
+               uint32_t temp;
+       } mem_sdc_fg;
+       struct {
+               uint32_t di;
+               bool interlaced;
+               uint32_t in_pixel_fmt;
+               uint32_t out_pixel_fmt;
+               bool alpha_chan_en;
+       } mem_dp_bg_sync;
+       struct {
+               uint32_t temp;
+       } mem_sdc_bg;
+       struct {
+               uint32_t di;
+               bool interlaced;
+               uint32_t in_pixel_fmt;
+               uint32_t out_pixel_fmt;
+               bool alpha_chan_en;
+       } mem_dp_fg_sync;
+       struct {
+               uint32_t di;
+       } direct_async;
+       struct {
+               display_port_t disp;
+               mcu_mode_t ch_mode;
+               uint32_t out_left;
+               uint32_t out_top;
+       } adc_sys1;
+       struct {
+               display_port_t disp;
+               mcu_mode_t ch_mode;
+               uint32_t out_left;
+               uint32_t out_top;
+       } adc_sys2;
+} ipu_channel_params_t;
+
+/*
+ * IPU_IRQF_ONESHOT - Interrupt is not reenabled after the irq handler finished.
+ */
+#define IPU_IRQF_NONE          0x00000000
+#define IPU_IRQF_ONESHOT       0x00000001
+
+/*!
+ * Enumeration of IPU interrupt sources.
+ */
+enum ipu_irq_line {
+       IPU_IRQ_CSI0_OUT_EOF = 0,
+       IPU_IRQ_CSI1_OUT_EOF = 1,
+       IPU_IRQ_CSI2_OUT_EOF = 2,
+       IPU_IRQ_CSI3_OUT_EOF = 3,
+       IPU_IRQ_VDIC_OUT_EOF = 5,
+       IPU_IRQ_VDI_P_IN_EOF = 8,
+       IPU_IRQ_VDI_C_IN_EOF = 9,
+       IPU_IRQ_VDI_N_IN_EOF = 10,
+       IPU_IRQ_PP_IN_EOF = 11,
+       IPU_IRQ_PRP_IN_EOF = 12,
+       IPU_IRQ_PRP_GRAPH_IN_EOF = 14,
+       IPU_IRQ_PP_GRAPH_IN_EOF = 15,
+       IPU_IRQ_PRP_ALPHA_IN_EOF = 17,
+       IPU_IRQ_PP_ALPHA_IN_EOF = 18,
+       IPU_IRQ_PRP_ENC_OUT_EOF = 20,
+       IPU_IRQ_PRP_VF_OUT_EOF = 21,
+       IPU_IRQ_PP_OUT_EOF = 22,
+       IPU_IRQ_BG_SYNC_EOF = 23,
+       IPU_IRQ_BG_ASYNC_EOF = 24,
+       IPU_IRQ_FG_SYNC_EOF = 27,
+       IPU_IRQ_DC_SYNC_EOF = 28,
+       IPU_IRQ_FG_ASYNC_EOF = 29,
+       IPU_IRQ_FG_ALPHA_SYNC_EOF = 31,
+
+       IPU_IRQ_FG_ALPHA_ASYNC_EOF = 33,
+       IPU_IRQ_DC_READ_EOF = 40,
+       IPU_IRQ_DC_ASYNC_EOF = 41,
+       IPU_IRQ_DC_CMD1_EOF = 42,
+       IPU_IRQ_DC_CMD2_EOF = 43,
+       IPU_IRQ_DC_MASK_EOF = 44,
+       IPU_IRQ_PRP_ENC_ROT_IN_EOF = 45,
+       IPU_IRQ_PRP_VF_ROT_IN_EOF = 46,
+       IPU_IRQ_PP_ROT_IN_EOF = 47,
+       IPU_IRQ_PRP_ENC_ROT_OUT_EOF = 48,
+       IPU_IRQ_PRP_VF_ROT_OUT_EOF = 49,
+       IPU_IRQ_PP_ROT_OUT_EOF = 50,
+       IPU_IRQ_BG_ALPHA_SYNC_EOF = 51,
+       IPU_IRQ_BG_ALPHA_ASYNC_EOF = 52,
+
+       IPU_IRQ_BG_SYNC_NFACK = 64 + 23,
+       IPU_IRQ_FG_SYNC_NFACK = 64 + 27,
+       IPU_IRQ_DC_SYNC_NFACK = 64 + 28,
+
+       IPU_IRQ_DP_SF_START = 448 + 2,
+       IPU_IRQ_DP_SF_END = 448 + 3,
+       IPU_IRQ_BG_SF_END = IPU_IRQ_DP_SF_END,
+       IPU_IRQ_DC_FC_0 = 448 + 8,
+       IPU_IRQ_DC_FC_1 = 448 + 9,
+       IPU_IRQ_DC_FC_2 = 448 + 10,
+       IPU_IRQ_DC_FC_3 = 448 + 11,
+       IPU_IRQ_DC_FC_4 = 448 + 12,
+       IPU_IRQ_DC_FC_6 = 448 + 13,
+       IPU_IRQ_VSYNC_PRE_0 = 448 + 14,
+       IPU_IRQ_VSYNC_PRE_1 = 448 + 15,
+
+       IPU_IRQ_COUNT
+};
+
+/*!
+ * Bitfield of Display Interface signal polarities.
+ */
+typedef struct {
+       unsigned datamask_en:1;
+       unsigned int_clk:1;
+       unsigned interlaced:1;
+       unsigned odd_field_first:1;
+       unsigned clksel_en:1;
+       unsigned clkidle_en:1;
+       unsigned data_pol:1;    /* true = inverted */
+       unsigned clk_pol:1;     /* true = rising edge */
+       unsigned enable_pol:1;
+       unsigned Hsync_pol:1;   /* true = active high */
+       unsigned Vsync_pol:1;
+} ipu_di_signal_cfg_t;
+
+/*!
+ * Bitfield of CSI signal polarities and modes.
+ */
+
+typedef struct {
+       unsigned data_width:4;
+       unsigned clk_mode:3;
+       unsigned ext_vsync:1;
+       unsigned Vsync_pol:1;
+       unsigned Hsync_pol:1;
+       unsigned pixclk_pol:1;
+       unsigned data_pol:1;
+       unsigned sens_clksrc:1;
+       unsigned pack_tight:1;
+       unsigned force_eof:1;
+       unsigned data_en_pol:1;
+       unsigned data_fmt;
+       unsigned csi;
+       unsigned mclk;
+} ipu_csi_signal_cfg_t;
+
+/*!
+ * Enumeration of CSI data bus widths.
+ */
+enum {
+       IPU_CSI_DATA_WIDTH_4 = 0,
+       IPU_CSI_DATA_WIDTH_8 = 1,
+       IPU_CSI_DATA_WIDTH_10 = 3,
+       IPU_CSI_DATA_WIDTH_16 = 9,
+};
+
+/*!
+ * Enumeration of CSI clock modes.
+ */
+enum {
+       IPU_CSI_CLK_MODE_GATED_CLK,
+       IPU_CSI_CLK_MODE_NONGATED_CLK,
+       IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE,
+       IPU_CSI_CLK_MODE_CCIR656_INTERLACED,
+       IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR,
+       IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR,
+       IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR,
+       IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR,
+};
+
+enum {
+       IPU_CSI_MIPI_DI0,
+       IPU_CSI_MIPI_DI1,
+       IPU_CSI_MIPI_DI2,
+       IPU_CSI_MIPI_DI3,
+};
+
+typedef enum {
+       RGB,
+       YCbCr,
+       YUV
+} ipu_color_space_t;
+
+/*!
+ * Enumeration of ADC vertical sync mode.
+ */
+typedef enum {
+       VsyncNone,
+       VsyncInternal,
+       VsyncCSI,
+       VsyncExternal
+} vsync_t;
+
+typedef enum {
+       DAT,
+       CMD
+} cmddata_t;
+
+/*!
+ * Enumeration of ADC display update mode.
+ */
+typedef enum {
+       IPU_ADC_REFRESH_NONE,
+       IPU_ADC_AUTO_REFRESH,
+       IPU_ADC_AUTO_REFRESH_SNOOP,
+       IPU_ADC_SNOOPING,
+} ipu_adc_update_mode_t;
+
+/*!
+ * Enumeration of ADC display interface types (serial or parallel).
+ */
+enum {
+       IPU_ADC_IFC_MODE_SYS80_TYPE1,
+       IPU_ADC_IFC_MODE_SYS80_TYPE2,
+       IPU_ADC_IFC_MODE_SYS68K_TYPE1,
+       IPU_ADC_IFC_MODE_SYS68K_TYPE2,
+       IPU_ADC_IFC_MODE_3WIRE_SERIAL,
+       IPU_ADC_IFC_MODE_4WIRE_SERIAL,
+       IPU_ADC_IFC_MODE_5WIRE_SERIAL_CLK,
+       IPU_ADC_IFC_MODE_5WIRE_SERIAL_CS,
+};
+
+enum {
+       IPU_ADC_IFC_WIDTH_8,
+       IPU_ADC_IFC_WIDTH_16,
+};
+
+/*!
+ * Enumeration of ADC display interface burst mode.
+ */
+enum {
+       IPU_ADC_BURST_WCS,
+       IPU_ADC_BURST_WBLCK,
+       IPU_ADC_BURST_NONE,
+       IPU_ADC_BURST_SERIAL,
+};
+
+/*!
+ * Enumeration of ADC display interface RW signal timing modes.
+ */
+enum {
+       IPU_ADC_SER_NO_RW,
+       IPU_ADC_SER_RW_BEFORE_RS,
+       IPU_ADC_SER_RW_AFTER_RS,
+};
+
+/*!
+ * Bitfield of ADC signal polarities and modes.
+ */
+typedef struct {
+       unsigned data_pol:1;
+       unsigned clk_pol:1;
+       unsigned cs_pol:1;
+       unsigned rs_pol:1;
+       unsigned addr_pol:1;
+       unsigned read_pol:1;
+       unsigned write_pol:1;
+       unsigned Vsync_pol:1;
+       unsigned burst_pol:1;
+       unsigned burst_mode:2;
+       unsigned ifc_mode:3;
+       unsigned ifc_width:5;
+       unsigned ser_preamble_len:4;
+       unsigned ser_preamble:8;
+       unsigned ser_rw_mode:2;
+} ipu_adc_sig_cfg_t;
+
+/*!
+ * Enumeration of ADC template commands.
+ */
+enum {
+       RD_DATA,
+       RD_ACK,
+       RD_WAIT,
+       WR_XADDR,
+       WR_YADDR,
+       WR_ADDR,
+       WR_CMND,
+       WR_DATA,
+};
+
+/*!
+ * Enumeration of ADC template command flow control.
+ */
+enum {
+       SINGLE_STEP,
+       PAUSE,
+       STOP,
+};
+
+
+/*Define template constants*/
+#define     ATM_ADDR_RANGE      0x20   /*offset address of DISP */
+#define     TEMPLATE_BUF_SIZE   0x20   /*size of template */
+
+/*!
+ * Define to create ADC template command entry.
+ */
+#define ipu_adc_template_gen(oc, rs, fc, dat) (((rs) << 29) | ((fc) << 27) | \
+                       ((oc) << 24) | (dat))
+
+typedef struct {
+       u32 reg;
+       u32 value;
+} ipu_lpmc_reg_t;
+
+#define IPU_LPMC_REG_READ       0x80000000L
+
+#define CSI_MCLK_VF  1
+#define CSI_MCLK_ENC 2
+#define CSI_MCLK_RAW 4
+#define CSI_MCLK_I2C 8
+
+struct ipu_soc;
+/* Common IPU API */
+struct ipu_soc *ipu_get_soc(int id);
+int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params);
+void ipu_uninit_channel(struct ipu_soc *ipu, ipu_channel_t channel);
+void ipu_disable_hsp_clk(struct ipu_soc *ipu);
+
+static inline bool ipu_can_rotate_in_place(ipu_rotate_mode_t rot)
+{
+#ifdef CONFIG_MXC_IPU_V3D
+       return (rot < IPU_ROTATE_HORIZ_FLIP);
+#else
+       return (rot < IPU_ROTATE_90_RIGHT);
+#endif
+}
+
+int32_t ipu_init_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type,
+                               uint32_t pixel_fmt,
+                               uint16_t width, uint16_t height,
+                               uint32_t stride,
+                               ipu_rotate_mode_t rot_mode,
+                               dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
+                               dma_addr_t phyaddr_2,
+                               uint32_t u_offset, uint32_t v_offset);
+
+int32_t ipu_update_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type,
+                                 uint32_t bufNum, dma_addr_t phyaddr);
+
+int32_t ipu_update_channel_offset(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type,
+                               uint32_t pixel_fmt,
+                               uint16_t width, uint16_t height,
+                               uint32_t stride,
+                               uint32_t u, uint32_t v,
+                               uint32_t vertical_offset, uint32_t horizontal_offset);
+
+int32_t ipu_get_channel_offset(uint32_t pixel_fmt,
+                              uint16_t width, uint16_t height,
+                              uint32_t stride,
+                              uint32_t u, uint32_t v,
+                              uint32_t vertical_offset, uint32_t horizontal_offset,
+                              uint32_t *u_offset, uint32_t *v_offset);
+
+int32_t ipu_select_buffer(struct ipu_soc *ipu, ipu_channel_t channel,
+                         ipu_buffer_t type, uint32_t bufNum);
+int32_t ipu_select_multi_vdi_buffer(struct ipu_soc *ipu, uint32_t bufNum);
+
+int32_t ipu_link_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel_t dest_ch);
+int32_t ipu_unlink_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel_t dest_ch);
+
+int32_t ipu_is_channel_busy(struct ipu_soc *ipu, ipu_channel_t channel);
+int32_t ipu_check_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type,
+               uint32_t bufNum);
+void ipu_clear_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type,
+               uint32_t bufNum);
+uint32_t ipu_get_cur_buffer_idx(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type);
+int32_t ipu_enable_channel(struct ipu_soc *ipu, ipu_channel_t channel);
+int32_t ipu_disable_channel(struct ipu_soc *ipu, ipu_channel_t channel, bool wait_for_stop);
+int32_t ipu_swap_channel(struct ipu_soc *ipu, ipu_channel_t from_ch, ipu_channel_t to_ch);
+uint32_t ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel);
+
+int32_t ipu_enable_csi(struct ipu_soc *ipu, uint32_t csi);
+int32_t ipu_disable_csi(struct ipu_soc *ipu, uint32_t csi);
+
+int ipu_lowpwr_display_enable(void);
+int ipu_lowpwr_display_disable(void);
+
+int ipu_enable_irq(struct ipu_soc *ipu, uint32_t irq);
+void ipu_disable_irq(struct ipu_soc *ipu, uint32_t irq);
+void ipu_clear_irq(struct ipu_soc *ipu, uint32_t irq);
+int ipu_request_irq(struct ipu_soc *ipu, uint32_t irq,
+                   irqreturn_t(*handler) (int, void *),
+                   uint32_t irq_flags, const char *devname, void *dev_id);
+void ipu_free_irq(struct ipu_soc *ipu, uint32_t irq, void *dev_id);
+bool ipu_get_irq_status(struct ipu_soc *ipu, uint32_t irq);
+void ipu_set_csc_coefficients(struct ipu_soc *ipu, ipu_channel_t channel, int32_t param[][3]);
+int32_t ipu_set_channel_bandmode(struct ipu_soc *ipu, ipu_channel_t channel,
+                                ipu_buffer_t type, uint32_t band_height);
+
+/* two stripe calculations */
+struct stripe_param{
+       unsigned int input_width; /* width of the input stripe */
+       unsigned int output_width; /* width of the output stripe */
+       unsigned int input_column; /* the first column on the input stripe */
+       unsigned int output_column; /* the first column on the output stripe */
+       unsigned int idr;
+       /* inverse downisizing ratio parameter; expressed as a power of 2 */
+       unsigned int irr;
+       /* inverse resizing ratio parameter; expressed as a multiple of 2^-13 */
+};
+int ipu_calc_stripes_sizes(const unsigned int input_frame_width,
+                               unsigned int output_frame_width,
+                               const unsigned int maximal_stripe_width,
+                               const unsigned long long cirr,
+                               const unsigned int equal_stripes,
+                               u32 input_pixelformat,
+                               u32 output_pixelformat,
+                               struct stripe_param *left,
+                               struct stripe_param *right);
+
+/* SDC API */
+int32_t ipu_init_sync_panel(struct ipu_soc *ipu, int disp,
+                           uint32_t pixel_clk,
+                           uint16_t width, uint16_t height,
+                           uint32_t pixel_fmt,
+                           uint16_t h_start_width, uint16_t h_sync_width,
+                           uint16_t h_end_width, uint16_t v_start_width,
+                           uint16_t v_sync_width, uint16_t v_end_width,
+                           uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig);
+
+void ipu_uninit_sync_panel(struct ipu_soc *ipu, int disp);
+
+int32_t ipu_disp_set_window_pos(struct ipu_soc *ipu, ipu_channel_t channel, int16_t x_pos,
+                               int16_t y_pos);
+int32_t ipu_disp_get_window_pos(struct ipu_soc *ipu, ipu_channel_t channel, int16_t *x_pos,
+                               int16_t *y_pos);
+int32_t ipu_disp_set_global_alpha(struct ipu_soc *ipu, ipu_channel_t channel, bool enable,
+                                 uint8_t alpha);
+int32_t ipu_disp_set_color_key(struct ipu_soc *ipu, ipu_channel_t channel, bool enable,
+                              uint32_t colorKey);
+int32_t ipu_disp_set_gamma_correction(struct ipu_soc *ipu, ipu_channel_t channel, bool enable,
+                               int constk[], int slopek[]);
+
+int ipu_init_async_panel(struct ipu_soc *ipu, int disp, int type, uint32_t cycle_time,
+                        uint32_t pixel_fmt, ipu_adc_sig_cfg_t sig);
+void ipu_reset_disp_panel(struct ipu_soc *ipu);
+
+/* CMOS Sensor Interface API */
+int32_t ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height,
+                              uint32_t pixel_fmt, ipu_csi_signal_cfg_t sig);
+
+int32_t ipu_csi_get_sensor_protocol(struct ipu_soc *ipu, uint32_t csi);
+
+int32_t ipu_csi_enable_mclk(struct ipu_soc *ipu, int src, bool flag, bool wait);
+
+static inline int32_t ipu_csi_enable_mclk_if(struct ipu_soc *ipu, int src, uint32_t csi,
+               bool flag, bool wait)
+{
+       return ipu_csi_enable_mclk(ipu, csi, flag, wait);
+}
+
+int ipu_csi_read_mclk_flag(void);
+
+void ipu_csi_flash_strobe(bool flag);
+
+void ipu_csi_get_window_size(struct ipu_soc *ipu, uint32_t *width, uint32_t *height, uint32_t csi);
+
+void ipu_csi_set_window_size(struct ipu_soc *ipu, uint32_t width, uint32_t height, uint32_t csi);
+
+void ipu_csi_set_window_pos(struct ipu_soc *ipu, uint32_t left, uint32_t top, uint32_t csi);
+
+uint32_t bytes_per_pixel(uint32_t fmt);
+
+bool ipu_ch_param_bad_alpha_pos(uint32_t fmt);
+int ipu_ch_param_get_axi_id(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type);
+ipu_color_space_t format_to_colorspace(uint32_t fmt);
+bool ipu_pixel_format_is_gpu_tile(uint32_t fmt);
+bool ipu_pixel_format_is_split_gpu_tile(uint32_t fmt);
+bool ipu_pixel_format_is_pre_yuv(uint32_t fmt);
+bool ipu_pixel_format_is_multiplanar_yuv(uint32_t fmt);
+
+struct ipuv3_fb_platform_data {
+       char                            disp_dev[32];
+       u32                             interface_pix_fmt;
+       char                            *mode_str;
+       int                             default_bpp;
+       bool                            int_clk;
+
+       /* reserved mem */
+       resource_size_t                 res_base[2];
+       resource_size_t                 res_size[2];
+
+       /*
+        * Late init to avoid display channel being
+        * re-initialized as we've probably setup the
+        * channel in bootloader.
+        */
+       bool                            late_init;
+
+       /* Enable prefetch engine or not? */
+       bool                            prefetch;
+
+       /* Enable the PRE resolve engine or not? */
+       bool                            resolve;
+};
+
+#endif /* __LINUX_IPU_V3_H_ */
diff --git a/include/linux/ipu.h b/include/linux/ipu.h
new file mode 100644 (file)
index 0000000..1090ac6
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2005-2015 Freescale Semiconductor, Inc.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License.  You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+/*!
+ * @defgroup IPU MXC Image Processing Unit (IPU) Driver
+ */
+/*!
+ * @file linux/ipu.h
+ *
+ * @brief This file contains the IPU driver API declarations.
+ *
+ * @ingroup IPU
+ */
+
+#ifndef __LINUX_IPU_H__
+#define __LINUX_IPU_H__
+
+#include <linux/interrupt.h>
+#include <uapi/linux/ipu.h>
+
+unsigned int fmt_to_bpp(unsigned int pixelformat);
+cs_t colorspaceofpixel(int fmt);
+int need_csc(int ifmt, int ofmt);
+
+int ipu_queue_task(struct ipu_task *task);
+int ipu_check_task(struct ipu_task *task);
+
+#endif
diff --git a/include/linux/mfd/mxc-hdmi-core.h b/include/linux/mfd/mxc-hdmi-core.h
new file mode 100644 (file)
index 0000000..b2696b9
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011-2015 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
+ */
+#ifndef __LINUX_MXC_HDMI_CORE_H_
+#define __LINUX_MXC_HDMI_CORE_H_
+
+#include <video/mxc_edid.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define IRQ_DISABLE_SUCCEED    0
+#define IRQ_DISABLE_FAIL       1
+
+bool hdmi_check_overflow(void);
+
+u8 hdmi_readb(unsigned int reg);
+void hdmi_writeb(u8 value, unsigned int reg);
+void hdmi_mask_writeb(u8 data, unsigned int addr, u8 shift, u8 mask);
+unsigned int hdmi_read4(unsigned int reg);
+void hdmi_write4(unsigned int value, unsigned int reg);
+
+void hdmi_irq_init(void);
+void hdmi_irq_enable(int irq);
+unsigned int hdmi_irq_disable(int irq);
+
+void hdmi_set_sample_rate(unsigned int rate);
+void hdmi_set_dma_mode(unsigned int dma_running);
+void hdmi_init_clk_regenerator(void);
+void hdmi_clk_regenerator_update_pixel_clock(u32 pixclock);
+
+void hdmi_set_edid_cfg(struct mxc_edid_cfg *cfg);
+void hdmi_get_edid_cfg(struct mxc_edid_cfg *cfg);
+
+extern int mxc_hdmi_ipu_id;
+extern int mxc_hdmi_disp_id;
+
+void hdmi_set_registered(int registered);
+int hdmi_get_registered(void);
+int mxc_hdmi_abort_stream(void);
+int mxc_hdmi_register_audio(struct snd_pcm_substream *substream);
+void mxc_hdmi_unregister_audio(struct snd_pcm_substream *substream);
+unsigned int hdmi_set_cable_state(unsigned int state);
+unsigned int hdmi_set_blank_state(unsigned int state);
+int check_hdmi_state(void);
+
+#endif
index 15d222b..3d0ff5c 100644 (file)
 
 #define IMX6Q_GPR5_L2_CLK_STOP                 BIT(8)
 #define IMX6Q_GPR5_ENET_TX_CLK_SEL             BIT(9)
+#define IMX6Q_GPR5_PRE_PRG_SEL0_MASK           (0x3 << 12)
+#define IMX6Q_GPR5_PRE_PRG_SEL0_SHIFT          12
+#define IMX6Q_GPR5_PRE_PRG_SEL0_MSB            13
+#define IMX6Q_GPR5_PRE_PRG_SEL0_LSB            12
+#define IMX6Q_GPR5_PRE_PRG_SEL0_PRE1_PRG0_CHAN1        (0x0 << 12)
+#define IMX6Q_GPR5_PRE_PRG_SEL0_PRE1_PRG0_CHAN2        (0x1 << 12)
+#define IMX6Q_GPR5_PRE_PRG_SEL0_PRE1_PRG1_CHAN1        (0x2 << 12)
+#define IMX6Q_GPR5_PRE_PRG_SEL0_PRE1_PRG1_CHAN2        (0x3 << 12)
+#define IMX6Q_GPR5_PRE_PRG_SEL1_MASK           (0x3 << 14)
+#define IMX6Q_GPR5_PRE_PRG_SEL1_SHIFT          14
+#define IMX6Q_GPR5_PRE_PRG_SEL1_MSB            15
+#define IMX6Q_GPR5_PRE_PRG_SEL1_LSB            14
+#define IMX6Q_GPR5_PRE_PRG_SEL1_PRE2_PRG0_CHAN1        (0x0 << 14)
+#define IMX6Q_GPR5_PRE_PRG_SEL1_PRE2_PRG0_CHAN2        (0x1 << 14)
+#define IMX6Q_GPR5_PRE_PRG_SEL1_PRE2_PRG1_CHAN1        (0x2 << 14)
+#define IMX6Q_GPR5_PRE_PRG_SEL1_PRE2_PRG1_CHAN2        (0x3 << 14)
 
 #define IMX6Q_GPR6_IPU1_ID00_WR_QOS_MASK       (0xf << 0)
 #define IMX6Q_GPR6_IPU1_ID01_WR_QOS_MASK       (0xf << 4)
 #define IMX6SX_GPR12_PCIE_RX_EQ_MASK                   (0x7 << 0)
 #define IMX6SX_GPR12_PCIE_RX_EQ_2                      (0x2 << 0)
 
+/* For imx6dl iomux gpr register field definitions */
+#define IMX6DL_GPR3_LVDS1_MUX_CTL_MASK         (0x3 << 8)
+#define IMX6DL_GPR3_LVDS1_MUX_CTL_IPU1_DI0     (0x0 << 8)
+#define IMX6DL_GPR3_LVDS1_MUX_CTL_IPU1_DI1     (0x1 << 8)
+#define IMX6DL_GPR3_LVDS1_MUX_CTL_LCDIF                (0x2 << 8)
+#define IMX6DL_GPR3_LVDS0_MUX_CTL_MASK         (0x3 << 6)
+#define IMX6DL_GPR3_LVDS0_MUX_CTL_IPU1_DI0     (0x0 << 6)
+#define IMX6DL_GPR3_LVDS0_MUX_CTL_IPU1_DI1     (0x1 << 6)
+#define IMX6DL_GPR3_LVDS0_MUX_CTL_LCDIF                (0x2 << 6)
+
 /* For imx6ul iomux gpr register field define */
 #define IMX6UL_GPR1_ENET1_CLK_DIR              (0x1 << 17)
 #define IMX6UL_GPR1_ENET2_CLK_DIR              (0x1 << 18)
diff --git a/include/uapi/linux/ipu.h b/include/uapi/linux/ipu.h
new file mode 100644 (file)
index 0000000..c92f292
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2013-2015 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 IPU MXC Image Processing Unit (IPU) Driver
+ */
+/*!
+ * @file uapi/linux/ipu.h
+ *
+ * @brief This file contains the IPU driver API declarations.
+ *
+ * @ingroup IPU
+ */
+
+#ifndef __ASM_ARCH_IPU_H__
+#define __ASM_ARCH_IPU_H__
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#ifndef __KERNEL__
+#ifndef __cplusplus
+typedef unsigned char bool;
+#endif
+#define irqreturn_t int
+#define dma_addr_t int
+#define uint32_t unsigned int
+#define uint16_t unsigned short
+#define uint8_t unsigned char
+#define u32 unsigned int
+#define u8 unsigned char
+#define __u32 u32
+#endif
+
+/*!
+ * Enumeration of IPU rotation modes
+ */
+typedef enum {
+       /* Note the enum values correspond to BAM value */
+       IPU_ROTATE_NONE = 0,
+       IPU_ROTATE_VERT_FLIP = 1,
+       IPU_ROTATE_HORIZ_FLIP = 2,
+       IPU_ROTATE_180 = 3,
+       IPU_ROTATE_90_RIGHT = 4,
+       IPU_ROTATE_90_RIGHT_VFLIP = 5,
+       IPU_ROTATE_90_RIGHT_HFLIP = 6,
+       IPU_ROTATE_90_LEFT = 7,
+} ipu_rotate_mode_t;
+
+/*!
+ * Enumeration of VDI MOTION select
+ */
+typedef enum {
+       MED_MOTION = 0,
+       LOW_MOTION = 1,
+       HIGH_MOTION = 2,
+} ipu_motion_sel;
+
+/*!
+ * Enumeration of DI ports for ADC.
+ */
+typedef enum {
+       DISP0,
+       DISP1,
+       DISP2,
+       DISP3
+} display_port_t;
+
+/*  IPU Pixel format definitions */
+/*  Four-character-code (FOURCC) */
+#define fourcc(a, b, c, d)\
+        (((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24))
+
+/*!
+ * @name IPU Pixel Formats
+ *
+ * Pixel formats are defined with ASCII FOURCC code. The pixel format codes are
+ * the same used by V4L2 API.
+ */
+
+/*! @{ */
+/*! @name GPU Tile Formats */
+/*! @{ */
+#define IPU_PIX_FMT_GPU32_SB_ST  fourcc('5', 'P', '4', 'S') /*!< 32bit split buf 4x4 standard */
+#define IPU_PIX_FMT_GPU32_SB_SRT fourcc('5', 'P', '4', 'R') /*!< 32bit split buf 4x4 super */
+#define IPU_PIX_FMT_GPU32_ST     fourcc('5', 'I', '4', 'S') /*!< 32bit single buf 4x4 standard */
+#define IPU_PIX_FMT_GPU32_SRT    fourcc('5', 'I', '4', 'R') /*!< 32bit single buf 4x4 super */
+#define IPU_PIX_FMT_GPU16_SB_ST  fourcc('4', 'P', '8', 'S') /*!< 16bit split buf 8x4 standard */
+#define IPU_PIX_FMT_GPU16_SB_SRT fourcc('4', 'P', '8', 'R') /*!< 16bit split buf 8x4 super */
+#define IPU_PIX_FMT_GPU16_ST     fourcc('4', 'I', '8', 'S') /*!< 16bit single buf 8x4 standard */
+#define IPU_PIX_FMT_GPU16_SRT    fourcc('4', 'I', '8', 'R') /*!< 16bit single buf 8x4 super */
+
+/*! @{ */
+/*! @name Generic or Raw Data Formats */
+/*! @{ */
+#define IPU_PIX_FMT_GENERIC fourcc('I', 'P', 'U', '0') /*!< IPU Generic Data */
+#define IPU_PIX_FMT_GENERIC_32 fourcc('I', 'P', 'U', '1')      /*!< IPU Generic Data */
+#define IPU_PIX_FMT_GENERIC_16 fourcc('I', 'P', 'U', '2')      /*!< IPU Generic Data */
+#define IPU_PIX_FMT_LVDS666 fourcc('L', 'V', 'D', '6') /*!< IPU Generic Data */
+#define IPU_PIX_FMT_LVDS888 fourcc('L', 'V', 'D', '8') /*!< IPU Generic Data */
+/*! @} */
+/*! @name RGB Formats */
+/*! @{ */
+#define IPU_PIX_FMT_RGB332  fourcc('R', 'G', 'B', '1') /*!<  8  RGB-3-3-2    */
+#define IPU_PIX_FMT_RGB555  fourcc('R', 'G', 'B', 'O') /*!< 16  RGB-5-5-5    */
+#define IPU_PIX_FMT_RGB565  fourcc('R', 'G', 'B', 'P') /*!< 1 6  RGB-5-6-5   */
+#define IPU_PIX_FMT_BGRA4444 fourcc('4', '4', '4', '4')        /*!< 16  RGBA-4-4-4-4 */
+#define IPU_PIX_FMT_BGRA5551 fourcc('5', '5', '5', '1')        /*!< 16  RGBA-5-5-5-1 */
+#define IPU_PIX_FMT_RGB666  fourcc('R', 'G', 'B', '6') /*!< 18  RGB-6-6-6    */
+#define IPU_PIX_FMT_BGR666  fourcc('B', 'G', 'R', '6') /*!< 18  BGR-6-6-6    */
+#define IPU_PIX_FMT_BGR24   fourcc('B', 'G', 'R', '3') /*!< 24  BGR-8-8-8    */
+#define IPU_PIX_FMT_RGB24   fourcc('R', 'G', 'B', '3') /*!< 24  RGB-8-8-8    */
+#define IPU_PIX_FMT_GBR24   fourcc('G', 'B', 'R', '3') /*!< 24  GBR-8-8-8    */
+#define IPU_PIX_FMT_BGR32   fourcc('B', 'G', 'R', '4') /*!< 32  BGR-8-8-8-8  */
+#define IPU_PIX_FMT_BGRA32  fourcc('B', 'G', 'R', 'A') /*!< 32  BGR-8-8-8-8  */
+#define IPU_PIX_FMT_RGB32   fourcc('R', 'G', 'B', '4') /*!< 32  RGB-8-8-8-8  */
+#define IPU_PIX_FMT_RGBA32  fourcc('R', 'G', 'B', 'A') /*!< 32  RGB-8-8-8-8  */
+#define IPU_PIX_FMT_ABGR32  fourcc('A', 'B', 'G', 'R') /*!< 32  ABGR-8-8-8-8 */
+/*! @} */
+/*! @name YUV Interleaved Formats */
+/*! @{ */
+#define IPU_PIX_FMT_YUYV    fourcc('Y', 'U', 'Y', 'V') /*!< 16 YUV 4:2:2 */
+#define IPU_PIX_FMT_UYVY    fourcc('U', 'Y', 'V', 'Y') /*!< 16 YUV 4:2:2 */
+#define IPU_PIX_FMT_YVYU    fourcc('Y', 'V', 'Y', 'U')  /*!< 16 YVYU 4:2:2 */
+#define IPU_PIX_FMT_VYUY    fourcc('V', 'Y', 'U', 'Y')  /*!< 16 VYYU 4:2:2 */
+#define IPU_PIX_FMT_Y41P    fourcc('Y', '4', '1', 'P') /*!< 12 YUV 4:1:1 */
+#define IPU_PIX_FMT_YUV444  fourcc('Y', '4', '4', '4') /*!< 24 YUV 4:4:4 */
+#define IPU_PIX_FMT_VYU444  fourcc('V', '4', '4', '4') /*!< 24 VYU 4:4:4 */
+#define IPU_PIX_FMT_AYUV    fourcc('A', 'Y', 'U', 'V') /*!< 32 AYUV 4:4:4:4 */
+/* two planes -- one Y, one Cb + Cr interleaved  */
+#define IPU_PIX_FMT_NV12    fourcc('N', 'V', '1', '2') /* 12  Y/CbCr 4:2:0  */
+#define PRE_PIX_FMT_NV21    fourcc('N', 'V', '2', '1') /* 12  Y/CbCr 4:2:0  */
+#define IPU_PIX_FMT_NV16    fourcc('N', 'V', '1', '6') /* 16  Y/CbCr 4:2:2  */
+#define PRE_PIX_FMT_NV61    fourcc('N', 'V', '6', '1') /* 16  Y/CbCr 4:2:2  */
+/* two planes -- 12  tiled Y/CbCr 4:2:0  */
+#define IPU_PIX_FMT_TILED_NV12    fourcc('T', 'N', 'V', 'P')
+#define IPU_PIX_FMT_TILED_NV12F   fourcc('T', 'N', 'V', 'F')
+
+/*! @} */
+/*! @name YUV Planar Formats */
+/*! @{ */
+#define IPU_PIX_FMT_GREY    fourcc('G', 'R', 'E', 'Y') /*!< 8  Greyscale */
+#define IPU_PIX_FMT_YVU410P fourcc('Y', 'V', 'U', '9') /*!< 9  YVU 4:1:0 */
+#define IPU_PIX_FMT_YUV410P fourcc('Y', 'U', 'V', '9') /*!< 9  YUV 4:1:0 */
+#define IPU_PIX_FMT_YVU420P fourcc('Y', 'V', '1', '2') /*!< 12 YVU 4:2:0 */
+#define IPU_PIX_FMT_YUV420P fourcc('I', '4', '2', '0') /*!< 12 YUV 4:2:0 */
+#define IPU_PIX_FMT_YUV420P2 fourcc('Y', 'U', '1', '2')        /*!< 12 YUV 4:2:0 */
+#define IPU_PIX_FMT_YVU422P fourcc('Y', 'V', '1', '6') /*!< 16 YVU 4:2:2 */
+#define IPU_PIX_FMT_YUV422P fourcc('4', '2', '2', 'P') /*!< 16 YUV 4:2:2 */
+/* non-interleaved 4:4:4 */
+#define IPU_PIX_FMT_YUV444P fourcc('4', '4', '4', 'P') /*!< 24 YUV 4:4:4 */
+/*! @} */
+#define IPU_PIX_FMT_TILED_NV12_MBALIGN (16)
+#define TILED_NV12_FRAME_SIZE(w, h)    \
+               (ALIGN((w) * (h), SZ_4K) + ALIGN((w) * (h) / 2, SZ_4K))
+/* IPU device */
+typedef enum {
+       RGB_CS,
+       YUV_CS,
+       NULL_CS
+} cs_t;
+
+struct ipu_pos {
+       u32 x;
+       u32 y;
+};
+
+struct ipu_crop {
+       struct ipu_pos pos;
+       u32 w;
+       u32 h;
+};
+
+struct ipu_deinterlace {
+       bool    enable;
+       u8      motion; /*see ipu_motion_sel*/
+#define IPU_DEINTERLACE_FIELD_TOP      0
+#define IPU_DEINTERLACE_FIELD_BOTTOM   1
+#define IPU_DEINTERLACE_FIELD_MASK     \
+               (IPU_DEINTERLACE_FIELD_TOP | IPU_DEINTERLACE_FIELD_BOTTOM)
+       /* deinterlace frame rate double flags */
+#define IPU_DEINTERLACE_RATE_EN                0x80
+#define IPU_DEINTERLACE_RATE_FRAME1    0x40
+#define IPU_DEINTERLACE_RATE_MASK      \
+               (IPU_DEINTERLACE_RATE_EN | IPU_DEINTERLACE_RATE_FRAME1)
+#define IPU_DEINTERLACE_MAX_FRAME      2
+       u8      field_fmt;
+};
+
+struct ipu_input {
+       u32 width;
+       u32 height;
+       u32 format;
+       struct ipu_crop crop;
+       dma_addr_t paddr;
+
+       struct ipu_deinterlace deinterlace;
+       dma_addr_t paddr_n; /*valid when deinterlace enable*/
+};
+
+struct ipu_alpha {
+#define IPU_ALPHA_MODE_GLOBAL  0
+#define IPU_ALPHA_MODE_LOCAL   1
+       u8 mode;
+       u8 gvalue; /* 0~255 */
+       dma_addr_t loc_alp_paddr;
+};
+
+struct ipu_colorkey {
+       bool enable;
+       u32 value; /* RGB 24bit */
+};
+
+struct ipu_overlay {
+       u32     width;
+       u32     height;
+       u32     format;
+       struct ipu_crop crop;
+       struct ipu_alpha alpha;
+       struct ipu_colorkey colorkey;
+       dma_addr_t paddr;
+};
+
+struct ipu_output {
+       u32     width;
+       u32     height;
+       u32     format;
+       u8      rotate;
+       struct ipu_crop crop;
+       dma_addr_t paddr;
+};
+
+struct ipu_task {
+       struct ipu_input input;
+       struct ipu_output output;
+
+       bool overlay_en;
+       struct ipu_overlay overlay;
+
+#define IPU_TASK_PRIORITY_NORMAL 0
+#define IPU_TASK_PRIORITY_HIGH 1
+       u8      priority;
+
+#define        IPU_TASK_ID_ANY 0
+#define        IPU_TASK_ID_VF  1
+#define        IPU_TASK_ID_PP  2
+#define        IPU_TASK_ID_MAX 3
+       u8      task_id;
+
+       int     timeout;
+};
+
+enum {
+       IPU_CHECK_OK = 0,
+       IPU_CHECK_WARN_INPUT_OFFS_NOT8ALIGN = 0x1,
+       IPU_CHECK_WARN_OUTPUT_OFFS_NOT8ALIGN = 0x2,
+       IPU_CHECK_WARN_OVERLAY_OFFS_NOT8ALIGN = 0x4,
+       IPU_CHECK_ERR_MIN,
+       IPU_CHECK_ERR_INPUT_CROP,
+       IPU_CHECK_ERR_OUTPUT_CROP,
+       IPU_CHECK_ERR_OVERLAY_CROP,
+       IPU_CHECK_ERR_INPUT_OVER_LIMIT,
+       IPU_CHECK_ERR_OV_OUT_NO_FIT,
+       IPU_CHECK_ERR_OVERLAY_WITH_VDI,
+       IPU_CHECK_ERR_PROC_NO_NEED,
+       IPU_CHECK_ERR_SPLIT_INPUTW_OVER,
+       IPU_CHECK_ERR_SPLIT_INPUTH_OVER,
+       IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER,
+       IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER,
+       IPU_CHECK_ERR_SPLIT_WITH_ROT,
+       IPU_CHECK_ERR_NOT_SUPPORT,
+       IPU_CHECK_ERR_NOT16ALIGN,
+       IPU_CHECK_ERR_W_DOWNSIZE_OVER,
+       IPU_CHECK_ERR_H_DOWNSIZE_OVER,
+};
+
+/* IOCTL commands */
+#define IPU_CHECK_TASK         _IOWR('I', 0x1, struct ipu_task)
+#define IPU_QUEUE_TASK         _IOW('I', 0x2, struct ipu_task)
+#define IPU_ALLOC              _IOWR('I', 0x3, int)
+#define IPU_FREE               _IOW('I', 0x4, int)
+
+#endif
diff --git a/include/video/mxc_edid.h b/include/video/mxc_edid.h
new file mode 100644 (file)
index 0000000..3a9e69f
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2009-2015 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 mxc_edid.h
+ *
+ * @brief MXC EDID tools
+ *
+ * @ingroup Framebuffer
+ */
+
+#ifndef MXC_EDID_H
+#define MXC_EDID_H
+
+#include <linux/fb.h>
+
+#define FB_VMODE_ASPECT_4_3    0x10
+#define FB_VMODE_ASPECT_16_9   0x20
+#define FB_VMODE_ASPECT_MASK   (FB_VMODE_ASPECT_4_3 | FB_VMODE_ASPECT_16_9)
+
+enum cea_audio_coding_types {
+       AUDIO_CODING_TYPE_REF_STREAM_HEADER     =  0,
+       AUDIO_CODING_TYPE_LPCM                  =  1,
+       AUDIO_CODING_TYPE_AC3                   =  2,
+       AUDIO_CODING_TYPE_MPEG1                 =  3,
+       AUDIO_CODING_TYPE_MP3                   =  4,
+       AUDIO_CODING_TYPE_MPEG2                 =  5,
+       AUDIO_CODING_TYPE_AACLC                 =  6,
+       AUDIO_CODING_TYPE_DTS                   =  7,
+       AUDIO_CODING_TYPE_ATRAC                 =  8,
+       AUDIO_CODING_TYPE_SACD                  =  9,
+       AUDIO_CODING_TYPE_EAC3                  = 10,
+       AUDIO_CODING_TYPE_DTS_HD                = 11,
+       AUDIO_CODING_TYPE_MLP                   = 12,
+       AUDIO_CODING_TYPE_DST                   = 13,
+       AUDIO_CODING_TYPE_WMAPRO                = 14,
+       AUDIO_CODING_TYPE_RESERVED              = 15,
+};
+
+struct mxc_hdmi_3d_format {
+       unsigned char vic_order_2d;
+       unsigned char struct_3d;
+       unsigned char detail_3d;
+       unsigned char reserved;
+};
+
+struct mxc_edid_cfg {
+       bool cea_underscan;
+       bool cea_basicaudio;
+       bool cea_ycbcr444;
+       bool cea_ycbcr422;
+       bool hdmi_cap;
+
+       /*VSD*/
+       bool vsd_support_ai;
+       bool vsd_dc_48bit;
+       bool vsd_dc_36bit;
+       bool vsd_dc_30bit;
+       bool vsd_dc_y444;
+       bool vsd_dvi_dual;
+
+       bool vsd_cnc0;
+       bool vsd_cnc1;
+       bool vsd_cnc2;
+       bool vsd_cnc3;
+
+       u8 vsd_video_latency;
+       u8 vsd_audio_latency;
+       u8 vsd_I_video_latency;
+       u8 vsd_I_audio_latency;
+
+       u8 physical_address[4];
+       u8 hdmi_vic[64];
+       struct mxc_hdmi_3d_format hdmi_3d_format[64];
+       u16 hdmi_3d_mask_all;
+       u16 hdmi_3d_struct_all;
+       u32 vsd_max_tmdsclk_rate;
+
+       u8 max_channels;
+       u8 sample_sizes;
+       u8 sample_rates;
+       u8 speaker_alloc;
+};
+
+int mxc_edid_var_to_vic(struct fb_var_screeninfo *var);
+int mxc_edid_mode_to_vic(const struct fb_videomode *mode);
+int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr,
+       unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi);
+int mxc_edid_parse_ext_blk(unsigned char *edid, struct mxc_edid_cfg *cfg,
+       struct fb_monspecs *specs);
+#endif
diff --git a/include/video/mxc_hdmi.h b/include/video/mxc_hdmi.h
new file mode 100644 (file)
index 0000000..73c3899
--- /dev/null
@@ -0,0 +1,1012 @@
+/*
+ * Copyright (C) 2011-2015 Freescale Semiconductor, Inc.
+ */
+
+/*
+ * 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
+ */
+
+#ifndef __MXC_HDMI_H__
+#define __MXC_HDMI_H__
+
+/*
+ * Hdmi controller registers
+ */
+
+/* Identification Registers */
+#define HDMI_DESIGN_ID                          0x0000
+#define HDMI_REVISION_ID                        0x0001
+#define HDMI_PRODUCT_ID0                        0x0002
+#define HDMI_PRODUCT_ID1                        0x0003
+#define HDMI_CONFIG0_ID                         0x0004
+#define HDMI_CONFIG1_ID                         0x0005
+#define HDMI_CONFIG2_ID                         0x0006
+#define HDMI_CONFIG3_ID                         0x0007
+
+/* Interrupt Registers */
+#define HDMI_IH_FC_STAT0                        0x0100
+#define HDMI_IH_FC_STAT1                        0x0101
+#define HDMI_IH_FC_STAT2                        0x0102
+#define HDMI_IH_AS_STAT0                        0x0103
+#define HDMI_IH_PHY_STAT0                       0x0104
+#define HDMI_IH_I2CM_STAT0                      0x0105
+#define HDMI_IH_CEC_STAT0                       0x0106
+#define HDMI_IH_VP_STAT0                        0x0107
+#define HDMI_IH_I2CMPHY_STAT0                   0x0108
+#define HDMI_IH_AHBDMAAUD_STAT0                 0x0109
+
+#define HDMI_IH_MUTE_FC_STAT0                   0x0180
+#define HDMI_IH_MUTE_FC_STAT1                   0x0181
+#define HDMI_IH_MUTE_FC_STAT2                   0x0182
+#define HDMI_IH_MUTE_AS_STAT0                   0x0183
+#define HDMI_IH_MUTE_PHY_STAT0                  0x0184
+#define HDMI_IH_MUTE_I2CM_STAT0                 0x0185
+#define HDMI_IH_MUTE_CEC_STAT0                  0x0186
+#define HDMI_IH_MUTE_VP_STAT0                   0x0187
+#define HDMI_IH_MUTE_I2CMPHY_STAT0              0x0188
+#define HDMI_IH_MUTE_AHBDMAAUD_STAT0            0x0189
+#define HDMI_IH_MUTE                            0x01FF
+
+/* Video Sample Registers */
+#define HDMI_TX_INVID0                          0x0200
+#define HDMI_TX_INSTUFFING                      0x0201
+#define HDMI_TX_GYDATA0                         0x0202
+#define HDMI_TX_GYDATA1                         0x0203
+#define HDMI_TX_RCRDATA0                        0x0204
+#define HDMI_TX_RCRDATA1                        0x0205
+#define HDMI_TX_BCBDATA0                        0x0206
+#define HDMI_TX_BCBDATA1                        0x0207
+
+/* Video Packetizer Registers */
+#define HDMI_VP_STATUS                          0x0800
+#define HDMI_VP_PR_CD                           0x0801
+#define HDMI_VP_STUFF                           0x0802
+#define HDMI_VP_REMAP                           0x0803
+#define HDMI_VP_CONF                            0x0804
+#define HDMI_VP_STAT                            0x0805
+#define HDMI_VP_INT                             0x0806
+#define HDMI_VP_MASK                            0x0807
+#define HDMI_VP_POL                             0x0808
+
+/* Frame Composer Registers */
+#define HDMI_FC_INVIDCONF                       0x1000
+#define HDMI_FC_INHACTV0                        0x1001
+#define HDMI_FC_INHACTV1                        0x1002
+#define HDMI_FC_INHBLANK0                       0x1003
+#define HDMI_FC_INHBLANK1                       0x1004
+#define HDMI_FC_INVACTV0                        0x1005
+#define HDMI_FC_INVACTV1                        0x1006
+#define HDMI_FC_INVBLANK                        0x1007
+#define HDMI_FC_HSYNCINDELAY0                   0x1008
+#define HDMI_FC_HSYNCINDELAY1                   0x1009
+#define HDMI_FC_HSYNCINWIDTH0                   0x100A
+#define HDMI_FC_HSYNCINWIDTH1                   0x100B
+#define HDMI_FC_VSYNCINDELAY                    0x100C
+#define HDMI_FC_VSYNCINWIDTH                    0x100D
+#define HDMI_FC_INFREQ0                         0x100E
+#define HDMI_FC_INFREQ1                         0x100F
+#define HDMI_FC_INFREQ2                         0x1010
+#define HDMI_FC_CTRLDUR                         0x1011
+#define HDMI_FC_EXCTRLDUR                       0x1012
+#define HDMI_FC_EXCTRLSPAC                      0x1013
+#define HDMI_FC_CH0PREAM                        0x1014
+#define HDMI_FC_CH1PREAM                        0x1015
+#define HDMI_FC_CH2PREAM                        0x1016
+#define HDMI_FC_AVICONF3                        0x1017
+#define HDMI_FC_GCP                             0x1018
+#define HDMI_FC_AVICONF0                        0x1019
+#define HDMI_FC_AVICONF1                        0x101A
+#define HDMI_FC_AVICONF2                        0x101B
+#define HDMI_FC_AVIVID                          0x101C
+#define HDMI_FC_AVIETB0                         0x101D
+#define HDMI_FC_AVIETB1                         0x101E
+#define HDMI_FC_AVISBB0                         0x101F
+#define HDMI_FC_AVISBB1                         0x1020
+#define HDMI_FC_AVIELB0                         0x1021
+#define HDMI_FC_AVIELB1                         0x1022
+#define HDMI_FC_AVISRB0                         0x1023
+#define HDMI_FC_AVISRB1                         0x1024
+#define HDMI_FC_AUDICONF0                       0x1025
+#define HDMI_FC_AUDICONF1                       0x1026
+#define HDMI_FC_AUDICONF2                       0x1027
+#define HDMI_FC_AUDICONF3                       0x1028
+#define HDMI_FC_VSDIEEEID0                      0x1029
+#define HDMI_FC_VSDSIZE                         0x102A
+#define HDMI_FC_VSDIEEEID1                      0x1030
+#define HDMI_FC_VSDIEEEID2                      0x1031
+#define HDMI_FC_VSDPAYLOAD0                     0x1032
+#define HDMI_FC_VSDPAYLOAD1                     0x1033
+#define HDMI_FC_VSDPAYLOAD2                     0x1034
+#define HDMI_FC_VSDPAYLOAD3                     0x1035
+#define HDMI_FC_VSDPAYLOAD4                     0x1036
+#define HDMI_FC_VSDPAYLOAD5                     0x1037
+#define HDMI_FC_VSDPAYLOAD6                     0x1038
+#define HDMI_FC_VSDPAYLOAD7                     0x1039
+#define HDMI_FC_VSDPAYLOAD8                     0x103A
+#define HDMI_FC_VSDPAYLOAD9                     0x103B
+#define HDMI_FC_VSDPAYLOAD10                    0x103C
+#define HDMI_FC_VSDPAYLOAD11                    0x103D
+#define HDMI_FC_VSDPAYLOAD12                    0x103E
+#define HDMI_FC_VSDPAYLOAD13                    0x103F
+#define HDMI_FC_VSDPAYLOAD14                    0x1040
+#define HDMI_FC_VSDPAYLOAD15                    0x1041
+#define HDMI_FC_VSDPAYLOAD16                    0x1042
+#define HDMI_FC_VSDPAYLOAD17                    0x1043
+#define HDMI_FC_VSDPAYLOAD18                    0x1044
+#define HDMI_FC_VSDPAYLOAD19                    0x1045
+#define HDMI_FC_VSDPAYLOAD20                    0x1046
+#define HDMI_FC_VSDPAYLOAD21                    0x1047
+#define HDMI_FC_VSDPAYLOAD22                    0x1048
+#define HDMI_FC_VSDPAYLOAD23                    0x1049
+#define HDMI_FC_SPDVENDORNAME0                  0x104A
+#define HDMI_FC_SPDVENDORNAME1                  0x104B
+#define HDMI_FC_SPDVENDORNAME2                  0x104C
+#define HDMI_FC_SPDVENDORNAME3                  0x104D
+#define HDMI_FC_SPDVENDORNAME4                  0x104E
+#define HDMI_FC_SPDVENDORNAME5                  0x104F
+#define HDMI_FC_SPDVENDORNAME6                  0x1050
+#define HDMI_FC_SPDVENDORNAME7                  0x1051
+#define HDMI_FC_SDPPRODUCTNAME0                 0x1052
+#define HDMI_FC_SDPPRODUCTNAME1                 0x1053
+#define HDMI_FC_SDPPRODUCTNAME2                 0x1054
+#define HDMI_FC_SDPPRODUCTNAME3                 0x1055
+#define HDMI_FC_SDPPRODUCTNAME4                 0x1056
+#define HDMI_FC_SDPPRODUCTNAME5                 0x1057
+#define HDMI_FC_SDPPRODUCTNAME6                 0x1058
+#define HDMI_FC_SDPPRODUCTNAME7                 0x1059
+#define HDMI_FC_SDPPRODUCTNAME8                 0x105A
+#define HDMI_FC_SDPPRODUCTNAME9                 0x105B
+#define HDMI_FC_SDPPRODUCTNAME10                0x105C
+#define HDMI_FC_SDPPRODUCTNAME11                0x105D
+#define HDMI_FC_SDPPRODUCTNAME12                0x105E
+#define HDMI_FC_SDPPRODUCTNAME13                0x105F
+#define HDMI_FC_SDPPRODUCTNAME14                0x1060
+#define HDMI_FC_SPDPRODUCTNAME15                0x1061
+#define HDMI_FC_SPDDEVICEINF                    0x1062
+#define HDMI_FC_AUDSCONF                        0x1063
+#define HDMI_FC_AUDSSTAT                        0x1064
+#define HDMI_FC_DATACH0FILL                     0x1070
+#define HDMI_FC_DATACH1FILL                     0x1071
+#define HDMI_FC_DATACH2FILL                     0x1072
+#define HDMI_FC_CTRLQHIGH                       0x1073
+#define HDMI_FC_CTRLQLOW                        0x1074
+#define HDMI_FC_ACP0                            0x1075
+#define HDMI_FC_ACP28                           0x1076
+#define HDMI_FC_ACP27                           0x1077
+#define HDMI_FC_ACP26                           0x1078
+#define HDMI_FC_ACP25                           0x1079
+#define HDMI_FC_ACP24                           0x107A
+#define HDMI_FC_ACP23                           0x107B
+#define HDMI_FC_ACP22                           0x107C
+#define HDMI_FC_ACP21                           0x107D
+#define HDMI_FC_ACP20                           0x107E
+#define HDMI_FC_ACP19                           0x107F
+#define HDMI_FC_ACP18                           0x1080
+#define HDMI_FC_ACP17                           0x1081
+#define HDMI_FC_ACP16                           0x1082
+#define HDMI_FC_ACP15                           0x1083
+#define HDMI_FC_ACP14                           0x1084
+#define HDMI_FC_ACP13                           0x1085
+#define HDMI_FC_ACP12                           0x1086
+#define HDMI_FC_ACP11                           0x1087
+#define HDMI_FC_ACP10                           0x1088
+#define HDMI_FC_ACP9                            0x1089
+#define HDMI_FC_ACP8                            0x108A
+#define HDMI_FC_ACP7                            0x108B
+#define HDMI_FC_ACP6                            0x108C
+#define HDMI_FC_ACP5                            0x108D
+#define HDMI_FC_ACP4                            0x108E
+#define HDMI_FC_ACP3                            0x108F
+#define HDMI_FC_ACP2                            0x1090
+#define HDMI_FC_ACP1                            0x1091
+#define HDMI_FC_ISCR1_0                         0x1092
+#define HDMI_FC_ISCR1_16                        0x1093
+#define HDMI_FC_ISCR1_15                        0x1094
+#define HDMI_FC_ISCR1_14                        0x1095
+#define HDMI_FC_ISCR1_13                        0x1096
+#define HDMI_FC_ISCR1_12                        0x1097
+#define HDMI_FC_ISCR1_11                        0x1098
+#define HDMI_FC_ISCR1_10                        0x1099
+#define HDMI_FC_ISCR1_9                         0x109A
+#define HDMI_FC_ISCR1_8                         0x109B
+#define HDMI_FC_ISCR1_7                         0x109C
+#define HDMI_FC_ISCR1_6                         0x109D
+#define HDMI_FC_ISCR1_5                         0x109E
+#define HDMI_FC_ISCR1_4                         0x109F
+#define HDMI_FC_ISCR1_3                         0x10A0
+#define HDMI_FC_ISCR1_2                         0x10A1
+#define HDMI_FC_ISCR1_1                         0x10A2
+#define HDMI_FC_ISCR2_15                        0x10A3
+#define HDMI_FC_ISCR2_14                        0x10A4
+#define HDMI_FC_ISCR2_13                        0x10A5
+#define HDMI_FC_ISCR2_12                        0x10A6
+#define HDMI_FC_ISCR2_11                        0x10A7
+#define HDMI_FC_ISCR2_10                        0x10A8
+#define HDMI_FC_ISCR2_9                         0x10A9
+#define HDMI_FC_ISCR2_8                         0x10AA
+#define HDMI_FC_ISCR2_7                         0x10AB
+#define HDMI_FC_ISCR2_6                         0x10AC
+#define HDMI_FC_ISCR2_5                         0x10AD
+#define HDMI_FC_ISCR2_4                         0x10AE
+#define HDMI_FC_ISCR2_3                         0x10AF
+#define HDMI_FC_ISCR2_2                         0x10B0
+#define HDMI_FC_ISCR2_1                         0x10B1
+#define HDMI_FC_ISCR2_0                         0x10B2
+#define HDMI_FC_DATAUTO0                        0x10B3
+#define HDMI_FC_DATAUTO1                        0x10B4
+#define HDMI_FC_DATAUTO2                        0x10B5
+#define HDMI_FC_DATMAN                          0x10B6
+#define HDMI_FC_DATAUTO3                        0x10B7
+#define HDMI_FC_RDRB0                           0x10B8
+#define HDMI_FC_RDRB1                           0x10B9
+#define HDMI_FC_RDRB2                           0x10BA
+#define HDMI_FC_RDRB3                           0x10BB
+#define HDMI_FC_RDRB4                           0x10BC
+#define HDMI_FC_RDRB5                           0x10BD
+#define HDMI_FC_RDRB6                           0x10BE
+#define HDMI_FC_RDRB7                           0x10BF
+#define HDMI_FC_STAT0                           0x10D0
+#define HDMI_FC_INT0                            0x10D1
+#define HDMI_FC_MASK0                           0x10D2
+#define HDMI_FC_POL0                            0x10D3
+#define HDMI_FC_STAT1                           0x10D4
+#define HDMI_FC_INT1                            0x10D5
+#define HDMI_FC_MASK1                           0x10D6
+#define HDMI_FC_POL1                            0x10D7
+#define HDMI_FC_STAT2                           0x10D8
+#define HDMI_FC_INT2                            0x10D9
+#define HDMI_FC_MASK2                           0x10DA
+#define HDMI_FC_POL2                            0x10DB
+#define HDMI_FC_PRCONF                          0x10E0
+
+#define HDMI_FC_GMD_STAT                        0x1100
+#define HDMI_FC_GMD_EN                          0x1101
+#define HDMI_FC_GMD_UP                          0x1102
+#define HDMI_FC_GMD_CONF                        0x1103
+#define HDMI_FC_GMD_HB                          0x1104
+#define HDMI_FC_GMD_PB0                         0x1105
+#define HDMI_FC_GMD_PB1                         0x1106
+#define HDMI_FC_GMD_PB2                         0x1107
+#define HDMI_FC_GMD_PB3                         0x1108
+#define HDMI_FC_GMD_PB4                         0x1109
+#define HDMI_FC_GMD_PB5                         0x110A
+#define HDMI_FC_GMD_PB6                         0x110B
+#define HDMI_FC_GMD_PB7                         0x110C
+#define HDMI_FC_GMD_PB8                         0x110D
+#define HDMI_FC_GMD_PB9                         0x110E
+#define HDMI_FC_GMD_PB10                        0x110F
+#define HDMI_FC_GMD_PB11                        0x1110
+#define HDMI_FC_GMD_PB12                        0x1111
+#define HDMI_FC_GMD_PB13                        0x1112
+#define HDMI_FC_GMD_PB14                        0x1113
+#define HDMI_FC_GMD_PB15                        0x1114
+#define HDMI_FC_GMD_PB16                        0x1115
+#define HDMI_FC_GMD_PB17                        0x1116
+#define HDMI_FC_GMD_PB18                        0x1117
+#define HDMI_FC_GMD_PB19                        0x1118
+#define HDMI_FC_GMD_PB20                        0x1119
+#define HDMI_FC_GMD_PB21                        0x111A
+#define HDMI_FC_GMD_PB22                        0x111B
+#define HDMI_FC_GMD_PB23                        0x111C
+#define HDMI_FC_GMD_PB24                        0x111D
+#define HDMI_FC_GMD_PB25                        0x111E
+#define HDMI_FC_GMD_PB26                        0x111F
+#define HDMI_FC_GMD_PB27                        0x1120
+
+#define HDMI_FC_DBGFORCE                        0x1200
+#define HDMI_FC_DBGAUD0CH0                      0x1201
+#define HDMI_FC_DBGAUD1CH0                      0x1202
+#define HDMI_FC_DBGAUD2CH0                      0x1203
+#define HDMI_FC_DBGAUD0CH1                      0x1204
+#define HDMI_FC_DBGAUD1CH1                      0x1205
+#define HDMI_FC_DBGAUD2CH1                      0x1206
+#define HDMI_FC_DBGAUD0CH2                      0x1207
+#define HDMI_FC_DBGAUD1CH2                      0x1208
+#define HDMI_FC_DBGAUD2CH2                      0x1209
+#define HDMI_FC_DBGAUD0CH3                      0x120A
+#define HDMI_FC_DBGAUD1CH3                      0x120B
+#define HDMI_FC_DBGAUD2CH3                      0x120C
+#define HDMI_FC_DBGAUD0CH4                      0x120D
+#define HDMI_FC_DBGAUD1CH4                      0x120E
+#define HDMI_FC_DBGAUD2CH4                      0x120F
+#define HDMI_FC_DBGAUD0CH5                      0x1210
+#define HDMI_FC_DBGAUD1CH5                      0x1211
+#define HDMI_FC_DBGAUD2CH5                      0x1212
+#define HDMI_FC_DBGAUD0CH6                      0x1213
+#define HDMI_FC_DBGAUD1CH6                      0x1214
+#define HDMI_FC_DBGAUD2CH6                      0x1215
+#define HDMI_FC_DBGAUD0CH7                      0x1216
+#define HDMI_FC_DBGAUD1CH7                      0x1217
+#define HDMI_FC_DBGAUD2CH7                      0x1218
+#define HDMI_FC_DBGTMDS0                        0x1219
+#define HDMI_FC_DBGTMDS1                        0x121A
+#define HDMI_FC_DBGTMDS2                        0x121B
+
+/* HDMI Source PHY Registers */
+#define HDMI_PHY_CONF0                          0x3000
+#define HDMI_PHY_TST0                           0x3001
+#define HDMI_PHY_TST1                           0x3002
+#define HDMI_PHY_TST2                           0x3003
+#define HDMI_PHY_STAT0                          0x3004
+#define HDMI_PHY_INT0                           0x3005
+#define HDMI_PHY_MASK0                          0x3006
+#define HDMI_PHY_POL0                           0x3007
+
+/* HDMI Master PHY Registers */
+#define HDMI_PHY_I2CM_SLAVE_ADDR                0x3020
+#define HDMI_PHY_I2CM_ADDRESS_ADDR              0x3021
+#define HDMI_PHY_I2CM_DATAO_1_ADDR              0x3022
+#define HDMI_PHY_I2CM_DATAO_0_ADDR              0x3023
+#define HDMI_PHY_I2CM_DATAI_1_ADDR              0x3024
+#define HDMI_PHY_I2CM_DATAI_0_ADDR              0x3025
+#define HDMI_PHY_I2CM_OPERATION_ADDR            0x3026
+#define HDMI_PHY_I2CM_INT_ADDR                  0x3027
+#define HDMI_PHY_I2CM_CTLINT_ADDR               0x3028
+#define HDMI_PHY_I2CM_DIV_ADDR                  0x3029
+#define HDMI_PHY_I2CM_SOFTRSTZ_ADDR             0x302a
+#define HDMI_PHY_I2CM_SS_SCL_HCNT_1_ADDR        0x302b
+#define HDMI_PHY_I2CM_SS_SCL_HCNT_0_ADDR        0x302c
+#define HDMI_PHY_I2CM_SS_SCL_LCNT_1_ADDR        0x302d
+#define HDMI_PHY_I2CM_SS_SCL_LCNT_0_ADDR        0x302e
+#define HDMI_PHY_I2CM_FS_SCL_HCNT_1_ADDR        0x302f
+#define HDMI_PHY_I2CM_FS_SCL_HCNT_0_ADDR        0x3030
+#define HDMI_PHY_I2CM_FS_SCL_LCNT_1_ADDR        0x3031
+#define HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR        0x3032
+
+/* Audio Sampler Registers */
+#define HDMI_AUD_CONF0                          0x3100
+#define HDMI_AUD_CONF1                          0x3101
+#define HDMI_AUD_INT                            0x3102
+#define HDMI_AUD_CONF2                          0x3103
+#define HDMI_AUD_N1                             0x3200
+#define HDMI_AUD_N2                             0x3201
+#define HDMI_AUD_N3                             0x3202
+#define HDMI_AUD_CTS1                           0x3203
+#define HDMI_AUD_CTS2                           0x3204
+#define HDMI_AUD_CTS3                           0x3205
+#define HDMI_AUD_INPUTCLKFS                     0x3206
+#define HDMI_AUD_SPDIFINT                      0x3302
+#define HDMI_AUD_CONF0_HBR                      0x3400
+#define HDMI_AUD_HBR_STATUS                     0x3401
+#define HDMI_AUD_HBR_INT                        0x3402
+#define HDMI_AUD_HBR_POL                        0x3403
+#define HDMI_AUD_HBR_MASK                       0x3404
+
+/* Generic Parallel Audio Interface Registers */
+/* Not used as GPAUD interface is not enabled in hw */
+#define HDMI_GP_CONF0                           0x3500
+#define HDMI_GP_CONF1                           0x3501
+#define HDMI_GP_CONF2                           0x3502
+#define HDMI_GP_STAT                            0x3503
+#define HDMI_GP_INT                             0x3504
+#define HDMI_GP_MASK                            0x3505
+#define HDMI_GP_POL                             0x3506
+
+/* Audio DMA Registers */
+#define HDMI_AHB_DMA_CONF0                      0x3600
+#define HDMI_AHB_DMA_START                      0x3601
+#define HDMI_AHB_DMA_STOP                       0x3602
+#define HDMI_AHB_DMA_THRSLD                     0x3603
+#define HDMI_AHB_DMA_STRADDR0                   0x3604
+#define HDMI_AHB_DMA_STRADDR1                   0x3605
+#define HDMI_AHB_DMA_STRADDR2                   0x3606
+#define HDMI_AHB_DMA_STRADDR3                   0x3607
+#define HDMI_AHB_DMA_STPADDR0                   0x3608
+#define HDMI_AHB_DMA_STPADDR1                   0x3609
+#define HDMI_AHB_DMA_STPADDR2                   0x360a
+#define HDMI_AHB_DMA_STPADDR3                   0x360b
+#define HDMI_AHB_DMA_BSTADDR0                   0x360c
+#define HDMI_AHB_DMA_BSTADDR1                   0x360d
+#define HDMI_AHB_DMA_BSTADDR2                   0x360e
+#define HDMI_AHB_DMA_BSTADDR3                   0x360f
+#define HDMI_AHB_DMA_MBLENGTH0                  0x3610
+#define HDMI_AHB_DMA_MBLENGTH1                  0x3611
+#define HDMI_AHB_DMA_STAT                       0x3612
+#define HDMI_AHB_DMA_INT                        0x3613
+#define HDMI_AHB_DMA_MASK                       0x3614
+#define HDMI_AHB_DMA_POL                        0x3615
+#define HDMI_AHB_DMA_CONF1                      0x3616
+#define HDMI_AHB_DMA_BUFFSTAT                   0x3617
+#define HDMI_AHB_DMA_BUFFINT                    0x3618
+#define HDMI_AHB_DMA_BUFFMASK                   0x3619
+#define HDMI_AHB_DMA_BUFFPOL                    0x361a
+
+/* Main Controller Registers */
+#define HDMI_MC_SFRDIV                          0x4000
+#define HDMI_MC_CLKDIS                          0x4001
+#define HDMI_MC_SWRSTZ                          0x4002
+#define HDMI_MC_OPCTRL                          0x4003
+#define HDMI_MC_FLOWCTRL                        0x4004
+#define HDMI_MC_PHYRSTZ                         0x4005
+#define HDMI_MC_LOCKONCLOCK                     0x4006
+#define HDMI_MC_HEACPHY_RST                     0x4007
+
+/* Color Space  Converter Registers */
+#define HDMI_CSC_CFG                            0x4100
+#define HDMI_CSC_SCALE                          0x4101
+#define HDMI_CSC_COEF_A1_MSB                    0x4102
+#define HDMI_CSC_COEF_A1_LSB                    0x4103
+#define HDMI_CSC_COEF_A2_MSB                    0x4104
+#define HDMI_CSC_COEF_A2_LSB                    0x4105
+#define HDMI_CSC_COEF_A3_MSB                    0x4106
+#define HDMI_CSC_COEF_A3_LSB                    0x4107
+#define HDMI_CSC_COEF_A4_MSB                    0x4108
+#define HDMI_CSC_COEF_A4_LSB                    0x4109
+#define HDMI_CSC_COEF_B1_MSB                    0x410A
+#define HDMI_CSC_COEF_B1_LSB                    0x410B
+#define HDMI_CSC_COEF_B2_MSB                    0x410C
+#define HDMI_CSC_COEF_B2_LSB                    0x410D
+#define HDMI_CSC_COEF_B3_MSB                    0x410E
+#define HDMI_CSC_COEF_B3_LSB                    0x410F
+#define HDMI_CSC_COEF_B4_MSB                    0x4110
+#define HDMI_CSC_COEF_B4_LSB                    0x4111
+#define HDMI_CSC_COEF_C1_MSB                    0x4112
+#define HDMI_CSC_COEF_C1_LSB                    0x4113
+#define HDMI_CSC_COEF_C2_MSB                    0x4114
+#define HDMI_CSC_COEF_C2_LSB                    0x4115
+#define HDMI_CSC_COEF_C3_MSB                    0x4116
+#define HDMI_CSC_COEF_C3_LSB                    0x4117
+#define HDMI_CSC_COEF_C4_MSB                    0x4118
+#define HDMI_CSC_COEF_C4_LSB                    0x4119
+
+/* HDCP Interrupt Registers */
+#define HDMI_A_APIINTCLR                        0x5006
+#define HDMI_A_APIINTSTAT                       0x5007
+#define HDMI_A_APIINTMSK                        0x5008
+
+/* CEC Engine Registers */
+#define HDMI_CEC_CTRL                           0x7D00
+#define HDMI_CEC_STAT                           0x7D01
+#define HDMI_CEC_MASK                           0x7D02
+#define HDMI_CEC_POLARITY                       0x7D03
+#define HDMI_CEC_INT                            0x7D04
+#define HDMI_CEC_ADDR_L                         0x7D05
+#define HDMI_CEC_ADDR_H                         0x7D06
+#define HDMI_CEC_TX_CNT                         0x7D07
+#define HDMI_CEC_RX_CNT                         0x7D08
+#define HDMI_CEC_TX_DATA0                       0x7D10
+#define HDMI_CEC_TX_DATA1                       0x7D11
+#define HDMI_CEC_TX_DATA2                       0x7D12
+#define HDMI_CEC_TX_DATA3                       0x7D13
+#define HDMI_CEC_TX_DATA4                       0x7D14
+#define HDMI_CEC_TX_DATA5                       0x7D15
+#define HDMI_CEC_TX_DATA6                       0x7D16
+#define HDMI_CEC_TX_DATA7                       0x7D17
+#define HDMI_CEC_TX_DATA8                       0x7D18
+#define HDMI_CEC_TX_DATA9                       0x7D19
+#define HDMI_CEC_TX_DATA10                      0x7D1a
+#define HDMI_CEC_TX_DATA11                      0x7D1b
+#define HDMI_CEC_TX_DATA12                      0x7D1c
+#define HDMI_CEC_TX_DATA13                      0x7D1d
+#define HDMI_CEC_TX_DATA14                      0x7D1e
+#define HDMI_CEC_TX_DATA15                      0x7D1f
+#define HDMI_CEC_RX_DATA0                       0x7D20
+#define HDMI_CEC_RX_DATA1                       0x7D21
+#define HDMI_CEC_RX_DATA2                       0x7D22
+#define HDMI_CEC_RX_DATA3                       0x7D23
+#define HDMI_CEC_RX_DATA4                       0x7D24
+#define HDMI_CEC_RX_DATA5                       0x7D25
+#define HDMI_CEC_RX_DATA6                       0x7D26
+#define HDMI_CEC_RX_DATA7                       0x7D27
+#define HDMI_CEC_RX_DATA8                       0x7D28
+#define HDMI_CEC_RX_DATA9                       0x7D29
+#define HDMI_CEC_RX_DATA10                      0x7D2a
+#define HDMI_CEC_RX_DATA11                      0x7D2b
+#define HDMI_CEC_RX_DATA12                      0x7D2c
+#define HDMI_CEC_RX_DATA13                      0x7D2d
+#define HDMI_CEC_RX_DATA14                      0x7D2e
+#define HDMI_CEC_RX_DATA15                      0x7D2f
+#define HDMI_CEC_LOCK                           0x7D30
+#define HDMI_CEC_WKUPCTRL                       0x7D31
+
+/* I2C Master Registers (E-DDC) */
+#define HDMI_I2CM_SLAVE                         0x7E00
+#define HDMI_I2CM_ADDRESS                       0x7E01
+#define HDMI_I2CM_DATAO                         0x7E02
+#define HDMI_I2CM_DATAI                         0x7E03
+#define HDMI_I2CM_OPERATION                     0x7E04
+#define HDMI_I2CM_INT                           0x7E05
+#define HDMI_I2CM_CTLINT                        0x7E06
+#define HDMI_I2CM_DIV                           0x7E07
+#define HDMI_I2CM_SEGADDR                       0x7E08
+#define HDMI_I2CM_SOFTRSTZ                      0x7E09
+#define HDMI_I2CM_SEGPTR                        0x7E0A
+#define HDMI_I2CM_SS_SCL_HCNT_1_ADDR            0x7E0B
+#define HDMI_I2CM_SS_SCL_HCNT_0_ADDR            0x7E0C
+#define HDMI_I2CM_SS_SCL_LCNT_1_ADDR            0x7E0D
+#define HDMI_I2CM_SS_SCL_LCNT_0_ADDR            0x7E0E
+#define HDMI_I2CM_FS_SCL_HCNT_1_ADDR            0x7E0F
+#define HDMI_I2CM_FS_SCL_HCNT_0_ADDR            0x7E10
+#define HDMI_I2CM_FS_SCL_LCNT_1_ADDR            0x7E11
+#define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
+
+/* Random Number Generator Registers (RNG) */
+#define HDMI_RNG_BASE                           0x8000
+
+
+/*
+ * Register field definitions
+ */
+enum {
+/* IH_FC_INT2 field values */
+       HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
+       HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
+       HDMI_IH_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* IH_FC_STAT2 field values */
+       HDMI_IH_FC_STAT2_OVERFLOW_MASK = 0x03,
+       HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+       HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* IH_PHY_STAT0 field values */
+       HDMI_IH_PHY_STAT0_RX_SENSE3 = 0x20,
+       HDMI_IH_PHY_STAT0_RX_SENSE2 = 0x10,
+       HDMI_IH_PHY_STAT0_RX_SENSE1 = 0x8,
+       HDMI_IH_PHY_STAT0_RX_SENSE0 = 0x4,
+       HDMI_IH_PHY_STAT0_TX_PHY_LOCK = 0x2,
+       HDMI_IH_PHY_STAT0_HPD = 0x1,
+
+/* IH_CEC_STAT0 field values */
+       HDMI_IH_CEC_STAT0_WAKEUP = 0x40,
+       HDMI_IH_CEC_STAT0_ERROR_FOLL = 0x20,
+       HDMI_IH_CEC_STAT0_ERROR_INIT = 0x10,
+       HDMI_IH_CEC_STAT0_ARB_LOST = 0x8,
+       HDMI_IH_CEC_STAT0_NACK = 0x4,
+       HDMI_IH_CEC_STAT0_EOM = 0x2,
+       HDMI_IH_CEC_STAT0_DONE = 0x1,
+
+
+/* IH_MUTE_I2CMPHY_STAT0 field values */
+       HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYDONE = 0x2,
+       HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYERROR = 0x1,
+
+/* IH_PHY_STAT0 field values */
+       HDMI_IH_MUTE_PHY_STAT0_RX_SENSE3 = 0x20,
+       HDMI_IH_MUTE_PHY_STAT0_RX_SENSE2 = 0x10,
+       HDMI_IH_MUTE_PHY_STAT0_RX_SENSE1 = 0x8,
+       HDMI_IH_MUTE_PHY_STAT0_RX_SENSE0 = 0x4,
+       HDMI_IH_MUTE_PHY_STAT0_TX_PHY_LOCK = 0x2,
+       HDMI_IH_MUTE_PHY_STAT0_HPD = 0x1,
+
+/* IH_AHBDMAAUD_STAT0 field values */
+       HDMI_IH_AHBDMAAUD_STAT0_ERROR = 0x20,
+       HDMI_IH_AHBDMAAUD_STAT0_LOST = 0x10,
+       HDMI_IH_AHBDMAAUD_STAT0_RETRY = 0x08,
+       HDMI_IH_AHBDMAAUD_STAT0_DONE = 0x04,
+       HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = 0x02,
+       HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01,
+
+/* IH_MUTE_FC_STAT2 field values */
+       HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK = 0x03,
+       HDMI_IH_MUTE_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+       HDMI_IH_MUTE_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* IH_MUTE_AHBDMAAUD_STAT0 field values */
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = 0x20,
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = 0x10,
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = 0x08,
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = 0x04,
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = 0x02,
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01,
+
+/* IH_MUTE field values */
+       HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT = 0x2,
+       HDMI_IH_MUTE_MUTE_ALL_INTERRUPT = 0x1,
+
+/* TX_INVID0 field values */
+       HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_MASK = 0x80,
+       HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_ENABLE = 0x80,
+       HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE = 0x00,
+       HDMI_TX_INVID0_VIDEO_MAPPING_MASK = 0x1F,
+       HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET = 0,
+
+/* TX_INSTUFFING field values */
+       HDMI_TX_INSTUFFING_BDBDATA_STUFFING_MASK = 0x4,
+       HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE = 0x4,
+       HDMI_TX_INSTUFFING_BDBDATA_STUFFING_DISABLE = 0x0,
+       HDMI_TX_INSTUFFING_RCRDATA_STUFFING_MASK = 0x2,
+       HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE = 0x2,
+       HDMI_TX_INSTUFFING_RCRDATA_STUFFING_DISABLE = 0x0,
+       HDMI_TX_INSTUFFING_GYDATA_STUFFING_MASK = 0x1,
+       HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE = 0x1,
+       HDMI_TX_INSTUFFING_GYDATA_STUFFING_DISABLE = 0x0,
+
+/* VP_PR_CD field values */
+       HDMI_VP_PR_CD_COLOR_DEPTH_MASK = 0xF0,
+       HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET = 4,
+       HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK = 0x0F,
+       HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET = 0,
+
+/* VP_STUFF field values */
+       HDMI_VP_STUFF_IDEFAULT_PHASE_MASK = 0x20,
+       HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET = 5,
+       HDMI_VP_STUFF_IFIX_PP_TO_LAST_MASK = 0x10,
+       HDMI_VP_STUFF_IFIX_PP_TO_LAST_OFFSET = 4,
+       HDMI_VP_STUFF_ICX_GOTO_P0_ST_MASK = 0x8,
+       HDMI_VP_STUFF_ICX_GOTO_P0_ST_OFFSET = 3,
+       HDMI_VP_STUFF_YCC422_STUFFING_MASK = 0x4,
+       HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE = 0x4,
+       HDMI_VP_STUFF_YCC422_STUFFING_DIRECT_MODE = 0x0,
+       HDMI_VP_STUFF_PP_STUFFING_MASK = 0x2,
+       HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE = 0x2,
+       HDMI_VP_STUFF_PP_STUFFING_DIRECT_MODE = 0x0,
+       HDMI_VP_STUFF_PR_STUFFING_MASK = 0x1,
+       HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE = 0x1,
+       HDMI_VP_STUFF_PR_STUFFING_DIRECT_MODE = 0x0,
+
+/* VP_CONF field values */
+       HDMI_VP_CONF_BYPASS_EN_MASK = 0x40,
+       HDMI_VP_CONF_BYPASS_EN_ENABLE = 0x40,
+       HDMI_VP_CONF_BYPASS_EN_DISABLE = 0x00,
+       HDMI_VP_CONF_PP_EN_ENMASK = 0x20,
+       HDMI_VP_CONF_PP_EN_ENABLE = 0x20,
+       HDMI_VP_CONF_PP_EN_DISABLE = 0x00,
+       HDMI_VP_CONF_PR_EN_MASK = 0x10,
+       HDMI_VP_CONF_PR_EN_ENABLE = 0x10,
+       HDMI_VP_CONF_PR_EN_DISABLE = 0x00,
+       HDMI_VP_CONF_YCC422_EN_MASK = 0x8,
+       HDMI_VP_CONF_YCC422_EN_ENABLE = 0x8,
+       HDMI_VP_CONF_YCC422_EN_DISABLE = 0x0,
+       HDMI_VP_CONF_BYPASS_SELECT_MASK = 0x4,
+       HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER = 0x4,
+       HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER = 0x0,
+       HDMI_VP_CONF_OUTPUT_SELECTOR_MASK = 0x3,
+       HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS = 0x3,
+       HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422 = 0x1,
+       HDMI_VP_CONF_OUTPUT_SELECTOR_PP = 0x0,
+
+/* VP_REMAP field values */
+       HDMI_VP_REMAP_MASK = 0x3,
+       HDMI_VP_REMAP_YCC422_24bit = 0x2,
+       HDMI_VP_REMAP_YCC422_20bit = 0x1,
+       HDMI_VP_REMAP_YCC422_16bit = 0x0,
+
+/* FC_INVIDCONF field values */
+       HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_MASK = 0x40,
+       HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH = 0x40,
+       HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW = 0x00,
+       HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_MASK = 0x20,
+       HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH = 0x20,
+       HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW = 0x00,
+       HDMI_FC_INVIDCONF_DE_IN_POLARITY_MASK = 0x10,
+       HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH = 0x10,
+       HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW = 0x00,
+       HDMI_FC_INVIDCONF_DVI_MODEZ_MASK = 0x8,
+       HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE = 0x8,
+       HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE = 0x0,
+       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_MASK = 0x2,
+       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH = 0x2,
+       HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW = 0x0,
+       HDMI_FC_INVIDCONF_IN_I_P_MASK = 0x1,
+       HDMI_FC_INVIDCONF_IN_I_P_INTERLACED = 0x1,
+       HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE = 0x0,
+
+/* FC_AUDICONF0 field values */
+       HDMI_FC_AUDICONF0_CC_OFFSET = 4,
+       HDMI_FC_AUDICONF0_CC_MASK = 0x70,
+       HDMI_FC_AUDICONF0_CT_OFFSET = 0,
+       HDMI_FC_AUDICONF0_CT_MASK = 0xF,
+
+/* FC_AUDICONF1 field values */
+       HDMI_FC_AUDICONF1_SS_OFFSET = 3,
+       HDMI_FC_AUDICONF1_SS_MASK = 0x18,
+       HDMI_FC_AUDICONF1_SF_OFFSET = 0,
+       HDMI_FC_AUDICONF1_SF_MASK = 0x7,
+
+/* FC_AUDICONF3 field values */
+       HDMI_FC_AUDICONF3_LFEPBL_OFFSET = 5,
+       HDMI_FC_AUDICONF3_LFEPBL_MASK = 0x60,
+       HDMI_FC_AUDICONF3_DM_INH_OFFSET = 4,
+       HDMI_FC_AUDICONF3_DM_INH_MASK = 0x10,
+       HDMI_FC_AUDICONF3_LSV_OFFSET = 0,
+       HDMI_FC_AUDICONF3_LSV_MASK = 0xF,
+
+/* FC_AUDSCHNLS0 field values */
+       HDMI_FC_AUDSCHNLS0_CGMSA_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS0_CGMSA_MASK = 0x30,
+       HDMI_FC_AUDSCHNLS0_COPYRIGHT_OFFSET = 0,
+       HDMI_FC_AUDSCHNLS0_COPYRIGHT_MASK = 0x01,
+
+/* FC_AUDSCHNLS3-6 field values */
+       HDMI_FC_AUDSCHNLS3_OIEC_CH0_OFFSET = 0,
+       HDMI_FC_AUDSCHNLS3_OIEC_CH0_MASK = 0x0f,
+       HDMI_FC_AUDSCHNLS3_OIEC_CH1_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS3_OIEC_CH1_MASK = 0xf0,
+       HDMI_FC_AUDSCHNLS4_OIEC_CH2_OFFSET = 0,
+       HDMI_FC_AUDSCHNLS4_OIEC_CH2_MASK = 0x0f,
+       HDMI_FC_AUDSCHNLS4_OIEC_CH3_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS4_OIEC_CH3_MASK = 0xf0,
+
+       HDMI_FC_AUDSCHNLS5_OIEC_CH0_OFFSET = 0,
+       HDMI_FC_AUDSCHNLS5_OIEC_CH0_MASK = 0x0f,
+       HDMI_FC_AUDSCHNLS5_OIEC_CH1_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS5_OIEC_CH1_MASK = 0xf0,
+       HDMI_FC_AUDSCHNLS6_OIEC_CH2_OFFSET = 0,
+       HDMI_FC_AUDSCHNLS6_OIEC_CH2_MASK = 0x0f,
+       HDMI_FC_AUDSCHNLS6_OIEC_CH3_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS6_OIEC_CH3_MASK = 0xf0,
+
+/* HDMI_FC_AUDSCHNLS7 field values */
+       HDMI_FC_AUDSCHNLS7_ACCURACY_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS7_ACCURACY_MASK = 0x30,
+
+/* HDMI_FC_AUDSCHNLS8 field values */
+       HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_MASK = 0xf0,
+       HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_OFFSET = 4,
+       HDMI_FC_AUDSCHNLS8_WORDLEGNTH_MASK = 0x0f,
+       HDMI_FC_AUDSCHNLS8_WORDLEGNTH_OFFSET = 0,
+
+/* FC_AUDSCONF field values */
+       HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_MASK = 0xF0,
+       HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_OFFSET = 4,
+       HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK = 0x1,
+       HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_OFFSET = 0,
+       HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1 = 0x1,
+       HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0 = 0x0,
+
+/* FC_STAT2 field values */
+       HDMI_FC_STAT2_OVERFLOW_MASK = 0x03,
+       HDMI_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+       HDMI_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* FC_INT2 field values */
+       HDMI_FC_INT2_OVERFLOW_MASK = 0x03,
+       HDMI_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
+       HDMI_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* FC_MASK2 field values */
+       HDMI_FC_MASK2_OVERFLOW_MASK = 0x03,
+       HDMI_FC_MASK2_LOW_PRIORITY_OVERFLOW = 0x02,
+       HDMI_FC_MASK2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* FC_PRCONF field values */
+       HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK = 0xF0,
+       HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET = 4,
+       HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK = 0x0F,
+       HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET = 0,
+
+/* FC_AVICONF0-FC_AVICONF3 field values */
+       HDMI_FC_AVICONF0_PIX_FMT_MASK = 0x03,
+       HDMI_FC_AVICONF0_PIX_FMT_RGB = 0x00,
+       HDMI_FC_AVICONF0_PIX_FMT_YCBCR422 = 0x01,
+       HDMI_FC_AVICONF0_PIX_FMT_YCBCR444 = 0x02,
+       HDMI_FC_AVICONF0_ACTIVE_FMT_MASK = 0x40,
+       HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT = 0x40,
+       HDMI_FC_AVICONF0_ACTIVE_FMT_NO_INFO = 0x00,
+       HDMI_FC_AVICONF0_BAR_DATA_MASK = 0x0C,
+       HDMI_FC_AVICONF0_BAR_DATA_NO_DATA = 0x00,
+       HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR = 0x04,
+       HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR = 0x08,
+       HDMI_FC_AVICONF0_BAR_DATA_VERT_HORIZ_BAR = 0x0C,
+       HDMI_FC_AVICONF0_SCAN_INFO_MASK = 0x30,
+       HDMI_FC_AVICONF0_SCAN_INFO_OVERSCAN = 0x10,
+       HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN = 0x20,
+       HDMI_FC_AVICONF0_SCAN_INFO_NODATA = 0x00,
+
+       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_MASK = 0x0F,
+       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_USE_CODED = 0x08,
+       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3 = 0x09,
+       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9 = 0x0A,
+       HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_14_9 = 0x0B,
+       HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_MASK = 0x30,
+       HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_NO_DATA = 0x00,
+       HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3 = 0x10,
+       HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9 = 0x20,
+       HDMI_FC_AVICONF1_COLORIMETRY_MASK = 0xC0,
+       HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA = 0x00,
+       HDMI_FC_AVICONF1_COLORIMETRY_SMPTE = 0x40,
+       HDMI_FC_AVICONF1_COLORIMETRY_ITUR = 0x80,
+       HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO = 0xC0,
+
+       HDMI_FC_AVICONF2_SCALING_MASK = 0x03,
+       HDMI_FC_AVICONF2_SCALING_NONE = 0x00,
+       HDMI_FC_AVICONF2_SCALING_HORIZ = 0x01,
+       HDMI_FC_AVICONF2_SCALING_VERT = 0x02,
+       HDMI_FC_AVICONF2_SCALING_HORIZ_VERT = 0x03,
+       HDMI_FC_AVICONF2_RGB_QUANT_MASK = 0x0C,
+       HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT = 0x00,
+       HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE = 0x04,
+       HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE = 0x08,
+       HDMI_FC_AVICONF2_EXT_COLORIMETRY_MASK = 0x70,
+       HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601 = 0x00,
+       HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709 = 0x10,
+       HDMI_FC_AVICONF2_EXT_COLORIMETRY_SYCC601 = 0x20,
+       HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_YCC601 = 0x30,
+       HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_RGB = 0x40,
+       HDMI_FC_AVICONF2_IT_CONTENT_MASK = 0x80,
+       HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA = 0x00,
+       HDMI_FC_AVICONF2_IT_CONTENT_VALID = 0x80,
+
+       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_MASK = 0x03,
+       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS = 0x00,
+       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_PHOTO = 0x01,
+       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_CINEMA = 0x02,
+       HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GAME = 0x03,
+       HDMI_FC_AVICONF3_QUANT_RANGE_MASK = 0x0C,
+       HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00,
+       HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04,
+
+/* FC_DBGFORCE field values */
+       HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10,
+       HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1,
+
+/* PHY_CONF0 field values */
+       HDMI_PHY_CONF0_PDZ_MASK = 0x80,
+       HDMI_PHY_CONF0_PDZ_OFFSET = 7,
+       HDMI_PHY_CONF0_ENTMDS_MASK = 0x40,
+       HDMI_PHY_CONF0_ENTMDS_OFFSET = 6,
+       HDMI_PHY_CONF0_SPARECTRL = 0x20,
+       HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10,
+       HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4,
+       HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8,
+       HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET = 3,
+       HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK = 0x4,
+       HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET = 2,
+       HDMI_PHY_CONF0_SELDATAENPOL_MASK = 0x2,
+       HDMI_PHY_CONF0_SELDATAENPOL_OFFSET = 1,
+       HDMI_PHY_CONF0_SELDIPIF_MASK = 0x1,
+       HDMI_PHY_CONF0_SELDIPIF_OFFSET = 0,
+
+/* PHY_TST0 field values */
+       HDMI_PHY_TST0_TSTCLR_MASK = 0x20,
+       HDMI_PHY_TST0_TSTCLR_OFFSET = 5,
+       HDMI_PHY_TST0_TSTEN_MASK = 0x10,
+       HDMI_PHY_TST0_TSTEN_OFFSET = 4,
+       HDMI_PHY_TST0_TSTCLK_MASK = 0x1,
+       HDMI_PHY_TST0_TSTCLK_OFFSET = 0,
+
+/* PHY_STAT0 field values */
+       HDMI_PHY_RX_SENSE3 = 0x80,
+       HDMI_PHY_RX_SENSE2 = 0x40,
+       HDMI_PHY_RX_SENSE1 = 0x20,
+       HDMI_PHY_RX_SENSE0 = 0x10,
+       HDMI_PHY_HPD = 0x02,
+       HDMI_PHY_TX_PHY_LOCK = 0x01,
+
+/* PHY_I2CM_SLAVE_ADDR field values */
+       HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 = 0x69,
+       HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY = 0x49,
+
+/* PHY_I2CM_OPERATION_ADDR field values */
+       HDMI_PHY_I2CM_OPERATION_ADDR_WRITE = 0x10,
+       HDMI_PHY_I2CM_OPERATION_ADDR_READ = 0x1,
+
+/* HDMI_PHY_I2CM_INT_ADDR */
+       HDMI_PHY_I2CM_INT_ADDR_DONE_POL = 0x08,
+       HDMI_PHY_I2CM_INT_ADDR_DONE_MASK = 0x04,
+
+/* HDMI_PHY_I2CM_CTLINT_ADDR */
+       HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL = 0x80,
+       HDMI_PHY_I2CM_CTLINT_ADDR_NAC_MASK = 0x40,
+       HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08,
+       HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04,
+
+/* AUD_CTS3 field values */
+       HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5,
+       HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0,
+       HDMI_AUD_CTS3_N_SHIFT_1 = 0,
+       HDMI_AUD_CTS3_N_SHIFT_16 = 0x20,
+       HDMI_AUD_CTS3_N_SHIFT_32 = 0x40,
+       HDMI_AUD_CTS3_N_SHIFT_64 = 0x60,
+       HDMI_AUD_CTS3_N_SHIFT_128 = 0x80,
+       HDMI_AUD_CTS3_N_SHIFT_256 = 0xa0,
+       /* note that the CTS3 MANUAL bit has been removed
+          from our part. Can't set it, will read as 0. */
+       HDMI_AUD_CTS3_CTS_MANUAL = 0x10,
+       HDMI_AUD_CTS3_AUDCTS19_16_MASK = 0x0f,
+
+/* AHB_DMA_CONF0 field values */
+       HDMI_AHB_DMA_CONF0_SW_FIFO_RST_OFFSET = 7,
+       HDMI_AHB_DMA_CONF0_SW_FIFO_RST_MASK = 0x80,
+       HDMI_AHB_DMA_CONF0_HBR = 0x10,
+       HDMI_AHB_DMA_CONF0_EN_HLOCK_OFFSET = 3,
+       HDMI_AHB_DMA_CONF0_EN_HLOCK_MASK = 0x08,
+       HDMI_AHB_DMA_CONF0_INCR_TYPE_OFFSET = 1,
+       HDMI_AHB_DMA_CONF0_INCR_TYPE_MASK = 0x06,
+       HDMI_AHB_DMA_CONF0_INCR4 = 0x0,
+       HDMI_AHB_DMA_CONF0_INCR8 = 0x2,
+       HDMI_AHB_DMA_CONF0_INCR16 = 0x4,
+       HDMI_AHB_DMA_CONF0_BURST_MODE = 0x1,
+
+/* HDMI_AHB_DMA_START field values */
+       HDMI_AHB_DMA_START_START_OFFSET = 0,
+       HDMI_AHB_DMA_START_START_MASK = 0x01,
+
+/* HDMI_AHB_DMA_STOP field values */
+       HDMI_AHB_DMA_STOP_STOP_OFFSET = 0,
+       HDMI_AHB_DMA_STOP_STOP_MASK = 0x01,
+
+/* AHB_DMA_STAT, AHB_DMA_INT, AHB_DMA_MASK, AHB_DMA_POL field values */
+       HDMI_AHB_DMA_DONE = 0x80,
+       HDMI_AHB_DMA_RETRY_SPLIT = 0x40,
+       HDMI_AHB_DMA_LOSTOWNERSHIP = 0x20,
+       HDMI_AHB_DMA_ERROR = 0x10,
+       HDMI_AHB_DMA_FIFO_THREMPTY = 0x04,
+       HDMI_AHB_DMA_FIFO_FULL = 0x02,
+       HDMI_AHB_DMA_FIFO_EMPTY = 0x01,
+
+/* AHB_DMA_BUFFSTAT, AHB_DMA_BUFFINT, AHB_DMA_BUFFMASK, AHB_DMA_BUFFPOL field values */
+       HDMI_AHB_DMA_BUFFSTAT_FULL = 0x02,
+       HDMI_AHB_DMA_BUFFSTAT_EMPTY = 0x01,
+
+/* MC_CLKDIS field values */
+       HDMI_MC_CLKDIS_HDCPCLK_DISABLE = 0x40,
+       HDMI_MC_CLKDIS_CECCLK_DISABLE = 0x20,
+       HDMI_MC_CLKDIS_CSCCLK_DISABLE = 0x10,
+       HDMI_MC_CLKDIS_AUDCLK_DISABLE = 0x8,
+       HDMI_MC_CLKDIS_PREPCLK_DISABLE = 0x4,
+       HDMI_MC_CLKDIS_TMDSCLK_DISABLE = 0x2,
+       HDMI_MC_CLKDIS_PIXELCLK_DISABLE = 0x1,
+
+/* MC_SWRSTZ field values */
+       HDMI_MC_SWRSTZ_TMDSSWRST_REQ = 0x02,
+
+/* MC_FLOWCTRL field values */
+       HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK = 0x1,
+       HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH = 0x1,
+       HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0,
+
+/* MC_PHYRSTZ field values */
+       HDMI_MC_PHYRSTZ_ASSERT = 0x0,
+       HDMI_MC_PHYRSTZ_DEASSERT = 0x1,
+
+/* MC_HEACPHY_RST field values */
+       HDMI_MC_HEACPHY_RST_ASSERT = 0x1,
+       HDMI_MC_HEACPHY_RST_DEASSERT = 0x0,
+
+/* CSC_CFG field values */
+       HDMI_CSC_CFG_INTMODE_MASK = 0x30,
+       HDMI_CSC_CFG_INTMODE_OFFSET = 4,
+       HDMI_CSC_CFG_INTMODE_DISABLE = 0x00,
+       HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1 = 0x10,
+       HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA2 = 0x20,
+       HDMI_CSC_CFG_DECMODE_MASK = 0x3,
+       HDMI_CSC_CFG_DECMODE_OFFSET = 0,
+       HDMI_CSC_CFG_DECMODE_DISABLE = 0x0,
+       HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1 = 0x1,
+       HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA2 = 0x2,
+       HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3 = 0x3,
+
+/* CSC_SCALE field values */
+       HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK = 0xF0,
+       HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP = 0x00,
+       HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP = 0x50,
+       HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP = 0x60,
+       HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP = 0x70,
+       HDMI_CSC_SCALE_CSCSCALE_MASK = 0x03,
+
+/* I2CM_OPERATION field values */
+       HDMI_I2CM_OPERATION_WRITE = 0x10,
+       HDMI_I2CM_OPERATION_READ_EXT = 0x2,
+       HDMI_I2CM_OPERATION_READ = 0x1,
+
+/* HDMI_I2CM_INT */
+       HDMI_I2CM_INT_DONE_POL = 0x08,
+       HDMI_I2CM_INT_DONE_MASK = 0x04,
+
+/* HDMI_I2CM_CTLINT */
+       HDMI_I2CM_CTLINT_NAC_POL = 0x80,
+       HDMI_I2CM_CTLINT_NAC_MASK = 0x40,
+       HDMI_I2CM_CTLINT_ARBITRATION_POL = 0x08,
+       HDMI_I2CM_CTLINT_ARBITRATION_MASK = 0x04,
+
+};
+
+enum imx_hdmi_type {
+       IMX6DL_HDMI,
+       IMX6Q_HDMI,
+};
+
+/* IOCTL commands */
+#define HDMI_IOC_MAGIC  'H'
+
+#define HDMI_IOC_GET_RESOURCE  _IO(HDMI_IOC_MAGIC, 0)
+#define HDMI_IOC_GET_CPU_TYPE  _IO(HDMI_IOC_MAGIC, 1)
+
+
+#endif /* __MXC_HDMI_H__ */