2010-12-09 13:47:32

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH RFC] i.MX51 Framebuffer support

Hi,

The following series adds i.MX51 framebuffer support based on the IPUv3. It
is not perfect but I decided it is good enough to open it for a wider audience
and collect first reviews. I tested this on a babbage board using both outputs
(VGA/DVI) with different resolutions up to 1680x1050 and different colour depths.
I also tested it on one custom board using a fixed display setting.

Sascha

The following changes since commit a96a688dcea84047c982dfbb61a335fa00d14f9a:

mx51: support FIQ on TZIC, revised (2010-12-07 22:29:32 +0100)

are available in the git repository at:
git://git.pengutronix.de/git/imx/linux-2.6.git ipuv3

Sascha Hauer (9):
ARM i.MX51: Add ipu clock support
ARM i.MX51: rename IPU irqs
Add a mfd IPUv3 driver
fb: export fb mode db table
Add i.MX5 framebuffer driver
ARM i.MX51: Add IPU device support
ARM i.MX5: Allow to increase max zone order
ARM i.MX5: increase dma consistent size for IPU support
ARM i.MX51 babbage: Add framebuffer support

arch/arm/Kconfig | 4 +-
arch/arm/mach-mx5/Kconfig | 1 +
arch/arm/mach-mx5/board-mx51_babbage.c | 74 ++
arch/arm/mach-mx5/clock-mx51-mx53.c | 140 ++++
arch/arm/mach-mx5/devices-imx51.h | 4 +
arch/arm/plat-mxc/devices/Kconfig | 4 +
arch/arm/plat-mxc/devices/Makefile | 1 +
arch/arm/plat-mxc/devices/platform-imx_ipuv3.c | 47 ++
arch/arm/plat-mxc/include/mach/devices-common.h | 10 +
arch/arm/plat-mxc/include/mach/ipu-v3.h | 48 ++
arch/arm/plat-mxc/include/mach/memory.h | 2 +-
arch/arm/plat-mxc/include/mach/mx51.h | 4 +-
drivers/mfd/Kconfig | 7 +
drivers/mfd/Makefile | 1 +
drivers/mfd/imx-ipu-v3/Makefile | 3 +
drivers/mfd/imx-ipu-v3/ipu-common.c | 661 ++++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-cpmem.c | 608 ++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-dc.c | 362 ++++++++++
drivers/mfd/imx-ipu-v3/ipu-di.c | 507 ++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-dmfc.c | 343 +++++++++
drivers/mfd/imx-ipu-v3/ipu-dp.c | 468 +++++++++++++
drivers/mfd/imx-ipu-v3/ipu-prv.h | 214 ++++++
drivers/video/Kconfig | 11 +
drivers/video/Makefile | 1 +
drivers/video/modedb.c | 7 +-
drivers/video/mx5fb.c | 846 +++++++++++++++++++++++
include/linux/fb.h | 3 +
include/linux/mfd/imx-ipu-v3.h | 218 ++++++
28 files changed, 4593 insertions(+), 6 deletions(-)
create mode 100644 arch/arm/plat-mxc/devices/platform-imx_ipuv3.c
create mode 100644 arch/arm/plat-mxc/include/mach/ipu-v3.h
create mode 100644 drivers/mfd/imx-ipu-v3/Makefile
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-common.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-cpmem.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-dc.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-di.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-dmfc.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-dp.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-prv.h
create mode 100644 drivers/video/mx5fb.c
create mode 100644 include/linux/mfd/imx-ipu-v3.h


2010-12-09 13:47:52

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 8/9] ARM i.MX5: increase dma consistent size for IPU support

Signed-off-by: Sascha Hauer <[email protected]>
---
arch/arm/plat-mxc/include/mach/memory.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/arm/plat-mxc/include/mach/memory.h b/arch/arm/plat-mxc/include/mach/memory.h
index 9a9a000..a8152db 100644
--- a/arch/arm/plat-mxc/include/mach/memory.h
+++ b/arch/arm/plat-mxc/include/mach/memory.h
@@ -40,7 +40,7 @@
# endif
#endif

-#if defined(CONFIG_MX3_VIDEO)
+#if defined(CONFIG_MX3_VIDEO) || defined(CONFIG_MFD_IMX_IPU_V3)
/*
* Increase size of DMA-consistent memory region.
* This is required for mx3 camera driver to capture at least two QXGA frames.
--
1.7.2.3

2010-12-09 13:47:49

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 1/9] ARM i.MX51: Add ipu clock support

Signed-off-by: Sascha Hauer <[email protected]>
---
arch/arm/mach-mx5/clock-mx51-mx53.c | 140 +++++++++++++++++++++++++++++++++++
1 files changed, 140 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-mx5/clock-mx51-mx53.c b/arch/arm/mach-mx5/clock-mx51-mx53.c
index 9fc65bb..f550d02 100644
--- a/arch/arm/mach-mx5/clock-mx51-mx53.c
+++ b/arch/arm/mach-mx5/clock-mx51-mx53.c
@@ -39,6 +39,9 @@ static struct clk periph_apm_clk;
static struct clk ahb_clk;
static struct clk ipg_clk;
static struct clk usboh3_clk;
+static struct clk emi_fast_clk;
+static struct clk ipu_clk;
+static struct clk mipi_hsc1_clk;

#define MAX_DPLL_WAIT_TRIES 1000 /* 1000 * udelay(1) = 1ms */

@@ -688,6 +691,19 @@ static unsigned long clk_emi_slow_get_rate(struct clk *clk)
return clk_get_rate(clk->parent) / div;
}

+static unsigned long _clk_ddr_hf_get_rate(struct clk *clk)
+{
+ unsigned long rate;
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ div = ((reg & MXC_CCM_CBCDR_DDR_PODF_MASK) >>
+ MXC_CCM_CBCDR_DDR_PODF_OFFSET) + 1;
+ rate = clk_get_rate(clk->parent) / div;
+
+ return rate;
+}
+
/* External high frequency clock */
static struct clk ckih_clk = {
.get_rate = get_high_reference_clock_rate,
@@ -846,6 +862,109 @@ static struct clk emi_slow_clk = {
.get_rate = clk_emi_slow_get_rate,
};

+static int clk_ipu_enable(struct clk *clk)
+{
+ u32 reg;
+
+ _clk_ccgr_enable(clk);
+
+ /* Enable handshake with IPU when certain clock rates are changed */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg &= ~MXC_CCM_CCDR_IPU_HS_MASK;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ /* Enable handshake with IPU when LPM is entered */
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
+ __raw_writel(reg, MXC_CCM_CLPCR);
+
+ return 0;
+}
+
+static void clk_ipu_disable(struct clk *clk)
+{
+ u32 reg;
+
+ _clk_ccgr_disable(clk);
+
+ /* Disable handshake with IPU whe dividers are changed */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg |= MXC_CCM_CCDR_IPU_HS_MASK;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ /* Disable handshake with IPU when LPM is entered */
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ reg |= MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
+ __raw_writel(reg, MXC_CCM_CLPCR);
+}
+
+static struct clk ahbmux1_clk = {
+ .parent = &ahb_clk,
+ .secondary = &ahb_max_clk,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGRx_CG8_OFFSET,
+ .enable = _clk_ccgr_enable,
+ .disable = _clk_ccgr_disable_inwait,
+};
+
+static struct clk ipu_sec_clk = {
+ .parent = &emi_fast_clk,
+ .secondary = &ahbmux1_clk,
+};
+
+static struct clk ddr_hf_clk = {
+ .parent = &pll1_sw_clk,
+ .get_rate = _clk_ddr_hf_get_rate,
+};
+
+static struct clk ddr_clk = {
+ .parent = &ddr_hf_clk,
+};
+
+/* clock definitions for MIPI HSC unit which has been removed
+ * from documentation, but not from hardware
+ */
+static int _clk_hsc_enable(struct clk *clk)
+{
+ u32 reg;
+
+ _clk_ccgr_enable(clk);
+ /* Handshake with IPU when certain clock rates are changed. */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg &= ~MXC_CCM_CCDR_HSC_HS_MASK;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ reg &= ~MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS;
+ __raw_writel(reg, MXC_CCM_CLPCR);
+
+ return 0;
+}
+
+static void _clk_hsc_disable(struct clk *clk)
+{
+ u32 reg;
+
+ _clk_ccgr_disable(clk);
+ /* No handshake with HSC as its not enabled. */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg |= MXC_CCM_CCDR_HSC_HS_MASK;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ reg |= MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS;
+ __raw_writel(reg, MXC_CCM_CLPCR);
+}
+
+static struct clk mipi_hsp_clk = {
+ .parent = &ipu_clk,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGRx_CG6_OFFSET,
+ .enable = _clk_hsc_enable,
+ .disable = _clk_hsc_disable,
+ .secondary = &mipi_hsc1_clk,
+};
+
#define DEFINE_CLOCK_CCGR(name, i, er, es, pfx, p, s) \
static struct clk name = { \
.id = i, \
@@ -1077,6 +1196,23 @@ DEFINE_CLOCK_FULL(esdhc2_ipg_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG2_OFFSET,
DEFINE_CLOCK_MAX(esdhc2_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG3_OFFSET,
clk_esdhc2, &pll2_sw_clk, &esdhc2_ipg_clk);

+DEFINE_CLOCK(mipi_esc_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG5_OFFSET, NULL, NULL, NULL, &pll2_sw_clk);
+DEFINE_CLOCK(mipi_hsc2_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG4_OFFSET, NULL, NULL, &mipi_esc_clk, &pll2_sw_clk);
+DEFINE_CLOCK(mipi_hsc1_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG3_OFFSET, NULL, NULL, &mipi_hsc2_clk, &pll2_sw_clk);
+
+/* IPU */
+DEFINE_CLOCK_FULL(ipu_clk, 0, MXC_CCM_CCGR5, MXC_CCM_CCGRx_CG5_OFFSET,
+ NULL, NULL, clk_ipu_enable, clk_ipu_disable, &ahb_clk, &ipu_sec_clk);
+
+DEFINE_CLOCK_FULL(emi_fast_clk, 0, MXC_CCM_CCGR5, MXC_CCM_CCGRx_CG7_OFFSET,
+ NULL, NULL, _clk_ccgr_enable, _clk_ccgr_disable_inwait,
+ &ddr_clk, NULL);
+
+DEFINE_CLOCK(ipu_di0_clk, 0, MXC_CCM_CCGR6, MXC_CCM_CCGRx_CG5_OFFSET,
+ NULL, NULL, &pll3_sw_clk, NULL);
+DEFINE_CLOCK(ipu_di1_clk, 0, MXC_CCM_CCGR6, MXC_CCM_CCGRx_CG6_OFFSET,
+ NULL, NULL, &pll3_sw_clk, NULL);
+
#define _REGISTER_CLOCK(d, n, c) \
{ \
.dev_id = d, \
@@ -1117,6 +1253,10 @@ static struct clk_lookup mx51_lookups[] = {
_REGISTER_CLOCK(NULL, "iim_clk", iim_clk)
_REGISTER_CLOCK("imx2-wdt.0", NULL, dummy_clk)
_REGISTER_CLOCK("imx2-wdt.1", NULL, dummy_clk)
+ _REGISTER_CLOCK(NULL, "mipi_hsp", mipi_hsp_clk)
+ _REGISTER_CLOCK("imx-ipuv3", NULL, ipu_clk)
+ _REGISTER_CLOCK("imx-ipuv3", "di0", ipu_di0_clk)
+ _REGISTER_CLOCK("imx-ipuv3", "di1", ipu_di1_clk)
};

static struct clk_lookup mx53_lookups[] = {
--
1.7.2.3

2010-12-09 13:47:50

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 7/9] ARM i.MX5: Allow to increase max zone order

default setting of 11 allows us to allocate at maximum
2MB chunks of contiguous memory. For resolutions up to
1920x1080 32bpp we need much more memory, so make zone
order configurable

Signed-off-by: Sascha Hauer <[email protected]>
---
arch/arm/Kconfig | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index db524e7..b3e070e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1410,8 +1410,8 @@ config SPARSE_IRQ
source "mm/Kconfig"

config FORCE_MAX_ZONEORDER
- int "Maximum zone order" if ARCH_SHMOBILE
- range 11 64 if ARCH_SHMOBILE
+ int "Maximum zone order" if ARCH_SHMOBILE || ARCH_MX5
+ range 11 64 if ARCH_SHMOBILE || ARCH_MX5
default "9" if SA1111
default "11"
help
--
1.7.2.3

2010-12-09 13:47:47

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 5/9] Add i.MX5 framebuffer driver

This patch adds framebuffer support to the Freescale i.MX SoCs
equipped with an IPU v3, so far these are the i.MX50/51/53.

This driver has been tested on the i.MX51 babbage board with
both DVI and analog VGA in different resolutions and color depths.
It has also been tested on a custom i.MX51 board using a fixed
resolution panel.

Signed-off-by: Sascha Hauer <[email protected]>
---
drivers/video/Kconfig | 11 +
drivers/video/Makefile | 1 +
drivers/video/mx5fb.c | 846 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 858 insertions(+), 0 deletions(-)
create mode 100644 drivers/video/mx5fb.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 27c1fb4..1901915 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2236,6 +2236,17 @@ config FB_MX3
far only synchronous displays are supported. If you plan to use
an LCD display with your i.MX31 system, say Y here.

+config FB_MX5
+ tristate "MX5 Framebuffer support"
+ depends on FB && MFD_IMX_IPU_V3
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ select FB_MODE_HELPERS
+ help
+ This is a framebuffer device for the i.MX51 LCD Controller. If you
+ plan to use an LCD display with your i.MX51 system, say Y here.
+
config FB_BROADSHEET
tristate "E-Ink Broadsheet/Epson S1D13521 controller support"
depends on FB
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 485e8ed..ad408d2 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -145,6 +145,7 @@ obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o
obj-$(CONFIG_FB_BFIN_LQ035Q1) += bfin-lq035q1-fb.o
obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o
obj-$(CONFIG_FB_MX3) += mx3fb.o
+obj-$(CONFIG_FB_MX5) += mx5fb.o
obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o

# the test framebuffer is last
diff --git a/drivers/video/mx5fb.c b/drivers/video/mx5fb.c
new file mode 100644
index 0000000..fd9baf4
--- /dev/null
+++ b/drivers/video/mx5fb.c
@@ -0,0 +1,846 @@
+/*
+ * Copyright 2004-2009 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
+ *
+ * Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/console.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <asm/uaccess.h>
+#include <mach/ipu-v3.h>
+
+#define DRIVER_NAME "imx-ipuv3-fb"
+
+struct imx_ipu_fb_info {
+ int ipu_channel_num;
+ struct ipu_channel *ipu_ch;
+ int dc;
+ int ipu_di;
+ u32 ipu_di_pix_fmt;
+ u32 ipu_in_pix_fmt;
+
+ u32 pseudo_palette[16];
+
+ struct ipu_dp *dp;
+ struct dmfc_channel *dmfc;
+ struct fb_info *slave;
+ struct fb_info *master;
+ bool enabled;
+};
+
+static int imx_ipu_fb_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->ypanstep = 1;
+
+ return 0;
+}
+
+static int imx_ipu_fb_map_video_memory(struct fb_info *fbi)
+{
+ int size;
+
+ size = fbi->var.yres_virtual * fbi->fix.line_length;
+
+ if (fbi->screen_base) {
+ if (fbi->fix.smem_len >= size)
+ return 0;
+
+ dma_free_writecombine(fbi->device, fbi->fix.smem_len,
+ fbi->screen_base, fbi->fix.smem_start);
+ }
+
+ fbi->screen_base = dma_alloc_writecombine(fbi->device,
+ size,
+ (dma_addr_t *)&fbi->fix.smem_start,
+ GFP_DMA);
+ if (fbi->screen_base == 0) {
+ dev_err(fbi->device, "Unable to allocate framebuffer memory (%d)\n",
+ fbi->fix.smem_len);
+ fbi->fix.smem_len = 0;
+ fbi->fix.smem_start = 0;
+ return -ENOMEM;
+ }
+
+ fbi->fix.smem_len = size;
+ fbi->screen_size = fbi->fix.smem_len;
+
+ dev_dbg(fbi->device, "allocated fb @ paddr=0x%08lx, size=%d\n",
+ fbi->fix.smem_start, fbi->fix.smem_len);
+
+ /* Clear the screen */
+ memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
+
+ return 0;
+}
+
+static void imx_ipu_fb_enable(struct fb_info *fbi)
+{
+ struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+
+ if (mxc_fbi->enabled)
+ return;
+
+ ipu_di_enable(mxc_fbi->ipu_di);
+ ipu_dmfc_enable_channel(mxc_fbi->dmfc);
+ ipu_idmac_enable_channel(mxc_fbi->ipu_ch);
+ ipu_dc_enable_channel(mxc_fbi->dc);
+ ipu_dp_enable_channel(mxc_fbi->dp);
+ mxc_fbi->enabled = 1;
+}
+
+static void imx_ipu_fb_disable(struct fb_info *fbi)
+{
+ struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+
+ if (!mxc_fbi->enabled)
+ return;
+
+ ipu_dp_disable_channel(mxc_fbi->dp);
+ ipu_dc_disable_channel(mxc_fbi->dc);
+ ipu_idmac_disable_channel(mxc_fbi->ipu_ch);
+ ipu_dmfc_disable_channel(mxc_fbi->dmfc);
+ ipu_di_disable(mxc_fbi->ipu_di);
+
+ mxc_fbi->enabled = 0;
+}
+
+static int calc_vref(struct fb_var_screeninfo *var)
+{
+ unsigned long htotal, vtotal;
+
+ htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin;
+ vtotal = var->yres + var->lower_margin + var->vsync_len + var->upper_margin;
+
+ if (!htotal || !vtotal)
+ return 60;
+
+ return PICOS2KHZ(var->pixclock) * 1000 / vtotal / htotal;
+}
+
+static int calc_bandwidth(struct fb_var_screeninfo *var, unsigned int vref)
+{
+ return var->xres * var->yres * vref;
+}
+
+static int imx_ipu_fb_set_par(struct fb_info *fbi)
+{
+ int ret;
+ struct ipu_di_signal_cfg sig_cfg;
+ struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+ u32 out_pixel_fmt;
+ int interlaced = 0;
+ struct fb_var_screeninfo *var = &fbi->var;
+ int enabled = mxc_fbi->enabled;
+
+ dev_dbg(fbi->device, "Reconfiguring framebuffer %dx%d-%d\n",
+ fbi->var.xres, fbi->var.yres, fbi->var.bits_per_pixel);
+
+ if (enabled)
+ imx_ipu_fb_disable(fbi);
+
+ fbi->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+ var->yres_virtual = var->yres;
+
+ ret = imx_ipu_fb_map_video_memory(fbi);
+ if (ret)
+ return ret;
+
+ if (var->vmode & FB_VMODE_INTERLACED)
+ interlaced = 1;
+
+ memset(&sig_cfg, 0, sizeof(sig_cfg));
+ out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+
+ if (var->vmode & FB_VMODE_INTERLACED)
+ sig_cfg.interlaced = 1;
+ if (var->vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
+ sig_cfg.odd_field_first = 1;
+ if (var->sync & FB_SYNC_EXT)
+ sig_cfg.ext_clk = 1;
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ sig_cfg.Hsync_pol = 1;
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ sig_cfg.Vsync_pol = 1;
+ if (!(var->sync & FB_SYNC_CLK_LAT_FALL))
+ sig_cfg.clk_pol = 1;
+ if (var->sync & FB_SYNC_DATA_INVERT)
+ sig_cfg.data_pol = 1;
+ if (!(var->sync & FB_SYNC_OE_LOW_ACT))
+ sig_cfg.enable_pol = 1;
+ if (var->sync & FB_SYNC_CLK_IDLE_EN)
+ sig_cfg.clkidle_en = 1;
+
+ dev_dbg(fbi->device, "pixclock = %lu.%03lu MHz\n",
+ PICOS2KHZ(var->pixclock) / 1000,
+ PICOS2KHZ(var->pixclock) % 1000);
+
+ sig_cfg.width = var->xres;
+ sig_cfg.height = var->yres;
+ sig_cfg.pixel_fmt = out_pixel_fmt;
+ sig_cfg.h_start_width = var->left_margin;
+ sig_cfg.h_sync_width = var->hsync_len;
+ sig_cfg.h_end_width = var->right_margin;
+ sig_cfg.v_start_width = var->upper_margin;
+ sig_cfg.v_sync_width = var->vsync_len;
+ sig_cfg.v_end_width = var->lower_margin;
+ sig_cfg.v_to_h_sync = 0;
+
+ if (mxc_fbi->dp) {
+ ret = ipu_dp_setup_channel(mxc_fbi->dp, mxc_fbi->ipu_in_pix_fmt,
+ out_pixel_fmt, 1);
+ if (ret) {
+ dev_dbg(fbi->device, "initializing display processor failed with %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ ret = ipu_dc_init_sync(mxc_fbi->dc, mxc_fbi->ipu_di, interlaced,
+ out_pixel_fmt, fbi->var.xres);
+ if (ret) {
+ dev_dbg(fbi->device, "initializing display controller failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ipu_di_init_sync_panel(mxc_fbi->ipu_di,
+ PICOS2KHZ(var->pixclock) * 1000UL,
+ &sig_cfg);
+ if (ret) {
+ dev_dbg(fbi->device, "initializing panel failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ fbi->mode = (struct fb_videomode *)fb_match_mode(var, &fbi->modelist);
+ var->xoffset = var->yoffset = 0;
+
+ if (fbi->var.vmode & FB_VMODE_INTERLACED)
+ interlaced = 1;
+
+ ret = ipu_idmac_init_channel_buffer(mxc_fbi->ipu_ch,
+ mxc_fbi->ipu_in_pix_fmt,
+ var->xres, var->yres,
+ fbi->fix.line_length,
+ IPU_ROTATE_NONE,
+ fbi->fix.smem_start,
+ 0,
+ 0, 0, interlaced);
+ if (ret) {
+ dev_dbg(fbi->device, "init channel buffer failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ipu_dmfc_init_channel(mxc_fbi->dmfc, var->xres);
+ if (ret) {
+ dev_dbg(fbi->device, "initializing dmfc channel failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ipu_dmfc_alloc_bandwidth(mxc_fbi->dmfc, calc_bandwidth(var, calc_vref(var)));
+ if (ret) {
+ dev_dbg(fbi->device, "allocating dmfc bandwidth failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ if (enabled)
+ imx_ipu_fb_enable(fbi);
+
+ return ret;
+}
+
+/*
+ * These are the bitfields for each
+ * display depth that we support.
+ */
+struct imxfb_rgb {
+ struct fb_bitfield red;
+ struct fb_bitfield green;
+ struct fb_bitfield blue;
+ struct fb_bitfield transp;
+};
+
+static struct imxfb_rgb def_rgb_8 = {
+ .red = { .offset = 5, .length = 3, },
+ .green = { .offset = 2, .length = 3, },
+ .blue = { .offset = 0, .length = 2, },
+ .transp = { .offset = 0, .length = 0, },
+};
+
+static struct imxfb_rgb def_rgb_16 = {
+ .red = { .offset = 11, .length = 5, },
+ .green = { .offset = 5, .length = 6, },
+ .blue = { .offset = 0, .length = 5, },
+ .transp = { .offset = 0, .length = 0, },
+};
+
+static struct imxfb_rgb def_rgb_24 = {
+ .red = { .offset = 16, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 0, .length = 8, },
+ .transp = { .offset = 0, .length = 0, },
+};
+
+static struct imxfb_rgb def_rgb_32 = {
+ .red = { .offset = 16, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 0, .length = 8, },
+ .transp = { .offset = 24, .length = 8, },
+};
+
+/*
+ * Check framebuffer variable parameters and adjust to valid values.
+ *
+ * @param var framebuffer variable parameters
+ *
+ * @param info framebuffer information pointer
+ */
+static int imx_ipu_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct imx_ipu_fb_info *mxc_fbi = info->par;
+ struct imxfb_rgb *rgb;
+
+ /* we don't support xpan, force xres_virtual to be equal to xres */
+ var->xres_virtual = var->xres;
+
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ switch (var->bits_per_pixel) {
+ case 8:
+ rgb = &def_rgb_8;
+ break;
+ case 16:
+ rgb = &def_rgb_16;
+ mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_RGB565;
+ break;
+ case 24:
+ rgb = &def_rgb_24;
+ mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR24;
+ break;
+ case 32:
+ rgb = &def_rgb_32;
+ mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR32;
+ break;
+ default:
+ var->bits_per_pixel = 24;
+ rgb = &def_rgb_24;
+ mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR24;
+ }
+
+ var->red = rgb->red;
+ var->green = rgb->green;
+ var->blue = rgb->blue;
+ var->transp = rgb->transp;
+
+ return 0;
+}
+
+static inline unsigned int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static int imx_ipu_fb_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;
+}
+
+static int imx_ipu_fb_blank(int blank, struct fb_info *info)
+{
+ dev_dbg(info->device, "blank = %d\n", blank);
+
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ imx_ipu_fb_disable(info);
+ break;
+ case FB_BLANK_UNBLANK:
+ imx_ipu_fb_enable(info);
+ break;
+ }
+
+ return 0;
+}
+
+static int imx_ipu_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct imx_ipu_fb_info *mxc_fbi = info->par;
+ unsigned long base;
+ int ret;
+
+ if (info->var.yoffset == var->yoffset)
+ return 0; /* No change, do nothing */
+
+ base = var->yoffset * var->xres_virtual * var->bits_per_pixel / 8;
+ base += info->fix.smem_start;
+
+ ret = ipu_wait_for_interrupt(IPU_IRQ_EOF(mxc_fbi->ipu_channel_num), 100);
+ if (ret)
+ return ret;
+
+ if (ipu_idmac_update_channel_buffer(mxc_fbi->ipu_ch, 0, base)) {
+ dev_err(info->device,
+ "Error updating SDC buf to address=0x%08lX\n", base);
+ }
+
+ info->var.yoffset = var->yoffset;
+
+ return 0;
+}
+
+static struct fb_ops imx_ipu_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_set_par = imx_ipu_fb_set_par,
+ .fb_check_var = imx_ipu_fb_check_var,
+ .fb_setcolreg = imx_ipu_fb_setcolreg,
+ .fb_pan_display = imx_ipu_fb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_blank = imx_ipu_fb_blank,
+};
+
+/*
+ * Overlay functions
+ */
+static int imx_ipu_fb_enable_overlay(struct fb_info *fbi)
+{
+ struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+
+ ipu_dmfc_enable_channel(mxc_fbi->dmfc);
+ ipu_idmac_enable_channel(mxc_fbi->ipu_ch);
+ ipu_dp_enable_fg(mxc_fbi->dp);
+
+ return 0;
+}
+
+static int imx_ipu_fb_disable_overlay(struct fb_info *fbi)
+{
+ struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+
+ ipu_dp_disable_fg(mxc_fbi->dp);
+ ipu_idmac_disable_channel(mxc_fbi->ipu_ch);
+ ipu_dmfc_disable_channel(mxc_fbi->dmfc);
+
+ return 0;
+}
+
+static int imx_ipu_fb_set_par_overlay(struct fb_info *fbi)
+{
+ struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+ struct fb_var_screeninfo *var = &fbi->var;
+ struct fb_info *fbi_master = mxc_fbi->master;
+ struct fb_var_screeninfo *var_master = &fbi_master->var;
+ int ret;
+ int interlaced = 0;
+ int enabled = mxc_fbi->enabled;
+
+ dev_dbg(fbi->device, "Reconfiguring framebuffer %dx%d-%d\n",
+ fbi->var.xres, fbi->var.yres, fbi->var.bits_per_pixel);
+
+ if (enabled)
+ imx_ipu_fb_disable_overlay(fbi);
+
+ fbi->fix.line_length = var->xres_virtual *
+ var->bits_per_pixel / 8;
+
+ ret = imx_ipu_fb_map_video_memory(fbi);
+ if (ret)
+ return ret;
+
+ ipu_dp_set_window_pos(mxc_fbi->dp, 64, 64);
+
+ var->xoffset = var->yoffset = 0;
+
+ if (var->vmode & FB_VMODE_INTERLACED)
+ interlaced = 1;
+
+ ret = ipu_idmac_init_channel_buffer(mxc_fbi->ipu_ch,
+ mxc_fbi->ipu_in_pix_fmt,
+ var->xres, var->yres,
+ fbi->fix.line_length,
+ IPU_ROTATE_NONE,
+ fbi->fix.smem_start,
+ 0,
+ 0, 0, interlaced);
+ if (ret) {
+ dev_dbg(fbi->device, "init channel buffer failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ipu_dmfc_init_channel(mxc_fbi->dmfc, var->xres);
+ if (ret) {
+ dev_dbg(fbi->device, "initializing dmfc channel failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ipu_dmfc_alloc_bandwidth(mxc_fbi->dmfc, calc_bandwidth(var, calc_vref(var_master)));
+ if (ret) {
+ dev_dbg(fbi->device, "allocating dmfc bandwidth failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ if (enabled)
+ imx_ipu_fb_enable_overlay(fbi);
+
+ return ret;
+}
+
+static int imx_ipu_fb_blank_overlay(int blank, struct fb_info *fbi)
+{
+ dev_dbg(fbi->device, "blank = %d\n", blank);
+
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ imx_ipu_fb_disable_overlay(fbi);
+ break;
+ case FB_BLANK_UNBLANK:
+ imx_ipu_fb_enable_overlay(fbi);
+ break;
+ }
+
+ return 0;
+}
+
+static struct fb_ops imx_ipu_fb_overlay_ops = {
+ .owner = THIS_MODULE,
+ .fb_set_par = imx_ipu_fb_set_par_overlay,
+ .fb_check_var = imx_ipu_fb_check_var,
+ .fb_setcolreg = imx_ipu_fb_setcolreg,
+ .fb_pan_display = imx_ipu_fb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_blank = imx_ipu_fb_blank_overlay,
+};
+
+static struct fb_info *imx_ipu_fb_init_fbinfo(struct device *dev, struct fb_ops *ops)
+{
+ struct fb_info *fbi;
+ struct imx_ipu_fb_info *mxc_fbi;
+
+ fbi = framebuffer_alloc(sizeof(struct imx_ipu_fb_info), dev);
+ if (!fbi)
+ return NULL;
+
+ BUG_ON(fbi->par == NULL);
+ mxc_fbi = fbi->par;
+
+ fbi->var.activate = FB_ACTIVATE_NOW;
+
+ fbi->fbops = ops;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->pseudo_palette = mxc_fbi->pseudo_palette;
+
+ fb_alloc_cmap(&fbi->cmap, 16, 0);
+
+ return fbi;
+}
+
+static int imx_ipu_fb_init_overlay(struct platform_device *pdev,
+ struct fb_info *fbi_master, int ipu_channel)
+{
+ struct imx_ipu_fb_info *mxc_fbi_master = fbi_master->par;
+ struct fb_info *ovlfbi;
+ struct imx_ipu_fb_info *ovl_mxc_fbi;
+ int ret;
+
+ ovlfbi = imx_ipu_fb_init_fbinfo(&pdev->dev, &imx_ipu_fb_overlay_ops);
+ if (!ovlfbi)
+ return -ENOMEM;
+
+ ovl_mxc_fbi = ovlfbi->par;
+ ovl_mxc_fbi->ipu_ch = ipu_idmac_get(ipu_channel);
+ ovl_mxc_fbi->dmfc = ipu_dmfc_get(ipu_channel);
+ ovl_mxc_fbi->ipu_di = -1;
+ ovl_mxc_fbi->dp = mxc_fbi_master->dp;
+ ovl_mxc_fbi->master = fbi_master;
+ mxc_fbi_master->slave = ovlfbi;
+
+ ovlfbi->var.xres = 240;
+ ovlfbi->var.yres = 320;
+ ovlfbi->var.yres_virtual = ovlfbi->var.yres;
+ ovlfbi->var.xres_virtual = ovlfbi->var.xres;
+ imx_ipu_fb_check_var(&ovlfbi->var, ovlfbi);
+ imx_ipu_fb_set_fix(ovlfbi);
+
+ ret = register_framebuffer(ovlfbi);
+ if (ret) {
+ framebuffer_release(ovlfbi);
+ return ret;
+ }
+
+ ipu_dp_set_global_alpha(ovl_mxc_fbi->dp, 1, 0x80, 1);
+ ipu_dp_set_color_key(ovl_mxc_fbi->dp, 0, 0);
+
+ imx_ipu_fb_set_par_overlay(ovlfbi);
+
+ return 0;
+}
+
+static void imx_ipu_fb_exit_overlay(struct platform_device *pdev,
+ struct fb_info *fbi_master, int ipu_channel)
+{
+ struct imx_ipu_fb_info *mxc_fbi_master = fbi_master->par;
+ struct fb_info *ovlfbi = mxc_fbi_master->slave;
+ struct imx_ipu_fb_info *ovl_mxc_fbi = ovlfbi->par;
+
+ imx_ipu_fb_blank_overlay(FB_BLANK_POWERDOWN, ovlfbi);
+
+ unregister_framebuffer(ovlfbi);
+
+ ipu_idmac_put(ovl_mxc_fbi->ipu_ch);
+ ipu_dmfc_free_bandwidth(ovl_mxc_fbi->dmfc);
+ ipu_dmfc_put(ovl_mxc_fbi->dmfc);
+
+ framebuffer_release(ovlfbi);
+}
+
+static int imx_ipu_fb_find_mode(struct fb_info *fbi)
+{
+ int ret;
+ struct fb_videomode *mode_array;
+ struct fb_modelist *modelist;
+ struct fb_var_screeninfo *var = &fbi->var;
+ int i = 0;
+
+ list_for_each_entry(modelist, &fbi->modelist, list)
+ i++;
+
+ mode_array = kmalloc(sizeof (struct fb_modelist) * i, GFP_KERNEL);
+ if (!mode_array)
+ return -ENOMEM;
+
+ i = 0;
+ list_for_each_entry(modelist, &fbi->modelist, list)
+ mode_array[i++] = modelist->mode;
+
+ ret = fb_find_mode(&fbi->var, fbi, NULL, mode_array, i, NULL, 16);
+ if (ret == 0)
+ return -EINVAL;
+
+ dev_dbg(fbi->device, "found %dx%d-%d hs:%d:%d:%d vs:%d:%d:%d\n",
+ var->xres, var->yres, var->bits_per_pixel,
+ var->hsync_len, var->left_margin, var->right_margin,
+ var->vsync_len, var->upper_margin, var->lower_margin);
+
+ kfree(mode_array);
+
+ return 0;
+}
+
+static int __devinit imx_ipu_fb_probe(struct platform_device *pdev)
+{
+ struct fb_info *fbi;
+ struct imx_ipu_fb_info *mxc_fbi;
+ struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
+ int ret = 0, i;
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+ fbi = imx_ipu_fb_init_fbinfo(&pdev->dev, &imx_ipu_fb_ops);
+ if (!fbi)
+ return -ENOMEM;
+
+ mxc_fbi = fbi->par;
+
+ mxc_fbi->ipu_channel_num = plat_data->ipu_channel_bg;
+ mxc_fbi->dc = plat_data->dc_channel;
+ mxc_fbi->ipu_di_pix_fmt = plat_data->interface_pix_fmt;
+ mxc_fbi->ipu_di = pdev->id;
+
+ mxc_fbi->ipu_ch = ipu_idmac_get(plat_data->ipu_channel_bg);
+ if (IS_ERR(mxc_fbi->ipu_ch)) {
+ ret = PTR_ERR(mxc_fbi->ipu_ch);
+ goto failed_request_ipu;
+ }
+
+ mxc_fbi->dmfc = ipu_dmfc_get(plat_data->ipu_channel_bg);
+ if (IS_ERR(mxc_fbi->ipu_ch)) {
+ ret = PTR_ERR(mxc_fbi->ipu_ch);
+ goto failed_request_dmfc;
+ }
+
+ if (plat_data->dp_channel >= 0) {
+ mxc_fbi->dp = ipu_dp_get(plat_data->dp_channel);
+ if (IS_ERR(mxc_fbi->dp)) {
+ ret = PTR_ERR(mxc_fbi->ipu_ch);
+ goto failed_request_dp;
+ }
+ }
+
+ fbi->var.yres_virtual = fbi->var.yres;
+
+ INIT_LIST_HEAD(&fbi->modelist);
+ for (i = 0; i < plat_data->num_modes; i++)
+ fb_add_videomode(&plat_data->modes[i], &fbi->modelist);
+
+ if (plat_data->flags & IMX_IPU_FB_USE_MODEDB) {
+ for (i = 0; i < num_fb_modes; i++)
+ fb_add_videomode(&fb_modes[i], &fbi->modelist);
+ }
+
+ imx_ipu_fb_find_mode(fbi);
+
+ imx_ipu_fb_check_var(&fbi->var, fbi);
+ imx_ipu_fb_set_fix(fbi);
+ ret = register_framebuffer(fbi);
+ if (ret < 0)
+ goto failed_register;
+
+ imx_ipu_fb_set_par(fbi);
+ imx_ipu_fb_blank(FB_BLANK_UNBLANK, fbi);
+
+ if (plat_data->ipu_channel_fg >= 0 && plat_data->flags & IMX_IPU_FB_USE_OVERLAY)
+ imx_ipu_fb_init_overlay(pdev, fbi, plat_data->ipu_channel_fg);
+
+ platform_set_drvdata(pdev, fbi);
+
+ return 0;
+
+failed_register:
+ if (plat_data->dp_channel >= 0)
+ ipu_dp_put(mxc_fbi->dp);
+failed_request_dp:
+ ipu_dmfc_put(mxc_fbi->dmfc);
+failed_request_dmfc:
+ ipu_idmac_put(mxc_fbi->ipu_ch);
+failed_request_ipu:
+ fb_dealloc_cmap(&fbi->cmap);
+ framebuffer_release(fbi);
+
+ return ret;
+}
+
+static int __devexit imx_ipu_fb_remove(struct platform_device *pdev)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+ struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
+
+ if (plat_data->ipu_channel_fg >= 0 && plat_data->flags & IMX_IPU_FB_USE_OVERLAY)
+ imx_ipu_fb_exit_overlay(pdev, fbi, plat_data->ipu_channel_fg);
+
+ imx_ipu_fb_blank(FB_BLANK_POWERDOWN, fbi);
+
+ dma_free_writecombine(fbi->device, fbi->fix.smem_len,
+ fbi->screen_base, fbi->fix.smem_start);
+
+ if (&fbi->cmap)
+ fb_dealloc_cmap(&fbi->cmap);
+
+ unregister_framebuffer(fbi);
+
+ if (plat_data->dp_channel >= 0)
+ ipu_dp_put(mxc_fbi->dp);
+ ipu_dmfc_free_bandwidth(mxc_fbi->dmfc);
+ ipu_dmfc_put(mxc_fbi->dmfc);
+ ipu_idmac_put(mxc_fbi->ipu_ch);
+
+ framebuffer_release(fbi);
+
+ return 0;
+}
+
+static struct platform_driver imx_ipu_fb_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = imx_ipu_fb_probe,
+ .remove = __devexit_p(imx_ipu_fb_remove),
+};
+
+static int __init imx_ipu_fb_init(void)
+{
+ return platform_driver_register(&imx_ipu_fb_driver);
+}
+
+static void __exit imx_ipu_fb_exit(void)
+{
+ platform_driver_unregister(&imx_ipu_fb_driver);
+}
+
+module_init(imx_ipu_fb_init);
+module_exit(imx_ipu_fb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX framebuffer driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("fb");
--
1.7.2.3

2010-12-09 13:47:46

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 3/9] Add a mfd IPUv3 driver

The IPU is the Image Processing Unit found on i.MX50/51/53 SoCs. It
features several units for image processing, this patch adds support
for the units needed for Framebuffer support, namely:

- Display Controller (dc)
- Display Interface (di)
- Display Multi Fifo Controller (dmfc)
- Display Processor (dp)
- Image DMA Controller (idmac)

This patch is based on the Freescale driver, but follows a different
approach. The Freescale code implements logical idmac channels and
the handling of the subunits is hidden in common idmac code pathes
in big switch/case statements. This patch instead just provides code
and resource management for the different subunits. The user, in this
case the framebuffer driver, decides how the different units play
together.

The IPU has other units missing in this patch:

- CMOS Sensor Interface (csi)
- Video Deinterlacer (vdi)
- Sensor Multi FIFO Controler (smfc)
- Image Converter (ic)
- Image Rotator (irt)

So expect more files to come in this directory.

Signed-off-by: Sascha Hauer <[email protected]>
---
arch/arm/plat-mxc/include/mach/ipu-v3.h | 48 +++
drivers/mfd/Kconfig | 7 +
drivers/mfd/Makefile | 1 +
drivers/mfd/imx-ipu-v3/Makefile | 3 +
drivers/mfd/imx-ipu-v3/ipu-common.c | 661 +++++++++++++++++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-cpmem.c | 608 ++++++++++++++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-dc.c | 362 +++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-di.c | 507 ++++++++++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-dmfc.c | 343 ++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-dp.c | 468 ++++++++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-prv.h | 214 ++++++++++
include/linux/mfd/imx-ipu-v3.h | 218 ++++++++++
12 files changed, 3440 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/plat-mxc/include/mach/ipu-v3.h
create mode 100644 drivers/mfd/imx-ipu-v3/Makefile
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-common.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-cpmem.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-dc.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-di.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-dmfc.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-dp.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-prv.h
create mode 100644 include/linux/mfd/imx-ipu-v3.h

diff --git a/arch/arm/plat-mxc/include/mach/ipu-v3.h b/arch/arm/plat-mxc/include/mach/ipu-v3.h
new file mode 100644
index 0000000..0a6c3e8
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/ipu-v3.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]>
+ *
+ * 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 __MACH_IPU_V3_H_
+#define __MACH_IPU_V3_H_
+
+/* IPU specific extensions to struct fb_videomode flag field */
+#define FB_SYNC_OE_LOW_ACT (1 << 8)
+#define FB_SYNC_CLK_LAT_FALL (1 << 9)
+#define FB_SYNC_DATA_INVERT (1 << 10)
+#define FB_SYNC_CLK_IDLE_EN (1 << 11)
+#define FB_SYNC_SHARP_MODE (1 << 12)
+#define FB_SYNC_SWAP_RGB (1 << 13)
+
+struct ipuv3_fb_platform_data {
+ const struct fb_videomode *modes;
+ int num_modes;
+ char *mode_str;
+ u32 interface_pix_fmt;
+
+#define IMX_IPU_FB_USE_MODEDB (1 << 0)
+#define IMX_IPU_FB_USE_OVERLAY (1 << 1)
+ unsigned long flags;
+
+ int ipu_channel_bg;
+ int ipu_channel_fg;
+ int dc_channel;
+ int dp_channel;
+};
+
+struct imx_ipuv3_platform_data {
+ int rev;
+ struct ipuv3_fb_platform_data *fb0_platform_data;
+ struct ipuv3_fb_platform_data *fb1_platform_data;
+};
+
+#endif /* __MACH_IPU_V3_H_ */
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3a1493b..3c81879 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -606,6 +606,13 @@ config MFD_VX855
VIA VX855/VX875 south bridge. You will need to enable the vx855_spi
and/or vx855_gpio drivers for this to do anything useful.

+config MFD_IMX_IPU_V3
+ tristate "Support the Image Processing Unit (IPU) found on the i.MX51"
+ depends on ARCH_MX51
+ select MFD_CORE
+ help
+ Say yes here to support the IPU on i.MX51.
+
endif # MFD_SUPPORT

menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f54b365..7873b13 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -81,3 +81,4 @@ obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o
obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o
obj-$(CONFIG_MFD_VX855) += vx855.o
+obj-$(CONFIG_MFD_IMX_IPU_V3) += imx-ipu-v3/
diff --git a/drivers/mfd/imx-ipu-v3/Makefile b/drivers/mfd/imx-ipu-v3/Makefile
new file mode 100644
index 0000000..ff70fe8
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_MFD_IMX_IPU_V3) += imx-ipu-v3.o
+
+imx-ipu-v3-objs := ipu-common.o ipu-dc.o ipu-di.o ipu-dp.o ipu-dmfc.o ipu-cpmem.o
diff --git a/drivers/mfd/imx-ipu-v3/ipu-common.c b/drivers/mfd/imx-ipu-v3/ipu-common.c
new file mode 100644
index 0000000..e6edb88
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-common.c
@@ -0,0 +1,661 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]>
+ * Copyright (C) 2005-2009 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.
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <linux/clk.h>
+#include <linux/list.h>
+#include <mach/common.h>
+#include <linux/mfd/core.h>
+#include <mach/ipu-v3.h>
+
+#include "ipu-prv.h"
+
+static struct clk *ipu_clk;
+static struct device *ipu_dev;
+
+static DEFINE_SPINLOCK(ipu_lock);
+void __iomem *ipu_cm_reg;
+void __iomem *ipu_idmac_reg;
+
+static struct ipu_channel channels[64];
+
+struct ipu_channel *ipu_idmac_get(unsigned num)
+{
+ struct ipu_channel *channel;
+
+ dev_dbg(ipu_dev, "%s %d\n", __func__, num);
+
+ if (num > 63)
+ return ERR_PTR(-ENODEV);
+
+ channel = &channels[num];
+
+ if (channel->busy)
+ return ERR_PTR(-EBUSY);
+
+ channel->busy = 1;
+ channel->num = num;
+
+ clk_enable(ipu_clk);
+
+ return channel;
+}
+EXPORT_SYMBOL(ipu_idmac_get);
+
+void ipu_idmac_put(struct ipu_channel *channel)
+{
+ dev_dbg(ipu_dev, "%s %d\n", __func__, channel->num);
+
+ clk_disable(ipu_clk);
+ channel->busy = 0;
+}
+EXPORT_SYMBOL(ipu_idmac_put);
+
+void ipu_idmac_set_double_buffer(struct ipu_channel *channel, bool doublebuffer)
+{
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&ipu_lock, flags);
+
+ reg = ipu_cm_read(IPU_CHA_DB_MODE_SEL(channel->num));
+ if (doublebuffer)
+ reg |= idma_mask(channel->num);
+ else
+ reg &= ~idma_mask(channel->num);
+ ipu_cm_write(reg, IPU_CHA_DB_MODE_SEL(channel->num));
+
+ spin_unlock_irqrestore(&ipu_lock, flags);
+}
+EXPORT_SYMBOL(ipu_idmac_set_double_buffer);
+
+void ipu_uninit_channel(struct ipu_channel *channel)
+{
+ unsigned long lock_flags;
+ u32 val;
+
+ /* Make sure channel is disabled */
+
+ if (idmac_idma_is_set(IDMAC_CHA_EN, channel->num)) {
+ dev_err(ipu_dev,
+ "Channel %d is not disabled, disable first\n",
+ channel->num);
+ return;
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ /* Reset the double buffer */
+ val = ipu_cm_read(IPU_CHA_DB_MODE_SEL(channel->num));
+ val &= ~idma_mask(channel->num);
+ ipu_cm_write(val, IPU_CHA_DB_MODE_SEL(channel->num));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+EXPORT_SYMBOL(ipu_uninit_channel);
+
+int ipu_module_enable(u32 mask)
+{
+ unsigned long lock_flags;
+ u32 ipu_conf;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ ipu_conf = ipu_cm_read(IPU_CONF);
+ ipu_conf |= mask;
+ ipu_cm_write(ipu_conf, IPU_CONF);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+
+int ipu_module_disable(u32 mask)
+{
+ unsigned long lock_flags;
+ u32 ipu_conf;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ ipu_conf = ipu_cm_read(IPU_CONF);
+ ipu_conf &= ~mask;
+ ipu_cm_write(ipu_conf, IPU_CONF);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+
+void ipu_idmac_select_buffer(struct ipu_channel *channel, u32 buf_num)
+{
+ unsigned int chno = channel->num;
+
+ /* Mark buffer as ready. */
+ if (buf_num == 0)
+ ipu_cm_write(idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
+ else
+ ipu_cm_write(idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
+}
+EXPORT_SYMBOL(ipu_idmac_select_buffer);
+
+int ipu_idmac_enable_channel(struct ipu_channel *channel)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu_lock, flags);
+
+ val = ipu_idmac_read(IDMAC_CHA_EN(channel->num));
+ val |= idma_mask(channel->num);
+ ipu_idmac_write(val, IDMAC_CHA_EN(channel->num));
+
+ spin_unlock_irqrestore(&ipu_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_idmac_enable_channel);
+
+int ipu_idmac_disable_channel(struct ipu_channel *channel)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu_lock, flags);
+
+ /* Disable DMA channel(s) */
+ val = ipu_idmac_read(IDMAC_CHA_EN(channel->num));
+ val &= ~idma_mask(channel->num);
+ ipu_idmac_write(val, IDMAC_CHA_EN(channel->num));
+
+ /* Set channel buffers NOT to be ready */
+ ipu_cm_write(0xf0000000, IPU_GPR); /* write one to clear */
+
+ if (ipu_idma_is_set(IPU_CHA_BUF0_RDY, channel->num)) {
+ ipu_cm_write(idma_mask(channel->num),
+ IPU_CHA_BUF0_RDY(channel->num));
+ }
+ if (ipu_idma_is_set(IPU_CHA_BUF1_RDY, channel->num)) {
+ ipu_cm_write(idma_mask(channel->num),
+ IPU_CHA_BUF1_RDY(channel->num));
+ }
+
+ ipu_cm_write(0x0, IPU_GPR); /* write one to set */
+
+ spin_unlock_irqrestore(&ipu_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_idmac_disable_channel);
+
+static LIST_HEAD(ipu_irq_handlers);
+
+static void ipu_irq_update_irq_mask(void)
+{
+ struct ipu_irq_handler *handler;
+ int i;
+
+ DECLARE_IPU_IRQ_BITMAP(irqs);
+
+ bitmap_zero(irqs, IPU_IRQ_COUNT);
+
+ list_for_each_entry(handler, &ipu_irq_handlers, list)
+ bitmap_or(irqs, irqs, handler->ipu_irqs, IPU_IRQ_COUNT);
+
+ for (i = 0; i < BITS_TO_LONGS(IPU_IRQ_COUNT); i++)
+ ipu_cm_write(irqs[i], IPU_INT_CTRL(i + 1));
+}
+
+int ipu_irq_add_handler(struct ipu_irq_handler *ipuirq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu_lock, flags);
+
+ list_add_tail(&ipuirq->list, &ipu_irq_handlers);
+ ipu_irq_update_irq_mask();
+
+ spin_unlock_irqrestore(&ipu_lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL(ipu_irq_add_handler);
+
+void ipu_irq_remove_handler(struct ipu_irq_handler *handler)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu_lock, flags);
+
+ list_del(&handler->list);
+ ipu_irq_update_irq_mask();
+
+ spin_unlock_irqrestore(&ipu_lock, flags);
+}
+EXPORT_SYMBOL(ipu_irq_remove_handler);
+
+int ipu_irq_update_handler(struct ipu_irq_handler *handler,
+ unsigned long *bitmap)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu_lock, flags);
+
+ bitmap_copy(handler->ipu_irqs, bitmap, IPU_IRQ_COUNT);
+ ipu_irq_update_irq_mask();
+
+ spin_unlock_irqrestore(&ipu_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_irq_update_handler);
+
+static void ipu_completion_handler(unsigned long *bitmask, void *context)
+{
+ struct completion *completion = context;
+
+ complete(completion);
+}
+
+int ipu_wait_for_interrupt(int interrupt, int timeout_ms)
+{
+ struct ipu_irq_handler handler;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ int ret;
+
+ bitmap_zero(handler.ipu_irqs, IPU_IRQ_COUNT);
+ bitmap_set(handler.ipu_irqs, interrupt, 1);
+
+ handler.handler = ipu_completion_handler;
+ handler.context = &completion;
+ ipu_irq_add_handler(&handler);
+
+ ret = wait_for_completion_timeout(&completion,
+ msecs_to_jiffies(timeout_ms));
+
+ ipu_irq_remove_handler(&handler);
+
+ if (ret > 0)
+ ret = 0;
+
+ return ret;
+}
+EXPORT_SYMBOL(ipu_wait_for_interrupt);
+
+static irqreturn_t ipu_irq_handler(int irq, void *desc)
+{
+ DECLARE_IPU_IRQ_BITMAP(status);
+ struct ipu_irq_handler *handler;
+ int i;
+
+ for (i = 0; i < BITS_TO_LONGS(IPU_IRQ_COUNT); i++) {
+ status[i] = ipu_cm_read(IPU_INT_STAT(i + 1));
+ ipu_cm_write(status[i], IPU_INT_STAT(i + 1));
+ }
+
+ list_for_each_entry(handler, &ipu_irq_handlers, list) {
+ DECLARE_IPU_IRQ_BITMAP(tmp);
+ if (bitmap_and(tmp, status, handler->ipu_irqs, IPU_IRQ_COUNT))
+ handler->handler(tmp, handler->context);
+ }
+
+ return IRQ_HANDLED;
+}
+
+ipu_color_space_t format_to_colorspace(u32 fmt)
+{
+ switch (fmt) {
+ case IPU_PIX_FMT_RGB666:
+ case IPU_PIX_FMT_RGB565:
+ case IPU_PIX_FMT_BGR24:
+ case IPU_PIX_FMT_RGB24:
+ 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:
+ return RGB;
+
+ default:
+ return YCbCr;
+ }
+}
+
+static int ipu_reset(void)
+{
+ int timeout = 10000;
+ u32 val;
+
+ /* hard reset the IPU */
+ val = readl(MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR));
+ val |= 1 << 3;
+ writel(val, MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR));
+
+ ipu_cm_write(0x807FFFFF, IPU_MEM_RST);
+
+ while (ipu_cm_read(IPU_MEM_RST) & 0x80000000) {
+ if (!timeout--)
+ return -ETIME;
+ udelay(100);
+ }
+
+ return 0;
+}
+
+/*
+ * The MIPI HSC unit has been removed from the i.MX51 Reference Manual by
+ * the Freescale marketing division. However this did not remove the
+ * hardware from the chip which still needs to be configured...
+ */
+static int __devinit ipu_mipi_setup(void)
+{
+ struct clk *hsc_clk;
+ void __iomem *hsc_addr;
+ int ret = 0;
+
+ hsc_addr = ioremap(MX51_MIPI_HSC_BASE_ADDR, PAGE_SIZE);
+ if (!hsc_addr)
+ return -ENOMEM;
+
+ hsc_clk = clk_get_sys(NULL, "mipi_hsp");
+ if (IS_ERR(hsc_clk)) {
+ ret = PTR_ERR(hsc_clk);
+ goto unmap;
+ }
+ clk_enable(hsc_clk);
+
+ /* setup MIPI module to legacy mode */
+ __raw_writel(0xF00, hsc_addr);
+
+ /* CSI mode: reserved; DI control mode: legacy (from Freescale BSP) */
+ __raw_writel(__raw_readl(hsc_addr + 0x800) | 0x30ff,
+ hsc_addr + 0x800);
+
+ clk_disable(hsc_clk);
+ clk_put(hsc_clk);
+unmap:
+ iounmap(hsc_addr);
+
+ return ret;
+}
+
+static int ipu_submodules_init(struct platform_device *pdev,
+ unsigned long ipu_base, struct clk *ipu_clk)
+{
+ char *unit;
+ int ret;
+
+ ret = ipu_di_init(pdev, 0, ipu_base + IPU_DI0_REG_BASE,
+ IPU_CONF_DI0_EN, ipu_clk);
+ if (ret) {
+ unit = "di0";
+ goto err_di_0;
+ }
+
+ ret = ipu_di_init(pdev, 1, ipu_base + IPU_DI1_REG_BASE,
+ IPU_CONF_DI1_EN, ipu_clk);
+ if (ret) {
+ unit = "di1";
+ goto err_di_1;
+ }
+
+ ret = ipu_dc_init(pdev, ipu_base + IPU_DC_REG_BASE,
+ ipu_base + IPU_DC_TMPL_REG_BASE);
+ if (ret) {
+ unit = "dc_template";
+ goto err_dc;
+ }
+
+ ret = ipu_dmfc_init(pdev, ipu_base + IPU_DMFC_REG_BASE, ipu_clk);
+ if (ret) {
+ unit = "dmfc";
+ goto err_dmfc;
+ }
+
+ ret = ipu_dp_init(pdev, ipu_base + IPU_SRM_REG_BASE);
+ if (ret) {
+ unit = "dp";
+ goto err_dp;
+ }
+
+ ret = ipu_cpmem_init(pdev, ipu_base + IPU_CPMEM_REG_BASE);
+ if (ret) {
+ unit = "cpmem";
+ goto err_cpmem;
+ }
+
+ return 0;
+
+err_cpmem:
+ ipu_dp_exit(pdev);
+err_dp:
+ ipu_dmfc_exit(pdev);
+err_dmfc:
+ ipu_dc_exit(pdev);
+err_dc:
+ ipu_di_exit(pdev, 1);
+err_di_1:
+ ipu_di_exit(pdev, 0);
+err_di_0:
+ dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
+ return ret;
+}
+
+static void ipu_submodules_exit(struct platform_device *pdev,
+ unsigned long ipu_base)
+{
+ ipu_cpmem_exit(pdev);
+ ipu_dp_exit(pdev);
+ ipu_dmfc_exit(pdev);
+ ipu_dc_exit(pdev);
+ ipu_di_exit(pdev, 1);
+ ipu_di_exit(pdev, 0);
+}
+
+static int ipu_add_subdevice_pdata(struct platform_device *pdev,
+ const char *name, int id, void *pdata, size_t pdata_size)
+{
+ struct mfd_cell cell = {
+ .platform_data = pdata,
+ .data_size = pdata_size,
+ };
+
+ cell.name = name;
+
+ return mfd_add_devices(&pdev->dev, id, &cell, 1, NULL, 0);
+}
+
+static int ipu_add_client_devices(struct platform_device *pdev)
+{
+ struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data;
+
+ if (plat_data->fb0_platform_data) {
+ plat_data->fb0_platform_data->ipu_channel_bg =
+ MX51_IPU_CHANNEL_MEM_BG_SYNC;
+ plat_data->fb0_platform_data->ipu_channel_fg =
+ MX51_IPU_CHANNEL_MEM_FG_SYNC;
+ plat_data->fb0_platform_data->dc_channel = 5;
+ plat_data->fb0_platform_data->dp_channel = IPU_DP_FLOW_SYNC;
+
+ ipu_add_subdevice_pdata(pdev, "imx-ipuv3-fb", 0,
+ plat_data->fb0_platform_data,
+ sizeof(struct ipuv3_fb_platform_data));
+ }
+
+ if (plat_data->fb1_platform_data) {
+ plat_data->fb1_platform_data->ipu_channel_bg =
+ MX51_IPU_CHANNEL_MEM_DC_SYNC;
+ plat_data->fb1_platform_data->ipu_channel_fg = -1;
+ plat_data->fb1_platform_data->dc_channel = 1;
+ plat_data->fb1_platform_data->dp_channel = -1;
+
+ ipu_add_subdevice_pdata(pdev, "imx-ipuv3-fb", 1,
+ plat_data->fb1_platform_data,
+ sizeof(struct ipuv3_fb_platform_data));
+ }
+
+ return 0;
+}
+
+static int __devinit ipu_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ unsigned long ipu_base;
+ int ret, irq1, irq2;
+
+ spin_lock_init(&ipu_lock);
+
+ ipu_dev = &pdev->dev;
+
+ irq1 = platform_get_irq(pdev, 0);
+ irq2 = platform_get_irq(pdev, 1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res || irq1 < 0 || irq2 < 0)
+ return -ENODEV;
+
+ ipu_base = res->start;
+
+ ipu_cm_reg = ioremap(ipu_base + IPU_CM_REG_BASE, PAGE_SIZE);
+ if (!ipu_cm_reg) {
+ ret = -ENOMEM;
+ goto failed_ioremap1;
+ }
+
+ ipu_idmac_reg = ioremap(ipu_base + IPU_IDMAC_REG_BASE, PAGE_SIZE);
+ if (!ipu_idmac_reg) {
+ ret = -ENOMEM;
+ goto failed_ioremap2;
+ }
+
+ ret = ipu_mipi_setup();
+ if (ret)
+ goto failed_mipi_setup;
+
+ ipu_clk = clk_get(&pdev->dev, "ipu");
+ if (IS_ERR(ipu_clk)) {
+ ret = PTR_ERR(ipu_clk);
+ dev_err(&pdev->dev, "clk_get failed with %d", ret);
+ goto failed_clk_get;
+ }
+
+ clk_enable(ipu_clk);
+
+ ret = request_irq(irq1, ipu_irq_handler, IRQF_DISABLED, pdev->name,
+ &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "request irq %d failed with: %d\n", irq1, ret);
+ goto failed_request_irq1;
+ }
+
+ ret = request_irq(irq2, ipu_irq_handler, IRQF_DISABLED, pdev->name,
+ &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "request irq %d failed with: %d\n", irq2, ret);
+ goto failed_request_irq2;
+ }
+
+ ipu_reset();
+
+ ret = ipu_submodules_init(pdev, ipu_base, ipu_clk);
+ if (ret)
+ goto failed_submodules_init;
+
+ /* Set sync refresh channels as high priority */
+ ipu_idmac_write(0x18800000, IDMAC_CHA_PRI(0));
+
+ ret = ipu_add_client_devices(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "adding client devices failed with %d\n", ret);
+ goto failed_add_clients;
+ }
+
+ return 0;
+
+failed_add_clients:
+ ipu_submodules_exit(pdev, ipu_base);
+failed_submodules_init:
+ free_irq(irq2, &pdev->dev);
+failed_request_irq2:
+ free_irq(irq1, &pdev->dev);
+failed_request_irq1:
+ clk_put(ipu_clk);
+failed_clk_get:
+failed_mipi_setup:
+ iounmap(ipu_idmac_reg);
+failed_ioremap2:
+ iounmap(ipu_cm_reg);
+failed_ioremap1:
+
+ return ret;
+}
+
+static int __devexit ipu_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+ unsigned long ipu_base;
+ int irq1, irq2;
+
+ irq1 = platform_get_irq(pdev, 0);
+ irq2 = platform_get_irq(pdev, 1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ipu_base = res->start;
+
+ mfd_remove_devices(&pdev->dev);
+ ipu_submodules_exit(pdev, ipu_base);
+ free_irq(irq2, &pdev->dev);
+ free_irq(irq1, &pdev->dev);
+ clk_disable(ipu_clk);
+ clk_put(ipu_clk);
+ iounmap(ipu_idmac_reg);
+ iounmap(ipu_cm_reg);
+
+ return 0;
+}
+
+static struct platform_driver mxcipu_driver = {
+ .driver = {
+ .name = "imx-ipuv3",
+ },
+ .probe = ipu_probe,
+ .remove = __devexit_p(ipu_remove),
+};
+
+static int __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);
+
+MODULE_DESCRIPTION("i.MX IPU v3 driver");
+MODULE_AUTHOR("Sascha Hauer <[email protected]>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/imx-ipu-v3/ipu-cpmem.c b/drivers/mfd/imx-ipu-v3/ipu-cpmem.c
new file mode 100644
index 0000000..587b487
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-cpmem.c
@@ -0,0 +1,608 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]>
+ * Copyright (C) 2005-2009 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <linux/types.h>
+#include <linux/bitrev.h>
+#include <linux/io.h>
+
+#include "ipu-prv.h"
+
+#define __F(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size))
+
+#define IPU_FIELD_XV __F(0, 0, 10)
+#define IPU_FIELD_YV __F(0, 10, 9)
+#define IPU_FIELD_XB __F(0, 19, 13)
+#define IPU_FIELD_YB __F(0, 32, 12)
+#define IPU_FIELD_NSB_B __F(0, 44, 1)
+#define IPU_FIELD_CF __F(0, 45, 1)
+#define IPU_FIELD_UBO __F(0, 46, 22)
+#define IPU_FIELD_VBO __F(0, 68, 22)
+#define IPU_FIELD_IOX __F(0, 90, 4)
+#define IPU_FIELD_RDRW __F(0, 94, 1)
+#define IPU_FIELD_SO __F(0, 113, 1)
+#define IPU_FIELD_BNDM __F(0, 114, 3)
+#define IPU_FIELD_BM __F(0, 117, 2)
+#define IPU_FIELD_ROT __F(0, 119, 1)
+#define IPU_FIELD_HF __F(0, 120, 1)
+#define IPU_FIELD_VF __F(0, 121, 1)
+#define IPU_FIELD_THE __F(0, 122, 1)
+#define IPU_FIELD_CAP __F(0, 123, 1)
+#define IPU_FIELD_CAE __F(0, 124, 1)
+#define IPU_FIELD_FW __F(0, 125, 13)
+#define IPU_FIELD_FH __F(0, 138, 12)
+#define IPU_FIELD_EBA0 __F(1, 0, 29)
+#define IPU_FIELD_EBA1 __F(1, 29, 29)
+#define IPU_FIELD_ILO __F(1, 58, 20)
+#define IPU_FIELD_NPB __F(1, 78, 7)
+#define IPU_FIELD_PFS __F(1, 85, 4)
+#define IPU_FIELD_ALU __F(1, 89, 1)
+#define IPU_FIELD_ALBM __F(1, 90, 3)
+#define IPU_FIELD_ID __F(1, 93, 2)
+#define IPU_FIELD_TH __F(1, 95, 7)
+#define IPU_FIELD_SLY __F(1, 102, 14)
+#define IPU_FIELD_WID3 __F(1, 125, 3)
+#define IPU_FIELD_SLUV __F(1, 128, 14)
+#define IPU_FIELD_CRE __F(1, 149, 1)
+
+#define IPU_FIELD_XV __F(0, 0, 10)
+#define IPU_FIELD_YV __F(0, 10, 9)
+#define IPU_FIELD_XB __F(0, 19, 13)
+#define IPU_FIELD_YB __F(0, 32, 12)
+#define IPU_FIELD_NSB_B __F(0, 44, 1)
+#define IPU_FIELD_CF __F(0, 45, 1)
+#define IPU_FIELD_SX __F(0, 46, 12)
+#define IPU_FIELD_SY __F(0, 58, 11)
+#define IPU_FIELD_NS __F(0, 69, 10)
+#define IPU_FIELD_SDX __F(0, 79, 7)
+#define IPU_FIELD_SM __F(0, 86, 10)
+#define IPU_FIELD_SCC __F(0, 96, 1)
+#define IPU_FIELD_SCE __F(0, 97, 1)
+#define IPU_FIELD_SDY __F(0, 98, 7)
+#define IPU_FIELD_SDRX __F(0, 105, 1)
+#define IPU_FIELD_SDRY __F(0, 106, 1)
+#define IPU_FIELD_BPP __F(0, 107, 3)
+#define IPU_FIELD_DEC_SEL __F(0, 110, 2)
+#define IPU_FIELD_DIM __F(0, 112, 1)
+#define IPU_FIELD_SO __F(0, 113, 1)
+#define IPU_FIELD_BNDM __F(0, 114, 3)
+#define IPU_FIELD_BM __F(0, 117, 2)
+#define IPU_FIELD_ROT __F(0, 119, 1)
+#define IPU_FIELD_HF __F(0, 120, 1)
+#define IPU_FIELD_VF __F(0, 121, 1)
+#define IPU_FIELD_THE __F(0, 122, 1)
+#define IPU_FIELD_CAP __F(0, 123, 1)
+#define IPU_FIELD_CAE __F(0, 124, 1)
+#define IPU_FIELD_FW __F(0, 125, 13)
+#define IPU_FIELD_FH __F(0, 138, 12)
+#define IPU_FIELD_EBA0 __F(1, 0, 29)
+#define IPU_FIELD_EBA1 __F(1, 29, 29)
+#define IPU_FIELD_ILO __F(1, 58, 20)
+#define IPU_FIELD_NPB __F(1, 78, 7)
+#define IPU_FIELD_PFS __F(1, 85, 4)
+#define IPU_FIELD_ALU __F(1, 89, 1)
+#define IPU_FIELD_ALBM __F(1, 90, 3)
+#define IPU_FIELD_ID __F(1, 93, 2)
+#define IPU_FIELD_TH __F(1, 95, 7)
+#define IPU_FIELD_SL __F(1, 102, 14)
+#define IPU_FIELD_WID0 __F(1, 116, 3)
+#define IPU_FIELD_WID1 __F(1, 119, 3)
+#define IPU_FIELD_WID2 __F(1, 122, 3)
+#define IPU_FIELD_WID3 __F(1, 125, 3)
+#define IPU_FIELD_OFS0 __F(1, 128, 5)
+#define IPU_FIELD_OFS1 __F(1, 133, 5)
+#define IPU_FIELD_OFS2 __F(1, 138, 5)
+#define IPU_FIELD_OFS3 __F(1, 143, 5)
+#define IPU_FIELD_SXYS __F(1, 148, 1)
+#define IPU_FIELD_CRE __F(1, 149, 1)
+#define IPU_FIELD_DEC_SEL2 __F(1, 150, 1)
+
+static u32 *ipu_cpmem_base;
+static struct device *ipu_dev;
+
+struct ipu_ch_param_word {
+ u32 data[5];
+ u32 res[3];
+};
+
+struct ipu_ch_param {
+ struct ipu_ch_param_word word[2];
+};
+
+static u32 ipu_bytes_per_pixel(u32 fmt)
+{
+ switch (fmt) {
+ case IPU_PIX_FMT_GENERIC: /* generic data */
+ case IPU_PIX_FMT_RGB332:
+ case IPU_PIX_FMT_YUV420P:
+ case IPU_PIX_FMT_YUV422P:
+ return 1;
+
+ case IPU_PIX_FMT_RGB565:
+ case IPU_PIX_FMT_YUYV:
+ case IPU_PIX_FMT_UYVY:
+ return 2;
+
+ case IPU_PIX_FMT_BGR24:
+ case IPU_PIX_FMT_RGB24:
+ return 3;
+
+ 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:
+ return 4;
+
+ default:
+ return 1;
+ }
+}
+
+bool ipu_pixel_format_has_alpha(u32 fmt)
+{
+ switch (fmt) {
+ case IPU_PIX_FMT_RGBA32:
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_ABGR32:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+#define ipu_ch_param_addr(ch) (((struct ipu_ch_param *)ipu_cpmem_base) + (ch))
+
+static inline void ipu_ch_param_set_field(struct ipu_ch_param *base, u32 wbs, u32 v)
+{
+ u32 bit = (wbs >> 8) % 160;
+ u32 size = wbs & 0xff;
+ u32 word = (wbs >> 8) / 160;
+ u32 i = bit / 32;
+ u32 ofs = bit % 32;
+ u32 mask = (1 << size) - 1;
+
+ pr_debug("%s %d %d %d\n", __func__, word, bit , size);
+
+ base->word[word].data[i] &= ~(mask << ofs);
+ base->word[word].data[i] |= v << ofs;
+
+ if ((bit + size - 1) / 32 > i) {
+ base->word[word].data[i + 1] &= ~(v >> (mask ? (32 - ofs) : 0));
+ base->word[word].data[i + 1] |= v >> (ofs ? (32 - ofs) : 0);
+ }
+}
+
+static inline u32 ipu_ch_param_read_field(struct ipu_ch_param *base, u32 wbs)
+{
+ u32 bit = (wbs >> 8) % 160;
+ u32 size = wbs & 0xff;
+ u32 word = (wbs >> 8) / 160;
+ u32 i = bit / 32;
+ u32 ofs = bit % 32;
+ u32 mask = (1 << size) - 1;
+ u32 val = 0;
+
+ pr_debug("%s %d %d %d\n", __func__, word, bit , size);
+
+ val = (base->word[word].data[i] >> ofs) & mask;
+
+ if ((bit + size - 1) / 32 > i) {
+ u32 tmp;
+ tmp = base->word[word].data[i + 1];
+ tmp &= mask >> (ofs ? (32 - ofs) : 0);
+ val |= tmp << (ofs ? (32 - ofs) : 0);
+ }
+
+ return val;
+}
+
+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, IPU_FIELD_WID0, red_width - 1);
+ ipu_ch_param_set_field(p, IPU_FIELD_OFS0, red_offset);
+ /* Setup green width and offset */
+ ipu_ch_param_set_field(p, IPU_FIELD_WID1, green_width - 1);
+ ipu_ch_param_set_field(p, IPU_FIELD_OFS1, green_offset);
+ /* Setup blue width and offset */
+ ipu_ch_param_set_field(p, IPU_FIELD_WID2, blue_width - 1);
+ ipu_ch_param_set_field(p, IPU_FIELD_OFS2, blue_offset);
+ /* Setup alpha width and offset */
+ ipu_ch_param_set_field(p, IPU_FIELD_WID3, alpha_width - 1);
+ ipu_ch_param_set_field(p, IPU_FIELD_OFS3, alpha_offset);
+}
+
+static inline void ipu_ch_param_dump(int ch)
+{
+ struct ipu_ch_param *p = ipu_ch_param_addr(ch);
+ pr_debug("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]);
+ pr_debug("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]);
+ pr_debug("PFS 0x%x\n", ipu_ch_param_read_field(p, IPU_FIELD_PFS));
+ pr_debug("BPP 0x%x\n", ipu_ch_param_read_field(p, IPU_FIELD_BPP));
+ pr_debug("NPB 0x%x\n", ipu_ch_param_read_field(p, IPU_FIELD_NPB));
+
+ pr_debug("FW %d\n", ipu_ch_param_read_field(p, IPU_FIELD_FW));
+ pr_debug("FH %d\n", ipu_ch_param_read_field(p, IPU_FIELD_FH));
+ pr_debug("Stride %d\n", ipu_ch_param_read_field(p, IPU_FIELD_SL));
+
+ pr_debug("Width0 %d+1\n", ipu_ch_param_read_field(p, IPU_FIELD_WID0));
+ pr_debug("Width1 %d+1\n", ipu_ch_param_read_field(p, IPU_FIELD_WID1));
+ pr_debug("Width2 %d+1\n", ipu_ch_param_read_field(p, IPU_FIELD_WID2));
+ pr_debug("Width3 %d+1\n", ipu_ch_param_read_field(p, IPU_FIELD_WID3));
+ pr_debug("Offset0 %d\n", ipu_ch_param_read_field(p, IPU_FIELD_OFS0));
+ pr_debug("Offset1 %d\n", ipu_ch_param_read_field(p, IPU_FIELD_OFS1));
+ pr_debug("Offset2 %d\n", ipu_ch_param_read_field(p, IPU_FIELD_OFS2));
+ pr_debug("Offset3 %d\n", ipu_ch_param_read_field(p, IPU_FIELD_OFS3));
+}
+
+static inline void ipu_ch_param_set_burst_size(u32 ch,
+ u16 burst_pixels)
+{
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_NPB,
+ burst_pixels - 1);
+};
+
+static inline int ipu_ch_param_get_burst_size(u32 ch)
+{
+ return ipu_ch_param_read_field(ipu_ch_param_addr(ch), IPU_FIELD_NPB) + 1;
+};
+
+static inline int ipu_ch_param_get_bpp(u32 ch)
+{
+ return ipu_ch_param_read_field(ipu_ch_param_addr(ch), IPU_FIELD_BPP);
+};
+
+static inline void ipu_ch_param_set_buffer(u32 ch, int bufNum,
+ dma_addr_t phyaddr)
+{
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch),
+ bufNum ? IPU_FIELD_EBA1 : IPU_FIELD_EBA0,
+ phyaddr / 8);
+};
+
+#define IPU_FIELD_ROT_HF_VF __F(0, 119, 3)
+
+static inline void ipu_ch_param_set_rotation(u32 ch,
+ ipu_rotate_mode_t rot)
+{
+ u32 temp_rot = bitrev8(rot) >> 5;
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ROT_HF_VF, temp_rot);
+};
+
+static inline void ipu_ch_param_set_block_mode(u32 ch)
+{
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_BM, 1);
+};
+
+static inline void ipu_ch_param_set_alpha_use_separate_channel(u32 ch,
+ bool option)
+{
+ if (option)
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ALU, 1);
+ else
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ALU, 0);
+};
+
+static inline void ipu_ch_param_set_alpha_condition_read(u32 ch)
+{
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_CRE, 1);
+};
+
+static inline void ipu_ch_param_set_alpha_buffer_memory(u32 ch)
+{
+ int alp_mem_idx;
+
+ 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 correlated channel of local alpha channel\n");
+ return;
+ }
+
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ALBM, alp_mem_idx);
+};
+
+static inline void ipu_ch_param_set_interlaced_scan(u32 ch)
+{
+ u32 stride;
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_SO, 1);
+ stride = ipu_ch_param_read_field(ipu_ch_param_addr(ch), IPU_FIELD_SL) + 1;
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ILO, stride / 8);
+ stride *= 2;
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_SLY, stride - 1);
+};
+
+static inline void ipu_ch_param_set_high_priority(u32 ch)
+{
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ID, 1);
+};
+
+static inline void ipu_ch_params_set_alpha_width(u32 ch, int alpha_width)
+{
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_WID3,
+ alpha_width - 1);
+}
+
+static int ipu_ch_param_init(int ch,
+ u32 pixel_fmt, u32 width,
+ u32 height, u32 stride,
+ u32 u, u32 v,
+ u32 uv_stride, dma_addr_t addr0,
+ dma_addr_t addr1)
+{
+ u32 u_offset = 0;
+ u32 v_offset = 0;
+ struct ipu_ch_param params;
+
+ memset(&params, 0, sizeof(params));
+
+ ipu_ch_param_set_field(&params, IPU_FIELD_FW, width - 1);
+ ipu_ch_param_set_field(&params, IPU_FIELD_FH, height - 1);
+ ipu_ch_param_set_field(&params, IPU_FIELD_SLY, stride - 1);
+ ipu_ch_param_set_field(&params, IPU_FIELD_EBA0, addr0 >> 3);
+ ipu_ch_param_set_field(&params, IPU_FIELD_EBA1, addr1 >> 3);
+
+ switch (pixel_fmt) {
+ case IPU_PIX_FMT_GENERIC:
+ /* Represents 8-bit Generic data */
+ ipu_ch_param_set_field(&params, IPU_FIELD_BPP, 5); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 6); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 63); /* 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, IPU_FIELD_BPP, 3); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 15); /* burst size */
+
+ ipu_ch_params_set_packing(&params, 5, 0, 6, 5, 5, 11, 8, 16);
+ break;
+ case IPU_PIX_FMT_BGR24:
+ ipu_ch_param_set_field(&params, IPU_FIELD_BPP, 1); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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, IPU_FIELD_BPP, 1); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 19); /* burst size */
+
+ ipu_ch_params_set_packing(&params, 8, 16, 8, 8, 8, 0, 8, 24);
+ break;
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_BGR32:
+ ipu_ch_param_set_field(&params, IPU_FIELD_BPP, 0); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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, IPU_FIELD_BPP, 0); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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, IPU_FIELD_BPP, 0); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); /* pix format */
+
+ 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, IPU_FIELD_BPP, 3); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 0xA); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 15); /* burst size */
+ break;
+ case IPU_PIX_FMT_YUYV:
+ ipu_ch_param_set_field(&params, IPU_FIELD_BPP, 3); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 0x8); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 31); /* burst size */
+ break;
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YUV420P:
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 2); /* pix format */
+
+ if (uv_stride < stride / 2)
+ uv_stride = stride / 2;
+
+ u_offset = stride * height;
+ v_offset = u_offset + (uv_stride * height / 2);
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 31); /* burst size */
+ break;
+ case IPU_PIX_FMT_YVU422P:
+ /* BPP & pixel format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 1); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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, IPU_FIELD_PFS, 1); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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_NV12:
+ /* BPP & pixel format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 4); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 31); /* burst size */
+ uv_stride = stride;
+ u_offset = (u == 0) ? stride * height : u;
+ break;
+ default:
+ dev_err(ipu_dev, "mxc ipu: unimplemented pixel format: %d\n",
+ pixel_fmt);
+ return -EINVAL;
+ }
+ /* set burst size to 16 */
+ if (uv_stride)
+ ipu_ch_param_set_field(&params, IPU_FIELD_SLUV, uv_stride - 1);
+
+ if (u > u_offset)
+ u_offset = u;
+
+ if (v > v_offset)
+ v_offset = v;
+
+ ipu_ch_param_set_field(&params, IPU_FIELD_UBO, u_offset / 8);
+ ipu_ch_param_set_field(&params, IPU_FIELD_VBO, v_offset / 8);
+
+ pr_debug("initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ch));
+ memcpy(ipu_ch_param_addr(ch), &params, sizeof(params));
+ return 0;
+}
+
+/*
+ * This function is called to initialize a buffer for a IPU channel.
+ *
+ * @param channel The IPU channel.
+ *
+ * @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 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
+ */
+int ipu_idmac_init_channel_buffer(struct ipu_channel *channel,
+ u32 pixel_fmt,
+ u16 width, u16 height,
+ u32 stride,
+ ipu_rotate_mode_t rot_mode,
+ dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
+ u32 u, u32 v, bool interlaced)
+{
+ int ret;
+ u32 dma_chan = channel->num;
+
+ if (stride < width * ipu_bytes_per_pixel(pixel_fmt))
+ stride = width * ipu_bytes_per_pixel(pixel_fmt);
+
+ if (stride % 4) {
+ dev_err(ipu_dev,
+ "Stride not 32-bit aligned, stride = %d\n", stride);
+ return -EINVAL;
+ }
+
+ /* Build parameter memory data for DMA channel */
+ ret = ipu_ch_param_init(dma_chan, pixel_fmt, width, height, stride, u, v, 0,
+ phyaddr_0, phyaddr_1);
+ if (ret)
+ return ret;
+
+ if (rot_mode)
+ ipu_ch_param_set_rotation(dma_chan, rot_mode);
+
+ if (interlaced)
+ ipu_ch_param_set_interlaced_scan(dma_chan);
+
+ if (idmac_idma_is_set(IDMAC_CHA_PRI, dma_chan))
+ ipu_ch_param_set_high_priority(dma_chan);
+
+ ipu_ch_param_dump(dma_chan);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_idmac_init_channel_buffer);
+
+int ipu_idmac_update_channel_buffer(struct ipu_channel *channel,
+ u32 buf_num, dma_addr_t phyaddr)
+{
+ u32 dma_chan = channel->num;
+
+ ipu_ch_param_set_buffer(dma_chan, buf_num, phyaddr);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_idmac_update_channel_buffer);
+
+int ipu_cpmem_init(struct platform_device *pdev, unsigned long base)
+{
+ ipu_cpmem_base = ioremap(base, PAGE_SIZE);
+ if (!ipu_cpmem_base)
+ return -ENOMEM;
+ ipu_dev = &pdev->dev;
+ return 0;
+}
+
+void ipu_cpmem_exit(struct platform_device *pdev)
+{
+ iounmap(ipu_cpmem_base);
+}
diff --git a/drivers/mfd/imx-ipu-v3/ipu-dc.c b/drivers/mfd/imx-ipu-v3/ipu-dc.c
new file mode 100644
index 0000000..23ba5d7
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-dc.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]>
+ * Copyright (C) 2005-2009 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.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <asm/atomic.h>
+
+#include "ipu-prv.h"
+
+#define ASYNC_SER_WAVE 6
+
+#define DC_DISP_ID_SERIAL 2
+#define DC_DISP_ID_ASYNC 3
+
+#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 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_WR_CH_CONF(ch) (ipu_dc_reg + dc_channels[ch].channel_offset)
+#define DC_WR_CH_ADDR(ch) (ipu_dc_reg + dc_channels[ch].channel_offset + 4)
+#define DC_RL_CH(ch, evt) (ipu_dc_reg + dc_channels[ch].channel_offset + 8 + ((evt) & ~0x1) * 2)
+
+#define DC_GEN (ipu_dc_reg + 0x00D4)
+#define DC_DISP_CONF1(disp) (ipu_dc_reg + 0x00D8 + disp * 4)
+#define DC_DISP_CONF2(disp) (ipu_dc_reg + 0x00E8 + disp * 4)
+#define DC_STAT (ipu_dc_reg + 0x01C8)
+
+#define WROD(lf) (0x18 | (lf << 1))
+
+#define DC_WR_CH_CONF_FIELD_MODE (1 << 9)
+#define DC_WR_CH_CONF_PROG_TYPE_OFFSET 5
+#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5)
+#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2)
+#define DC_WR_CH_CONF_PROG_DISP_ID_OFFSET 3
+#define DC_WR_CH_CONF_PROG_DISP_ID_MASK (3 << 3)
+
+static void __iomem *ipu_dc_reg;
+static void __iomem *ipu_dc_tmpl_reg;
+static struct device *ipu_dev;
+
+struct ipu_dc {
+ unsigned int di; /* The display interface number assigned to this dc channel */
+ unsigned int channel_offset;
+};
+
+static struct ipu_dc dc_channels[10];
+
+static void ipu_dc_link_event(int chan, int event, int addr, int priority)
+{
+ u32 reg;
+
+ reg = __raw_readl(DC_RL_CH(chan, event));
+ reg &= ~(0xFFFF << (16 * (event & 0x1)));
+ reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
+ __raw_writel(reg, DC_RL_CH(chan, event));
+}
+
+static void ipu_dc_write_tmpl(int word, u32 opcode, u32 operand, int map,
+ int wave, int glue, int sync)
+{
+ u32 reg;
+ int stop = 1;
+
+ reg = sync;
+ reg |= (glue << 4);
+ reg |= (++wave << 11);
+ reg |= (++map << 15);
+ reg |= (operand << 20) & 0xFFF00000;
+ __raw_writel(reg, ipu_dc_tmpl_reg + word * 8);
+
+ reg = (operand >> 12);
+ reg |= opcode << 4;
+ reg |= (stop << 9);
+ __raw_writel(reg, ipu_dc_tmpl_reg + word * 8 + 4);
+}
+
+static int ipu_pixfmt_to_map(u32 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;
+ }
+
+ return -EINVAL;
+}
+
+#define SYNC_WAVE 0
+
+int ipu_dc_init_sync(int dc_chan, int di, bool interlaced, u32 pixel_fmt, u32 width)
+{
+ u32 reg = 0, map;
+
+ dc_channels[dc_chan].di = di;
+
+ map = ipu_pixfmt_to_map(pixel_fmt);
+ if (map < 0) {
+ dev_dbg(ipu_dev, "IPU_DISP: No MAP\n");
+ return -EINVAL;
+ }
+
+ if (interlaced) {
+ ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 3);
+ ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 2);
+ ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 1);
+
+ /* Init template microcode */
+ ipu_dc_write_tmpl(0, WROD(0), 0, map, SYNC_WAVE, 0, 8);
+ } else {
+ if (di) {
+ ipu_dc_link_event(dc_chan, DC_EVT_NL, 2, 3);
+ ipu_dc_link_event(dc_chan, DC_EVT_EOL, 3, 2);
+ ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 4, 1);
+ /* Init template microcode */
+ ipu_dc_write_tmpl(2, WROD(0), 0, map, SYNC_WAVE, 8, 5);
+ ipu_dc_write_tmpl(3, WROD(0), 0, map, SYNC_WAVE, 4, 5);
+ ipu_dc_write_tmpl(4, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+ } else {
+ ipu_dc_link_event(dc_chan, DC_EVT_NL, 5, 3);
+ ipu_dc_link_event(dc_chan, DC_EVT_EOL, 6, 2);
+ ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 7, 1);
+ /* Init template microcode */
+ ipu_dc_write_tmpl(5, WROD(0), 0, map, SYNC_WAVE, 8, 5);
+ ipu_dc_write_tmpl(6, WROD(0), 0, map, SYNC_WAVE, 4, 5);
+ ipu_dc_write_tmpl(7, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+ }
+ }
+ ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0);
+ ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0);
+ ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0);
+ ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0);
+ ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0);
+ ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0);
+
+ reg = 0x2;
+ reg |= di << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET;
+ reg |= di << 2;
+ if (interlaced)
+ reg |= DC_WR_CH_CONF_FIELD_MODE;
+
+ __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+
+ __raw_writel(0x00000000, DC_WR_CH_ADDR(dc_chan));
+
+ __raw_writel(0x00000084, DC_GEN);
+
+ __raw_writel(width, DC_DISP_CONF2(di));
+
+ ipu_module_enable(IPU_CONF_DC_EN);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dc_init_sync);
+
+void ipu_dc_init_async(int dc_chan, int di, bool interlaced)
+{
+ u32 reg = 0;
+ dc_channels[dc_chan].di = di;
+ ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0x64, 1);
+ ipu_dc_link_event(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;
+ __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+
+ __raw_writel(0x00000000, DC_WR_CH_ADDR(dc_chan));
+
+ __raw_writel(0x00000084, DC_GEN);
+
+ ipu_module_enable(IPU_CONF_DC_EN);
+}
+EXPORT_SYMBOL(ipu_dc_init_async);
+
+void ipu_dc_enable_channel(u32 dc_chan)
+{
+ int di;
+ u32 reg;
+
+ di = dc_channels[dc_chan].di;
+
+ /* Make sure other DC sync channel is not assigned same DI */
+ reg = __raw_readl(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;
+ __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan));
+ }
+
+ reg = __raw_readl(DC_WR_CH_CONF(dc_chan));
+ reg |= 4 << DC_WR_CH_CONF_PROG_TYPE_OFFSET;
+ __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+}
+EXPORT_SYMBOL(ipu_dc_enable_channel);
+
+void ipu_dc_disable_channel(u32 dc_chan)
+{
+ u32 reg;
+ int irq = 0, ret, timeout = 50;
+
+ if (dc_chan == 1) {
+ irq = IPU_IRQ_DC_FC_1;
+ } else if (dc_chan == 5) {
+ irq = IPU_IRQ_DP_SF_END;
+ } else {
+ return;
+ }
+
+ ret = ipu_wait_for_interrupt(irq, 50);
+ if (ret)
+ return;
+
+ /* Wait for DC triple buffer to empty */
+ if (dc_channels[dc_chan].di == 0)
+ while ((__raw_readl(DC_STAT) & 0x00000002)
+ != 0x00000002) {
+ msleep(2);
+ timeout -= 2;
+ if (timeout <= 0)
+ break;
+ }
+ else if (dc_channels[dc_chan].di == 1)
+ while ((__raw_readl(DC_STAT) & 0x00000020)
+ != 0x00000020) {
+ msleep(2);
+ timeout -= 2;
+ if (timeout <= 0)
+ break;
+ }
+
+ reg = __raw_readl(DC_WR_CH_CONF(dc_chan));
+ reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+ __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+}
+EXPORT_SYMBOL(ipu_dc_disable_channel);
+
+static void ipu_dc_map_config(int map, int byte_num, int offset, int mask)
+{
+ int ptr = map * 3 + byte_num;
+ u32 reg;
+
+ reg = __raw_readl(DC_MAP_CONF_VAL(ptr));
+ reg &= ~(0xffff << (16 * (ptr & 0x1)));
+ reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
+ __raw_writel(reg, DC_MAP_CONF_VAL(ptr));
+
+ reg = __raw_readl(DC_MAP_CONF_PTR(map));
+ reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num)));
+ reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
+ __raw_writel(reg, DC_MAP_CONF_PTR(map));
+}
+
+static void ipu_dc_map_clear(int map)
+{
+ u32 reg = __raw_readl(DC_MAP_CONF_PTR(map));
+ __raw_writel(reg & ~(0xffff << (16 * (map & 0x1))),
+ DC_MAP_CONF_PTR(map));
+}
+
+int ipu_dc_init(struct platform_device *pdev, unsigned long base, unsigned long template_base)
+{
+ static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, 0x78, 0, 0x94, 0xb4};
+ int i;
+
+ ipu_dc_reg = ioremap(base, PAGE_SIZE);
+ if (!ipu_dc_reg)
+ return -ENOMEM;
+
+ ipu_dev = &pdev->dev;
+
+ ipu_dc_tmpl_reg = ioremap(template_base, PAGE_SIZE);
+ if (!ipu_dc_tmpl_reg) {
+ iounmap(ipu_dc_reg);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < 10; i++)
+ dc_channels[i].channel_offset = channel_offsets[i];
+
+ /* IPU_PIX_FMT_RGB24 */
+ ipu_dc_map_clear(0);
+ ipu_dc_map_config(0, 0, 7, 0xff);
+ ipu_dc_map_config(0, 1, 15, 0xff);
+ ipu_dc_map_config(0, 2, 23, 0xff);
+
+ /* IPU_PIX_FMT_RGB666 */
+ ipu_dc_map_clear(1);
+ ipu_dc_map_config(1, 0, 5, 0xfc);
+ ipu_dc_map_config(1, 1, 11, 0xfc);
+ ipu_dc_map_config(1, 2, 17, 0xfc);
+
+ /* IPU_PIX_FMT_YUV444 */
+ ipu_dc_map_clear(2);
+ ipu_dc_map_config(2, 0, 15, 0xff);
+ ipu_dc_map_config(2, 1, 23, 0xff);
+ ipu_dc_map_config(2, 2, 7, 0xff);
+
+ /* IPU_PIX_FMT_RGB565 */
+ ipu_dc_map_clear(3);
+ ipu_dc_map_config(3, 0, 4, 0xf8);
+ ipu_dc_map_config(3, 1, 10, 0xfc);
+ ipu_dc_map_config(3, 2, 15, 0xf8);
+
+ /* IPU_PIX_FMT_LVDS666 */
+ ipu_dc_map_clear(4);
+ ipu_dc_map_config(4, 0, 5, 0xfc);
+ ipu_dc_map_config(4, 1, 13, 0xfc);
+ ipu_dc_map_config(4, 2, 21, 0xfc);
+
+ return 0;
+}
+
+void ipu_dc_exit(struct platform_device *pdev)
+{
+ iounmap(ipu_dc_reg);
+ iounmap(ipu_dc_tmpl_reg);
+}
diff --git a/drivers/mfd/imx-ipu-v3/ipu-di.c b/drivers/mfd/imx-ipu-v3/ipu-di.c
new file mode 100644
index 0000000..73ebd51
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-di.c
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]>
+ * Copyright (C) 2005-2009 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.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <linux/platform_device.h>
+#include <asm/atomic.h>
+
+#include "ipu-prv.h"
+
+#define SYNC_WAVE 0
+
+#define DC_DISP_ID_SYNC(di) (di)
+
+struct ipu_di {
+ void __iomem *base;
+ int id;
+ u32 module;
+ struct clk *clk;
+ struct clk *ipu_clk;
+ bool external_clk;
+};
+
+static struct ipu_di dis[2];
+
+static DEFINE_MUTEX(di_mutex);
+static struct device *ipu_dev;
+
+struct di_sync_config {
+ 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;
+};
+
+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 = 0,
+ DI_SYNC_CLK = 1,
+ DI_SYNC_INT_HSYNC = 2,
+ DI_SYNC_HSYNC = 3,
+ DI_SYNC_VSYNC = 4,
+ DI_SYNC_DE = 6,
+};
+
+#define DI_GENERAL 0x0000
+#define DI_BS_CLKGEN0 0x0004
+#define DI_BS_CLKGEN1 0x0008
+#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1))
+#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1))
+#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2))
+#define DI_SYNC_AS_GEN 0x0054
+#define DI_DW_GEN(gen) (0x0058 + 4 * (gen))
+#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set)))
+#define DI_SER_CONF 0x015c
+#define DI_SSC 0x0160
+#define DI_POL 0x0164
+#define DI_AW0 0x0168
+#define DI_AW1 0x016c
+#define DI_SCR_CONF 0x0170
+#define DI_STAT 0x0174
+
+#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19)
+#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16)
+#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3)
+#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0)
+
+#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29)
+#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25)
+#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12)
+#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9)
+#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16)
+#define DI_SW_GEN1_CNT_UP(x) (x)
+#define DI_SW_GEN1_AUTO_RELOAD (0x10000000)
+
+#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24
+#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16
+
+#define DI_GEN_DI_CLK_EXT (1 << 20)
+#define DI_GEN_POLARITY_1 (1 << 0)
+#define DI_GEN_POLARITY_2 (1 << 1)
+#define DI_GEN_POLARITY_3 (1 << 2)
+#define DI_GEN_POLARITY_4 (1 << 3)
+#define DI_GEN_POLARITY_5 (1 << 4)
+#define DI_GEN_POLARITY_6 (1 << 5)
+#define DI_GEN_POLARITY_7 (1 << 6)
+#define DI_GEN_POLARITY_8 (1 << 7)
+
+#define DI_POL_DRDY_DATA_POLARITY (1 << 7)
+#define DI_POL_DRDY_POLARITY_15 (1 << 4)
+
+#define DI_VSYNC_SEL_OFFSET 13
+
+#define DI0_COUNTER_RELEASE (1 << 24)
+#define DI1_COUNTER_RELEASE (1 << 25)
+
+static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset)
+{
+ return __raw_readl(di->base + offset);
+}
+
+static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset)
+{
+ __raw_writel(value, di->base + offset);
+}
+
+static void ipu_di_data_wave_config(struct ipu_di *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(di, reg, DI_DW_GEN(wave_gen));
+}
+
+static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin, int set,
+ int up, int down)
+{
+ u32 reg;
+
+ reg = ipu_di_read(di, DI_DW_GEN(wave_gen));
+ reg &= ~(0x3 << (di_pin * 2));
+ reg |= set << (di_pin * 2);
+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen));
+
+ ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set));
+}
+
+static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, int count)
+{
+ u32 reg;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ struct di_sync_config *c = &config[i];
+ int wave_gen = i + 1;
+
+ pr_debug("%s %d\n", __func__, wave_gen);
+ if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) || (c->repeat_count >= 0x1000) ||
+ (c->cnt_up >= 0x400) || (c->cnt_down >= 0x400)) {
+ dev_err(ipu_dev, "DI%d counters out of range.\n", di->id);
+ return;
+ }
+
+ reg = DI_SW_GEN0_RUN_COUNT(c->run_count) |
+ DI_SW_GEN0_RUN_SRC(c->run_src) |
+ DI_SW_GEN0_OFFSET_COUNT(c->offset_count) |
+ DI_SW_GEN0_OFFSET_SRC(c->offset_src);
+ ipu_di_write(di, reg, DI_SW_GEN0(wave_gen));
+
+ reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) |
+ DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) |
+ DI_SW_GEN1_CNT_POL_TRIGGER_SRC(c->cnt_polarity_trigger_src) |
+ DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) |
+ DI_SW_GEN1_CNT_DOWN(c->cnt_down) |
+ DI_SW_GEN1_CNT_UP(c->cnt_up);
+
+ if (c->repeat_count == 0) {
+ /* Enable auto reload */
+ reg |= DI_SW_GEN1_AUTO_RELOAD;
+ }
+
+ ipu_di_write(di, reg, DI_SW_GEN1(wave_gen));
+
+ reg = ipu_di_read(di, DI_STP_REP(wave_gen));
+ reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1)));
+ reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1));
+ ipu_di_write(di, reg, DI_STP_REP(wave_gen));
+ }
+}
+
+static void ipu_di_sync_config_interlaced(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
+{
+ u32 h_total = sig->width + sig->h_sync_width + sig->h_start_width + sig->h_end_width;
+ u32 v_total = sig->height + sig->v_sync_width + sig->v_start_width + sig->v_end_width;
+ u32 reg;
+ struct di_sync_config cfg[] = {
+ {
+ .run_count = h_total / 2 - 1,
+ .run_src = DI_SYNC_CLK,
+ }, {
+ .run_count = h_total - 11,
+ .run_src = DI_SYNC_CLK,
+ .cnt_down = 4,
+ }, {
+ .run_count = v_total * 2 - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = 1,
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = 4,
+ }, {
+ .run_count = v_total / 2 - 1,
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = sig->v_start_width,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = 2,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ }, {
+ .run_src = DI_SYNC_HSYNC,
+ .repeat_count = sig->height / 2,
+ .cnt_clr_src = 4,
+ }, {
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_HSYNC,
+ }, {
+ .run_count = v_total / 2 - 1,
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = 9,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = 2,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ }, {
+ .run_src = DI_SYNC_CLK,
+ .offset_count = sig->h_start_width,
+ .offset_src = DI_SYNC_CLK,
+ .repeat_count = sig->width,
+ .cnt_clr_src = 5,
+ }, {
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = v_total / 2,
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_clr_src = DI_SYNC_HSYNC,
+ .cnt_down = 4,
+ }
+ };
+
+ ipu_di_sync_config(di, cfg, ARRAY_SIZE(cfg));
+
+ /* set gentime select and tag sel */
+ reg = ipu_di_read(di, DI_SW_GEN1(9));
+ reg &= 0x1FFFFFFF;
+ reg |= (3 - 1) << 29 | 0x00008000;
+ ipu_di_write(di, reg, DI_SW_GEN1(9));
+
+ ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);
+}
+
+static void ipu_di_sync_config_noninterlaced(struct ipu_di *di,
+ struct ipu_di_signal_cfg *sig, int div)
+{
+ u32 h_total = sig->width + sig->h_sync_width + sig->h_start_width +
+ sig->h_end_width;
+ u32 v_total = sig->height + sig->v_sync_width + sig->v_start_width +
+ sig->v_end_width + (sig->v_end_width < 2 ? 1 : 0);
+ struct di_sync_config cfg[] = {
+ {
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ } , {
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ .offset_count = div * sig->v_to_h_sync,
+ .offset_src = DI_SYNC_CLK,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_CLK,
+ .cnt_down = sig->h_sync_width * 2,
+ } , {
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = sig->v_sync_width * 2,
+ } , {
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = sig->v_sync_width + sig->v_start_width,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = sig->height,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ } , {
+ .run_src = DI_SYNC_CLK,
+ .offset_count = sig->h_sync_width + sig->h_start_width,
+ .offset_src = DI_SYNC_CLK,
+ .repeat_count = sig->width,
+ .cnt_clr_src = 5,
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ },
+ };
+
+ ipu_di_write(di, v_total - 1, DI_SCR_CONF);
+ ipu_di_sync_config(di, cfg, ARRAY_SIZE(cfg));
+}
+
+int ipu_di_init_sync_panel(int disp, u32 pixel_clk, struct ipu_di_signal_cfg *sig)
+{
+ struct ipu_di *di = &dis[disp];
+ u32 reg;
+ u32 disp_gen, di_gen, vsync_cnt;
+ u32 div;
+ u32 h_total, v_total;
+ struct clk *di_clk;
+
+ dev_dbg(ipu_dev, "disp %d: panel size = %d x %d\n",
+ disp, sig->width, sig->height);
+
+ if (disp > 1)
+ return -EINVAL;
+
+ if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0))
+ return -EINVAL;
+
+ h_total = sig->width + sig->h_sync_width + sig->h_start_width + sig->h_end_width;
+ v_total = sig->height + sig->v_sync_width + sig->v_start_width + sig->v_end_width;
+
+ mutex_lock(&di_mutex);
+
+ /* Init clocking */
+ if (sig->ext_clk) {
+ di->external_clk = true;
+ di_clk = di->clk;
+ } else {
+ di->external_clk = false;
+ di_clk = di->ipu_clk;
+ }
+
+ /*
+ * Calculate divider
+ * Fractional part is 4 bits,
+ * so simply multiply by 2^4 to get fractional part.
+ */
+ div = (clk_get_rate(di_clk) * 16) / pixel_clk;
+ if (div < 0x10) /* Min DI disp clock divider is 1 */
+ div = 0x10;
+
+ disp_gen = ipu_cm_read(IPU_DISP_GEN);
+ disp_gen &= disp ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE;
+ ipu_cm_write(disp_gen, IPU_DISP_GEN);
+
+ ipu_di_write(di, div, DI_BS_CLKGEN0);
+
+ /* Setup pixel clock timing */
+ /* Down time is half of period */
+ ipu_di_write(di, (div / 16) << 16, DI_BS_CLKGEN1);
+
+ ipu_di_data_wave_config(di, SYNC_WAVE, div / 16 - 1, div / 16 - 1);
+ ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div / 16 * 2);
+
+ div = div / 16; /* Now divider is integer portion */
+
+ di_gen = 0;
+ if (sig->ext_clk)
+ di_gen |= DI_GEN_DI_CLK_EXT;
+
+ if (sig->interlaced) {
+ ipu_di_sync_config_interlaced(di, sig);
+
+ /* set y_sel = 1 */
+ di_gen |= 0x10000000;
+ di_gen |= DI_GEN_POLARITY_5;
+ di_gen |= DI_GEN_POLARITY_8;
+
+ vsync_cnt = 7;
+
+ if (sig->Hsync_pol)
+ di_gen |= DI_GEN_POLARITY_3;
+ if (sig->Vsync_pol)
+ di_gen |= DI_GEN_POLARITY_2;
+ } else {
+ ipu_di_sync_config_noninterlaced(di, sig, div);
+
+ vsync_cnt = 3;
+
+ if (sig->Hsync_pol)
+ di_gen |= DI_GEN_POLARITY_2;
+ if (sig->Vsync_pol)
+ di_gen |= DI_GEN_POLARITY_3;
+ }
+
+ ipu_di_write(di, di_gen, DI_GENERAL);
+ ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002,
+ DI_SYNC_AS_GEN);
+
+ reg = ipu_di_read(di, 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(di, reg, DI_POL);
+
+ mutex_unlock(&di_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_di_init_sync_panel);
+
+int ipu_di_enable(int disp)
+{
+ struct ipu_di *di = &dis[disp];
+ u32 reg;
+
+ reg = ipu_cm_read(IPU_DISP_GEN);
+ if (disp)
+ reg |= DI1_COUNTER_RELEASE;
+ else
+ reg |= DI0_COUNTER_RELEASE;
+ ipu_cm_write(reg, IPU_DISP_GEN);
+
+ if (di->external_clk)
+ clk_enable(di->clk);
+
+ ipu_module_enable(di->module);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_di_enable);
+
+int ipu_di_disable(int disp)
+{
+ struct ipu_di *di = &dis[disp];
+ u32 reg;
+
+ ipu_module_disable(di->module);
+
+ if (di->external_clk)
+ clk_disable(di->clk);
+
+ reg = ipu_cm_read(IPU_DISP_GEN);
+ if (disp)
+ reg &= ~DI1_COUNTER_RELEASE;
+ else
+ reg &= ~DI0_COUNTER_RELEASE;
+ ipu_cm_write(reg, IPU_DISP_GEN);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_di_disable);
+
+int ipu_di_init(struct platform_device *pdev, int id, unsigned long base,
+ u32 module, struct clk *ipu_clk)
+{
+ char *clkid;
+
+ if (id > 1)
+ return -EINVAL;
+
+ if (id)
+ clkid = "di1";
+ else
+ clkid = "di0";
+
+ ipu_dev = &pdev->dev;
+
+ dis[id].clk = clk_get(&pdev->dev, clkid);
+ dis[id].module = module;
+ dis[id].id = id;
+ dis[id].ipu_clk = ipu_clk;
+ dis[id].base = ioremap(base, PAGE_SIZE);
+ if (!dis[id].base)
+ return -ENOMEM;
+
+ /* Set MCU_T to divide MCU access window into 2 */
+ ipu_cm_write(0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN);
+
+ return 0;
+}
+
+void ipu_di_exit(struct platform_device *pdev, int id)
+{
+ clk_put(dis[id].clk);
+ iounmap(dis[id].base);
+}
diff --git a/drivers/mfd/imx-ipu-v3/ipu-dmfc.c b/drivers/mfd/imx-ipu-v3/ipu-dmfc.c
new file mode 100644
index 0000000..25782a7
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-dmfc.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]>
+ * Copyright (C) 2005-2009 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.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/imx-ipu-v3.h>
+
+#include "ipu-prv.h"
+
+#define DMFC_RD_CHAN 0x0000
+#define DMFC_WR_CHAN 0x0004
+#define DMFC_WR_CHAN_DEF 0x0008
+#define DMFC_DP_CHAN 0x000c
+#define DMFC_DP_CHAN_DEF 0x0010
+#define DMFC_GENERAL1 0x0014
+#define DMFC_GENERAL2 0x0018
+#define DMFC_IC_CTRL 0x001c
+#define DMFC_STAT 0x0020
+
+#define DMFC_WR_CHAN_1_28 0
+#define DMFC_WR_CHAN_2_41 8
+#define DMFC_WR_CHAN_1C_42 16
+#define DMFC_WR_CHAN_2C_43 24
+
+#define DMFC_DP_CHAN_5B_23 0
+#define DMFC_DP_CHAN_5F_27 8
+#define DMFC_DP_CHAN_6B_24 16
+#define DMFC_DP_CHAN_6F_29 24
+
+#define DMFC_FIFO_SIZE_64 (3 << 3)
+#define DMFC_FIFO_SIZE_128 (2 << 3)
+#define DMFC_FIFO_SIZE_256 (1 << 3)
+#define DMFC_FIFO_SIZE_512 (0 << 3)
+
+#define DMFC_SEGMENT(x) ((x & 0x7) << 0)
+#define DMFC_BURSTSIZE_32 (0 << 6)
+#define DMFC_BURSTSIZE_16 (1 << 6)
+#define DMFC_BURSTSIZE_8 (2 << 6)
+#define DMFC_BURSTSIZE_4 (3 << 6)
+
+static struct device *ipu_dev;
+
+struct dmfc_channel {
+ int ipu_channel;
+ unsigned long channel_reg;
+ unsigned long shift;
+ unsigned eot_shift;
+ unsigned slots;
+ unsigned max_fifo_lines;
+ unsigned slotmask;
+ unsigned segment;
+};
+
+static struct dmfc_channel dmfcs[] = {
+ {
+ .ipu_channel = 23,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_5B_23,
+ .eot_shift = 20,
+ .max_fifo_lines = 3,
+ }, {
+ .ipu_channel = 24,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_6B_24,
+ .eot_shift = 22,
+ .max_fifo_lines = 1,
+ }, {
+ .ipu_channel = 27,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_5F_27,
+ .eot_shift = 21,
+ .max_fifo_lines = 2,
+ }, {
+ .ipu_channel = 28,
+ .channel_reg = DMFC_WR_CHAN,
+ .shift = DMFC_WR_CHAN_1_28,
+ .eot_shift = 16,
+ .max_fifo_lines = 2,
+ }, {
+ .ipu_channel = 29,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_6F_29,
+ .eot_shift = 23,
+ .max_fifo_lines = 1,
+ },
+};
+
+#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcs)
+
+static int dmfc_use_count;
+static void __iomem *dmfc_regs;
+static unsigned long dmfc_bandwidth_per_slot;
+static DEFINE_MUTEX(dmfc_mutex);
+
+int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc)
+{
+ mutex_lock(&dmfc_mutex);
+
+ if (!dmfc_use_count)
+ ipu_module_enable(IPU_CONF_DMFC_EN);
+
+ dmfc_use_count++;
+
+ mutex_unlock(&dmfc_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dmfc_enable_channel);
+
+void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
+{
+ mutex_lock(&dmfc_mutex);
+
+ dmfc_use_count--;
+
+ if (!dmfc_use_count)
+ ipu_module_disable(IPU_CONF_DMFC_EN);
+
+ if (dmfc_use_count < 0)
+ dmfc_use_count = 0;
+
+ mutex_unlock(&dmfc_mutex);
+}
+EXPORT_SYMBOL(ipu_dmfc_disable_channel);
+
+static int ipu_dmfc_setup_channel(struct dmfc_channel *dmfc, int slots, int segment)
+{
+ u32 val, field;
+
+ dev_dbg(ipu_dev, "dmfc: using %d slots starting from segment %d for IPU channel %d\n",
+ slots, segment, dmfc->ipu_channel);
+
+ if (!dmfc)
+ return -EINVAL;
+
+ switch (slots) {
+ case 1:
+ field = DMFC_FIFO_SIZE_64;
+ break;
+ case 2:
+ field = DMFC_FIFO_SIZE_128;
+ break;
+ case 4:
+ field = DMFC_FIFO_SIZE_256;
+ break;
+ case 8:
+ field = DMFC_FIFO_SIZE_512;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ field |= DMFC_SEGMENT(segment) | DMFC_BURSTSIZE_8;
+
+ val = readl(dmfc_regs + dmfc->channel_reg);
+
+ val &= ~(0xff << dmfc->shift);
+ val |= field << dmfc->shift;
+
+ writel(val, dmfc_regs + dmfc->channel_reg);
+
+ dmfc->slots = slots;
+ dmfc->segment = segment;
+ dmfc->slotmask = ((1 << slots) - 1) << segment;
+
+ return 0;
+}
+
+static int dmfc_bandwidth_to_slots(unsigned long bandwidth)
+{
+ int slots = 1;
+
+ while (slots * dmfc_bandwidth_per_slot < bandwidth)
+ slots *= 2;
+
+ return slots;
+}
+
+static int dmfc_find_slots(int slots)
+{
+ unsigned slotmask_need, slotmask_used = 0;
+ int i, segment = 0;
+
+ slotmask_need = (1 << slots) - 1;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ slotmask_used |= dmfcs[i].slotmask;
+
+ while (slotmask_need <= 0xff) {
+ if (!(slotmask_used & slotmask_need))
+ return segment;
+
+ slotmask_need <<= 1;
+ segment++;
+ }
+
+ return -EBUSY;
+}
+
+void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc)
+{
+ int i;
+
+ dev_dbg(ipu_dev, "dmfc: freeing %d slots starting from segment %d\n",
+ dmfc->slots, dmfc->segment);
+
+ mutex_lock(&dmfc_mutex);
+
+ if (!dmfc->slots)
+ goto out;
+
+ dmfc->slotmask = 0;
+ dmfc->slots = 0;
+ dmfc->segment = 0;
+
+ for (i = 0; i < ARRAY_SIZE(dmfcs); i++)
+ dmfcs[i].slotmask = 0;
+
+ for (i = 0; i < ARRAY_SIZE(dmfcs); i++) {
+ if (dmfcs[i].slots > 0) {
+ dmfcs[i].segment = dmfc_find_slots(dmfcs[i].slots);
+ dmfcs[i].slotmask = ((1 << dmfcs[i].slots) - 1) << dmfcs[i].segment;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dmfcs); i++) {
+ if (dmfcs[i].slots > 0)
+ ipu_dmfc_setup_channel(&dmfcs[i], dmfcs[i].slots, dmfcs[i].segment);
+ }
+out:
+ mutex_unlock(&dmfc_mutex);
+}
+EXPORT_SYMBOL(ipu_dmfc_free_bandwidth);
+
+int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
+ unsigned long bandwidth_pixel_per_second)
+{
+ int slots = dmfc_bandwidth_to_slots(bandwidth_pixel_per_second);
+ int segment = 0;
+
+ dev_dbg(ipu_dev, "dmfc: trying to allocate %ldMpixel/s for IPU channel %d\n",
+ bandwidth_pixel_per_second / 1000000, dmfc->ipu_channel);
+
+ ipu_dmfc_free_bandwidth(dmfc);
+
+ mutex_lock(&dmfc_mutex);
+
+ if (slots > 8)
+ return -EBUSY;
+
+ segment = dmfc_find_slots(slots);
+ if (segment < 0)
+ return -EBUSY;
+
+ ipu_dmfc_setup_channel(dmfc, slots, segment);
+
+ mutex_unlock(&dmfc_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dmfc_alloc_bandwidth);
+
+int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width)
+{
+ u32 dmfc_gen1;
+
+ dmfc_gen1 = readl(dmfc_regs + DMFC_GENERAL1);
+
+ if ((dmfc->slots * 64 * 4) / width > dmfc->max_fifo_lines)
+ dmfc_gen1 |= 1 << dmfc->eot_shift;
+ else
+ dmfc_gen1 &= ~(1 << dmfc->eot_shift);
+
+ writel(dmfc_gen1, dmfc_regs + DMFC_GENERAL1);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dmfc_init_channel);
+
+struct dmfc_channel *ipu_dmfc_get(int ipu_channel)
+{
+ int i;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ if (dmfcs[i].ipu_channel == ipu_channel)
+ return &dmfcs[i];
+ return NULL;
+}
+EXPORT_SYMBOL(ipu_dmfc_get);
+
+void ipu_dmfc_put(struct dmfc_channel *dmfc)
+{
+ ipu_dmfc_free_bandwidth(dmfc);
+}
+EXPORT_SYMBOL(ipu_dmfc_put);
+
+int ipu_dmfc_init(struct platform_device *pdev, unsigned long base,
+ struct clk *ipu_clk)
+{
+ dmfc_regs = ioremap(base, PAGE_SIZE);
+
+ if (!dmfc_regs)
+ return -ENOMEM;
+
+ ipu_dev = &pdev->dev;
+
+ writel(0x0, dmfc_regs + DMFC_WR_CHAN);
+ writel(0x0, dmfc_regs + DMFC_DP_CHAN);
+
+ /*
+ * We have a total bandwidth of clkrate * 4pixel divided
+ * into 8 slots.
+ */
+ dmfc_bandwidth_per_slot = clk_get_rate(ipu_clk) / 4;
+
+ dev_dbg(ipu_dev, "dmfc: 8 slots with %ldMpixel/s bandwidth each\n",
+ dmfc_bandwidth_per_slot / 1000000);
+
+ writel(0x202020f6, dmfc_regs + DMFC_WR_CHAN_DEF);
+ writel(0x2020f6f6, dmfc_regs + DMFC_DP_CHAN_DEF);
+ writel(0x00000003, dmfc_regs + DMFC_GENERAL1);
+
+ return 0;
+}
+
+void ipu_dmfc_exit(struct platform_device *pdev)
+{
+ iounmap(dmfc_regs);
+}
diff --git a/drivers/mfd/imx-ipu-v3/ipu-dp.c b/drivers/mfd/imx-ipu-v3/ipu-dp.c
new file mode 100644
index 0000000..505107d
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-dp.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]>
+ * Copyright (C) 2005-2009 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.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <linux/err.h>
+#include <asm/atomic.h>
+
+#include "ipu-prv.h"
+
+#define DP_SYNC 0
+#define DP_ASYNC0 0x60
+#define DP_ASYNC1 0xBC
+
+#define DP_COM_CONF(flow) (ipu_dp_base + flow)
+#define DP_GRAPH_WIND_CTRL(flow) (ipu_dp_base + 0x0004 + flow)
+#define DP_FG_POS(flow) (ipu_dp_base + 0x0008 + flow)
+#define DP_CSC_A_0(flow) (ipu_dp_base + 0x0044 + flow)
+#define DP_CSC_A_1(flow) (ipu_dp_base + 0x0048 + flow)
+#define DP_CSC_A_2(flow) (ipu_dp_base + 0x004C + flow)
+#define DP_CSC_A_3(flow) (ipu_dp_base + 0x0050 + flow)
+#define DP_CSC_0(flow) (ipu_dp_base + 0x0054 + flow)
+#define DP_CSC_1(flow) (ipu_dp_base + 0x0058 + flow)
+
+#define DP_COM_CONF_FG_EN (1 << 0)
+#define DP_COM_CONF_GWSEL (1 << 1)
+#define DP_COM_CONF_GWAM (1 << 2)
+#define DP_COM_CONF_GWCKE (1 << 3)
+#define DP_COM_CONF_CSC_DEF_MASK (3 << 8)
+#define DP_COM_CONF_CSC_DEF_OFFSET 8
+#define DP_COM_CONF_CSC_DEF_FG (3 << 8)
+#define DP_COM_CONF_CSC_DEF_BG (2 << 8)
+#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8)
+
+struct ipu_dp {
+ u32 flow;
+ bool in_use;
+};
+
+static struct ipu_dp ipu_dp[3];
+static struct device *ipu_dev;
+
+static u32 ipu_flows[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
+
+enum csc_type_t {
+ RGB2YUV = 0,
+ YUV2RGB,
+ RGB2RGB,
+ YUV2YUV,
+ CSC_NONE,
+ CSC_NUM
+};
+
+static void __iomem *ipu_dp_base;
+static int dp_use_count;
+static DEFINE_MUTEX(dp_mutex);
+
+/* Y = R * .299 + G * .587 + B * .114;
+ U = R * -.169 + G * -.332 + B * .500 + 128.;
+ V = R * .500 + G * -.419 + B * -.0813 + 128.;*/
+static const int rgb2ycbcr_coeff[5][3] = {
+ { 153, 301, 58, },
+ { -87, -170, 0x0100, },
+ { 0x100, -215, -42, },
+ { 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 */
+};
+
+/* Please keep S0, S1 and S2 as 0x2 by using this conversion */
+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;
+}
+
+struct dp_csc_param_t {
+ int mode;
+ void *coeff;
+};
+
+/*
+ * 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, },
+ }
+};
+
+static enum csc_type_t fg_csc_type = CSC_NONE, bg_csc_type = CSC_NONE;
+
+static int color_key_4rgb = 1;
+
+int ipu_dp_set_color_key(struct ipu_dp *dp, bool enable, u32 color_key)
+{
+ u32 reg;
+ int y, u, v;
+ int red, green, blue;
+
+ mutex_lock(&dp_mutex);
+
+ color_key_4rgb = 1;
+ /* Transform color key from rgb to yuv if CSC is enabled */
+ if (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) ||
+ ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) ||
+ ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) ||
+ ((fg_csc_type == YUV2RGB) && (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;
+
+ color_key_4rgb = 0;
+
+ dev_dbg(ipu_dev, "color key change to yuv fmt 0x%x\n", color_key);
+ }
+
+ if (enable) {
+ reg = __raw_readl(DP_GRAPH_WIND_CTRL(dp->flow)) & 0xFF000000L;
+ __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL(dp->flow));
+
+ reg = __raw_readl(DP_COM_CONF(dp->flow));
+ __raw_writel(reg | DP_COM_CONF_GWCKE, DP_COM_CONF(dp->flow));
+ } else {
+ reg = __raw_readl(DP_COM_CONF(dp->flow));
+ __raw_writel(reg & ~DP_COM_CONF_GWCKE, DP_COM_CONF(dp->flow));
+ }
+
+ reg = ipu_cm_read(IPU_SRM_PRI2) | 0x8;
+ ipu_cm_write(reg, IPU_SRM_PRI2);
+
+ mutex_unlock(&dp_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dp_set_color_key);
+
+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha, bool bg_chan)
+{
+ u32 reg;
+
+ mutex_lock(&dp_mutex);
+
+ reg = __raw_readl(DP_COM_CONF(dp->flow));
+ if (bg_chan)
+ reg &= ~DP_COM_CONF_GWSEL;
+ else
+ reg |= DP_COM_CONF_GWSEL;
+ __raw_writel(reg, DP_COM_CONF(dp->flow));
+
+ if (enable) {
+ reg = __raw_readl(DP_GRAPH_WIND_CTRL(dp->flow)) & 0x00FFFFFFL;
+ __raw_writel(reg | ((u32) alpha << 24),
+ DP_GRAPH_WIND_CTRL(dp->flow));
+
+ reg = __raw_readl(DP_COM_CONF(dp->flow));
+ __raw_writel(reg | DP_COM_CONF_GWAM, DP_COM_CONF(dp->flow));
+ } else {
+ reg = __raw_readl(DP_COM_CONF(dp->flow));
+ __raw_writel(reg & ~DP_COM_CONF_GWAM, DP_COM_CONF(dp->flow));
+ }
+
+ reg = ipu_cm_read(IPU_SRM_PRI2) | 0x8;
+ ipu_cm_write(reg, IPU_SRM_PRI2);
+
+ mutex_unlock(&dp_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dp_set_global_alpha);
+
+int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
+{
+ u32 reg;
+
+ mutex_lock(&dp_mutex);
+
+ __raw_writel((x_pos << 16) | y_pos, DP_FG_POS(dp->flow));
+
+ reg = ipu_cm_read(IPU_SRM_PRI2);
+ reg |= 0x8;
+ ipu_cm_write(reg, IPU_SRM_PRI2);
+
+ mutex_unlock(&dp_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dp_set_window_pos);
+
+#define mask_a(a) ((u32)(a) & 0x3FF)
+#define mask_b(b) ((u32)(b) & 0x3FFF)
+
+void __ipu_dp_csc_setup(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 = __raw_readl(DP_COM_CONF(dp));
+ reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+ reg |= dp_csc_param.mode;
+ __raw_writel(reg, DP_COM_CONF(dp));
+ }
+
+ coeff = dp_csc_param.coeff;
+
+ if (coeff) {
+ __raw_writel(mask_a((*coeff)[0][0]) |
+ (mask_a((*coeff)[0][1]) << 16), DP_CSC_A_0(dp));
+ __raw_writel(mask_a((*coeff)[0][2]) |
+ (mask_a((*coeff)[1][0]) << 16), DP_CSC_A_1(dp));
+ __raw_writel(mask_a((*coeff)[1][1]) |
+ (mask_a((*coeff)[1][2]) << 16), DP_CSC_A_2(dp));
+ __raw_writel(mask_a((*coeff)[2][0]) |
+ (mask_a((*coeff)[2][1]) << 16), DP_CSC_A_3(dp));
+ __raw_writel(mask_a((*coeff)[2][2]) |
+ (mask_b((*coeff)[3][0]) << 16) |
+ ((*coeff)[4][0] << 30), DP_CSC_0(dp));
+ __raw_writel(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_SRM_PRI2) | 0x8;
+ ipu_cm_write(reg, IPU_SRM_PRI2);
+ }
+}
+
+int ipu_dp_setup_channel(struct ipu_dp *dp, u32 in_pixel_fmt,
+ u32 out_pixel_fmt, int bg)
+{
+ int in_fmt, out_fmt;
+ enum csc_type_t *csc_type;
+ u32 reg;
+
+ if (bg)
+ csc_type = &bg_csc_type;
+ else
+ csc_type = &fg_csc_type;
+
+ in_fmt = format_to_colorspace(in_pixel_fmt);
+ out_fmt = format_to_colorspace(out_pixel_fmt);
+
+ if (in_fmt == RGB) {
+ if (out_fmt == RGB)
+ *csc_type = RGB2RGB;
+ else
+ *csc_type = RGB2YUV;
+ } else {
+ if (out_fmt == RGB)
+ *csc_type = YUV2RGB;
+ else
+ *csc_type = YUV2YUV;
+ }
+
+ /* Transform color key from rgb to yuv if CSC is enabled */
+ reg = __raw_readl(DP_COM_CONF(dp->flow));
+ if (color_key_4rgb && (reg & DP_COM_CONF_GWCKE) &&
+ (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) ||
+ ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) ||
+ ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) ||
+ ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB)))) {
+ int red, green, blue;
+ int y, u, v;
+ u32 color_key = __raw_readl(DP_GRAPH_WIND_CTRL(dp->flow)) & 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 = __raw_readl(DP_GRAPH_WIND_CTRL(dp->flow)) & 0xFF000000L;
+ __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL(dp->flow));
+ color_key_4rgb = 0;
+
+ dev_dbg(ipu_dev, "_ipu_dp_init color key change to yuv fmt 0x%x!\n", color_key);
+ }
+
+ __ipu_dp_csc_setup(dp->flow, dp_csc_array[bg_csc_type][fg_csc_type], true);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dp_setup_channel);
+
+int ipu_dp_enable_channel(struct ipu_dp *dp)
+{
+ mutex_lock(&dp_mutex);
+
+ if (!dp_use_count)
+ ipu_module_enable(IPU_CONF_DP_EN);
+
+ dp_use_count++;
+
+ mutex_unlock(&dp_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dp_enable_channel);
+
+void ipu_dp_disable_channel(struct ipu_dp *dp)
+{
+ mutex_lock(&dp_mutex);
+
+ dp_use_count--;
+
+ if (!dp_use_count)
+ ipu_module_disable(IPU_CONF_DP_EN);
+
+ if (dp_use_count < 0)
+ dp_use_count = 0;
+
+ mutex_unlock(&dp_mutex);
+}
+EXPORT_SYMBOL(ipu_dp_disable_channel);
+
+void ipu_dp_enable_fg(struct ipu_dp *dp)
+{
+ u32 reg;
+
+ /* Enable FG channel */
+ reg = __raw_readl(DP_COM_CONF(DP_SYNC));
+ __raw_writel(reg | DP_COM_CONF_FG_EN, DP_COM_CONF(DP_SYNC));
+
+ reg = ipu_cm_read(IPU_SRM_PRI2);
+ reg |= 0x8;
+ ipu_cm_write(reg, IPU_SRM_PRI2);
+}
+EXPORT_SYMBOL(ipu_dp_enable_fg);
+
+void ipu_dp_disable_fg(struct ipu_dp *dp)
+{
+ u32 reg, csc;
+
+ reg = __raw_readl(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;
+ __raw_writel(reg, DP_COM_CONF(DP_SYNC));
+
+ reg = ipu_cm_read(IPU_SRM_PRI2) | 0x8;
+ ipu_cm_write(reg, IPU_SRM_PRI2);
+}
+EXPORT_SYMBOL(ipu_dp_disable_fg);
+
+struct ipu_dp *ipu_dp_get(unsigned int flow)
+{
+ struct ipu_dp *dp;
+
+ if (flow > 2)
+ return ERR_PTR(-EINVAL);
+
+ dp = &ipu_dp[flow];
+
+ if (dp->in_use)
+ return ERR_PTR(-EBUSY);
+
+ dp->in_use = true;
+ dp->flow = ipu_flows[flow];
+
+ return dp;
+}
+EXPORT_SYMBOL(ipu_dp_get);
+
+void ipu_dp_put(struct ipu_dp *dp)
+{
+ dp->in_use = false;
+}
+EXPORT_SYMBOL(ipu_dp_put);
+
+int ipu_dp_init(struct platform_device *pdev, unsigned long base)
+{
+ ipu_dp_base = ioremap(base, PAGE_SIZE);
+ if (!ipu_dp_base)
+ return -ENOMEM;
+
+ ipu_dev = &pdev->dev;
+
+ return 0;
+}
+
+void ipu_dp_exit(struct platform_device *pdev)
+{
+ iounmap(ipu_dp_base);
+}
diff --git a/drivers/mfd/imx-ipu-v3/ipu-prv.h b/drivers/mfd/imx-ipu-v3/ipu-prv.h
new file mode 100644
index 0000000..9d54ad0
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-prv.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]>
+ * Copyright (C) 2005-2009 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 __IPU_PRV_H__
+#define __IPU_PRV_H__
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <mach/hardware.h>
+
+#define MX51_IPU_CHANNEL_CSI0 0
+#define MX51_IPU_CHANNEL_CSI1 1
+#define MX51_IPU_CHANNEL_CSI2 2
+#define MX51_IPU_CHANNEL_CSI3 3
+#define MX51_IPU_CHANNEL_MEM_BG_SYNC 23
+#define MX51_IPU_CHANNEL_MEM_FG_SYNC 27
+#define MX51_IPU_CHANNEL_MEM_DC_SYNC 28
+#define MX51_IPU_CHANNEL_MEM_FG_SYNC_ALPHA 31
+#define MX51_IPU_CHANNEL_MEM_DC_ASYNC 41
+#define MX51_IPU_CHANNEL_ROT_ENC_MEM 45
+#define MX51_IPU_CHANNEL_ROT_VF_MEM 46
+#define MX51_IPU_CHANNEL_ROT_PP_MEM 47
+#define MX51_IPU_CHANNEL_ROT_ENC_MEM_OUT 48
+#define MX51_IPU_CHANNEL_ROT_VF_MEM_OUT 49
+#define MX51_IPU_CHANNEL_ROT_PP_MEM_OUT 50
+#define MX51_IPU_CHANNEL_MEM_BG_SYNC_ALPHA 51
+
+#define IPU_DISP0_BASE 0x00000000
+#define IPU_MCU_T_DEFAULT 8
+#define IPU_DISP1_BASE (IPU_MCU_T_DEFAULT << 25)
+#define IPU_CM_REG_BASE 0x1e000000
+#define IPU_IDMAC_REG_BASE 0x1e008000
+#define IPU_ISP_REG_BASE 0x1e010000
+#define IPU_DP_REG_BASE 0x1e018000
+#define IPU_IC_REG_BASE 0x1e020000
+#define IPU_IRT_REG_BASE 0x1e028000
+#define IPU_CSI0_REG_BASE 0x1e030000
+#define IPU_CSI1_REG_BASE 0x1e038000
+#define IPU_DI0_REG_BASE 0x1e040000
+#define IPU_DI1_REG_BASE 0x1e048000
+#define IPU_SMFC_REG_BASE 0x1e050000
+#define IPU_DC_REG_BASE 0x1e058000
+#define IPU_DMFC_REG_BASE 0x1e060000
+#define IPU_CPMEM_REG_BASE 0x1f000000
+#define IPU_LUT_REG_BASE 0x1f020000
+#define IPU_SRM_REG_BASE 0x1f040000
+#define IPU_TPM_REG_BASE 0x1f060000
+#define IPU_DC_TMPL_REG_BASE 0x1f080000
+#define IPU_ISP_TBPR_REG_BASE 0x1f0c0000
+#define IPU_VDI_REG_BASE 0x1e068000
+
+/* 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))
+#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32))
+#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244)
+#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248)
+#define IPU_SRM_STAT IPU_CM_REG(0x024C)
+#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250)
+#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254)
+#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32))
+#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32))
+#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32))
+#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32))
+
+#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * ((n) - 1))
+#define IPU_INT_CTRL_IRQ(irq) IPU_INT_CTRL(((irq) / 32))
+#define IPU_INT_STAT_IRQ(irq) IPU_INT_STAT(((irq) / 32))
+#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * ((n) - 1))
+
+extern void __iomem *ipu_cm_reg;
+
+static inline u32 ipu_cm_read(unsigned offset)
+{
+ return __raw_readl(ipu_cm_reg + offset);
+}
+
+static inline void ipu_cm_write(u32 value, unsigned offset)
+{
+ __raw_writel(value, ipu_cm_reg + offset);
+}
+
+#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 IPU_IDMAC_REG(0x0024)
+#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028)
+#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c)
+#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030)
+#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034)
+#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32))
+#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32))
+
+extern void __iomem *ipu_idmac_reg;
+
+static inline u32 ipu_idmac_read(unsigned offset)
+{
+ return __raw_readl(ipu_idmac_reg + offset);
+}
+
+static inline void ipu_idmac_write(u32 value, unsigned offset)
+{
+ __raw_writel(value, ipu_idmac_reg + offset);
+}
+
+#define idmac_idma_is_set(reg, dma) (ipu_idmac_read(reg(dma)) & idma_mask(dma))
+#define idma_mask(ch) (1 << (ch & 0x1f))
+#define ipu_idma_is_set(reg, dma) (ipu_cm_read(reg(dma)) & idma_mask(dma))
+
+enum ipu_modules {
+ IPU_CONF_CSI0_EN = (1 << 0),
+ IPU_CONF_CSI1_EN = (1 << 1),
+ IPU_CONF_IC_EN = (1 << 2),
+ IPU_CONF_ROT_EN = (1 << 3),
+ IPU_CONF_ISP_EN = (1 << 4),
+ IPU_CONF_DP_EN = (1 << 5),
+ IPU_CONF_DI0_EN = (1 << 6),
+ IPU_CONF_DI1_EN = (1 << 7),
+ IPU_CONF_SMFC_EN = (1 << 8),
+ IPU_CONF_DC_EN = (1 << 9),
+ IPU_CONF_DMFC_EN = (1 << 10),
+
+ IPU_CONF_VDI_EN = (1 << 12),
+
+ IPU_CONF_IDMAC_DIS = (1 << 22),
+
+ IPU_CONF_IC_DMFC_SEL = (1 << 25),
+ IPU_CONF_IC_DMFC_SYNC = (1 << 26),
+ IPU_CONF_VDI_DMFC_SYNC = (1 << 27),
+
+ IPU_CONF_CSI0_DATA_SOURCE = (1 << 28),
+ IPU_CONF_CSI1_DATA_SOURCE = (1 << 29),
+ IPU_CONF_IC_INPUT = (1 << 30),
+ IPU_CONF_CSI_SEL = (1 << 31),
+};
+
+struct ipu_channel {
+ unsigned int num;
+
+ bool enabled;
+ bool busy;
+};
+
+ipu_color_space_t format_to_colorspace(u32 fmt);
+bool ipu_pixel_format_has_alpha(u32 fmt);
+
+u32 _ipu_channel_status(struct ipu_channel *channel);
+
+int _ipu_chan_is_interlaced(struct ipu_channel *channel);
+
+int ipu_module_enable(u32 mask);
+int ipu_module_disable(u32 mask);
+
+int ipu_di_init(struct platform_device *pdev, int id, unsigned long base,
+ u32 module, struct clk *ipu_clk);
+void ipu_di_exit(struct platform_device *pdev, int id);
+
+int ipu_dmfc_init(struct platform_device *pdev, unsigned long base,
+ struct clk *ipu_clk);
+void ipu_dmfc_exit(struct platform_device *pdev);
+
+int ipu_dp_init(struct platform_device *pdev, unsigned long base);
+void ipu_dp_exit(struct platform_device *pdev);
+
+int ipu_dc_init(struct platform_device *pdev, unsigned long base,
+ unsigned long template_base);
+void ipu_dc_exit(struct platform_device *pdev);
+
+int ipu_cpmem_init(struct platform_device *pdev, unsigned long base);
+void ipu_cpmem_exit(struct platform_device *pdev);
+
+#endif /* __IPU_PRV_H__ */
diff --git a/include/linux/mfd/imx-ipu-v3.h b/include/linux/mfd/imx-ipu-v3.h
new file mode 100644
index 0000000..0288c51
--- /dev/null
+++ b/include/linux/mfd/imx-ipu-v3.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2005-2009 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
+ */
+
+#ifndef __ASM_ARCH_IPU_H__
+#define __ASM_ARCH_IPU_H__
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/interrupt.h>
+#include <linux/bitmap.h>
+
+/*
+ * 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;
+
+/*
+ * IPU Pixel Formats
+ *
+ * Pixel formats are defined with ASCII FOURCC code. The pixel format codes are
+ * the same used by V4L2 API.
+ */
+
+/* Generic or Raw Data Formats */
+#define IPU_PIX_FMT_GENERIC v4l2_fourcc('I', 'P', 'U', '0') /* IPU Generic Data */
+#define IPU_PIX_FMT_GENERIC_32 v4l2_fourcc('I', 'P', 'U', '1') /* IPU Generic Data */
+#define IPU_PIX_FMT_LVDS666 v4l2_fourcc('L', 'V', 'D', '6') /* IPU Generic Data */
+#define IPU_PIX_FMT_LVDS888 v4l2_fourcc('L', 'V', 'D', '8') /* IPU Generic Data */
+/* RGB Formats */
+#define IPU_PIX_FMT_RGB332 V4L2_PIX_FMT_RGB332 /* 8 RGB-3-3-2 */
+#define IPU_PIX_FMT_RGB555 V4L2_PIX_FMT_RGB555 /* 16 RGB-5-5-5 */
+#define IPU_PIX_FMT_RGB565 V4L2_PIX_FMT_RGB565 /* 1 6 RGB-5-6-5 */
+#define IPU_PIX_FMT_RGB666 v4l2_fourcc('R', 'G', 'B', '6') /* 18 RGB-6-6-6 */
+#define IPU_PIX_FMT_BGR666 v4l2_fourcc('B', 'G', 'R', '6') /* 18 BGR-6-6-6 */
+#define IPU_PIX_FMT_BGR24 V4L2_PIX_FMT_BGR24 /* 24 BGR-8-8-8 */
+#define IPU_PIX_FMT_RGB24 V4L2_PIX_FMT_RGB24 /* 24 RGB-8-8-8 */
+#define IPU_PIX_FMT_BGR32 V4L2_PIX_FMT_BGR32 /* 32 BGR-8-8-8-8 */
+#define IPU_PIX_FMT_BGRA32 v4l2_fourcc('B', 'G', 'R', 'A') /* 32 BGR-8-8-8-8 */
+#define IPU_PIX_FMT_RGB32 V4L2_PIX_FMT_RGB32 /* 32 RGB-8-8-8-8 */
+#define IPU_PIX_FMT_RGBA32 v4l2_fourcc('R', 'G', 'B', 'A') /* 32 RGB-8-8-8-8 */
+#define IPU_PIX_FMT_ABGR32 v4l2_fourcc('A', 'B', 'G', 'R') /* 32 ABGR-8-8-8-8 */
+/* YUV Interleaved Formats */
+#define IPU_PIX_FMT_YUYV V4L2_PIX_FMT_YUYV /* 16 YUV 4:2:2 */
+#define IPU_PIX_FMT_UYVY V4L2_PIX_FMT_UYVY /* 16 YUV 4:2:2 */
+#define IPU_PIX_FMT_Y41P V4L2_PIX_FMT_Y41P /* 12 YUV 4:1:1 */
+#define IPU_PIX_FMT_YUV444 V4L2_PIX_FMT_YUV444 /* 24 YUV 4:4:4 */
+/* two planes -- one Y, one Cb + Cr interleaved */
+#define IPU_PIX_FMT_NV12 V4L2_PIX_FMT_NV12 /* 12 Y/CbCr 4:2:0 */
+/* YUV Planar Formats */
+#define IPU_PIX_FMT_GREY V4L2_PIX_FMT_GREY /* 8 Greyscale */
+#define IPU_PIX_FMT_YVU410P V4L2_PIX_FMT_YVU410P /* 9 YVU 4:1:0 */
+#define IPU_PIX_FMT_YUV410P V4L2_PIX_FMT_YUV410P /* 9 YUV 4:1:0 */
+#define IPU_PIX_FMT_YVU420P v4l2_fourcc('Y', 'V', '1', '2') /* 12 YVU 4:2:0 */
+#define IPU_PIX_FMT_YUV420P v4l2_fourcc('I', '4', '2', '0') /* 12 YUV 4:2:0 */
+#define IPU_PIX_FMT_YUV420P2 v4l2_fourcc('Y', 'U', '1', '2') /* 12 YUV 4:2:0 */
+#define IPU_PIX_FMT_YVU422P v4l2_fourcc('Y', 'V', '1', '6') /* 16 YVU 4:2:2 */
+#define IPU_PIX_FMT_YUV422P V4L2_PIX_FMT_YUV422P /* 16 YUV 4:2:2 */
+
+/*
+ * Bitfield of Display Interface signal polarities.
+ */
+struct ipu_di_signal_cfg {
+ unsigned datamask_en:1;
+ unsigned ext_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;
+
+ u16 width;
+ u16 height;
+ u32 pixel_fmt;
+ u16 h_start_width;
+ u16 h_sync_width;
+ u16 h_end_width;
+ u16 v_start_width;
+ u16 v_sync_width;
+ u16 v_end_width;
+ u32 v_to_h_sync;
+};
+
+typedef enum {
+ RGB,
+ YCbCr,
+ YUV
+} ipu_color_space_t;
+
+#define IPU_IRQ_EOF(channel) (channel) /* 0 .. 63 */
+#define IPU_IRQ_NFACK(channel) ((channel) + 64) /* 64 .. 127 */
+#define IPU_IRQ_NFB4EOF(channel) ((channel) + 128) /* 128 .. 191 */
+#define IPU_IRQ_EOS(channel) ((channel) + 192) /* 192 .. 255 */
+
+#define IPU_IRQ_DP_SF_START (448 + 2)
+#define IPU_IRQ_DP_SF_END (448 + 3)
+#define IPU_IRQ_BG_SF_END IPU_IRQ_DP_SF_END,
+#define IPU_IRQ_DC_FC_0 (448 + 8)
+#define IPU_IRQ_DC_FC_1 (448 + 9)
+#define IPU_IRQ_DC_FC_2 (448 + 10)
+#define IPU_IRQ_DC_FC_3 (448 + 11)
+#define IPU_IRQ_DC_FC_4 (448 + 12)
+#define IPU_IRQ_DC_FC_6 (448 + 13)
+#define IPU_IRQ_VSYNC_PRE_0 (448 + 14)
+#define IPU_IRQ_VSYNC_PRE_1 (448 + 15)
+
+#define IPU_IRQ_COUNT (15 * 32)
+
+#define DECLARE_IPU_IRQ_BITMAP(name) DECLARE_BITMAP(name, IPU_IRQ_COUNT)
+
+struct ipu_irq_handler {
+ struct list_head list;
+ void (*handler)(unsigned long *, void *);
+ void *context;
+ DECLARE_IPU_IRQ_BITMAP(ipu_irqs);
+};
+
+int ipu_irq_add_handler(struct ipu_irq_handler *ipuirq);
+void ipu_irq_remove_handler(struct ipu_irq_handler *handler);
+int ipu_irq_update_handler(struct ipu_irq_handler *handler,
+ unsigned long *bitmap);
+int ipu_wait_for_interrupt(int interrupt, int timeout_ms);
+
+struct ipu_channel;
+
+/*
+ * IPU Image DMA Controller (idmac) functions
+ */
+struct ipu_channel *ipu_idmac_get(unsigned channel);
+void ipu_idmac_put(struct ipu_channel *);
+int ipu_idmac_init_channel_buffer(struct ipu_channel *channel,
+ u32 pixel_fmt,
+ u16 width, u16 height,
+ u32 stride,
+ ipu_rotate_mode_t rot_mode,
+ dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
+ u32 u_offset, u32 v_offset, bool interlaced);
+
+int ipu_idmac_update_channel_buffer(struct ipu_channel *channel,
+ u32 bufNum, dma_addr_t phyaddr);
+
+int ipu_idmac_enable_channel(struct ipu_channel *channel);
+int ipu_idmac_disable_channel(struct ipu_channel *channel);
+
+void ipu_idmac_set_double_buffer(struct ipu_channel *channel, bool doublebuffer);
+void ipu_idmac_select_buffer(struct ipu_channel *channel, u32 buf_num);
+
+/*
+ * IPU Display Controller (dc) functions
+ */
+int ipu_dc_init_sync(int dc_chan, int di, bool interlaced, u32 pixel_fmt, u32 width);
+void ipu_dc_init_async(int dc_chan, int di, bool interlaced);
+void ipu_dc_enable_channel(u32 dc_chan);
+void ipu_dc_disable_channel(u32 dc_chan);
+
+/*
+ * IPU Display Interface (di) functions
+ */
+int ipu_di_disable(int disp);
+int ipu_di_enable(int disp);
+int ipu_di_init_sync_panel(int disp, u32 pixel_clk,
+ struct ipu_di_signal_cfg *sig);
+
+/*
+ * IPU Display Multi FIFO Controller (dmfc) functions
+ */
+struct dmfc_channel;
+int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc);
+void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc);
+int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc, unsigned long bandwidth_mbs);
+void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc);
+int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width);
+struct dmfc_channel *ipu_dmfc_get(int ipu_channel);
+void ipu_dmfc_put(struct dmfc_channel *dmfc);
+
+/*
+ * IPU Display Processor (dp) functions
+ */
+#define IPU_DP_FLOW_SYNC 0
+#define IPU_DP_FLOW_ASYNC0 1
+#define IPU_DP_FLOW_ASYNC1 2
+
+struct ipu_dp *ipu_dp_get(unsigned int flow);
+void ipu_dp_put(struct ipu_dp *);
+int ipu_dp_enable_channel(struct ipu_dp *dp);
+void ipu_dp_disable_channel(struct ipu_dp *dp);
+void ipu_dp_enable_fg(struct ipu_dp *dp);
+void ipu_dp_disable_fg(struct ipu_dp *dp);
+int ipu_dp_setup_channel(struct ipu_dp *dp, u32 in_pixel_fmt,
+ u32 out_pixel_fmt, int bg);
+int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos);
+int ipu_dp_set_color_key(struct ipu_dp *dp, bool enable, u32 colorKey);
+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha,
+ bool bg_chan);
+
+#endif
--
1.7.2.3

2010-12-09 13:49:07

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 6/9] ARM i.MX51: Add IPU device support

Signed-off-by: Sascha Hauer <[email protected]>
---
arch/arm/mach-mx5/devices-imx51.h | 4 ++
arch/arm/plat-mxc/devices/Kconfig | 4 ++
arch/arm/plat-mxc/devices/Makefile | 1 +
arch/arm/plat-mxc/devices/platform-imx_ipuv3.c | 47 +++++++++++++++++++++++
arch/arm/plat-mxc/include/mach/devices-common.h | 10 +++++
5 files changed, 66 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/plat-mxc/devices/platform-imx_ipuv3.c

diff --git a/arch/arm/mach-mx5/devices-imx51.h b/arch/arm/mach-mx5/devices-imx51.h
index 6302e46..851c114 100644
--- a/arch/arm/mach-mx5/devices-imx51.h
+++ b/arch/arm/mach-mx5/devices-imx51.h
@@ -47,3 +47,7 @@ extern const struct imx_spi_imx_data imx51_ecspi_data[] __initconst;
extern const struct imx_imx2_wdt_data imx51_imx2_wdt_data[] __initconst;
#define imx51_add_imx2_wdt(id, pdata) \
imx_add_imx2_wdt(&imx51_imx2_wdt_data[id])
+
+extern const struct imx_ipuv3_data imx51_ipuv3_data __initconst;
+#define imx51_add_ipuv3(pdata) \
+ imx_add_ipuv3(&imx51_ipuv3_data, pdata)
diff --git a/arch/arm/plat-mxc/devices/Kconfig b/arch/arm/plat-mxc/devices/Kconfig
index 2537166..262d9c5 100644
--- a/arch/arm/plat-mxc/devices/Kconfig
+++ b/arch/arm/plat-mxc/devices/Kconfig
@@ -71,3 +71,7 @@ config IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX

config IMX_HAVE_PLATFORM_SPI_IMX
bool
+
+config IMX_HAVE_PLATFORM_IMX_IPUV3
+ bool
+
diff --git a/arch/arm/plat-mxc/devices/Makefile b/arch/arm/plat-mxc/devices/Makefile
index 75cd2ec..0a6be0a 100644
--- a/arch/arm/plat-mxc/devices/Makefile
+++ b/arch/arm/plat-mxc/devices/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_RNGA) += platform-mxc_rnga.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_W1) += platform-mxc_w1.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX) += platform-sdhci-esdhc-imx.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_SPI_IMX) += platform-spi_imx.o
+obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_IPUV3) += platform-imx_ipuv3.o
diff --git a/arch/arm/plat-mxc/devices/platform-imx_ipuv3.c b/arch/arm/plat-mxc/devices/platform-imx_ipuv3.c
new file mode 100644
index 0000000..a470edb
--- /dev/null
+++ b/arch/arm/plat-mxc/devices/platform-imx_ipuv3.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 Pengutronix
+ * Uwe Kleine-Koenig <[email protected]>
+ *
+ * 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 <mach/hardware.h>
+#include <mach/devices-common.h>
+
+#define imx51_ipuv3_data_entry_single(soc) \
+ { \
+ .iobase = soc ## _IPU_CTRL_BASE_ADDR, \
+ .irq_err = soc ## _INT_IPU_ERR, \
+ .irq = soc ## _INT_IPU_SYN, \
+ }
+
+#ifdef CONFIG_SOC_IMX51
+const struct imx_ipuv3_data imx51_ipuv3_data __initconst =
+ imx51_ipuv3_data_entry_single(MX51);
+#endif /* ifdef CONFIG_SOC_IMX35 */
+
+struct platform_device *__init imx_add_ipuv3(
+ const struct imx_ipuv3_data *data,
+ const struct imx_ipuv3_platform_data *pdata)
+{
+ struct resource res[] = {
+ {
+ .start = data->iobase,
+ .end = data->iobase + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = data->irq_err,
+ .end = data->irq_err,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = data->irq,
+ .end = data->irq,
+ .flags = IORESOURCE_IRQ,
+ },
+ };
+
+ return imx_add_platform_device("imx-ipuv3", -1,
+ res, ARRAY_SIZE(res), pdata, sizeof(*pdata));
+}
+
diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h
index 8658c9c..8f5197f 100644
--- a/arch/arm/plat-mxc/include/mach/devices-common.h
+++ b/arch/arm/plat-mxc/include/mach/devices-common.h
@@ -264,3 +264,13 @@ struct imx_spi_imx_data {
struct platform_device *__init imx_add_spi_imx(
const struct imx_spi_imx_data *data,
const struct spi_imx_master *pdata);
+
+#include <mach/ipu-v3.h>
+struct imx_ipuv3_data {
+ resource_size_t iobase;
+ resource_size_t irq_err;
+ resource_size_t irq;
+};
+struct platform_device *__init imx_add_ipuv3(
+ const struct imx_ipuv3_data *data,
+ const struct imx_ipuv3_platform_data *pdata);
--
1.7.2.3

2010-12-09 13:49:09

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 9/9] ARM i.MX51 babbage: Add framebuffer support

Signed-off-by: Sascha Hauer <[email protected]>
---
arch/arm/mach-mx5/Kconfig | 1 +
arch/arm/mach-mx5/board-mx51_babbage.c | 74 ++++++++++++++++++++++++++++++++
2 files changed, 75 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig
index 5011f42..2a936b7 100644
--- a/arch/arm/mach-mx5/Kconfig
+++ b/arch/arm/mach-mx5/Kconfig
@@ -22,6 +22,7 @@ config MACH_MX51_BABBAGE
select IMX_HAVE_PLATFORM_IMX_UART
select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX
select IMX_HAVE_PLATFORM_SPI_IMX
+ select IMX_HAVE_PLATFORM_IMX_IPUV3
help
Include support for MX51 Babbage platform, also known as MX51EVK in
u-boot. This includes specific configurations for the board and its
diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c b/arch/arm/mach-mx5/board-mx51_babbage.c
index a896f84..169c48c 100644
--- a/arch/arm/mach-mx5/board-mx51_babbage.c
+++ b/arch/arm/mach-mx5/board-mx51_babbage.c
@@ -22,11 +22,13 @@
#include <linux/input.h>
#include <linux/spi/flash.h>
#include <linux/spi/spi.h>
+#include <linux/mfd/imx-ipu-v3.h>

#include <mach/common.h>
#include <mach/hardware.h>
#include <mach/iomux-mx51.h>
#include <mach/mxc_ehci.h>
+#include <mach/ipu-v3.h>

#include <asm/irq.h>
#include <asm/setup.h>
@@ -158,6 +160,41 @@ static iomux_v3_cfg_t mx51babbage_pads[] = {
MX51_PAD_CSPI1_SCLK__ECSPI1_SCLK,
MX51_PAD_CSPI1_SS0__GPIO_4_24,
MX51_PAD_CSPI1_SS1__GPIO_4_25,
+
+ /* Display */
+ MX51_PAD_DISPB2_SER_DIN__GPIO_3_5,
+ MX51_PAD_DISPB2_SER_DIO__GPIO_3_6,
+ MX51_PAD_NANDF_D12__GPIO_3_28,
+
+ MX51_PAD_DISP1_DAT0__DISP1_DAT0,
+ MX51_PAD_DISP1_DAT1__DISP1_DAT1,
+ MX51_PAD_DISP1_DAT2__DISP1_DAT2,
+ MX51_PAD_DISP1_DAT3__DISP1_DAT3,
+ MX51_PAD_DISP1_DAT4__DISP1_DAT4,
+ MX51_PAD_DISP1_DAT5__DISP1_DAT5,
+ MX51_PAD_DISP1_DAT6__DISP1_DAT6,
+ MX51_PAD_DISP1_DAT7__DISP1_DAT7,
+ MX51_PAD_DISP1_DAT8__DISP1_DAT8,
+ MX51_PAD_DISP1_DAT9__DISP1_DAT9,
+ MX51_PAD_DISP1_DAT10__DISP1_DAT10,
+ MX51_PAD_DISP1_DAT11__DISP1_DAT11,
+ MX51_PAD_DISP1_DAT12__DISP1_DAT12,
+ MX51_PAD_DISP1_DAT13__DISP1_DAT13,
+ MX51_PAD_DISP1_DAT14__DISP1_DAT14,
+ MX51_PAD_DISP1_DAT15__DISP1_DAT15,
+ MX51_PAD_DISP1_DAT16__DISP1_DAT16,
+ MX51_PAD_DISP1_DAT17__DISP1_DAT17,
+ MX51_PAD_DISP1_DAT18__DISP1_DAT18,
+ MX51_PAD_DISP1_DAT19__DISP1_DAT19,
+ MX51_PAD_DISP1_DAT20__DISP1_DAT20,
+ MX51_PAD_DISP1_DAT21__DISP1_DAT21,
+ MX51_PAD_DISP1_DAT22__DISP1_DAT22,
+ MX51_PAD_DISP1_DAT23__DISP1_DAT23,
+#define MX51_PAD_DI_GP4__IPU_DI2_PIN15 IOMUX_PAD(0x758, 0x350, 4, 0x0, 0, NO_PAD_CTRL)
+ MX51_PAD_DI_GP4__IPU_DI2_PIN15,
+
+ /* I2C DVI enable */
+ MX51_PAD_CSI2_HSYNC__GPIO_4_14,
};

/* Serial ports */
@@ -342,6 +379,21 @@ static const struct spi_imx_master mx51_babbage_spi_pdata __initconst = {
.num_chipselect = ARRAY_SIZE(mx51_babbage_spi_cs),
};

+static struct ipuv3_fb_platform_data babbage_fb0_data = {
+ .interface_pix_fmt = IPU_PIX_FMT_RGB24,
+ .flags = IMX_IPU_FB_USE_MODEDB | IMX_IPU_FB_USE_OVERLAY,
+};
+
+static struct ipuv3_fb_platform_data babbage_fb1_data = {
+ .interface_pix_fmt = IPU_PIX_FMT_RGB565,
+ .flags = IMX_IPU_FB_USE_MODEDB,
+};
+
+static struct imx_ipuv3_platform_data ipu_data = {
+ .fb0_platform_data = &babbage_fb0_data,
+ .fb1_platform_data = &babbage_fb1_data,
+};
+
/*
* Board specific initialization.
*/
@@ -388,6 +440,28 @@ static void __init mxc_board_init(void)
ARRAY_SIZE(mx51_babbage_spi_board_info));
imx51_add_ecspi(0, &mx51_babbage_spi_pdata);
imx51_add_imx2_wdt(0, NULL);
+
+#define GPIO_DVI_DETECT (2 * 32 + 28)
+#define GPIO_DVI_RESET (2 * 32 + 5)
+#define GPIO_DVI_PWRDN (2 * 32 + 6)
+#define GPIO_DVI_I2C (3 * 32 + 14)
+
+ /* DVI Detect */
+ gpio_request(GPIO_DVI_DETECT, "dvi detect");
+ gpio_direction_input(GPIO_DVI_DETECT);
+ /* DVI Reset - Assert for i2c disabled mode */
+ gpio_request(GPIO_DVI_RESET, "dvi reset");
+ gpio_set_value(GPIO_DVI_RESET, 0);
+ gpio_direction_output(GPIO_DVI_RESET, 0);
+ /* DVI Power-down */
+ gpio_request(GPIO_DVI_PWRDN, "dvi pwdn");
+ gpio_direction_output(GPIO_DVI_PWRDN, 0);
+ gpio_set_value(GPIO_DVI_PWRDN, 1);
+
+ gpio_request(GPIO_DVI_I2C, "dvi i2c");
+ gpio_direction_output(GPIO_DVI_I2C, 0);
+
+ imx51_add_ipuv3(&ipu_data);
}

static void __init mx51_babbage_timer_init(void)
--
1.7.2.3

2010-12-09 13:49:03

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 4/9] fb: export fb mode db table

The different modes can be useful for drivers. Currently there is
no way to expose the modes to sysfs, so export them.

Signed-off-by: Sascha Hauer <[email protected]>
---
drivers/video/modedb.c | 7 ++++++-
include/linux/fb.h | 3 +++
2 files changed, 9 insertions(+), 1 deletions(-)

diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
index 0a4dbdc..82122a9 100644
--- a/drivers/video/modedb.c
+++ b/drivers/video/modedb.c
@@ -36,7 +36,7 @@ EXPORT_SYMBOL_GPL(fb_mode_option);
* Standard video mode definitions (taken from XFree86)
*/

-static const struct fb_videomode modedb[] = {
+const struct fb_videomode modedb[] = {
{
/* 640x400 @ 70 Hz, 31.5 kHz hsync */
NULL, 70, 640, 400, 39721, 40, 24, 39, 9, 96, 2,
@@ -277,6 +277,11 @@ static const struct fb_videomode modedb[] = {
},
};

+const struct fb_videomode *fb_modes = modedb;
+EXPORT_SYMBOL(fb_modes);
+const int num_fb_modes = ARRAY_SIZE(modedb);
+EXPORT_SYMBOL(num_fb_modes);
+
#ifdef CONFIG_FB_MODE_HELPERS
const struct fb_videomode vesa_modes[] = {
/* 0 640x350-85 VESA */
diff --git a/include/linux/fb.h b/include/linux/fb.h
index d1631d3..e006172 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -1151,6 +1151,9 @@ struct fb_videomode {
extern const char *fb_mode_option;
extern const struct fb_videomode vesa_modes[];

+extern const struct fb_videomode *fb_modes;
+extern const int num_fb_modes;
+
struct fb_modelist {
struct list_head list;
struct fb_videomode mode;
--
1.7.2.3

2010-12-09 13:49:05

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 2/9] ARM i.MX51: rename IPU irqs

Signed-off-by: Sascha Hauer <[email protected]>
---
arch/arm/plat-mxc/include/mach/mx51.h | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/plat-mxc/include/mach/mx51.h b/arch/arm/plat-mxc/include/mach/mx51.h
index fa3a2a5..873807f 100644
--- a/arch/arm/plat-mxc/include/mach/mx51.h
+++ b/arch/arm/plat-mxc/include/mach/mx51.h
@@ -251,8 +251,8 @@
#define MX51_MXC_INT_IOMUX 7
#define MX51_INT_NFC 8
#define MX51_MXC_INT_VPU 9
-#define MX51_MXC_INT_IPU_ERR 10
-#define MX51_MXC_INT_IPU_SYN 11
+#define MX51_INT_IPU_ERR 10
+#define MX51_INT_IPU_SYN 11
#define MX51_MXC_INT_GPU 12
#define MX51_MXC_INT_RESV13 13
#define MX51_MXC_INT_USB_H1 14
--
1.7.2.3

2010-12-09 14:34:50

by Uwe Kleine-König

[permalink] [raw]
Subject: Re: [PATCH 2/9] ARM i.MX51: rename IPU irqs

On Thu, Dec 09, 2010 at 02:47:14PM +0100, Sascha Hauer wrote:
> -#define MX51_MXC_INT_IPU_ERR 10
> -#define MX51_MXC_INT_IPU_SYN 11
> +#define MX51_INT_IPU_ERR 10
> +#define MX51_INT_IPU_SYN 11
This one is easy. Acked-by: me. Maybe it's worth to do

git ls-files | xargs perl -p -i -e 's/\bMX51_MXC_INT_/MX51_INT_/

and then fix up the layout damage.

Best regards
Uwe


--
Pengutronix e.K. | Uwe Kleine-K?nig |
Industrial Linux Solutions | http://www.pengutronix.de/ |

2010-12-12 01:42:53

by Liu Ying

[permalink] [raw]
Subject: Re: [PATCH 9/9] ARM i.MX51 babbage: Add framebuffer support

Hello, Sascha,

I have following 3 comments to this patch:
1) I think DISP1_DATx pins need not be set specially, as they keep the
default register values.
2) Please define 'MX51_PAD_DI_GP4__IPU_DI2_PIN15' in
arch/arm/plat-mxc/include/mach/iomux-mx51.h, and rename the pin to be
'MX51_PAD_DI_GP4__DI2_PIN15', as we name it according to the MX51
iomux reference manual.
3) It is better to exchange the following two lines or just remove the
first line:
+ gpio_set_value(GPIO_DVI_RESET, 0);
+ gpio_direction_output(GPIO_DVI_RESET, 0);

Best Regards,
Liu Ying

2010/12/9 Sascha Hauer <[email protected]>:
> Signed-off-by: Sascha Hauer <[email protected]>
> ---
> ?arch/arm/mach-mx5/Kconfig ? ? ? ? ? ? ?| ? ?1 +
> ?arch/arm/mach-mx5/board-mx51_babbage.c | ? 74 ++++++++++++++++++++++++++++++++
> ?2 files changed, 75 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig
> index 5011f42..2a936b7 100644
> --- a/arch/arm/mach-mx5/Kconfig
> +++ b/arch/arm/mach-mx5/Kconfig
> @@ -22,6 +22,7 @@ config MACH_MX51_BABBAGE
> ? ? ? ?select IMX_HAVE_PLATFORM_IMX_UART
> ? ? ? ?select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX
> ? ? ? ?select IMX_HAVE_PLATFORM_SPI_IMX
> + ? ? ? select IMX_HAVE_PLATFORM_IMX_IPUV3
> ? ? ? ?help
> ? ? ? ? ?Include support for MX51 Babbage platform, also known as MX51EVK in
> ? ? ? ? ?u-boot. This includes specific configurations for the board and its
> diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c b/arch/arm/mach-mx5/board-mx51_babbage.c
> index a896f84..169c48c 100644
> --- a/arch/arm/mach-mx5/board-mx51_babbage.c
> +++ b/arch/arm/mach-mx5/board-mx51_babbage.c
> @@ -22,11 +22,13 @@
> ?#include <linux/input.h>
> ?#include <linux/spi/flash.h>
> ?#include <linux/spi/spi.h>
> +#include <linux/mfd/imx-ipu-v3.h>
>
> ?#include <mach/common.h>
> ?#include <mach/hardware.h>
> ?#include <mach/iomux-mx51.h>
> ?#include <mach/mxc_ehci.h>
> +#include <mach/ipu-v3.h>
>
> ?#include <asm/irq.h>
> ?#include <asm/setup.h>
> @@ -158,6 +160,41 @@ static iomux_v3_cfg_t mx51babbage_pads[] = {
> ? ? ? ?MX51_PAD_CSPI1_SCLK__ECSPI1_SCLK,
> ? ? ? ?MX51_PAD_CSPI1_SS0__GPIO_4_24,
> ? ? ? ?MX51_PAD_CSPI1_SS1__GPIO_4_25,
> +
> + ? ? ? /* Display */
> + ? ? ? MX51_PAD_DISPB2_SER_DIN__GPIO_3_5,
> + ? ? ? MX51_PAD_DISPB2_SER_DIO__GPIO_3_6,
> + ? ? ? MX51_PAD_NANDF_D12__GPIO_3_28,
> +
> + ? ? ? MX51_PAD_DISP1_DAT0__DISP1_DAT0,
> + ? ? ? MX51_PAD_DISP1_DAT1__DISP1_DAT1,
> + ? ? ? MX51_PAD_DISP1_DAT2__DISP1_DAT2,
> + ? ? ? MX51_PAD_DISP1_DAT3__DISP1_DAT3,
> + ? ? ? MX51_PAD_DISP1_DAT4__DISP1_DAT4,
> + ? ? ? MX51_PAD_DISP1_DAT5__DISP1_DAT5,
> + ? ? ? MX51_PAD_DISP1_DAT6__DISP1_DAT6,
> + ? ? ? MX51_PAD_DISP1_DAT7__DISP1_DAT7,
> + ? ? ? MX51_PAD_DISP1_DAT8__DISP1_DAT8,
> + ? ? ? MX51_PAD_DISP1_DAT9__DISP1_DAT9,
> + ? ? ? MX51_PAD_DISP1_DAT10__DISP1_DAT10,
> + ? ? ? MX51_PAD_DISP1_DAT11__DISP1_DAT11,
> + ? ? ? MX51_PAD_DISP1_DAT12__DISP1_DAT12,
> + ? ? ? MX51_PAD_DISP1_DAT13__DISP1_DAT13,
> + ? ? ? MX51_PAD_DISP1_DAT14__DISP1_DAT14,
> + ? ? ? MX51_PAD_DISP1_DAT15__DISP1_DAT15,
> + ? ? ? MX51_PAD_DISP1_DAT16__DISP1_DAT16,
> + ? ? ? MX51_PAD_DISP1_DAT17__DISP1_DAT17,
> + ? ? ? MX51_PAD_DISP1_DAT18__DISP1_DAT18,
> + ? ? ? MX51_PAD_DISP1_DAT19__DISP1_DAT19,
> + ? ? ? MX51_PAD_DISP1_DAT20__DISP1_DAT20,
> + ? ? ? MX51_PAD_DISP1_DAT21__DISP1_DAT21,
> + ? ? ? MX51_PAD_DISP1_DAT22__DISP1_DAT22,
> + ? ? ? MX51_PAD_DISP1_DAT23__DISP1_DAT23,
> +#define MX51_PAD_DI_GP4__IPU_DI2_PIN15 ? ? ? ? ? ? ? ? IOMUX_PAD(0x758, 0x350, 4, 0x0, ? 0, NO_PAD_CTRL)
> + ? ? ? MX51_PAD_DI_GP4__IPU_DI2_PIN15,
> +
> + ? ? ? /* I2C DVI enable */
> + ? ? ? MX51_PAD_CSI2_HSYNC__GPIO_4_14,
> ?};
>
> ?/* Serial ports */
> @@ -342,6 +379,21 @@ static const struct spi_imx_master mx51_babbage_spi_pdata __initconst = {
> ? ? ? ?.num_chipselect = ARRAY_SIZE(mx51_babbage_spi_cs),
> ?};
>
> +static struct ipuv3_fb_platform_data babbage_fb0_data = {
> + ? ? ? .interface_pix_fmt = IPU_PIX_FMT_RGB24,
> + ? ? ? .flags = IMX_IPU_FB_USE_MODEDB | IMX_IPU_FB_USE_OVERLAY,
> +};
> +
> +static struct ipuv3_fb_platform_data babbage_fb1_data = {
> + ? ? ? .interface_pix_fmt = IPU_PIX_FMT_RGB565,
> + ? ? ? .flags = IMX_IPU_FB_USE_MODEDB,
> +};
> +
> +static struct imx_ipuv3_platform_data ipu_data = {
> + ? ? ? .fb0_platform_data = &babbage_fb0_data,
> + ? ? ? .fb1_platform_data = &babbage_fb1_data,
> +};
> +
> ?/*
> ?* Board specific initialization.
> ?*/
> @@ -388,6 +440,28 @@ static void __init mxc_board_init(void)
> ? ? ? ? ? ? ? ?ARRAY_SIZE(mx51_babbage_spi_board_info));
> ? ? ? ?imx51_add_ecspi(0, &mx51_babbage_spi_pdata);
> ? ? ? ?imx51_add_imx2_wdt(0, NULL);
> +
> +#define GPIO_DVI_DETECT ? ? ? ?(2 * 32 + 28)
> +#define GPIO_DVI_RESET (2 * 32 + 5)
> +#define GPIO_DVI_PWRDN (2 * 32 + 6)
> +#define GPIO_DVI_I2C ? (3 * 32 + 14)
> +
> + ? ? ? /* DVI Detect */
> + ? ? ? gpio_request(GPIO_DVI_DETECT, "dvi detect");
> + ? ? ? gpio_direction_input(GPIO_DVI_DETECT);
> + ? ? ? /* DVI Reset - Assert for i2c disabled mode */
> + ? ? ? gpio_request(GPIO_DVI_RESET, "dvi reset");
> + ? ? ? gpio_set_value(GPIO_DVI_RESET, 0);
> + ? ? ? gpio_direction_output(GPIO_DVI_RESET, 0);
> + ? ? ? /* DVI Power-down */
> + ? ? ? gpio_request(GPIO_DVI_PWRDN, "dvi pwdn");
> + ? ? ? gpio_direction_output(GPIO_DVI_PWRDN, 0);
> + ? ? ? gpio_set_value(GPIO_DVI_PWRDN, 1);
> +
> + ? ? ? gpio_request(GPIO_DVI_I2C, "dvi i2c");
> + ? ? ? gpio_direction_output(GPIO_DVI_I2C, 0);
> +
> + ? ? ? imx51_add_ipuv3(&ipu_data);
> ?}
>
> ?static void __init mx51_babbage_timer_init(void)
> --
> 1.7.2.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at ?http://www.tux.org/lkml/
>

2010-12-12 05:40:45

by Liu Ying

[permalink] [raw]
Subject: Re: [PATCH 3/9] Add a mfd IPUv3 driver

Hello, Sascha,

IPU is not embedded in i,MX50 SoC, but in i.MX51/53 SoCs, please
modify the commit message.

I have the following comments for the files editted respectively:
1) ipu-common.c
- ipu_idmac_get()/ipu_idmac_put() need a mechanism to protect IPU
IDMAC resources, namely, get rid of potential race condition issue. As
only framebuffer support is added in your patches, there should be no
race condition issue now. After IC channels are supported(maybe as DMA
engine), perhaps, there will be such kind of issue.
- ipu_idmac_select_buffer() need to add spinlock to protect
IPU_CHA_BUFx_RDY registers.
- It looks that ipu_uninit_channel() only clears
IPU_CHA_DB_MODE_SEL register, so why not put it in
ipu_idmac_disable_channel()?
- It looks that ipu_add_client_devices() and your framebuffer
patch assume /dev/fb0 uses DP_BG, /dev/fb1 uses DP_FG and /dev/fb2
uses DC.
But fb1_platform_data->ipu_channel_bg is
MX51_IPU_CHANNEL_MEM_DC_SYNC, this may make confusion for driver
readers and it is not easy for code maintenance.
- It also looks that ipu_add_client_devices() and your framebuffer
driver assume DP_BG/DP_FG are bound with DI0 and DC is bound with DI1.
It is ok for babbage board to support this kind of
configuration, but it may bring some limitation. For example, TVE(TV
encoder) module embedded in MX51 SoC can only connected with DI1 and
users may like to use TV as the primary device(support HW overlay),
and FSL Android BSP does support to use DI1 with LCD as the primary
device on babbage board.

2) ipu-cpmem.c
- In order to improve performance, maybe writing
ipu_ch_param_addr(ch) directly will be fine, but not using memset()
and memcpy() in ipu_ch_param_init().

3) ipu-dc.c
- Looks '#include <asm/atomic.h>' and '#include
<linux/spinlock.h>' are unnecessary.
- Need to call 'ipu_module_disable(IPU_CONF_DC_EN);' somewhere.

4) ipu-di.c
- Looks '#include <asm/atomic.h>' and '#include <linux/delay.h>'
are unnecessary.

5) ipu-dmfc.c
- Looks '#include <linux/delay.h>' are unnecessary.

6) ipu-dp.c
- Looks '#include <asm/atomic.h>' and '#include <linux/delay.h>'
are unnecessary.

7) ipu-prv.h
- Looks '#include <linux/interrupt.h>' is unnecessary.
- Please rename 'MX51_IPU_CHANNEL_xxxx' to be 'IPU_CHANNEL_xxxx',
IPUv3 channel names do not depend on SoC name.

8) include/linux/mfd/imx-ipu-v3.h
- Looks '#include <linux/slab.h>' and '#include
<linux/interrupt.h>' are unnecessary.

9) arch/arm/plat-mxc/include/mach/ipu-v3.h, drivers/mfd/Kconfig,
drivers/mfd/Makefile, drivers/mfd/imx-ipu-v3/Makefile
- No comment.

Best Regards,
Liu Ying

2010/12/9 Sascha Hauer <[email protected]>:
> The IPU is the Image Processing Unit found on i.MX50/51/53 SoCs. It
> features several units for image processing, this patch adds support
> for the units needed for Framebuffer support, namely:
>
> - Display Controller (dc)
> - Display Interface (di)
> - Display Multi Fifo Controller (dmfc)
> - Display Processor (dp)
> - Image DMA Controller (idmac)
>
> This patch is based on the Freescale driver, but follows a different
> approach. The Freescale code implements logical idmac channels and
> the handling of the subunits is hidden in common idmac code pathes
> in big switch/case statements. This patch instead just provides code
> and resource management for the different subunits. The user, in this
> case the framebuffer driver, decides how the different units play
> together.
>
> The IPU has other units missing in this patch:
>
> - CMOS Sensor Interface (csi)
> - Video Deinterlacer (vdi)
> - Sensor Multi FIFO Controler (smfc)
> - Image Converter (ic)
> - Image Rotator (irt)
>
> So expect more files to come in this directory.
>
> Signed-off-by: Sascha Hauer <[email protected]>
> ---
> ?arch/arm/plat-mxc/include/mach/ipu-v3.h | ? 48 +++
> ?drivers/mfd/Kconfig ? ? ? ? ? ? ? ? ? ? | ? ?7 +
> ?drivers/mfd/Makefile ? ? ? ? ? ? ? ? ? ?| ? ?1 +
> ?drivers/mfd/imx-ipu-v3/Makefile ? ? ? ? | ? ?3 +
> ?drivers/mfd/imx-ipu-v3/ipu-common.c ? ? | ?661 +++++++++++++++++++++++++++++++
> ?drivers/mfd/imx-ipu-v3/ipu-cpmem.c ? ? ?| ?608 ++++++++++++++++++++++++++++
> ?drivers/mfd/imx-ipu-v3/ipu-dc.c ? ? ? ? | ?362 +++++++++++++++++
> ?drivers/mfd/imx-ipu-v3/ipu-di.c ? ? ? ? | ?507 ++++++++++++++++++++++++
> ?drivers/mfd/imx-ipu-v3/ipu-dmfc.c ? ? ? | ?343 ++++++++++++++++
> ?drivers/mfd/imx-ipu-v3/ipu-dp.c ? ? ? ? | ?468 ++++++++++++++++++++++
> ?drivers/mfd/imx-ipu-v3/ipu-prv.h ? ? ? ?| ?214 ++++++++++
> ?include/linux/mfd/imx-ipu-v3.h ? ? ? ? ?| ?218 ++++++++++
> ?12 files changed, 3440 insertions(+), 0 deletions(-)
> ?create mode 100644 arch/arm/plat-mxc/include/mach/ipu-v3.h
> ?create mode 100644 drivers/mfd/imx-ipu-v3/Makefile
> ?create mode 100644 drivers/mfd/imx-ipu-v3/ipu-common.c
> ?create mode 100644 drivers/mfd/imx-ipu-v3/ipu-cpmem.c
> ?create mode 100644 drivers/mfd/imx-ipu-v3/ipu-dc.c
> ?create mode 100644 drivers/mfd/imx-ipu-v3/ipu-di.c
> ?create mode 100644 drivers/mfd/imx-ipu-v3/ipu-dmfc.c
> ?create mode 100644 drivers/mfd/imx-ipu-v3/ipu-dp.c
> ?create mode 100644 drivers/mfd/imx-ipu-v3/ipu-prv.h
> ?create mode 100644 include/linux/mfd/imx-ipu-v3.h
>
> diff --git a/arch/arm/plat-mxc/include/mach/ipu-v3.h b/arch/arm/plat-mxc/include/mach/ipu-v3.h
> new file mode 100644
> index 0000000..0a6c3e8
> --- /dev/null
> +++ b/arch/arm/plat-mxc/include/mach/ipu-v3.h
> @@ -0,0 +1,48 @@
> +/*
> + * Copyright (c) 2010 Sascha Hauer <[email protected]>
> + *
> + * 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 __MACH_IPU_V3_H_
> +#define __MACH_IPU_V3_H_
> +
> +/* IPU specific extensions to struct fb_videomode flag field */
> +#define FB_SYNC_OE_LOW_ACT ? ? (1 << 8)
> +#define FB_SYNC_CLK_LAT_FALL ? (1 << 9)
> +#define FB_SYNC_DATA_INVERT ? ?(1 << 10)
> +#define FB_SYNC_CLK_IDLE_EN ? ?(1 << 11)
> +#define FB_SYNC_SHARP_MODE ? ? (1 << 12)
> +#define FB_SYNC_SWAP_RGB ? ? ? (1 << 13)
> +
> +struct ipuv3_fb_platform_data {
> + ? ? ? const struct fb_videomode ? ? ? *modes;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? ? ? ? ? num_modes;
> + ? ? ? char ? ? ? ? ? ? ? ? ? ? ? ? ? ?*mode_str;
> + ? ? ? u32 ? ? ? ? ? ? ? ? ? ? ? ? ? ? interface_pix_fmt;
> +
> +#define IMX_IPU_FB_USE_MODEDB ?(1 << 0)
> +#define IMX_IPU_FB_USE_OVERLAY (1 << 1)
> + ? ? ? unsigned long ? ? ? ? ? ? ? ? ? flags;
> +
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? ? ? ? ? ipu_channel_bg;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? ? ? ? ? ipu_channel_fg;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? ? ? ? ? dc_channel;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? ? ? ? ? dp_channel;
> +};
> +
> +struct imx_ipuv3_platform_data {
> + ? ? ? int rev;
> + ? ? ? struct ipuv3_fb_platform_data ? *fb0_platform_data;
> + ? ? ? struct ipuv3_fb_platform_data ? *fb1_platform_data;
> +};
> +
> +#endif /* __MACH_IPU_V3_H_ */
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 3a1493b..3c81879 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -606,6 +606,13 @@ config MFD_VX855
> ? ? ? ? ?VIA VX855/VX875 south bridge. You will need to enable the vx855_spi
> ? ? ? ? ?and/or vx855_gpio drivers for this to do anything useful.
>
> +config MFD_IMX_IPU_V3
> + ? ? ? tristate "Support the Image Processing Unit (IPU) found on the i.MX51"
> + ? ? ? depends on ARCH_MX51
> + ? ? ? select MFD_CORE
> + ? ? ? help
> + ? ? ? ? Say yes here to support the IPU on i.MX51.
> +
> ?endif # MFD_SUPPORT
>
> ?menu "Multimedia Capabilities Port drivers"
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index f54b365..7873b13 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -81,3 +81,4 @@ obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
> ?obj-$(CONFIG_MFD_JZ4740_ADC) ? += jz4740-adc.o
> ?obj-$(CONFIG_MFD_TPS6586X) ? ? += tps6586x.o
> ?obj-$(CONFIG_MFD_VX855) ? ? ? ? ? ? ? ?+= vx855.o
> +obj-$(CONFIG_MFD_IMX_IPU_V3) ? += imx-ipu-v3/
> diff --git a/drivers/mfd/imx-ipu-v3/Makefile b/drivers/mfd/imx-ipu-v3/Makefile
> new file mode 100644
> index 0000000..ff70fe8
> --- /dev/null
> +++ b/drivers/mfd/imx-ipu-v3/Makefile
> @@ -0,0 +1,3 @@
> +obj-$(CONFIG_MFD_IMX_IPU_V3) += imx-ipu-v3.o
> +
> +imx-ipu-v3-objs := ipu-common.o ipu-dc.o ipu-di.o ipu-dp.o ipu-dmfc.o ipu-cpmem.o
> diff --git a/drivers/mfd/imx-ipu-v3/ipu-common.c b/drivers/mfd/imx-ipu-v3/ipu-common.c
> new file mode 100644
> index 0000000..e6edb88
> --- /dev/null
> +++ b/drivers/mfd/imx-ipu-v3/ipu-common.c
> @@ -0,0 +1,661 @@
> +/*
> + * Copyright (c) 2010 Sascha Hauer <[email protected]>
> + * Copyright (C) 2005-2009 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.
> + */
> +
> +#include <linux/types.h>
> +#include <linux/init.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/spinlock.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/mfd/imx-ipu-v3.h>
> +#include <linux/clk.h>
> +#include <linux/list.h>
> +#include <mach/common.h>
> +#include <linux/mfd/core.h>
> +#include <mach/ipu-v3.h>
> +
> +#include "ipu-prv.h"
> +
> +static struct clk *ipu_clk;
> +static struct device *ipu_dev;
> +
> +static DEFINE_SPINLOCK(ipu_lock);
> +void __iomem *ipu_cm_reg;
> +void __iomem *ipu_idmac_reg;
> +
> +static struct ipu_channel channels[64];
> +
> +struct ipu_channel *ipu_idmac_get(unsigned num)
> +{
> + ? ? ? struct ipu_channel *channel;
> +
> + ? ? ? dev_dbg(ipu_dev, "%s %d\n", __func__, num);
> +
> + ? ? ? if (num > 63)
> + ? ? ? ? ? ? ? return ERR_PTR(-ENODEV);
> +
> + ? ? ? channel = &channels[num];
> +
> + ? ? ? if (channel->busy)
> + ? ? ? ? ? ? ? return ERR_PTR(-EBUSY);
> +
> + ? ? ? channel->busy = 1;
> + ? ? ? channel->num = num;
> +
> + ? ? ? clk_enable(ipu_clk);
> +
> + ? ? ? return channel;
> +}
> +EXPORT_SYMBOL(ipu_idmac_get);
> +
> +void ipu_idmac_put(struct ipu_channel *channel)
> +{
> + ? ? ? dev_dbg(ipu_dev, "%s %d\n", __func__, channel->num);
> +
> + ? ? ? clk_disable(ipu_clk);
> + ? ? ? channel->busy = 0;
> +}
> +EXPORT_SYMBOL(ipu_idmac_put);
> +
> +void ipu_idmac_set_double_buffer(struct ipu_channel *channel, bool doublebuffer)
> +{
> + ? ? ? unsigned long flags;
> + ? ? ? u32 reg;
> +
> + ? ? ? spin_lock_irqsave(&ipu_lock, flags);
> +
> + ? ? ? reg = ipu_cm_read(IPU_CHA_DB_MODE_SEL(channel->num));
> + ? ? ? if (doublebuffer)
> + ? ? ? ? ? ? ? reg |= idma_mask(channel->num);
> + ? ? ? else
> + ? ? ? ? ? ? ? reg &= ~idma_mask(channel->num);
> + ? ? ? ipu_cm_write(reg, IPU_CHA_DB_MODE_SEL(channel->num));
> +
> + ? ? ? spin_unlock_irqrestore(&ipu_lock, flags);
> +}
> +EXPORT_SYMBOL(ipu_idmac_set_double_buffer);
> +
> +void ipu_uninit_channel(struct ipu_channel *channel)
> +{
> + ? ? ? unsigned long lock_flags;
> + ? ? ? u32 val;
> +
> + ? ? ? /* Make sure channel is disabled */
> +
> + ? ? ? if (idmac_idma_is_set(IDMAC_CHA_EN, channel->num)) {
> + ? ? ? ? ? ? ? dev_err(ipu_dev,
> + ? ? ? ? ? ? ? ? ? ? ? "Channel %d is not disabled, disable first\n",
> + ? ? ? ? ? ? ? ? ? ? ? channel->num);
> + ? ? ? ? ? ? ? return;
> + ? ? ? }
> +
> + ? ? ? spin_lock_irqsave(&ipu_lock, lock_flags);
> +
> + ? ? ? /* Reset the double buffer */
> + ? ? ? val = ipu_cm_read(IPU_CHA_DB_MODE_SEL(channel->num));
> + ? ? ? val &= ~idma_mask(channel->num);
> + ? ? ? ipu_cm_write(val, IPU_CHA_DB_MODE_SEL(channel->num));
> +
> + ? ? ? spin_unlock_irqrestore(&ipu_lock, lock_flags);
> +}
> +EXPORT_SYMBOL(ipu_uninit_channel);
> +
> +int ipu_module_enable(u32 mask)
> +{
> + ? ? ? unsigned long lock_flags;
> + ? ? ? u32 ipu_conf;
> +
> + ? ? ? spin_lock_irqsave(&ipu_lock, lock_flags);
> +
> + ? ? ? ipu_conf = ipu_cm_read(IPU_CONF);
> + ? ? ? ipu_conf |= mask;
> + ? ? ? ipu_cm_write(ipu_conf, IPU_CONF);
> +
> + ? ? ? spin_unlock_irqrestore(&ipu_lock, lock_flags);
> +
> + ? ? ? return 0;
> +}
> +
> +int ipu_module_disable(u32 mask)
> +{
> + ? ? ? unsigned long lock_flags;
> + ? ? ? u32 ipu_conf;
> +
> + ? ? ? spin_lock_irqsave(&ipu_lock, lock_flags);
> +
> + ? ? ? ipu_conf = ipu_cm_read(IPU_CONF);
> + ? ? ? ipu_conf &= ~mask;
> + ? ? ? ipu_cm_write(ipu_conf, IPU_CONF);
> +
> + ? ? ? spin_unlock_irqrestore(&ipu_lock, lock_flags);
> +
> + ? ? ? return 0;
> +}
> +
> +void ipu_idmac_select_buffer(struct ipu_channel *channel, u32 buf_num)
> +{
> + ? ? ? unsigned int chno = channel->num;
> +
> + ? ? ? /* Mark buffer as ready. */
> + ? ? ? if (buf_num == 0)
> + ? ? ? ? ? ? ? ipu_cm_write(idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
> + ? ? ? else
> + ? ? ? ? ? ? ? ipu_cm_write(idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
> +}
> +EXPORT_SYMBOL(ipu_idmac_select_buffer);
> +
> +int ipu_idmac_enable_channel(struct ipu_channel *channel)
> +{
> + ? ? ? u32 val;
> + ? ? ? unsigned long flags;
> +
> + ? ? ? spin_lock_irqsave(&ipu_lock, flags);
> +
> + ? ? ? val = ipu_idmac_read(IDMAC_CHA_EN(channel->num));
> + ? ? ? val |= idma_mask(channel->num);
> + ? ? ? ipu_idmac_write(val, IDMAC_CHA_EN(channel->num));
> +
> + ? ? ? spin_unlock_irqrestore(&ipu_lock, flags);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_idmac_enable_channel);
> +
> +int ipu_idmac_disable_channel(struct ipu_channel *channel)
> +{
> + ? ? ? u32 val;
> + ? ? ? unsigned long flags;
> +
> + ? ? ? spin_lock_irqsave(&ipu_lock, flags);
> +
> + ? ? ? /* Disable DMA channel(s) */
> + ? ? ? val = ipu_idmac_read(IDMAC_CHA_EN(channel->num));
> + ? ? ? val &= ~idma_mask(channel->num);
> + ? ? ? ipu_idmac_write(val, IDMAC_CHA_EN(channel->num));
> +
> + ? ? ? /* Set channel buffers NOT to be ready */
> + ? ? ? ipu_cm_write(0xf0000000, IPU_GPR); /* write one to clear */
> +
> + ? ? ? if (ipu_idma_is_set(IPU_CHA_BUF0_RDY, channel->num)) {
> + ? ? ? ? ? ? ? ipu_cm_write(idma_mask(channel->num),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?IPU_CHA_BUF0_RDY(channel->num));
> + ? ? ? }
> + ? ? ? if (ipu_idma_is_set(IPU_CHA_BUF1_RDY, channel->num)) {
> + ? ? ? ? ? ? ? ipu_cm_write(idma_mask(channel->num),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?IPU_CHA_BUF1_RDY(channel->num));
> + ? ? ? }
> +
> + ? ? ? ipu_cm_write(0x0, IPU_GPR); /* write one to set */
> +
> + ? ? ? spin_unlock_irqrestore(&ipu_lock, flags);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_idmac_disable_channel);
> +
> +static LIST_HEAD(ipu_irq_handlers);
> +
> +static void ipu_irq_update_irq_mask(void)
> +{
> + ? ? ? struct ipu_irq_handler *handler;
> + ? ? ? int i;
> +
> + ? ? ? DECLARE_IPU_IRQ_BITMAP(irqs);
> +
> + ? ? ? bitmap_zero(irqs, IPU_IRQ_COUNT);
> +
> + ? ? ? list_for_each_entry(handler, &ipu_irq_handlers, list)
> + ? ? ? ? ? ? ? bitmap_or(irqs, irqs, handler->ipu_irqs, IPU_IRQ_COUNT);
> +
> + ? ? ? for (i = 0; i < BITS_TO_LONGS(IPU_IRQ_COUNT); i++)
> + ? ? ? ? ? ? ? ipu_cm_write(irqs[i], IPU_INT_CTRL(i + 1));
> +}
> +
> +int ipu_irq_add_handler(struct ipu_irq_handler *ipuirq)
> +{
> + ? ? ? unsigned long flags;
> +
> + ? ? ? spin_lock_irqsave(&ipu_lock, flags);
> +
> + ? ? ? list_add_tail(&ipuirq->list, &ipu_irq_handlers);
> + ? ? ? ipu_irq_update_irq_mask();
> +
> + ? ? ? spin_unlock_irqrestore(&ipu_lock, flags);
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_irq_add_handler);
> +
> +void ipu_irq_remove_handler(struct ipu_irq_handler *handler)
> +{
> + ? ? ? unsigned long flags;
> +
> + ? ? ? spin_lock_irqsave(&ipu_lock, flags);
> +
> + ? ? ? list_del(&handler->list);
> + ? ? ? ipu_irq_update_irq_mask();
> +
> + ? ? ? spin_unlock_irqrestore(&ipu_lock, flags);
> +}
> +EXPORT_SYMBOL(ipu_irq_remove_handler);
> +
> +int ipu_irq_update_handler(struct ipu_irq_handler *handler,
> + ? ? ? ? ? ? ? unsigned long *bitmap)
> +{
> + ? ? ? unsigned long flags;
> +
> + ? ? ? spin_lock_irqsave(&ipu_lock, flags);
> +
> + ? ? ? bitmap_copy(handler->ipu_irqs, bitmap, IPU_IRQ_COUNT);
> + ? ? ? ipu_irq_update_irq_mask();
> +
> + ? ? ? spin_unlock_irqrestore(&ipu_lock, flags);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_irq_update_handler);
> +
> +static void ipu_completion_handler(unsigned long *bitmask, void *context)
> +{
> + ? ? ? struct completion *completion = context;
> +
> + ? ? ? complete(completion);
> +}
> +
> +int ipu_wait_for_interrupt(int interrupt, int timeout_ms)
> +{
> + ? ? ? struct ipu_irq_handler handler;
> + ? ? ? DECLARE_COMPLETION_ONSTACK(completion);
> + ? ? ? int ret;
> +
> + ? ? ? bitmap_zero(handler.ipu_irqs, IPU_IRQ_COUNT);
> + ? ? ? bitmap_set(handler.ipu_irqs, interrupt, 1);
> +
> + ? ? ? handler.handler = ipu_completion_handler;
> + ? ? ? handler.context = &completion;
> + ? ? ? ipu_irq_add_handler(&handler);
> +
> + ? ? ? ret = wait_for_completion_timeout(&completion,
> + ? ? ? ? ? ? ? ? ? ? ? msecs_to_jiffies(timeout_ms));
> +
> + ? ? ? ipu_irq_remove_handler(&handler);
> +
> + ? ? ? if (ret > 0)
> + ? ? ? ? ? ? ? ret = 0;
> +
> + ? ? ? return ret;
> +}
> +EXPORT_SYMBOL(ipu_wait_for_interrupt);
> +
> +static irqreturn_t ipu_irq_handler(int irq, void *desc)
> +{
> + ? ? ? DECLARE_IPU_IRQ_BITMAP(status);
> + ? ? ? struct ipu_irq_handler *handler;
> + ? ? ? int i;
> +
> + ? ? ? for (i = 0; i < BITS_TO_LONGS(IPU_IRQ_COUNT); i++) {
> + ? ? ? ? ? ? ? status[i] = ipu_cm_read(IPU_INT_STAT(i + 1));
> + ? ? ? ? ? ? ? ipu_cm_write(status[i], IPU_INT_STAT(i + 1));
> + ? ? ? }
> +
> + ? ? ? list_for_each_entry(handler, &ipu_irq_handlers, list) {
> + ? ? ? ? ? ? ? DECLARE_IPU_IRQ_BITMAP(tmp);
> + ? ? ? ? ? ? ? if (bitmap_and(tmp, status, handler->ipu_irqs, IPU_IRQ_COUNT))
> + ? ? ? ? ? ? ? ? ? ? ? handler->handler(tmp, handler->context);
> + ? ? ? }
> +
> + ? ? ? return IRQ_HANDLED;
> +}
> +
> +ipu_color_space_t format_to_colorspace(u32 fmt)
> +{
> + ? ? ? switch (fmt) {
> + ? ? ? case IPU_PIX_FMT_RGB666:
> + ? ? ? case IPU_PIX_FMT_RGB565:
> + ? ? ? case IPU_PIX_FMT_BGR24:
> + ? ? ? case IPU_PIX_FMT_RGB24:
> + ? ? ? 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:
> + ? ? ? ? ? ? ? return RGB;
> +
> + ? ? ? default:
> + ? ? ? ? ? ? ? return YCbCr;
> + ? ? ? }
> +}
> +
> +static int ipu_reset(void)
> +{
> + ? ? ? int timeout = 10000;
> + ? ? ? u32 val;
> +
> + ? ? ? /* hard reset the IPU */
> + ? ? ? val = readl(MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR));
> + ? ? ? val |= 1 << 3;
> + ? ? ? writel(val, MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR));
> +
> + ? ? ? ipu_cm_write(0x807FFFFF, IPU_MEM_RST);
> +
> + ? ? ? while (ipu_cm_read(IPU_MEM_RST) & 0x80000000) {
> + ? ? ? ? ? ? ? if (!timeout--)
> + ? ? ? ? ? ? ? ? ? ? ? return -ETIME;
> + ? ? ? ? ? ? ? udelay(100);
> + ? ? ? }
> +
> + ? ? ? return 0;
> +}
> +
> +/*
> + * The MIPI HSC unit has been removed from the i.MX51 Reference Manual by
> + * the Freescale marketing division. However this did not remove the
> + * hardware from the chip which still needs to be configured...
> + */
> +static int __devinit ipu_mipi_setup(void)
> +{
> + ? ? ? struct clk *hsc_clk;
> + ? ? ? void __iomem *hsc_addr;
> + ? ? ? int ret = 0;
> +
> + ? ? ? hsc_addr = ioremap(MX51_MIPI_HSC_BASE_ADDR, PAGE_SIZE);
> + ? ? ? if (!hsc_addr)
> + ? ? ? ? ? ? ? return -ENOMEM;
> +
> + ? ? ? hsc_clk = clk_get_sys(NULL, "mipi_hsp");
> + ? ? ? if (IS_ERR(hsc_clk)) {
> + ? ? ? ? ? ? ? ret = PTR_ERR(hsc_clk);
> + ? ? ? ? ? ? ? goto unmap;
> + ? ? ? }
> + ? ? ? clk_enable(hsc_clk);
> +
> + ? ? ? /* setup MIPI module to legacy mode */
> + ? ? ? __raw_writel(0xF00, hsc_addr);
> +
> + ? ? ? /* CSI mode: reserved; DI control mode: legacy (from Freescale BSP) */
> + ? ? ? __raw_writel(__raw_readl(hsc_addr + 0x800) | 0x30ff,
> + ? ? ? ? ? ? ? hsc_addr + 0x800);
> +
> + ? ? ? clk_disable(hsc_clk);
> + ? ? ? clk_put(hsc_clk);
> +unmap:
> + ? ? ? iounmap(hsc_addr);
> +
> + ? ? ? return ret;
> +}
> +
> +static int ipu_submodules_init(struct platform_device *pdev,
> + ? ? ? ? ? ? ? unsigned long ipu_base, struct clk *ipu_clk)
> +{
> + ? ? ? char *unit;
> + ? ? ? int ret;
> +
> + ? ? ? ret = ipu_di_init(pdev, 0, ipu_base + IPU_DI0_REG_BASE,
> + ? ? ? ? ? ? ? ? ? ? ? IPU_CONF_DI0_EN, ipu_clk);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? unit = "di0";
> + ? ? ? ? ? ? ? goto err_di_0;
> + ? ? ? }
> +
> + ? ? ? ret = ipu_di_init(pdev, 1, ipu_base + IPU_DI1_REG_BASE,
> + ? ? ? ? ? ? ? ? ? ? ? IPU_CONF_DI1_EN, ipu_clk);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? unit = "di1";
> + ? ? ? ? ? ? ? goto err_di_1;
> + ? ? ? }
> +
> + ? ? ? ret = ipu_dc_init(pdev, ipu_base + IPU_DC_REG_BASE,
> + ? ? ? ? ? ? ? ? ? ? ? ipu_base + IPU_DC_TMPL_REG_BASE);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? unit = "dc_template";
> + ? ? ? ? ? ? ? goto err_dc;
> + ? ? ? }
> +
> + ? ? ? ret = ipu_dmfc_init(pdev, ipu_base + IPU_DMFC_REG_BASE, ipu_clk);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? unit = "dmfc";
> + ? ? ? ? ? ? ? goto err_dmfc;
> + ? ? ? }
> +
> + ? ? ? ret = ipu_dp_init(pdev, ipu_base + IPU_SRM_REG_BASE);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? unit = "dp";
> + ? ? ? ? ? ? ? goto err_dp;
> + ? ? ? }
> +
> + ? ? ? ret = ipu_cpmem_init(pdev, ipu_base + IPU_CPMEM_REG_BASE);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? unit = "cpmem";
> + ? ? ? ? ? ? ? goto err_cpmem;
> + ? ? ? }
> +
> + ? ? ? return 0;
> +
> +err_cpmem:
> + ? ? ? ipu_dp_exit(pdev);
> +err_dp:
> + ? ? ? ipu_dmfc_exit(pdev);
> +err_dmfc:
> + ? ? ? ipu_dc_exit(pdev);
> +err_dc:
> + ? ? ? ipu_di_exit(pdev, 1);
> +err_di_1:
> + ? ? ? ipu_di_exit(pdev, 0);
> +err_di_0:
> + ? ? ? dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
> + ? ? ? return ret;
> +}
> +
> +static void ipu_submodules_exit(struct platform_device *pdev,
> + ? ? ? ? ? ? ? unsigned long ipu_base)
> +{
> + ? ? ? ipu_cpmem_exit(pdev);
> + ? ? ? ipu_dp_exit(pdev);
> + ? ? ? ipu_dmfc_exit(pdev);
> + ? ? ? ipu_dc_exit(pdev);
> + ? ? ? ipu_di_exit(pdev, 1);
> + ? ? ? ipu_di_exit(pdev, 0);
> +}
> +
> +static int ipu_add_subdevice_pdata(struct platform_device *pdev,
> + ? ? ? ? ? ? ? const char *name, int id, void *pdata, size_t pdata_size)
> +{
> + ? ? ? struct mfd_cell cell = {
> + ? ? ? ? ? ? ? .platform_data = pdata,
> + ? ? ? ? ? ? ? .data_size = pdata_size,
> + ? ? ? };
> +
> + ? ? ? cell.name = name;
> +
> + ? ? ? return mfd_add_devices(&pdev->dev, id, &cell, 1, NULL, 0);
> +}
> +
> +static int ipu_add_client_devices(struct platform_device *pdev)
> +{
> + ? ? ? struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data;
> +
> + ? ? ? if (plat_data->fb0_platform_data) {
> + ? ? ? ? ? ? ? plat_data->fb0_platform_data->ipu_channel_bg =
> + ? ? ? ? ? ? ? ? ? ? ? MX51_IPU_CHANNEL_MEM_BG_SYNC;
> + ? ? ? ? ? ? ? plat_data->fb0_platform_data->ipu_channel_fg =
> + ? ? ? ? ? ? ? ? ? ? ? MX51_IPU_CHANNEL_MEM_FG_SYNC;
> + ? ? ? ? ? ? ? plat_data->fb0_platform_data->dc_channel = 5;
> + ? ? ? ? ? ? ? plat_data->fb0_platform_data->dp_channel = IPU_DP_FLOW_SYNC;
> +
> + ? ? ? ? ? ? ? ipu_add_subdevice_pdata(pdev, "imx-ipuv3-fb", 0,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? plat_data->fb0_platform_data,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sizeof(struct ipuv3_fb_platform_data));
> + ? ? ? }
> +
> + ? ? ? if (plat_data->fb1_platform_data) {
> + ? ? ? ? ? ? ? plat_data->fb1_platform_data->ipu_channel_bg =
> + ? ? ? ? ? ? ? ? ? ? ? MX51_IPU_CHANNEL_MEM_DC_SYNC;
> + ? ? ? ? ? ? ? plat_data->fb1_platform_data->ipu_channel_fg = -1;
> + ? ? ? ? ? ? ? plat_data->fb1_platform_data->dc_channel = 1;
> + ? ? ? ? ? ? ? plat_data->fb1_platform_data->dp_channel = -1;
> +
> + ? ? ? ? ? ? ? ipu_add_subdevice_pdata(pdev, "imx-ipuv3-fb", 1,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? plat_data->fb1_platform_data,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sizeof(struct ipuv3_fb_platform_data));
> + ? ? ? }
> +
> + ? ? ? return 0;
> +}
> +
> +static int __devinit ipu_probe(struct platform_device *pdev)
> +{
> + ? ? ? struct resource *res;
> + ? ? ? unsigned long ipu_base;
> + ? ? ? int ret, irq1, irq2;
> +
> + ? ? ? spin_lock_init(&ipu_lock);
> +
> + ? ? ? ipu_dev = &pdev->dev;
> +
> + ? ? ? irq1 = platform_get_irq(pdev, 0);
> + ? ? ? irq2 = platform_get_irq(pdev, 1);
> + ? ? ? res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> + ? ? ? if (!res || irq1 < 0 || irq2 < 0)
> + ? ? ? ? ? ? ? return -ENODEV;
> +
> + ? ? ? ipu_base = res->start;
> +
> + ? ? ? ipu_cm_reg = ioremap(ipu_base + IPU_CM_REG_BASE, PAGE_SIZE);
> + ? ? ? if (!ipu_cm_reg) {
> + ? ? ? ? ? ? ? ret = -ENOMEM;
> + ? ? ? ? ? ? ? goto failed_ioremap1;
> + ? ? ? }
> +
> + ? ? ? ipu_idmac_reg = ioremap(ipu_base + IPU_IDMAC_REG_BASE, PAGE_SIZE);
> + ? ? ? if (!ipu_idmac_reg) {
> + ? ? ? ? ? ? ? ret = -ENOMEM;
> + ? ? ? ? ? ? ? goto failed_ioremap2;
> + ? ? ? }
> +
> + ? ? ? ret = ipu_mipi_setup();
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? goto failed_mipi_setup;
> +
> + ? ? ? ipu_clk = clk_get(&pdev->dev, "ipu");
> + ? ? ? if (IS_ERR(ipu_clk)) {
> + ? ? ? ? ? ? ? ret = PTR_ERR(ipu_clk);
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "clk_get failed with %d", ret);
> + ? ? ? ? ? ? ? goto failed_clk_get;
> + ? ? ? }
> +
> + ? ? ? clk_enable(ipu_clk);
> +
> + ? ? ? ret = request_irq(irq1, ipu_irq_handler, IRQF_DISABLED, pdev->name,
> + ? ? ? ? ? ? ? ? ? ? ? &pdev->dev);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "request irq %d failed with: %d\n", irq1, ret);
> + ? ? ? ? ? ? ? goto failed_request_irq1;
> + ? ? ? }
> +
> + ? ? ? ret = request_irq(irq2, ipu_irq_handler, IRQF_DISABLED, pdev->name,
> + ? ? ? ? ? ? ? ? ? ? ? &pdev->dev);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "request irq %d failed with: %d\n", irq2, ret);
> + ? ? ? ? ? ? ? goto failed_request_irq2;
> + ? ? ? }
> +
> + ? ? ? ipu_reset();
> +
> + ? ? ? ret = ipu_submodules_init(pdev, ipu_base, ipu_clk);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? goto failed_submodules_init;
> +
> + ? ? ? /* Set sync refresh channels as high priority */
> + ? ? ? ipu_idmac_write(0x18800000, IDMAC_CHA_PRI(0));
> +
> + ? ? ? ret = ipu_add_client_devices(pdev);
> + ? ? ? ?if (ret) {
> + ? ? ? ? ? ? ? ?dev_err(&pdev->dev, "adding client devices failed with %d\n", ret);
> + ? ? ? ? ? ? ? goto failed_add_clients;
> + ? ? ? ?}
> +
> + ? ? ? return 0;
> +
> +failed_add_clients:
> + ? ? ? ipu_submodules_exit(pdev, ipu_base);
> +failed_submodules_init:
> + ? ? ? free_irq(irq2, &pdev->dev);
> +failed_request_irq2:
> + ? ? ? free_irq(irq1, &pdev->dev);
> +failed_request_irq1:
> + ? ? ? clk_put(ipu_clk);
> +failed_clk_get:
> +failed_mipi_setup:
> + ? ? ? iounmap(ipu_idmac_reg);
> +failed_ioremap2:
> + ? ? ? iounmap(ipu_cm_reg);
> +failed_ioremap1:
> +
> + ? ? ? return ret;
> +}
> +
> +static int __devexit ipu_remove(struct platform_device *pdev)
> +{
> + ? ? ? struct resource *res;
> + ? ? ? unsigned long ipu_base;
> + ? ? ? int irq1, irq2;
> +
> + ? ? ? irq1 = platform_get_irq(pdev, 0);
> + ? ? ? irq2 = platform_get_irq(pdev, 1);
> + ? ? ? res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + ? ? ? ipu_base = res->start;
> +
> + ? ? ? mfd_remove_devices(&pdev->dev);
> + ? ? ? ipu_submodules_exit(pdev, ipu_base);
> + ? ? ? free_irq(irq2, &pdev->dev);
> + ? ? ? free_irq(irq1, &pdev->dev);
> + ? ? ? clk_disable(ipu_clk);
> + ? ? ? clk_put(ipu_clk);
> + ? ? ? iounmap(ipu_idmac_reg);
> + ? ? ? iounmap(ipu_cm_reg);
> +
> + ? ? ? return 0;
> +}
> +
> +static struct platform_driver mxcipu_driver = {
> + ? ? ? .driver = {
> + ? ? ? ? ? ? ? .name = "imx-ipuv3",
> + ? ? ? },
> + ? ? ? .probe = ipu_probe,
> + ? ? ? .remove = __devexit_p(ipu_remove),
> +};
> +
> +static int __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);
> +
> +MODULE_DESCRIPTION("i.MX IPU v3 driver");
> +MODULE_AUTHOR("Sascha Hauer <[email protected]>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/mfd/imx-ipu-v3/ipu-cpmem.c b/drivers/mfd/imx-ipu-v3/ipu-cpmem.c
> new file mode 100644
> index 0000000..587b487
> --- /dev/null
> +++ b/drivers/mfd/imx-ipu-v3/ipu-cpmem.c
> @@ -0,0 +1,608 @@
> +/*
> + * Copyright (c) 2010 Sascha Hauer <[email protected]>
> + * Copyright (C) 2005-2009 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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/mfd/imx-ipu-v3.h>
> +#include <linux/types.h>
> +#include <linux/bitrev.h>
> +#include <linux/io.h>
> +
> +#include "ipu-prv.h"
> +
> +#define __F(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size))
> +
> +#define IPU_FIELD_XV ? ? ? ? ? __F(0, 0, 10)
> +#define IPU_FIELD_YV ? ? ? ? ? __F(0, 10, 9)
> +#define IPU_FIELD_XB ? ? ? ? ? __F(0, 19, 13)
> +#define IPU_FIELD_YB ? ? ? ? ? __F(0, 32, 12)
> +#define IPU_FIELD_NSB_B ? ? ? ? ? ? ? ?__F(0, 44, 1)
> +#define IPU_FIELD_CF ? ? ? ? ? __F(0, 45, 1)
> +#define IPU_FIELD_UBO ? ? ? ? ?__F(0, 46, 22)
> +#define IPU_FIELD_VBO ? ? ? ? ?__F(0, 68, 22)
> +#define IPU_FIELD_IOX ? ? ? ? ?__F(0, 90, 4)
> +#define IPU_FIELD_RDRW ? ? ? ? __F(0, 94, 1)
> +#define IPU_FIELD_SO ? ? ? ? ? __F(0, 113, 1)
> +#define IPU_FIELD_BNDM ? ? ? ? __F(0, 114, 3)
> +#define IPU_FIELD_BM ? ? ? ? ? __F(0, 117, 2)
> +#define IPU_FIELD_ROT ? ? ? ? ?__F(0, 119, 1)
> +#define IPU_FIELD_HF ? ? ? ? ? __F(0, 120, 1)
> +#define IPU_FIELD_VF ? ? ? ? ? __F(0, 121, 1)
> +#define IPU_FIELD_THE ? ? ? ? ?__F(0, 122, 1)
> +#define IPU_FIELD_CAP ? ? ? ? ?__F(0, 123, 1)
> +#define IPU_FIELD_CAE ? ? ? ? ?__F(0, 124, 1)
> +#define IPU_FIELD_FW ? ? ? ? ? __F(0, 125, 13)
> +#define IPU_FIELD_FH ? ? ? ? ? __F(0, 138, 12)
> +#define IPU_FIELD_EBA0 ? ? ? ? __F(1, 0, 29)
> +#define IPU_FIELD_EBA1 ? ? ? ? __F(1, 29, 29)
> +#define IPU_FIELD_ILO ? ? ? ? ?__F(1, 58, 20)
> +#define IPU_FIELD_NPB ? ? ? ? ?__F(1, 78, 7)
> +#define IPU_FIELD_PFS ? ? ? ? ?__F(1, 85, 4)
> +#define IPU_FIELD_ALU ? ? ? ? ?__F(1, 89, 1)
> +#define IPU_FIELD_ALBM ? ? ? ? __F(1, 90, 3)
> +#define IPU_FIELD_ID ? ? ? ? ? __F(1, 93, 2)
> +#define IPU_FIELD_TH ? ? ? ? ? __F(1, 95, 7)
> +#define IPU_FIELD_SLY ? ? ? ? ?__F(1, 102, 14)
> +#define IPU_FIELD_WID3 ? ? ? ? __F(1, 125, 3)
> +#define IPU_FIELD_SLUV ? ? ? ? __F(1, 128, 14)
> +#define IPU_FIELD_CRE ? ? ? ? ?__F(1, 149, 1)
> +
> +#define IPU_FIELD_XV ? ? ? ? ? __F(0, 0, 10)
> +#define IPU_FIELD_YV ? ? ? ? ? __F(0, 10, 9)
> +#define IPU_FIELD_XB ? ? ? ? ? __F(0, 19, 13)
> +#define IPU_FIELD_YB ? ? ? ? ? __F(0, 32, 12)
> +#define IPU_FIELD_NSB_B ? ? ? ? ? ? ? ?__F(0, 44, 1)
> +#define IPU_FIELD_CF ? ? ? ? ? __F(0, 45, 1)
> +#define IPU_FIELD_SX ? ? ? ? ? __F(0, 46, 12)
> +#define IPU_FIELD_SY ? ? ? ? ? __F(0, 58, 11)
> +#define IPU_FIELD_NS ? ? ? ? ? __F(0, 69, 10)
> +#define IPU_FIELD_SDX ? ? ? ? ?__F(0, 79, 7)
> +#define IPU_FIELD_SM ? ? ? ? ? __F(0, 86, 10)
> +#define IPU_FIELD_SCC ? ? ? ? ?__F(0, 96, 1)
> +#define IPU_FIELD_SCE ? ? ? ? ?__F(0, 97, 1)
> +#define IPU_FIELD_SDY ? ? ? ? ?__F(0, 98, 7)
> +#define IPU_FIELD_SDRX ? ? ? ? __F(0, 105, 1)
> +#define IPU_FIELD_SDRY ? ? ? ? __F(0, 106, 1)
> +#define IPU_FIELD_BPP ? ? ? ? ?__F(0, 107, 3)
> +#define IPU_FIELD_DEC_SEL ? ? ?__F(0, 110, 2)
> +#define IPU_FIELD_DIM ? ? ? ? ?__F(0, 112, 1)
> +#define IPU_FIELD_SO ? ? ? ? ? __F(0, 113, 1)
> +#define IPU_FIELD_BNDM ? ? ? ? __F(0, 114, 3)
> +#define IPU_FIELD_BM ? ? ? ? ? __F(0, 117, 2)
> +#define IPU_FIELD_ROT ? ? ? ? ?__F(0, 119, 1)
> +#define IPU_FIELD_HF ? ? ? ? ? __F(0, 120, 1)
> +#define IPU_FIELD_VF ? ? ? ? ? __F(0, 121, 1)
> +#define IPU_FIELD_THE ? ? ? ? ?__F(0, 122, 1)
> +#define IPU_FIELD_CAP ? ? ? ? ?__F(0, 123, 1)
> +#define IPU_FIELD_CAE ? ? ? ? ?__F(0, 124, 1)
> +#define IPU_FIELD_FW ? ? ? ? ? __F(0, 125, 13)
> +#define IPU_FIELD_FH ? ? ? ? ? __F(0, 138, 12)
> +#define IPU_FIELD_EBA0 ? ? ? ? __F(1, 0, 29)
> +#define IPU_FIELD_EBA1 ? ? ? ? __F(1, 29, 29)
> +#define IPU_FIELD_ILO ? ? ? ? ?__F(1, 58, 20)
> +#define IPU_FIELD_NPB ? ? ? ? ?__F(1, 78, 7)
> +#define IPU_FIELD_PFS ? ? ? ? ?__F(1, 85, 4)
> +#define IPU_FIELD_ALU ? ? ? ? ?__F(1, 89, 1)
> +#define IPU_FIELD_ALBM ? ? ? ? __F(1, 90, 3)
> +#define IPU_FIELD_ID ? ? ? ? ? __F(1, 93, 2)
> +#define IPU_FIELD_TH ? ? ? ? ? __F(1, 95, 7)
> +#define IPU_FIELD_SL ? ? ? ? ? __F(1, 102, 14)
> +#define IPU_FIELD_WID0 ? ? ? ? __F(1, 116, 3)
> +#define IPU_FIELD_WID1 ? ? ? ? __F(1, 119, 3)
> +#define IPU_FIELD_WID2 ? ? ? ? __F(1, 122, 3)
> +#define IPU_FIELD_WID3 ? ? ? ? __F(1, 125, 3)
> +#define IPU_FIELD_OFS0 ? ? ? ? __F(1, 128, 5)
> +#define IPU_FIELD_OFS1 ? ? ? ? __F(1, 133, 5)
> +#define IPU_FIELD_OFS2 ? ? ? ? __F(1, 138, 5)
> +#define IPU_FIELD_OFS3 ? ? ? ? __F(1, 143, 5)
> +#define IPU_FIELD_SXYS ? ? ? ? __F(1, 148, 1)
> +#define IPU_FIELD_CRE ? ? ? ? ?__F(1, 149, 1)
> +#define IPU_FIELD_DEC_SEL2 ? ? __F(1, 150, 1)
> +
> +static u32 *ipu_cpmem_base;
> +static struct device *ipu_dev;
> +
> +struct ipu_ch_param_word {
> + ? ? ? u32 data[5];
> + ? ? ? u32 res[3];
> +};
> +
> +struct ipu_ch_param {
> + ? ? ? struct ipu_ch_param_word word[2];
> +};
> +
> +static u32 ipu_bytes_per_pixel(u32 fmt)
> +{
> + ? ? ? switch (fmt) {
> + ? ? ? case IPU_PIX_FMT_GENERIC: ? ? ? /* generic data */
> + ? ? ? case IPU_PIX_FMT_RGB332:
> + ? ? ? case IPU_PIX_FMT_YUV420P:
> + ? ? ? case IPU_PIX_FMT_YUV422P:
> + ? ? ? ? ? ? ? return 1;
> +
> + ? ? ? case IPU_PIX_FMT_RGB565:
> + ? ? ? case IPU_PIX_FMT_YUYV:
> + ? ? ? case IPU_PIX_FMT_UYVY:
> + ? ? ? ? ? ? ? return 2;
> +
> + ? ? ? case IPU_PIX_FMT_BGR24:
> + ? ? ? case IPU_PIX_FMT_RGB24:
> + ? ? ? ? ? ? ? return 3;
> +
> + ? ? ? 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:
> + ? ? ? ? ? ? ? return 4;
> +
> + ? ? ? default:
> + ? ? ? ? ? ? ? return 1;
> + ? ? ? }
> +}
> +
> +bool ipu_pixel_format_has_alpha(u32 fmt)
> +{
> + ? ? ? switch (fmt) {
> + ? ? ? case IPU_PIX_FMT_RGBA32:
> + ? ? ? case IPU_PIX_FMT_BGRA32:
> + ? ? ? case IPU_PIX_FMT_ABGR32:
> + ? ? ? ? ? ? ? return true;
> +
> + ? ? ? default:
> + ? ? ? ? ? ? ? return false;
> + ? ? ? }
> +}
> +
> +#define ipu_ch_param_addr(ch) (((struct ipu_ch_param *)ipu_cpmem_base) + (ch))
> +
> +static inline void ipu_ch_param_set_field(struct ipu_ch_param *base, u32 wbs, u32 v)
> +{
> + ? ? ? u32 bit = (wbs >> 8) % 160;
> + ? ? ? u32 size = wbs & 0xff;
> + ? ? ? u32 word = (wbs >> 8) / 160;
> + ? ? ? u32 i = bit / 32;
> + ? ? ? u32 ofs = bit % 32;
> + ? ? ? u32 mask = (1 << size) - 1;
> +
> + ? ? ? pr_debug("%s %d %d %d\n", __func__, word, bit , size);
> +
> + ? ? ? base->word[word].data[i] &= ~(mask << ofs);
> + ? ? ? base->word[word].data[i] |= v << ofs;
> +
> + ? ? ? if ((bit + size - 1) / 32 > i) {
> + ? ? ? ? ? ? ? base->word[word].data[i + 1] &= ~(v >> (mask ? (32 - ofs) : 0));
> + ? ? ? ? ? ? ? base->word[word].data[i + 1] |= v >> (ofs ? (32 - ofs) : 0);
> + ? ? ? }
> +}
> +
> +static inline u32 ipu_ch_param_read_field(struct ipu_ch_param *base, u32 wbs)
> +{
> + ? ? ? u32 bit = (wbs >> 8) % 160;
> + ? ? ? u32 size = wbs & 0xff;
> + ? ? ? u32 word = (wbs >> 8) / 160;
> + ? ? ? u32 i = bit / 32;
> + ? ? ? u32 ofs = bit % 32;
> + ? ? ? u32 mask = (1 << size) - 1;
> + ? ? ? u32 val = 0;
> +
> + ? ? ? pr_debug("%s %d %d %d\n", __func__, word, bit , size);
> +
> + ? ? ? val = (base->word[word].data[i] >> ofs) & mask;
> +
> + ? ? ? if ((bit + size - 1) / 32 > i) {
> + ? ? ? ? ? ? ? u32 tmp;
> + ? ? ? ? ? ? ? tmp = base->word[word].data[i + 1];
> + ? ? ? ? ? ? ? tmp &= mask >> (ofs ? (32 - ofs) : 0);
> + ? ? ? ? ? ? ? val |= tmp << (ofs ? (32 - ofs) : 0);
> + ? ? ? }
> +
> + ? ? ? return val;
> +}
> +
> +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, IPU_FIELD_WID0, red_width - 1);
> + ? ? ? ipu_ch_param_set_field(p, IPU_FIELD_OFS0, red_offset);
> + ? ? ? /* Setup green width and offset */
> + ? ? ? ipu_ch_param_set_field(p, IPU_FIELD_WID1, green_width - 1);
> + ? ? ? ipu_ch_param_set_field(p, IPU_FIELD_OFS1, green_offset);
> + ? ? ? /* Setup blue width and offset */
> + ? ? ? ipu_ch_param_set_field(p, IPU_FIELD_WID2, blue_width - 1);
> + ? ? ? ipu_ch_param_set_field(p, IPU_FIELD_OFS2, blue_offset);
> + ? ? ? /* Setup alpha width and offset */
> + ? ? ? ipu_ch_param_set_field(p, IPU_FIELD_WID3, alpha_width - 1);
> + ? ? ? ipu_ch_param_set_field(p, IPU_FIELD_OFS3, alpha_offset);
> +}
> +
> +static inline void ipu_ch_param_dump(int ch)
> +{
> + ? ? ? struct ipu_ch_param *p = ipu_ch_param_addr(ch);
> + ? ? ? pr_debug("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]);
> + ? ? ? pr_debug("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]);
> + ? ? ? pr_debug("PFS 0x%x\n", ipu_ch_param_read_field(p, IPU_FIELD_PFS));
> + ? ? ? pr_debug("BPP 0x%x\n", ipu_ch_param_read_field(p, IPU_FIELD_BPP));
> + ? ? ? pr_debug("NPB 0x%x\n", ipu_ch_param_read_field(p, IPU_FIELD_NPB));
> +
> + ? ? ? pr_debug("FW %d\n", ipu_ch_param_read_field(p, IPU_FIELD_FW));
> + ? ? ? pr_debug("FH %d\n", ipu_ch_param_read_field(p, IPU_FIELD_FH));
> + ? ? ? pr_debug("Stride %d\n", ipu_ch_param_read_field(p, IPU_FIELD_SL));
> +
> + ? ? ? pr_debug("Width0 %d+1\n", ipu_ch_param_read_field(p, IPU_FIELD_WID0));
> + ? ? ? pr_debug("Width1 %d+1\n", ipu_ch_param_read_field(p, IPU_FIELD_WID1));
> + ? ? ? pr_debug("Width2 %d+1\n", ipu_ch_param_read_field(p, IPU_FIELD_WID2));
> + ? ? ? pr_debug("Width3 %d+1\n", ipu_ch_param_read_field(p, IPU_FIELD_WID3));
> + ? ? ? pr_debug("Offset0 %d\n", ipu_ch_param_read_field(p, IPU_FIELD_OFS0));
> + ? ? ? pr_debug("Offset1 %d\n", ipu_ch_param_read_field(p, IPU_FIELD_OFS1));
> + ? ? ? pr_debug("Offset2 %d\n", ipu_ch_param_read_field(p, IPU_FIELD_OFS2));
> + ? ? ? pr_debug("Offset3 %d\n", ipu_ch_param_read_field(p, IPU_FIELD_OFS3));
> +}
> +
> +static inline void ipu_ch_param_set_burst_size(u32 ch,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u16 burst_pixels)
> +{
> + ? ? ? ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_NPB,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?burst_pixels - 1);
> +};
> +
> +static inline int ipu_ch_param_get_burst_size(u32 ch)
> +{
> + ? ? ? return ipu_ch_param_read_field(ipu_ch_param_addr(ch), IPU_FIELD_NPB) + 1;
> +};
> +
> +static inline int ipu_ch_param_get_bpp(u32 ch)
> +{
> + ? ? ? return ipu_ch_param_read_field(ipu_ch_param_addr(ch), IPU_FIELD_BPP);
> +};
> +
> +static inline void ipu_ch_param_set_buffer(u32 ch, int bufNum,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dma_addr_t phyaddr)
> +{
> + ? ? ? ipu_ch_param_set_field(ipu_ch_param_addr(ch),
> + ? ? ? ? ? ? ? ? ? ? ? bufNum ? IPU_FIELD_EBA1 : IPU_FIELD_EBA0,
> + ? ? ? ? ? ? ? ? ? ? ? phyaddr / 8);
> +};
> +
> +#define IPU_FIELD_ROT_HF_VF ? ? ? ? ? ?__F(0, 119, 3)
> +
> +static inline void ipu_ch_param_set_rotation(u32 ch,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ipu_rotate_mode_t rot)
> +{
> + ? ? ? u32 temp_rot = bitrev8(rot) >> 5;
> + ? ? ? ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ROT_HF_VF, temp_rot);
> +};
> +
> +static inline void ipu_ch_param_set_block_mode(u32 ch)
> +{
> + ? ? ? ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_BM, 1);
> +};
> +
> +static inline void ipu_ch_param_set_alpha_use_separate_channel(u32 ch,
> + ? ? ? ? ? ? ? bool option)
> +{
> + ? ? ? if (option)
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ALU, 1);
> + ? ? ? else
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ALU, 0);
> +};
> +
> +static inline void ipu_ch_param_set_alpha_condition_read(u32 ch)
> +{
> + ? ? ? ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_CRE, 1);
> +};
> +
> +static inline void ipu_ch_param_set_alpha_buffer_memory(u32 ch)
> +{
> + ? ? ? int alp_mem_idx;
> +
> + ? ? ? 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 correlated channel of local alpha channel\n");
> + ? ? ? ? ? ? ? return;
> + ? ? ? }
> +
> + ? ? ? ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ALBM, alp_mem_idx);
> +};
> +
> +static inline void ipu_ch_param_set_interlaced_scan(u32 ch)
> +{
> + ? ? ? u32 stride;
> + ? ? ? ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_SO, 1);
> + ? ? ? stride = ipu_ch_param_read_field(ipu_ch_param_addr(ch), IPU_FIELD_SL) + 1;
> + ? ? ? ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ILO, stride / 8);
> + ? ? ? stride *= 2;
> + ? ? ? ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_SLY, stride - 1);
> +};
> +
> +static inline void ipu_ch_param_set_high_priority(u32 ch)
> +{
> + ? ? ? ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ID, 1);
> +};
> +
> +static inline void ipu_ch_params_set_alpha_width(u32 ch, int alpha_width)
> +{
> + ? ? ? ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_WID3,
> + ? ? ? ? ? ? ? ? ? ? ? alpha_width - 1);
> +}
> +
> +static int ipu_ch_param_init(int ch,
> + ? ? ? ? ? ? ? ? ? ? ? u32 pixel_fmt, u32 width,
> + ? ? ? ? ? ? ? ? ? ? ? u32 height, u32 stride,
> + ? ? ? ? ? ? ? ? ? ? ? u32 u, u32 v,
> + ? ? ? ? ? ? ? ? ? ? ? u32 uv_stride, dma_addr_t addr0,
> + ? ? ? ? ? ? ? ? ? ? ? dma_addr_t addr1)
> +{
> + ? ? ? u32 u_offset = 0;
> + ? ? ? u32 v_offset = 0;
> + ? ? ? struct ipu_ch_param params;
> +
> + ? ? ? memset(&params, 0, sizeof(params));
> +
> + ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_FW, width - 1);
> + ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_FH, height - 1);
> + ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_SLY, stride - 1);
> + ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_EBA0, addr0 >> 3);
> + ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_EBA1, addr1 >> 3);
> +
> + ? ? ? switch (pixel_fmt) {
> + ? ? ? case IPU_PIX_FMT_GENERIC:
> + ? ? ? ? ? ? ? /* Represents 8-bit Generic data */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_BPP, 5); ? ? ?/* bits/pixel */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 6); ? ? ?/* pix format */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 63); ? ? /* 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, IPU_FIELD_BPP, 3); ? ? ?/* bits/pixel */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); ? ? ?/* pix format */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 15); ? ? /* burst size */
> +
> + ? ? ? ? ? ? ? ipu_ch_params_set_packing(&params, 5, 0, 6, 5, 5, 11, 8, 16);
> + ? ? ? ? ? ? ? break;
> + ? ? ? case IPU_PIX_FMT_BGR24:
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_BPP, 1); ? ? ?/* bits/pixel */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); ? ? ?/* pix format */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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, IPU_FIELD_BPP, 1); ? ? ?/* bits/pixel */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); ? ? ?/* pix format */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 19); ? ? /* burst size */
> +
> + ? ? ? ? ? ? ? ipu_ch_params_set_packing(&params, 8, 16, 8, 8, 8, 0, 8, 24);
> + ? ? ? ? ? ? ? break;
> + ? ? ? case IPU_PIX_FMT_BGRA32:
> + ? ? ? case IPU_PIX_FMT_BGR32:
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_BPP, 0); ? ? ?/* bits/pixel */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); ? ? ?/* pix format */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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, IPU_FIELD_BPP, 0); ? ? ?/* bits/pixel */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); ? ? ?/* pix format */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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, IPU_FIELD_BPP, 0); ? ? ?/* bits/pixel */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); ? ? ?/* pix format */
> +
> + ? ? ? ? ? ? ? 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, IPU_FIELD_BPP, 3); ? ? ?/* bits/pixel */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 0xA); ? ?/* pix format */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 15); ? ? /* burst size */
> + ? ? ? ? ? ? ? break;
> + ? ? ? case IPU_PIX_FMT_YUYV:
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_BPP, 3); ? ? ?/* bits/pixel */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 0x8); ? ?/* pix format */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 31); ? ? /* burst size */
> + ? ? ? ? ? ? ? break;
> + ? ? ? case IPU_PIX_FMT_YUV420P2:
> + ? ? ? case IPU_PIX_FMT_YUV420P:
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 2); ? ? ?/* pix format */
> +
> + ? ? ? ? ? ? ? if (uv_stride < stride / 2)
> + ? ? ? ? ? ? ? ? ? ? ? uv_stride = stride / 2;
> +
> + ? ? ? ? ? ? ? u_offset = stride * height;
> + ? ? ? ? ? ? ? v_offset = u_offset + (uv_stride * height / 2);
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 31); ?/* burst size */
> + ? ? ? ? ? ? ? break;
> + ? ? ? case IPU_PIX_FMT_YVU422P:
> + ? ? ? ? ? ? ? /* BPP & pixel format */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 1); ? ? ?/* pix format */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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, IPU_FIELD_PFS, 1); ? ? ?/* pix format */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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_NV12:
> + ? ? ? ? ? ? ? /* BPP & pixel format */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 4); ? ? ?/* pix format */
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 31); ? ? /* burst size */
> + ? ? ? ? ? ? ? uv_stride = stride;
> + ? ? ? ? ? ? ? u_offset = (u == 0) ? stride * height : u;
> + ? ? ? ? ? ? ? break;
> + ? ? ? default:
> + ? ? ? ? ? ? ? dev_err(ipu_dev, "mxc ipu: unimplemented pixel format: %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? pixel_fmt);
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> + ? ? ? /* set burst size to 16 */
> + ? ? ? if (uv_stride)
> + ? ? ? ? ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_SLUV, uv_stride - 1);
> +
> + ? ? ? if (u > u_offset)
> + ? ? ? ? ? ? ? u_offset = u;
> +
> + ? ? ? if (v > v_offset)
> + ? ? ? ? ? ? ? v_offset = v;
> +
> + ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_UBO, u_offset / 8);
> + ? ? ? ipu_ch_param_set_field(&params, IPU_FIELD_VBO, v_offset / 8);
> +
> + ? ? ? pr_debug("initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ch));
> + ? ? ? memcpy(ipu_ch_param_addr(ch), &params, sizeof(params));
> + ? ? ? return 0;
> +}
> +
> +/*
> + * This function is called to initialize a buffer for a IPU channel.
> + *
> + * @param ? ? ? channel ? ? ? ? The IPU channel.
> + *
> + * @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 ? ? ? 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
> + */
> +int ipu_idmac_init_channel_buffer(struct ipu_channel *channel,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u32 pixel_fmt,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u16 width, u16 height,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u32 stride,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ipu_rotate_mode_t rot_mode,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u32 u, u32 v, bool interlaced)
> +{
> + ? ? ? int ret;
> + ? ? ? u32 dma_chan = channel->num;
> +
> + ? ? ? if (stride < width * ipu_bytes_per_pixel(pixel_fmt))
> + ? ? ? ? ? ? ? stride = width * ipu_bytes_per_pixel(pixel_fmt);
> +
> + ? ? ? if (stride % 4) {
> + ? ? ? ? ? ? ? dev_err(ipu_dev,
> + ? ? ? ? ? ? ? ? ? ? ? "Stride not 32-bit aligned, stride = %d\n", stride);
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> +
> + ? ? ? /* Build parameter memory data for DMA channel */
> + ? ? ? ret = ipu_ch_param_init(dma_chan, pixel_fmt, width, height, stride, u, v, 0,
> + ? ? ? ? ? ? ? ? ? ? ? ? ?phyaddr_0, phyaddr_1);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? return ret;
> +
> + ? ? ? if (rot_mode)
> + ? ? ? ? ? ? ? ipu_ch_param_set_rotation(dma_chan, rot_mode);
> +
> + ? ? ? if (interlaced)
> + ? ? ? ? ? ? ? ipu_ch_param_set_interlaced_scan(dma_chan);
> +
> + ? ? ? if (idmac_idma_is_set(IDMAC_CHA_PRI, dma_chan))
> + ? ? ? ? ? ? ? ipu_ch_param_set_high_priority(dma_chan);
> +
> + ? ? ? ipu_ch_param_dump(dma_chan);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_idmac_init_channel_buffer);
> +
> +int ipu_idmac_update_channel_buffer(struct ipu_channel *channel,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u32 buf_num, dma_addr_t phyaddr)
> +{
> + ? ? ? u32 dma_chan = channel->num;
> +
> + ? ? ? ipu_ch_param_set_buffer(dma_chan, buf_num, phyaddr);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_idmac_update_channel_buffer);
> +
> +int ipu_cpmem_init(struct platform_device *pdev, unsigned long base)
> +{
> + ? ? ? ipu_cpmem_base = ioremap(base, PAGE_SIZE);
> + ? ? ? if (!ipu_cpmem_base)
> + ? ? ? ? ? ? ? return -ENOMEM;
> + ? ? ? ipu_dev = &pdev->dev;
> + ? ? ? return 0;
> +}
> +
> +void ipu_cpmem_exit(struct platform_device *pdev)
> +{
> + ? ? ? iounmap(ipu_cpmem_base);
> +}
> diff --git a/drivers/mfd/imx-ipu-v3/ipu-dc.c b/drivers/mfd/imx-ipu-v3/ipu-dc.c
> new file mode 100644
> index 0000000..23ba5d7
> --- /dev/null
> +++ b/drivers/mfd/imx-ipu-v3/ipu-dc.c
> @@ -0,0 +1,362 @@
> +/*
> + * Copyright (c) 2010 Sascha Hauer <[email protected]>
> + * Copyright (C) 2005-2009 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.
> + */
> +
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>
> +#include <linux/spinlock.h>
> +#include <linux/io.h>
> +#include <linux/mfd/imx-ipu-v3.h>
> +#include <asm/atomic.h>
> +
> +#include "ipu-prv.h"
> +
> +#define ASYNC_SER_WAVE 6
> +
> +#define DC_DISP_ID_SERIAL ? ? ?2
> +#define DC_DISP_ID_ASYNC ? ? ? 3
> +
> +#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 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_WR_CH_CONF(ch) ? ? ?(ipu_dc_reg + dc_channels[ch].channel_offset)
> +#define DC_WR_CH_ADDR(ch) ? ? ?(ipu_dc_reg + dc_channels[ch].channel_offset + 4)
> +#define DC_RL_CH(ch, evt) ? ? ?(ipu_dc_reg + dc_channels[ch].channel_offset + 8 + ((evt) & ~0x1) * 2)
> +
> +#define DC_GEN ? ? ? ? ? ? ? ? (ipu_dc_reg + 0x00D4)
> +#define DC_DISP_CONF1(disp) ? ?(ipu_dc_reg + 0x00D8 + disp * 4)
> +#define DC_DISP_CONF2(disp) ? ?(ipu_dc_reg + 0x00E8 + disp * 4)
> +#define DC_STAT ? ? ? ? ? ? ? ? ? ? ? ?(ipu_dc_reg + 0x01C8)
> +
> +#define WROD(lf) ? ? ? ? ? ? ? (0x18 | (lf << 1))
> +
> +#define DC_WR_CH_CONF_FIELD_MODE ? ? ? ? ? ? ? (1 << 9)
> +#define DC_WR_CH_CONF_PROG_TYPE_OFFSET ? ? ? ? 5
> +#define DC_WR_CH_CONF_PROG_TYPE_MASK ? ? ? ? ? (7 << 5)
> +#define DC_WR_CH_CONF_PROG_DI_ID ? ? ? ? ? ? ? (1 << 2)
> +#define DC_WR_CH_CONF_PROG_DISP_ID_OFFSET ? ? ?3
> +#define DC_WR_CH_CONF_PROG_DISP_ID_MASK ? ? ? ? ? ? ? ?(3 << 3)
> +
> +static void __iomem *ipu_dc_reg;
> +static void __iomem *ipu_dc_tmpl_reg;
> +static struct device *ipu_dev;
> +
> +struct ipu_dc {
> + ? ? ? unsigned int di; /* The display interface number assigned to this dc channel */
> + ? ? ? unsigned int channel_offset;
> +};
> +
> +static struct ipu_dc dc_channels[10];
> +
> +static void ipu_dc_link_event(int chan, int event, int addr, int priority)
> +{
> + ? ? ? u32 reg;
> +
> + ? ? ? reg = __raw_readl(DC_RL_CH(chan, event));
> + ? ? ? reg &= ~(0xFFFF << (16 * (event & 0x1)));
> + ? ? ? reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
> + ? ? ? __raw_writel(reg, DC_RL_CH(chan, event));
> +}
> +
> +static void ipu_dc_write_tmpl(int word, u32 opcode, u32 operand, int map,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int wave, int glue, int sync)
> +{
> + ? ? ? u32 reg;
> + ? ? ? int stop = 1;
> +
> + ? ? ? reg = sync;
> + ? ? ? reg |= (glue << 4);
> + ? ? ? reg |= (++wave << 11);
> + ? ? ? reg |= (++map << 15);
> + ? ? ? reg |= (operand << 20) & 0xFFF00000;
> + ? ? ? __raw_writel(reg, ipu_dc_tmpl_reg + word * 8);
> +
> + ? ? ? reg = (operand >> 12);
> + ? ? ? reg |= opcode << 4;
> + ? ? ? reg |= (stop << 9);
> + ? ? ? __raw_writel(reg, ipu_dc_tmpl_reg + word * 8 + 4);
> +}
> +
> +static int ipu_pixfmt_to_map(u32 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;
> + ? ? ? }
> +
> + ? ? ? return -EINVAL;
> +}
> +
> +#define SYNC_WAVE 0
> +
> +int ipu_dc_init_sync(int dc_chan, int di, bool interlaced, u32 pixel_fmt, u32 width)
> +{
> + ? ? ? u32 reg = 0, map;
> +
> + ? ? ? dc_channels[dc_chan].di = di;
> +
> + ? ? ? map = ipu_pixfmt_to_map(pixel_fmt);
> + ? ? ? if (map < 0) {
> + ? ? ? ? ? ? ? dev_dbg(ipu_dev, "IPU_DISP: No MAP\n");
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> +
> + ? ? ? if (interlaced) {
> + ? ? ? ? ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 3);
> + ? ? ? ? ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 2);
> + ? ? ? ? ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 1);
> +
> + ? ? ? ? ? ? ? /* Init template microcode */
> + ? ? ? ? ? ? ? ipu_dc_write_tmpl(0, WROD(0), 0, map, SYNC_WAVE, 0, 8);
> + ? ? ? } else {
> + ? ? ? ? ? ? ? if (di) {
> + ? ? ? ? ? ? ? ? ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_NL, 2, 3);
> + ? ? ? ? ? ? ? ? ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_EOL, 3, 2);
> + ? ? ? ? ? ? ? ? ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 4, 1);
> + ? ? ? ? ? ? ? ? ? ? ? /* Init template microcode */
> + ? ? ? ? ? ? ? ? ? ? ? ipu_dc_write_tmpl(2, WROD(0), 0, map, SYNC_WAVE, 8, 5);
> + ? ? ? ? ? ? ? ? ? ? ? ipu_dc_write_tmpl(3, WROD(0), 0, map, SYNC_WAVE, 4, 5);
> + ? ? ? ? ? ? ? ? ? ? ? ipu_dc_write_tmpl(4, WROD(0), 0, map, SYNC_WAVE, 0, 5);
> + ? ? ? ? ? ? ? } else {
> + ? ? ? ? ? ? ? ? ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_NL, 5, 3);
> + ? ? ? ? ? ? ? ? ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_EOL, 6, 2);
> + ? ? ? ? ? ? ? ? ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 7, 1);
> + ? ? ? ? ? ? ? ? ? ? ? /* Init template microcode */
> + ? ? ? ? ? ? ? ? ? ? ? ipu_dc_write_tmpl(5, WROD(0), 0, map, SYNC_WAVE, 8, 5);
> + ? ? ? ? ? ? ? ? ? ? ? ipu_dc_write_tmpl(6, WROD(0), 0, map, SYNC_WAVE, 4, 5);
> + ? ? ? ? ? ? ? ? ? ? ? ipu_dc_write_tmpl(7, WROD(0), 0, map, SYNC_WAVE, 0, 5);
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> + ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0);
> + ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0);
> + ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0);
> + ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0);
> + ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0);
> + ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0);
> +
> + ? ? ? reg = 0x2;
> + ? ? ? reg |= di << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET;
> + ? ? ? reg |= di << 2;
> + ? ? ? if (interlaced)
> + ? ? ? ? ? ? ? reg |= DC_WR_CH_CONF_FIELD_MODE;
> +
> + ? ? ? __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
> +
> + ? ? ? __raw_writel(0x00000000, DC_WR_CH_ADDR(dc_chan));
> +
> + ? ? ? __raw_writel(0x00000084, DC_GEN);
> +
> + ? ? ? __raw_writel(width, DC_DISP_CONF2(di));
> +
> + ? ? ? ipu_module_enable(IPU_CONF_DC_EN);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_dc_init_sync);
> +
> +void ipu_dc_init_async(int dc_chan, int di, bool interlaced)
> +{
> + ? ? ? u32 reg = 0;
> + ? ? ? dc_channels[dc_chan].di = di;
> + ? ? ? ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0x64, 1);
> + ? ? ? ipu_dc_link_event(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;
> + ? ? ? __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
> +
> + ? ? ? __raw_writel(0x00000000, DC_WR_CH_ADDR(dc_chan));
> +
> + ? ? ? __raw_writel(0x00000084, DC_GEN);
> +
> + ? ? ? ipu_module_enable(IPU_CONF_DC_EN);
> +}
> +EXPORT_SYMBOL(ipu_dc_init_async);
> +
> +void ipu_dc_enable_channel(u32 dc_chan)
> +{
> + ? ? ? int di;
> + ? ? ? u32 reg;
> +
> + ? ? ? di = dc_channels[dc_chan].di;
> +
> + ? ? ? /* Make sure other DC sync channel is not assigned same DI */
> + ? ? ? reg = __raw_readl(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;
> + ? ? ? ? ? ? ? __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan));
> + ? ? ? }
> +
> + ? ? ? reg = __raw_readl(DC_WR_CH_CONF(dc_chan));
> + ? ? ? reg |= 4 << DC_WR_CH_CONF_PROG_TYPE_OFFSET;
> + ? ? ? __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
> +}
> +EXPORT_SYMBOL(ipu_dc_enable_channel);
> +
> +void ipu_dc_disable_channel(u32 dc_chan)
> +{
> + ? ? ? u32 reg;
> + ? ? ? int irq = 0, ret, timeout = 50;
> +
> + ? ? ? if (dc_chan == 1) {
> + ? ? ? ? ? ? ? irq = IPU_IRQ_DC_FC_1;
> + ? ? ? } else if (dc_chan == 5) {
> + ? ? ? ? ? ? ? irq = IPU_IRQ_DP_SF_END;
> + ? ? ? } else {
> + ? ? ? ? ? ? ? return;
> + ? ? ? }
> +
> + ? ? ? ret = ipu_wait_for_interrupt(irq, 50);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? return;
> +
> + ? ? ? /* Wait for DC triple buffer to empty */
> + ? ? ? if (dc_channels[dc_chan].di == 0)
> + ? ? ? ? ? ? ? while ((__raw_readl(DC_STAT) & 0x00000002)
> + ? ? ? ? ? ? ? ? ? ? ? != 0x00000002) {
> + ? ? ? ? ? ? ? ? ? ? ? msleep(2);
> + ? ? ? ? ? ? ? ? ? ? ? timeout -= 2;
> + ? ? ? ? ? ? ? ? ? ? ? if (timeout <= 0)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? ? }
> + ? ? ? else if (dc_channels[dc_chan].di == 1)
> + ? ? ? ? ? ? ? while ((__raw_readl(DC_STAT) & 0x00000020)
> + ? ? ? ? ? ? ? ? ? ? ? != 0x00000020) {
> + ? ? ? ? ? ? ? ? ? ? ? msleep(2);
> + ? ? ? ? ? ? ? ? ? ? ? timeout -= 2;
> + ? ? ? ? ? ? ? ? ? ? ? if (timeout <= 0)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? }
> +
> + ? ? ? reg = __raw_readl(DC_WR_CH_CONF(dc_chan));
> + ? ? ? reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
> + ? ? ? __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
> +}
> +EXPORT_SYMBOL(ipu_dc_disable_channel);
> +
> +static void ipu_dc_map_config(int map, int byte_num, int offset, int mask)
> +{
> + ? ? ? int ptr = map * 3 + byte_num;
> + ? ? ? u32 reg;
> +
> + ? ? ? reg = __raw_readl(DC_MAP_CONF_VAL(ptr));
> + ? ? ? reg &= ~(0xffff << (16 * (ptr & 0x1)));
> + ? ? ? reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
> + ? ? ? __raw_writel(reg, DC_MAP_CONF_VAL(ptr));
> +
> + ? ? ? reg = __raw_readl(DC_MAP_CONF_PTR(map));
> + ? ? ? reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num)));
> + ? ? ? reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
> + ? ? ? __raw_writel(reg, DC_MAP_CONF_PTR(map));
> +}
> +
> +static void ipu_dc_map_clear(int map)
> +{
> + ? ? ? u32 reg = __raw_readl(DC_MAP_CONF_PTR(map));
> + ? ? ? __raw_writel(reg & ~(0xffff << (16 * (map & 0x1))),
> + ? ? ? ? ? ? ? ? ? ?DC_MAP_CONF_PTR(map));
> +}
> +
> +int ipu_dc_init(struct platform_device *pdev, unsigned long base, unsigned long template_base)
> +{
> + ? ? ? static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, 0x78, 0, 0x94, 0xb4};
> + ? ? ? int i;
> +
> + ? ? ? ipu_dc_reg = ioremap(base, PAGE_SIZE);
> + ? ? ? if (!ipu_dc_reg)
> + ? ? ? ? ? ? ? return -ENOMEM;
> +
> + ? ? ? ipu_dev = &pdev->dev;
> +
> + ? ? ? ipu_dc_tmpl_reg = ioremap(template_base, PAGE_SIZE);
> + ? ? ? if (!ipu_dc_tmpl_reg) {
> + ? ? ? ? ? ? ? iounmap(ipu_dc_reg);
> + ? ? ? ? ? ? ? return -ENOMEM;
> + ? ? ? }
> +
> + ? ? ? for (i = 0; i < 10; i++)
> + ? ? ? ? ? ? ? dc_channels[i].channel_offset = channel_offsets[i];
> +
> + ? ? ? /* IPU_PIX_FMT_RGB24 */
> + ? ? ? ipu_dc_map_clear(0);
> + ? ? ? ipu_dc_map_config(0, 0, 7, 0xff);
> + ? ? ? ipu_dc_map_config(0, 1, 15, 0xff);
> + ? ? ? ipu_dc_map_config(0, 2, 23, 0xff);
> +
> + ? ? ? /* IPU_PIX_FMT_RGB666 */
> + ? ? ? ipu_dc_map_clear(1);
> + ? ? ? ipu_dc_map_config(1, 0, 5, 0xfc);
> + ? ? ? ipu_dc_map_config(1, 1, 11, 0xfc);
> + ? ? ? ipu_dc_map_config(1, 2, 17, 0xfc);
> +
> + ? ? ? /* IPU_PIX_FMT_YUV444 */
> + ? ? ? ipu_dc_map_clear(2);
> + ? ? ? ipu_dc_map_config(2, 0, 15, 0xff);
> + ? ? ? ipu_dc_map_config(2, 1, 23, 0xff);
> + ? ? ? ipu_dc_map_config(2, 2, 7, 0xff);
> +
> + ? ? ? /* IPU_PIX_FMT_RGB565 */
> + ? ? ? ipu_dc_map_clear(3);
> + ? ? ? ipu_dc_map_config(3, 0, 4, 0xf8);
> + ? ? ? ipu_dc_map_config(3, 1, 10, 0xfc);
> + ? ? ? ipu_dc_map_config(3, 2, 15, 0xf8);
> +
> + ? ? ? /* IPU_PIX_FMT_LVDS666 */
> + ? ? ? ipu_dc_map_clear(4);
> + ? ? ? ipu_dc_map_config(4, 0, 5, 0xfc);
> + ? ? ? ipu_dc_map_config(4, 1, 13, 0xfc);
> + ? ? ? ipu_dc_map_config(4, 2, 21, 0xfc);
> +
> + ? ? ? return 0;
> +}
> +
> +void ipu_dc_exit(struct platform_device *pdev)
> +{
> + ? ? ? iounmap(ipu_dc_reg);
> + ? ? ? iounmap(ipu_dc_tmpl_reg);
> +}
> diff --git a/drivers/mfd/imx-ipu-v3/ipu-di.c b/drivers/mfd/imx-ipu-v3/ipu-di.c
> new file mode 100644
> index 0000000..73ebd51
> --- /dev/null
> +++ b/drivers/mfd/imx-ipu-v3/ipu-di.c
> @@ -0,0 +1,507 @@
> +/*
> + * Copyright (c) 2010 Sascha Hauer <[email protected]>
> + * Copyright (C) 2005-2009 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.
> + */
> +
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/mfd/imx-ipu-v3.h>
> +#include <linux/platform_device.h>
> +#include <asm/atomic.h>
> +
> +#include "ipu-prv.h"
> +
> +#define SYNC_WAVE 0
> +
> +#define DC_DISP_ID_SYNC(di) ? ?(di)
> +
> +struct ipu_di {
> + ? ? ? void __iomem *base;
> + ? ? ? int id;
> + ? ? ? u32 module;
> + ? ? ? struct clk *clk;
> + ? ? ? struct clk *ipu_clk;
> + ? ? ? bool external_clk;
> +};
> +
> +static struct ipu_di dis[2];
> +
> +static DEFINE_MUTEX(di_mutex);
> +static struct device *ipu_dev;
> +
> +struct di_sync_config {
> + ? ? ? 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;
> +};
> +
> +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 = 0,
> + ? ? ? DI_SYNC_CLK = 1,
> + ? ? ? DI_SYNC_INT_HSYNC = 2,
> + ? ? ? DI_SYNC_HSYNC = 3,
> + ? ? ? DI_SYNC_VSYNC = 4,
> + ? ? ? DI_SYNC_DE = 6,
> +};
> +
> +#define DI_GENERAL ? ? ? ? ? ? 0x0000
> +#define DI_BS_CLKGEN0 ? ? ? ? ?0x0004
> +#define DI_BS_CLKGEN1 ? ? ? ? ?0x0008
> +#define DI_SW_GEN0(gen) ? ? ? ? ? ? ? ?(0x000c + 4 * ((gen) - 1))
> +#define DI_SW_GEN1(gen) ? ? ? ? ? ? ? ?(0x0030 + 4 * ((gen) - 1))
> +#define DI_STP_REP(gen) ? ? ? ? ? ? ? ?(0x0148 + 4 * (((gen) - 1)/2))
> +#define DI_SYNC_AS_GEN ? ? ? ? 0x0054
> +#define DI_DW_GEN(gen) ? ? ? ? (0x0058 + 4 * (gen))
> +#define DI_DW_SET(gen, set) ? ?(0x0088 + 4 * ((gen) + 0xc * (set)))
> +#define DI_SER_CONF ? ? ? ? ? ?0x015c
> +#define DI_SSC ? ? ? ? ? ? ? ? 0x0160
> +#define DI_POL ? ? ? ? ? ? ? ? 0x0164
> +#define DI_AW0 ? ? ? ? ? ? ? ? 0x0168
> +#define DI_AW1 ? ? ? ? ? ? ? ? 0x016c
> +#define DI_SCR_CONF ? ? ? ? ? ?0x0170
> +#define DI_STAT ? ? ? ? ? ? ? ? ? ? ? ?0x0174
> +
> +#define DI_SW_GEN0_RUN_COUNT(x) ? ? ? ? ? ? ? ? ? ? ? ?((x) << 19)
> +#define DI_SW_GEN0_RUN_SRC(x) ? ? ? ? ? ? ? ? ?((x) << 16)
> +#define DI_SW_GEN0_OFFSET_COUNT(x) ? ? ? ? ? ? ((x) << 3)
> +#define DI_SW_GEN0_OFFSET_SRC(x) ? ? ? ? ? ? ? ((x) << 0)
> +
> +#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ? ? ? ? ? ((x) << 29)
> +#define DI_SW_GEN1_CNT_CLR_SRC(x) ? ? ? ? ? ? ?((x) << 25)
> +#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ? ? ?((x) << 12)
> +#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ? ? ? ? ?((x) << 9)
> +#define DI_SW_GEN1_CNT_DOWN(x) ? ? ? ? ? ? ? ? ((x) << 16)
> +#define DI_SW_GEN1_CNT_UP(x) ? ? ? ? ? ? ? ? ? (x)
> +#define DI_SW_GEN1_AUTO_RELOAD ? ? ? ? ? ? ? ? (0x10000000)
> +
> +#define DI_DW_GEN_ACCESS_SIZE_OFFSET ? ? ? ? ? 24
> +#define DI_DW_GEN_COMPONENT_SIZE_OFFSET ? ? ? ? ? ? ? ?16
> +
> +#define DI_GEN_DI_CLK_EXT ? ? ? ? ? ? ? ? ? ? ?(1 << 20)
> +#define DI_GEN_POLARITY_1 ? ? ? ? ? ? ? ? ? ? ?(1 << 0)
> +#define DI_GEN_POLARITY_2 ? ? ? ? ? ? ? ? ? ? ?(1 << 1)
> +#define DI_GEN_POLARITY_3 ? ? ? ? ? ? ? ? ? ? ?(1 << 2)
> +#define DI_GEN_POLARITY_4 ? ? ? ? ? ? ? ? ? ? ?(1 << 3)
> +#define DI_GEN_POLARITY_5 ? ? ? ? ? ? ? ? ? ? ?(1 << 4)
> +#define DI_GEN_POLARITY_6 ? ? ? ? ? ? ? ? ? ? ?(1 << 5)
> +#define DI_GEN_POLARITY_7 ? ? ? ? ? ? ? ? ? ? ?(1 << 6)
> +#define DI_GEN_POLARITY_8 ? ? ? ? ? ? ? ? ? ? ?(1 << 7)
> +
> +#define DI_POL_DRDY_DATA_POLARITY ? ? ? ? ? ? ?(1 << 7)
> +#define DI_POL_DRDY_POLARITY_15 ? ? ? ? ? ? ? ? ? ? ? ?(1 << 4)
> +
> +#define DI_VSYNC_SEL_OFFSET ? ? ? ? ? ? ? ? ? ?13
> +
> +#define DI0_COUNTER_RELEASE ? ? ? ? ? ? ? ? ? ?(1 << 24)
> +#define DI1_COUNTER_RELEASE ? ? ? ? ? ? ? ? ? ?(1 << 25)
> +
> +static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset)
> +{
> + ? ? ? return __raw_readl(di->base + offset);
> +}
> +
> +static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset)
> +{
> + ? ? ? __raw_writel(value, di->base + offset);
> +}
> +
> +static void ipu_di_data_wave_config(struct ipu_di *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(di, reg, DI_DW_GEN(wave_gen));
> +}
> +
> +static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin, int set,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int up, int down)
> +{
> + ? ? ? u32 reg;
> +
> + ? ? ? reg = ipu_di_read(di, DI_DW_GEN(wave_gen));
> + ? ? ? reg &= ~(0x3 << (di_pin * 2));
> + ? ? ? reg |= set << (di_pin * 2);
> + ? ? ? ipu_di_write(di, reg, DI_DW_GEN(wave_gen));
> +
> + ? ? ? ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set));
> +}
> +
> +static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, int count)
> +{
> + ? ? ? u32 reg;
> + ? ? ? int i;
> +
> + ? ? ? for (i = 0; i < count; i++) {
> + ? ? ? ? ? ? ? struct di_sync_config *c = &config[i];
> + ? ? ? ? ? ? ? int wave_gen = i + 1;
> +
> + ? ? ? ? ? ? ? pr_debug("%s %d\n", __func__, wave_gen);
> + ? ? ? ? ? ? ? if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) || (c->repeat_count >= 0x1000) ||
> + ? ? ? ? ? ? ? ? ? ? ? (c->cnt_up >= 0x400) || (c->cnt_down >= 0x400)) {
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(ipu_dev, "DI%d counters out of range.\n", di->id);
> + ? ? ? ? ? ? ? ? ? ? ? return;
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? reg = DI_SW_GEN0_RUN_COUNT(c->run_count) |
> + ? ? ? ? ? ? ? ? ? ? ? DI_SW_GEN0_RUN_SRC(c->run_src) |
> + ? ? ? ? ? ? ? ? ? ? ? DI_SW_GEN0_OFFSET_COUNT(c->offset_count) |
> + ? ? ? ? ? ? ? ? ? ? ? DI_SW_GEN0_OFFSET_SRC(c->offset_src);
> + ? ? ? ? ? ? ? ipu_di_write(di, reg, DI_SW_GEN0(wave_gen));
> +
> + ? ? ? ? ? ? ? reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) |
> + ? ? ? ? ? ? ? ? ? ? ? DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) |
> + ? ? ? ? ? ? ? ? ? ? ? DI_SW_GEN1_CNT_POL_TRIGGER_SRC(c->cnt_polarity_trigger_src) |
> + ? ? ? ? ? ? ? ? ? ? ? DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) |
> + ? ? ? ? ? ? ? ? ? ? ? DI_SW_GEN1_CNT_DOWN(c->cnt_down) |
> + ? ? ? ? ? ? ? ? ? ? ? DI_SW_GEN1_CNT_UP(c->cnt_up);
> +
> + ? ? ? ? ? ? ? if (c->repeat_count == 0) {
> + ? ? ? ? ? ? ? ? ? ? ? /* Enable auto reload */
> + ? ? ? ? ? ? ? ? ? ? ? reg |= DI_SW_GEN1_AUTO_RELOAD;
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? ipu_di_write(di, reg, DI_SW_GEN1(wave_gen));
> +
> + ? ? ? ? ? ? ? reg = ipu_di_read(di, DI_STP_REP(wave_gen));
> + ? ? ? ? ? ? ? reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1)));
> + ? ? ? ? ? ? ? reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1));
> + ? ? ? ? ? ? ? ipu_di_write(di, reg, DI_STP_REP(wave_gen));
> + ? ? ? }
> +}
> +
> +static void ipu_di_sync_config_interlaced(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
> +{
> + ? ? ? u32 h_total = sig->width + sig->h_sync_width + sig->h_start_width + sig->h_end_width;
> + ? ? ? u32 v_total = sig->height + sig->v_sync_width + sig->v_start_width + sig->v_end_width;
> + ? ? ? u32 reg;
> + ? ? ? struct di_sync_config cfg[] = {
> + ? ? ? ? ? ? ? {
> + ? ? ? ? ? ? ? ? ? ? ? .run_count = h_total / 2 - 1,
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_CLK,
> + ? ? ? ? ? ? ? }, {
> + ? ? ? ? ? ? ? ? ? ? ? .run_count = h_total - 11,
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_CLK,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_down = 4,
> + ? ? ? ? ? ? ? }, {
> + ? ? ? ? ? ? ? ? ? ? ? .run_count = v_total * 2 - 1,
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_INT_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_count = 1,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_src = DI_SYNC_INT_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_down = 4,
> + ? ? ? ? ? ? ? }, {
> + ? ? ? ? ? ? ? ? ? ? ? .run_count = v_total / 2 - 1,
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_count = sig->v_start_width,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_src = DI_SYNC_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .repeat_count = 2,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_clr_src = DI_SYNC_VSYNC,
> + ? ? ? ? ? ? ? }, {
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .repeat_count = sig->height / 2,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_clr_src = 4,
> + ? ? ? ? ? ? ? }, {
> + ? ? ? ? ? ? ? ? ? ? ? .run_count = v_total - 1,
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_HSYNC,
> + ? ? ? ? ? ? ? }, {
> + ? ? ? ? ? ? ? ? ? ? ? .run_count = v_total / 2 - 1,
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_count = 9,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_src = DI_SYNC_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .repeat_count = 2,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_clr_src = DI_SYNC_VSYNC,
> + ? ? ? ? ? ? ? }, {
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_CLK,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_count = sig->h_start_width,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_src = DI_SYNC_CLK,
> + ? ? ? ? ? ? ? ? ? ? ? .repeat_count = sig->width,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_clr_src = 5,
> + ? ? ? ? ? ? ? }, {
> + ? ? ? ? ? ? ? ? ? ? ? .run_count = v_total - 1,
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_INT_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_count = v_total / 2,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_src = DI_SYNC_INT_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_clr_src = DI_SYNC_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_down = 4,
> + ? ? ? ? ? ? ? }
> + ? ? ? };
> +
> + ? ? ? ipu_di_sync_config(di, cfg, ARRAY_SIZE(cfg));
> +
> + ? ? ? /* set gentime select and tag sel */
> + ? ? ? reg = ipu_di_read(di, DI_SW_GEN1(9));
> + ? ? ? reg &= 0x1FFFFFFF;
> + ? ? ? reg |= (3 - 1) << 29 | 0x00008000;
> + ? ? ? ipu_di_write(di, reg, DI_SW_GEN1(9));
> +
> + ? ? ? ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);
> +}
> +
> +static void ipu_di_sync_config_noninterlaced(struct ipu_di *di,
> + ? ? ? ? ? ? ? struct ipu_di_signal_cfg *sig, int div)
> +{
> + ? ? ? u32 h_total = sig->width + sig->h_sync_width + sig->h_start_width +
> + ? ? ? ? ? ? ? sig->h_end_width;
> + ? ? ? u32 v_total = sig->height + sig->v_sync_width + sig->v_start_width +
> + ? ? ? ? ? ? ? sig->v_end_width + (sig->v_end_width < 2 ? 1 : 0);
> + ? ? ? struct di_sync_config cfg[] = {
> + ? ? ? ? ? ? ? {
> + ? ? ? ? ? ? ? ? ? ? ? .run_count = h_total - 1,
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_CLK,
> + ? ? ? ? ? ? ? } , {
> + ? ? ? ? ? ? ? ? ? ? ? .run_count = h_total - 1,
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_CLK,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_count = div * sig->v_to_h_sync,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_src = DI_SYNC_CLK,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_polarity_gen_en = 1,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_polarity_trigger_src = DI_SYNC_CLK,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_down = sig->h_sync_width * 2,
> + ? ? ? ? ? ? ? } , {
> + ? ? ? ? ? ? ? ? ? ? ? .run_count = v_total - 1,
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_INT_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_polarity_gen_en = 1,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_down = sig->v_sync_width * 2,
> + ? ? ? ? ? ? ? } , {
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_count = sig->v_sync_width + sig->v_start_width,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_src = DI_SYNC_HSYNC,
> + ? ? ? ? ? ? ? ? ? ? ? .repeat_count = sig->height,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_clr_src = DI_SYNC_VSYNC,
> + ? ? ? ? ? ? ? } , {
> + ? ? ? ? ? ? ? ? ? ? ? .run_src = DI_SYNC_CLK,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_count = sig->h_sync_width + sig->h_start_width,
> + ? ? ? ? ? ? ? ? ? ? ? .offset_src = DI_SYNC_CLK,
> + ? ? ? ? ? ? ? ? ? ? ? .repeat_count = sig->width,
> + ? ? ? ? ? ? ? ? ? ? ? .cnt_clr_src = 5,
> + ? ? ? ? ? ? ? } , {
> + ? ? ? ? ? ? ? ? ? ? ? /* unused */
> + ? ? ? ? ? ? ? } , {
> + ? ? ? ? ? ? ? ? ? ? ? /* unused */
> + ? ? ? ? ? ? ? } , {
> + ? ? ? ? ? ? ? ? ? ? ? /* unused */
> + ? ? ? ? ? ? ? } , {
> + ? ? ? ? ? ? ? ? ? ? ? /* unused */
> + ? ? ? ? ? ? ? },
> + ? ? ? };
> +
> + ? ? ? ipu_di_write(di, v_total - 1, DI_SCR_CONF);
> + ? ? ? ipu_di_sync_config(di, cfg, ARRAY_SIZE(cfg));
> +}
> +
> +int ipu_di_init_sync_panel(int disp, u32 pixel_clk, struct ipu_di_signal_cfg *sig)
> +{
> + ? ? ? struct ipu_di *di = &dis[disp];
> + ? ? ? u32 reg;
> + ? ? ? u32 disp_gen, di_gen, vsync_cnt;
> + ? ? ? u32 div;
> + ? ? ? u32 h_total, v_total;
> + ? ? ? struct clk *di_clk;
> +
> + ? ? ? dev_dbg(ipu_dev, "disp %d: panel size = %d x %d\n",
> + ? ? ? ? ? ? ? disp, sig->width, sig->height);
> +
> + ? ? ? if (disp > 1)
> + ? ? ? ? ? ? ? return -EINVAL;
> +
> + ? ? ? if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0))
> + ? ? ? ? ? ? ? return -EINVAL;
> +
> + ? ? ? h_total = sig->width + sig->h_sync_width + sig->h_start_width + sig->h_end_width;
> + ? ? ? v_total = sig->height + sig->v_sync_width + sig->v_start_width + sig->v_end_width;
> +
> + ? ? ? mutex_lock(&di_mutex);
> +
> + ? ? ? /* Init clocking */
> + ? ? ? if (sig->ext_clk) {
> + ? ? ? ? ? ? ? di->external_clk = true;
> + ? ? ? ? ? ? ? di_clk = di->clk;
> + ? ? ? } else {
> + ? ? ? ? ? ? ? di->external_clk = false;
> + ? ? ? ? ? ? ? di_clk = di->ipu_clk;
> + ? ? ? }
> +
> + ? ? ? /*
> + ? ? ? ?* Calculate divider
> + ? ? ? ?* Fractional part is 4 bits,
> + ? ? ? ?* so simply multiply by 2^4 to get fractional part.
> + ? ? ? ?*/
> + ? ? ? div = (clk_get_rate(di_clk) * 16) / pixel_clk;
> + ? ? ? if (div < 0x10) /* Min DI disp clock divider is 1 */
> + ? ? ? ? ? ? ? div = 0x10;
> +
> + ? ? ? disp_gen = ipu_cm_read(IPU_DISP_GEN);
> + ? ? ? disp_gen &= disp ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE;
> + ? ? ? ipu_cm_write(disp_gen, IPU_DISP_GEN);
> +
> + ? ? ? ipu_di_write(di, div, DI_BS_CLKGEN0);
> +
> + ? ? ? /* Setup pixel clock timing */
> + ? ? ? /* Down time is half of period */
> + ? ? ? ipu_di_write(di, (div / 16) << 16, DI_BS_CLKGEN1);
> +
> + ? ? ? ipu_di_data_wave_config(di, SYNC_WAVE, div / 16 - 1, div / 16 - 1);
> + ? ? ? ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div / 16 * 2);
> +
> + ? ? ? div = div / 16; ? ? ? ? /* Now divider is integer portion */
> +
> + ? ? ? di_gen = 0;
> + ? ? ? if (sig->ext_clk)
> + ? ? ? ? ? ? ? di_gen |= DI_GEN_DI_CLK_EXT;
> +
> + ? ? ? if (sig->interlaced) {
> + ? ? ? ? ? ? ? ipu_di_sync_config_interlaced(di, sig);
> +
> + ? ? ? ? ? ? ? /* set y_sel = 1 */
> + ? ? ? ? ? ? ? di_gen |= 0x10000000;
> + ? ? ? ? ? ? ? di_gen |= DI_GEN_POLARITY_5;
> + ? ? ? ? ? ? ? di_gen |= DI_GEN_POLARITY_8;
> +
> + ? ? ? ? ? ? ? vsync_cnt = 7;
> +
> + ? ? ? ? ? ? ? if (sig->Hsync_pol)
> + ? ? ? ? ? ? ? ? ? ? ? di_gen |= DI_GEN_POLARITY_3;
> + ? ? ? ? ? ? ? if (sig->Vsync_pol)
> + ? ? ? ? ? ? ? ? ? ? ? di_gen |= DI_GEN_POLARITY_2;
> + ? ? ? } else {
> + ? ? ? ? ? ? ? ipu_di_sync_config_noninterlaced(di, sig, div);
> +
> + ? ? ? ? ? ? ? vsync_cnt = 3;
> +
> + ? ? ? ? ? ? ? if (sig->Hsync_pol)
> + ? ? ? ? ? ? ? ? ? ? ? di_gen |= DI_GEN_POLARITY_2;
> + ? ? ? ? ? ? ? if (sig->Vsync_pol)
> + ? ? ? ? ? ? ? ? ? ? ? di_gen |= DI_GEN_POLARITY_3;
> + ? ? ? }
> +
> + ? ? ? ipu_di_write(di, di_gen, DI_GENERAL);
> + ? ? ? ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002,
> + ? ? ? ? ? ? ? ? ? ?DI_SYNC_AS_GEN);
> +
> + ? ? ? reg = ipu_di_read(di, 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(di, reg, DI_POL);
> +
> + ? ? ? mutex_unlock(&di_mutex);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_di_init_sync_panel);
> +
> +int ipu_di_enable(int disp)
> +{
> + ? ? ? struct ipu_di *di = &dis[disp];
> + ? ? ? u32 reg;
> +
> + ? ? ? reg = ipu_cm_read(IPU_DISP_GEN);
> + ? ? ? if (disp)
> + ? ? ? ? ? ? ? reg |= DI1_COUNTER_RELEASE;
> + ? ? ? else
> + ? ? ? ? ? ? ? reg |= DI0_COUNTER_RELEASE;
> + ? ? ? ipu_cm_write(reg, IPU_DISP_GEN);
> +
> + ? ? ? if (di->external_clk)
> + ? ? ? ? ? ? ? clk_enable(di->clk);
> +
> + ? ? ? ipu_module_enable(di->module);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_di_enable);
> +
> +int ipu_di_disable(int disp)
> +{
> + ? ? ? struct ipu_di *di = &dis[disp];
> + ? ? ? u32 reg;
> +
> + ? ? ? ipu_module_disable(di->module);
> +
> + ? ? ? if (di->external_clk)
> + ? ? ? ? ? ? ? clk_disable(di->clk);
> +
> + ? ? ? reg = ipu_cm_read(IPU_DISP_GEN);
> + ? ? ? if (disp)
> + ? ? ? ? ? ? ? reg &= ~DI1_COUNTER_RELEASE;
> + ? ? ? else
> + ? ? ? ? ? ? ? reg &= ~DI0_COUNTER_RELEASE;
> + ? ? ? ipu_cm_write(reg, IPU_DISP_GEN);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_di_disable);
> +
> +int ipu_di_init(struct platform_device *pdev, int id, unsigned long base,
> + ? ? ? ? ? ? ? u32 module, struct clk *ipu_clk)
> +{
> + ? ? ? char *clkid;
> +
> + ? ? ? if (id > 1)
> + ? ? ? ? ? ? ? return -EINVAL;
> +
> + ? ? ? if (id)
> + ? ? ? ? ? ? ? clkid = "di1";
> + ? ? ? else
> + ? ? ? ? ? ? ? clkid = "di0";
> +
> + ? ? ? ipu_dev = &pdev->dev;
> +
> + ? ? ? dis[id].clk = clk_get(&pdev->dev, clkid);
> + ? ? ? dis[id].module = module;
> + ? ? ? dis[id].id = id;
> + ? ? ? dis[id].ipu_clk = ipu_clk;
> + ? ? ? dis[id].base = ioremap(base, PAGE_SIZE);
> + ? ? ? if (!dis[id].base)
> + ? ? ? ? ? ? ? return -ENOMEM;
> +
> + ? ? ? /* Set MCU_T to divide MCU access window into 2 */
> + ? ? ? ipu_cm_write(0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN);
> +
> + ? ? ? return 0;
> +}
> +
> +void ipu_di_exit(struct platform_device *pdev, int id)
> +{
> + ? ? ? clk_put(dis[id].clk);
> + ? ? ? iounmap(dis[id].base);
> +}
> diff --git a/drivers/mfd/imx-ipu-v3/ipu-dmfc.c b/drivers/mfd/imx-ipu-v3/ipu-dmfc.c
> new file mode 100644
> index 0000000..25782a7
> --- /dev/null
> +++ b/drivers/mfd/imx-ipu-v3/ipu-dmfc.c
> @@ -0,0 +1,343 @@
> +/*
> + * Copyright (c) 2010 Sascha Hauer <[email protected]>
> + * Copyright (C) 2005-2009 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.
> + */
> +
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/mfd/imx-ipu-v3.h>
> +
> +#include "ipu-prv.h"
> +
> +#define DMFC_RD_CHAN ? ? ? ? ? 0x0000
> +#define DMFC_WR_CHAN ? ? ? ? ? 0x0004
> +#define DMFC_WR_CHAN_DEF ? ? ? 0x0008
> +#define DMFC_DP_CHAN ? ? ? ? ? 0x000c
> +#define DMFC_DP_CHAN_DEF ? ? ? 0x0010
> +#define DMFC_GENERAL1 ? ? ? ? ?0x0014
> +#define DMFC_GENERAL2 ? ? ? ? ?0x0018
> +#define DMFC_IC_CTRL ? ? ? ? ? 0x001c
> +#define DMFC_STAT ? ? ? ? ? ? ?0x0020
> +
> +#define DMFC_WR_CHAN_1_28 ? ? ? ? ? ? ?0
> +#define DMFC_WR_CHAN_2_41 ? ? ? ? ? ? ?8
> +#define DMFC_WR_CHAN_1C_42 ? ? ? ? ? ? 16
> +#define DMFC_WR_CHAN_2C_43 ? ? ? ? ? ? 24
> +
> +#define DMFC_DP_CHAN_5B_23 ? ? ? ? ? ? 0
> +#define DMFC_DP_CHAN_5F_27 ? ? ? ? ? ? 8
> +#define DMFC_DP_CHAN_6B_24 ? ? ? ? ? ? 16
> +#define DMFC_DP_CHAN_6F_29 ? ? ? ? ? ? 24
> +
> +#define DMFC_FIFO_SIZE_64 ? ? ? ? ? ? ?(3 << 3)
> +#define DMFC_FIFO_SIZE_128 ? ? ? ? ? ? (2 << 3)
> +#define DMFC_FIFO_SIZE_256 ? ? ? ? ? ? (1 << 3)
> +#define DMFC_FIFO_SIZE_512 ? ? ? ? ? ? (0 << 3)
> +
> +#define DMFC_SEGMENT(x) ? ? ? ? ? ? ? ? ? ? ? ?((x & 0x7) << 0)
> +#define DMFC_BURSTSIZE_32 ? ? ? ? ? ? ?(0 << 6)
> +#define DMFC_BURSTSIZE_16 ? ? ? ? ? ? ?(1 << 6)
> +#define DMFC_BURSTSIZE_8 ? ? ? ? ? ? ? (2 << 6)
> +#define DMFC_BURSTSIZE_4 ? ? ? ? ? ? ? (3 << 6)
> +
> +static struct device *ipu_dev;
> +
> +struct dmfc_channel {
> + ? ? ? int ? ? ? ? ? ? ipu_channel;
> + ? ? ? unsigned long ? channel_reg;
> + ? ? ? unsigned long ? shift;
> + ? ? ? unsigned ? ? ? ?eot_shift;
> + ? ? ? unsigned ? ? ? ?slots;
> + ? ? ? unsigned ? ? ? ?max_fifo_lines;
> + ? ? ? unsigned ? ? ? ?slotmask;
> + ? ? ? unsigned ? ? ? ?segment;
> +};
> +
> +static struct dmfc_channel dmfcs[] = {
> + ? ? ? {
> + ? ? ? ? ? ? ? .ipu_channel ? ?= 23,
> + ? ? ? ? ? ? ? .channel_reg ? ?= DMFC_DP_CHAN,
> + ? ? ? ? ? ? ? .shift ? ? ? ? ?= DMFC_DP_CHAN_5B_23,
> + ? ? ? ? ? ? ? .eot_shift ? ? ?= 20,
> + ? ? ? ? ? ? ? .max_fifo_lines = 3,
> + ? ? ? }, {
> + ? ? ? ? ? ? ? .ipu_channel ? ?= 24,
> + ? ? ? ? ? ? ? .channel_reg ? ?= DMFC_DP_CHAN,
> + ? ? ? ? ? ? ? .shift ? ? ? ? ?= DMFC_DP_CHAN_6B_24,
> + ? ? ? ? ? ? ? .eot_shift ? ? ?= 22,
> + ? ? ? ? ? ? ? .max_fifo_lines = 1,
> + ? ? ? }, {
> + ? ? ? ? ? ? ? .ipu_channel ? ?= 27,
> + ? ? ? ? ? ? ? .channel_reg ? ?= DMFC_DP_CHAN,
> + ? ? ? ? ? ? ? .shift ? ? ? ? ?= DMFC_DP_CHAN_5F_27,
> + ? ? ? ? ? ? ? .eot_shift ? ? ?= 21,
> + ? ? ? ? ? ? ? .max_fifo_lines = 2,
> + ? ? ? }, {
> + ? ? ? ? ? ? ? .ipu_channel ? ?= 28,
> + ? ? ? ? ? ? ? .channel_reg ? ?= DMFC_WR_CHAN,
> + ? ? ? ? ? ? ? .shift ? ? ? ? ?= DMFC_WR_CHAN_1_28,
> + ? ? ? ? ? ? ? .eot_shift ? ? ?= 16,
> + ? ? ? ? ? ? ? .max_fifo_lines = 2,
> + ? ? ? }, {
> + ? ? ? ? ? ? ? .ipu_channel ? ?= 29,
> + ? ? ? ? ? ? ? .channel_reg ? ?= DMFC_DP_CHAN,
> + ? ? ? ? ? ? ? .shift ? ? ? ? ?= DMFC_DP_CHAN_6F_29,
> + ? ? ? ? ? ? ? .eot_shift ? ? ?= 23,
> + ? ? ? ? ? ? ? .max_fifo_lines = 1,
> + ? ? ? },
> +};
> +
> +#define DMFC_NUM_CHANNELS ? ? ?ARRAY_SIZE(dmfcs)
> +
> +static int dmfc_use_count;
> +static void __iomem *dmfc_regs;
> +static unsigned long dmfc_bandwidth_per_slot;
> +static DEFINE_MUTEX(dmfc_mutex);
> +
> +int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc)
> +{
> + ? ? ? mutex_lock(&dmfc_mutex);
> +
> + ? ? ? if (!dmfc_use_count)
> + ? ? ? ? ? ? ? ipu_module_enable(IPU_CONF_DMFC_EN);
> +
> + ? ? ? dmfc_use_count++;
> +
> + ? ? ? mutex_unlock(&dmfc_mutex);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_dmfc_enable_channel);
> +
> +void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
> +{
> + ? ? ? mutex_lock(&dmfc_mutex);
> +
> + ? ? ? dmfc_use_count--;
> +
> + ? ? ? if (!dmfc_use_count)
> + ? ? ? ? ? ? ? ipu_module_disable(IPU_CONF_DMFC_EN);
> +
> + ? ? ? if (dmfc_use_count < 0)
> + ? ? ? ? ? ? ? dmfc_use_count = 0;
> +
> + ? ? ? mutex_unlock(&dmfc_mutex);
> +}
> +EXPORT_SYMBOL(ipu_dmfc_disable_channel);
> +
> +static int ipu_dmfc_setup_channel(struct dmfc_channel *dmfc, int slots, int segment)
> +{
> + ? ? ? u32 val, field;
> +
> + ? ? ? dev_dbg(ipu_dev, "dmfc: using %d slots starting from segment %d for IPU channel %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? slots, segment, dmfc->ipu_channel);
> +
> + ? ? ? if (!dmfc)
> + ? ? ? ? ? ? ? return -EINVAL;
> +
> + ? ? ? switch (slots) {
> + ? ? ? case 1:
> + ? ? ? ? ? ? ? field = DMFC_FIFO_SIZE_64;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 2:
> + ? ? ? ? ? ? ? field = DMFC_FIFO_SIZE_128;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 4:
> + ? ? ? ? ? ? ? field = DMFC_FIFO_SIZE_256;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 8:
> + ? ? ? ? ? ? ? field = DMFC_FIFO_SIZE_512;
> + ? ? ? ? ? ? ? break;
> + ? ? ? default:
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> +
> + ? ? ? field |= DMFC_SEGMENT(segment) | DMFC_BURSTSIZE_8;
> +
> + ? ? ? val = readl(dmfc_regs + dmfc->channel_reg);
> +
> + ? ? ? val &= ~(0xff << dmfc->shift);
> + ? ? ? val |= field << dmfc->shift;
> +
> + ? ? ? writel(val, dmfc_regs + dmfc->channel_reg);
> +
> + ? ? ? dmfc->slots = slots;
> + ? ? ? dmfc->segment = segment;
> + ? ? ? dmfc->slotmask = ((1 << slots) - 1) << segment;
> +
> + ? ? ? return 0;
> +}
> +
> +static int dmfc_bandwidth_to_slots(unsigned long bandwidth)
> +{
> + ? ? ? int slots = 1;
> +
> + ? ? ? while (slots * dmfc_bandwidth_per_slot < bandwidth)
> + ? ? ? ? ? ? ? slots *= 2;
> +
> + ? ? ? return slots;
> +}
> +
> +static int dmfc_find_slots(int slots)
> +{
> + ? ? ? unsigned slotmask_need, slotmask_used = 0;
> + ? ? ? int i, segment = 0;
> +
> + ? ? ? slotmask_need = (1 << slots) - 1;
> +
> + ? ? ? for (i = 0; i < DMFC_NUM_CHANNELS; i++)
> + ? ? ? ? ? ? ? slotmask_used |= dmfcs[i].slotmask;
> +
> + ? ? ? while (slotmask_need <= 0xff) {
> + ? ? ? ? ? ? ? if (!(slotmask_used & slotmask_need))
> + ? ? ? ? ? ? ? ? ? ? ? return segment;
> +
> + ? ? ? ? ? ? ? slotmask_need <<= 1;
> + ? ? ? ? ? ? ? segment++;
> + ? ? ? }
> +
> + ? ? ? return -EBUSY;
> +}
> +
> +void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc)
> +{
> + ? ? ? int i;
> +
> + ? ? ? dev_dbg(ipu_dev, "dmfc: freeing %d slots starting from segment %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? dmfc->slots, dmfc->segment);
> +
> + ? ? ? mutex_lock(&dmfc_mutex);
> +
> + ? ? ? if (!dmfc->slots)
> + ? ? ? ? ? ? ? goto out;
> +
> + ? ? ? dmfc->slotmask = 0;
> + ? ? ? dmfc->slots = 0;
> + ? ? ? dmfc->segment = 0;
> +
> + ? ? ? for (i = 0; i < ARRAY_SIZE(dmfcs); i++)
> + ? ? ? ? ? ? ? dmfcs[i].slotmask = 0;
> +
> + ? ? ? for (i = 0; i < ARRAY_SIZE(dmfcs); i++) {
> + ? ? ? ? ? ? ? if (dmfcs[i].slots > 0) {
> + ? ? ? ? ? ? ? ? ? ? ? dmfcs[i].segment = dmfc_find_slots(dmfcs[i].slots);
> + ? ? ? ? ? ? ? ? ? ? ? dmfcs[i].slotmask = ((1 << dmfcs[i].slots) - 1) << dmfcs[i].segment;
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> +
> + ? ? ? for (i = 0; i < ARRAY_SIZE(dmfcs); i++) {
> + ? ? ? ? ? ? ? if (dmfcs[i].slots > 0)
> + ? ? ? ? ? ? ? ? ? ? ? ipu_dmfc_setup_channel(&dmfcs[i], dmfcs[i].slots, dmfcs[i].segment);
> + ? ? ? }
> +out:
> + ? ? ? mutex_unlock(&dmfc_mutex);
> +}
> +EXPORT_SYMBOL(ipu_dmfc_free_bandwidth);
> +
> +int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
> + ? ? ? ? ? ? ? unsigned long bandwidth_pixel_per_second)
> +{
> + ? ? ? int slots = dmfc_bandwidth_to_slots(bandwidth_pixel_per_second);
> + ? ? ? int segment = 0;
> +
> + ? ? ? dev_dbg(ipu_dev, "dmfc: trying to allocate %ldMpixel/s for IPU channel %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? bandwidth_pixel_per_second / 1000000, dmfc->ipu_channel);
> +
> + ? ? ? ipu_dmfc_free_bandwidth(dmfc);
> +
> + ? ? ? mutex_lock(&dmfc_mutex);
> +
> + ? ? ? if (slots > 8)
> + ? ? ? ? ? ? ? return -EBUSY;
> +
> + ? ? ? segment = dmfc_find_slots(slots);
> + ? ? ? if (segment < 0)
> + ? ? ? ? ? ? ? return -EBUSY;
> +
> + ? ? ? ipu_dmfc_setup_channel(dmfc, slots, segment);
> +
> + ? ? ? mutex_unlock(&dmfc_mutex);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_dmfc_alloc_bandwidth);
> +
> +int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width)
> +{
> + ? ? ? u32 dmfc_gen1;
> +
> + ? ? ? dmfc_gen1 = readl(dmfc_regs + DMFC_GENERAL1);
> +
> + ? ? ? if ((dmfc->slots * 64 * 4) / width > dmfc->max_fifo_lines)
> + ? ? ? ? ? ? ? dmfc_gen1 |= 1 << dmfc->eot_shift;
> + ? ? ? else
> + ? ? ? ? ? ? ? dmfc_gen1 &= ~(1 << dmfc->eot_shift);
> +
> + ? ? ? writel(dmfc_gen1, dmfc_regs + DMFC_GENERAL1);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_dmfc_init_channel);
> +
> +struct dmfc_channel *ipu_dmfc_get(int ipu_channel)
> +{
> + ? ? ? int i;
> +
> + ? ? ? for (i = 0; i < DMFC_NUM_CHANNELS; i++)
> + ? ? ? ? ? ? ? if (dmfcs[i].ipu_channel == ipu_channel)
> + ? ? ? ? ? ? ? ? ? ? ? return &dmfcs[i];
> + ? ? ? return NULL;
> +}
> +EXPORT_SYMBOL(ipu_dmfc_get);
> +
> +void ipu_dmfc_put(struct dmfc_channel *dmfc)
> +{
> + ? ? ? ipu_dmfc_free_bandwidth(dmfc);
> +}
> +EXPORT_SYMBOL(ipu_dmfc_put);
> +
> +int ipu_dmfc_init(struct platform_device *pdev, unsigned long base,
> + ? ? ? ? ? ? ? struct clk *ipu_clk)
> +{
> + ? ? ? dmfc_regs = ioremap(base, PAGE_SIZE);
> +
> + ? ? ? if (!dmfc_regs)
> + ? ? ? ? ? ? ? return -ENOMEM;
> +
> + ? ? ? ipu_dev = &pdev->dev;
> +
> + ? ? ? writel(0x0, dmfc_regs + DMFC_WR_CHAN);
> + ? ? ? writel(0x0, dmfc_regs + DMFC_DP_CHAN);
> +
> + ? ? ? /*
> + ? ? ? ?* We have a total bandwidth of clkrate * 4pixel divided
> + ? ? ? ?* into 8 slots.
> + ? ? ? ?*/
> + ? ? ? dmfc_bandwidth_per_slot = clk_get_rate(ipu_clk) / 4;
> +
> + ? ? ? dev_dbg(ipu_dev, "dmfc: 8 slots with %ldMpixel/s bandwidth each\n",
> + ? ? ? ? ? ? ? ? ? ? ? dmfc_bandwidth_per_slot / 1000000);
> +
> + ? ? ? writel(0x202020f6, dmfc_regs + DMFC_WR_CHAN_DEF);
> + ? ? ? writel(0x2020f6f6, dmfc_regs + DMFC_DP_CHAN_DEF);
> + ? ? ? writel(0x00000003, dmfc_regs + DMFC_GENERAL1);
> +
> + ? ? ? return 0;
> +}
> +
> +void ipu_dmfc_exit(struct platform_device *pdev)
> +{
> + ? ? ? iounmap(dmfc_regs);
> +}
> diff --git a/drivers/mfd/imx-ipu-v3/ipu-dp.c b/drivers/mfd/imx-ipu-v3/ipu-dp.c
> new file mode 100644
> index 0000000..505107d
> --- /dev/null
> +++ b/drivers/mfd/imx-ipu-v3/ipu-dp.c
> @@ -0,0 +1,468 @@
> +/*
> + * Copyright (c) 2010 Sascha Hauer <[email protected]>
> + * Copyright (C) 2005-2009 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.
> + */
> +
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/mfd/imx-ipu-v3.h>
> +#include <linux/err.h>
> +#include <asm/atomic.h>
> +
> +#include "ipu-prv.h"
> +
> +#define DP_SYNC 0
> +#define DP_ASYNC0 0x60
> +#define DP_ASYNC1 0xBC
> +
> +#define DP_COM_CONF(flow) ? ? ? ? ? ? ?(ipu_dp_base + flow)
> +#define DP_GRAPH_WIND_CTRL(flow) ? ? ? (ipu_dp_base + 0x0004 + flow)
> +#define DP_FG_POS(flow) ? ? ? ? ? ? ? ? ? ? ? ?(ipu_dp_base + 0x0008 + flow)
> +#define DP_CSC_A_0(flow) ? ? ? ? ? ? ? (ipu_dp_base + 0x0044 + flow)
> +#define DP_CSC_A_1(flow) ? ? ? ? ? ? ? (ipu_dp_base + 0x0048 + flow)
> +#define DP_CSC_A_2(flow) ? ? ? ? ? ? ? (ipu_dp_base + 0x004C + flow)
> +#define DP_CSC_A_3(flow) ? ? ? ? ? ? ? (ipu_dp_base + 0x0050 + flow)
> +#define DP_CSC_0(flow) ? ? ? ? ? ? ? ? (ipu_dp_base + 0x0054 + flow)
> +#define DP_CSC_1(flow) ? ? ? ? ? ? ? ? (ipu_dp_base + 0x0058 + flow)
> +
> +#define DP_COM_CONF_FG_EN ? ? ? ? ? ? ?(1 << 0)
> +#define DP_COM_CONF_GWSEL ? ? ? ? ? ? ?(1 << 1)
> +#define DP_COM_CONF_GWAM ? ? ? ? ? ? ? (1 << 2)
> +#define DP_COM_CONF_GWCKE ? ? ? ? ? ? ?(1 << 3)
> +#define DP_COM_CONF_CSC_DEF_MASK ? ? ? (3 << 8)
> +#define DP_COM_CONF_CSC_DEF_OFFSET ? ? 8
> +#define DP_COM_CONF_CSC_DEF_FG ? ? ? ? (3 << 8)
> +#define DP_COM_CONF_CSC_DEF_BG ? ? ? ? (2 << 8)
> +#define DP_COM_CONF_CSC_DEF_BOTH ? ? ? (1 << 8)
> +
> +struct ipu_dp {
> + ? ? ? u32 flow;
> + ? ? ? bool in_use;
> +};
> +
> +static struct ipu_dp ipu_dp[3];
> +static struct device *ipu_dev;
> +
> +static u32 ipu_flows[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
> +
> +enum csc_type_t {
> + ? ? ? RGB2YUV = 0,
> + ? ? ? YUV2RGB,
> + ? ? ? RGB2RGB,
> + ? ? ? YUV2YUV,
> + ? ? ? CSC_NONE,
> + ? ? ? CSC_NUM
> +};
> +
> +static void __iomem *ipu_dp_base;
> +static int dp_use_count;
> +static DEFINE_MUTEX(dp_mutex);
> +
> +/* ? ? Y = R * ?.299 + G * ?.587 + B * ?.114;
> + ? ? ? U = R * -.169 + G * -.332 + B * ?.500 + 128.;
> + ? ? ? V = R * ?.500 + G * -.419 + B * -.0813 + 128.;*/
> +static const int rgb2ycbcr_coeff[5][3] = {
> + ? ? ? { 153, 301, 58, },
> + ? ? ? { -87, -170, 0x0100, },
> + ? ? ? { 0x100, -215, -42, },
> + ? ? ? { 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 */
> +};
> +
> +/* Please keep S0, S1 and S2 as 0x2 by using this conversion */
> +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;
> +}
> +
> +struct dp_csc_param_t {
> + ? ? ? int mode;
> + ? ? ? void *coeff;
> +};
> +
> +/*
> + * 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, },
> + ? ? ? }
> +};
> +
> +static enum csc_type_t fg_csc_type = CSC_NONE, bg_csc_type = CSC_NONE;
> +
> +static int color_key_4rgb = 1;
> +
> +int ipu_dp_set_color_key(struct ipu_dp *dp, bool enable, u32 color_key)
> +{
> + ? ? ? u32 reg;
> + ? ? ? int y, u, v;
> + ? ? ? int red, green, blue;
> +
> + ? ? ? mutex_lock(&dp_mutex);
> +
> + ? ? ? color_key_4rgb = 1;
> + ? ? ? /* Transform color key from rgb to yuv if CSC is enabled */
> + ? ? ? if (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) ||
> + ? ? ? ? ? ? ? ? ? ? ? ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) ||
> + ? ? ? ? ? ? ? ? ? ? ? ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) ||
> + ? ? ? ? ? ? ? ? ? ? ? ((fg_csc_type == YUV2RGB) && (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;
> +
> + ? ? ? ? ? ? ? color_key_4rgb = 0;
> +
> + ? ? ? ? ? ? ? dev_dbg(ipu_dev, "color key change to yuv fmt 0x%x\n", color_key);
> + ? ? ? }
> +
> + ? ? ? if (enable) {
> + ? ? ? ? ? ? ? reg = __raw_readl(DP_GRAPH_WIND_CTRL(dp->flow)) & 0xFF000000L;
> + ? ? ? ? ? ? ? __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL(dp->flow));
> +
> + ? ? ? ? ? ? ? reg = __raw_readl(DP_COM_CONF(dp->flow));
> + ? ? ? ? ? ? ? __raw_writel(reg | DP_COM_CONF_GWCKE, DP_COM_CONF(dp->flow));
> + ? ? ? } else {
> + ? ? ? ? ? ? ? reg = __raw_readl(DP_COM_CONF(dp->flow));
> + ? ? ? ? ? ? ? __raw_writel(reg & ~DP_COM_CONF_GWCKE, DP_COM_CONF(dp->flow));
> + ? ? ? }
> +
> + ? ? ? reg = ipu_cm_read(IPU_SRM_PRI2) | 0x8;
> + ? ? ? ipu_cm_write(reg, IPU_SRM_PRI2);
> +
> + ? ? ? mutex_unlock(&dp_mutex);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_dp_set_color_key);
> +
> +int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha, bool bg_chan)
> +{
> + ? ? ? u32 reg;
> +
> + ? ? ? mutex_lock(&dp_mutex);
> +
> + ? ? ? reg = __raw_readl(DP_COM_CONF(dp->flow));
> + ? ? ? if (bg_chan)
> + ? ? ? ? ? ? ? reg &= ~DP_COM_CONF_GWSEL;
> + ? ? ? else
> + ? ? ? ? ? ? ? reg |= DP_COM_CONF_GWSEL;
> + ? ? ? __raw_writel(reg, DP_COM_CONF(dp->flow));
> +
> + ? ? ? if (enable) {
> + ? ? ? ? ? ? ? reg = __raw_readl(DP_GRAPH_WIND_CTRL(dp->flow)) & 0x00FFFFFFL;
> + ? ? ? ? ? ? ? __raw_writel(reg | ((u32) alpha << 24),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?DP_GRAPH_WIND_CTRL(dp->flow));
> +
> + ? ? ? ? ? ? ? reg = __raw_readl(DP_COM_CONF(dp->flow));
> + ? ? ? ? ? ? ? __raw_writel(reg | DP_COM_CONF_GWAM, DP_COM_CONF(dp->flow));
> + ? ? ? } else {
> + ? ? ? ? ? ? ? reg = __raw_readl(DP_COM_CONF(dp->flow));
> + ? ? ? ? ? ? ? __raw_writel(reg & ~DP_COM_CONF_GWAM, DP_COM_CONF(dp->flow));
> + ? ? ? }
> +
> + ? ? ? reg = ipu_cm_read(IPU_SRM_PRI2) | 0x8;
> + ? ? ? ipu_cm_write(reg, IPU_SRM_PRI2);
> +
> + ? ? ? mutex_unlock(&dp_mutex);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_dp_set_global_alpha);
> +
> +int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
> +{
> + ? ? ? u32 reg;
> +
> + ? ? ? mutex_lock(&dp_mutex);
> +
> + ? ? ? __raw_writel((x_pos << 16) | y_pos, DP_FG_POS(dp->flow));
> +
> + ? ? ? reg = ipu_cm_read(IPU_SRM_PRI2);
> + ? ? ? reg |= 0x8;
> + ? ? ? ipu_cm_write(reg, IPU_SRM_PRI2);
> +
> + ? ? ? mutex_unlock(&dp_mutex);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_dp_set_window_pos);
> +
> +#define mask_a(a) ((u32)(a) & 0x3FF)
> +#define mask_b(b) ((u32)(b) & 0x3FFF)
> +
> +void __ipu_dp_csc_setup(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 = __raw_readl(DP_COM_CONF(dp));
> + ? ? ? ? ? ? ? reg &= ~DP_COM_CONF_CSC_DEF_MASK;
> + ? ? ? ? ? ? ? reg |= dp_csc_param.mode;
> + ? ? ? ? ? ? ? __raw_writel(reg, DP_COM_CONF(dp));
> + ? ? ? }
> +
> + ? ? ? coeff = dp_csc_param.coeff;
> +
> + ? ? ? if (coeff) {
> + ? ? ? ? ? ? ? __raw_writel(mask_a((*coeff)[0][0]) |
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (mask_a((*coeff)[0][1]) << 16), DP_CSC_A_0(dp));
> + ? ? ? ? ? ? ? __raw_writel(mask_a((*coeff)[0][2]) |
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (mask_a((*coeff)[1][0]) << 16), DP_CSC_A_1(dp));
> + ? ? ? ? ? ? ? __raw_writel(mask_a((*coeff)[1][1]) |
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (mask_a((*coeff)[1][2]) << 16), DP_CSC_A_2(dp));
> + ? ? ? ? ? ? ? __raw_writel(mask_a((*coeff)[2][0]) |
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (mask_a((*coeff)[2][1]) << 16), DP_CSC_A_3(dp));
> + ? ? ? ? ? ? ? __raw_writel(mask_a((*coeff)[2][2]) |
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (mask_b((*coeff)[3][0]) << 16) |
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ((*coeff)[4][0] << 30), DP_CSC_0(dp));
> + ? ? ? ? ? ? ? __raw_writel(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_SRM_PRI2) | 0x8;
> + ? ? ? ? ? ? ? ipu_cm_write(reg, IPU_SRM_PRI2);
> + ? ? ? }
> +}
> +
> +int ipu_dp_setup_channel(struct ipu_dp *dp, u32 in_pixel_fmt,
> + ? ? ? ? ? ? ? ?u32 out_pixel_fmt, int bg)
> +{
> + ? ? ? int in_fmt, out_fmt;
> + ? ? ? enum csc_type_t *csc_type;
> + ? ? ? u32 reg;
> +
> + ? ? ? if (bg)
> + ? ? ? ? ? ? ? csc_type = &bg_csc_type;
> + ? ? ? else
> + ? ? ? ? ? ? ? csc_type = &fg_csc_type;
> +
> + ? ? ? in_fmt = format_to_colorspace(in_pixel_fmt);
> + ? ? ? out_fmt = format_to_colorspace(out_pixel_fmt);
> +
> + ? ? ? if (in_fmt == RGB) {
> + ? ? ? ? ? ? ? if (out_fmt == RGB)
> + ? ? ? ? ? ? ? ? ? ? ? *csc_type = RGB2RGB;
> + ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? *csc_type = RGB2YUV;
> + ? ? ? } else {
> + ? ? ? ? ? ? ? if (out_fmt == RGB)
> + ? ? ? ? ? ? ? ? ? ? ? *csc_type = YUV2RGB;
> + ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? *csc_type = YUV2YUV;
> + ? ? ? }
> +
> + ? ? ? /* Transform color key from rgb to yuv if CSC is enabled */
> + ? ? ? reg = __raw_readl(DP_COM_CONF(dp->flow));
> + ? ? ? if (color_key_4rgb && (reg & DP_COM_CONF_GWCKE) &&
> + ? ? ? ? ? ? ? ? ? ? ? (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) ||
> + ? ? ? ? ? ? ? ? ? ? ? ?((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) ||
> + ? ? ? ? ? ? ? ? ? ? ? ?((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) ||
> + ? ? ? ? ? ? ? ? ? ? ? ?((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB)))) {
> + ? ? ? ? ? ? ? int red, green, blue;
> + ? ? ? ? ? ? ? int y, u, v;
> + ? ? ? ? ? ? ? u32 color_key = __raw_readl(DP_GRAPH_WIND_CTRL(dp->flow)) & 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 = __raw_readl(DP_GRAPH_WIND_CTRL(dp->flow)) & 0xFF000000L;
> + ? ? ? ? ? ? ? __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL(dp->flow));
> + ? ? ? ? ? ? ? color_key_4rgb = 0;
> +
> + ? ? ? ? ? ? ? dev_dbg(ipu_dev, "_ipu_dp_init color key change to yuv fmt 0x%x!\n", color_key);
> + ? ? ? }
> +
> + ? ? ? __ipu_dp_csc_setup(dp->flow, dp_csc_array[bg_csc_type][fg_csc_type], true);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_dp_setup_channel);
> +
> +int ipu_dp_enable_channel(struct ipu_dp *dp)
> +{
> + ? ? ? mutex_lock(&dp_mutex);
> +
> + ? ? ? if (!dp_use_count)
> + ? ? ? ? ? ? ? ipu_module_enable(IPU_CONF_DP_EN);
> +
> + ? ? ? dp_use_count++;
> +
> + ? ? ? mutex_unlock(&dp_mutex);
> +
> + ? ? ? return 0;
> +}
> +EXPORT_SYMBOL(ipu_dp_enable_channel);
> +
> +void ipu_dp_disable_channel(struct ipu_dp *dp)
> +{
> + ? ? ? mutex_lock(&dp_mutex);
> +
> + ? ? ? dp_use_count--;
> +
> + ? ? ? if (!dp_use_count)
> + ? ? ? ? ? ? ? ipu_module_disable(IPU_CONF_DP_EN);
> +
> + ? ? ? if (dp_use_count < 0)
> + ? ? ? ? ? ? ? dp_use_count = 0;
> +
> + ? ? ? mutex_unlock(&dp_mutex);
> +}
> +EXPORT_SYMBOL(ipu_dp_disable_channel);
> +
> +void ipu_dp_enable_fg(struct ipu_dp *dp)
> +{
> + ? ? ? u32 reg;
> +
> + ? ? ? /* Enable FG channel */
> + ? ? ? reg = __raw_readl(DP_COM_CONF(DP_SYNC));
> + ? ? ? __raw_writel(reg | DP_COM_CONF_FG_EN, DP_COM_CONF(DP_SYNC));
> +
> + ? ? ? reg = ipu_cm_read(IPU_SRM_PRI2);
> + ? ? ? reg |= 0x8;
> + ? ? ? ipu_cm_write(reg, IPU_SRM_PRI2);
> +}
> +EXPORT_SYMBOL(ipu_dp_enable_fg);
> +
> +void ipu_dp_disable_fg(struct ipu_dp *dp)
> +{
> + ? ? ? u32 reg, csc;
> +
> + ? ? ? reg = __raw_readl(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;
> + ? ? ? __raw_writel(reg, DP_COM_CONF(DP_SYNC));
> +
> + ? ? ? reg = ipu_cm_read(IPU_SRM_PRI2) | 0x8;
> + ? ? ? ipu_cm_write(reg, IPU_SRM_PRI2);
> +}
> +EXPORT_SYMBOL(ipu_dp_disable_fg);
> +
> +struct ipu_dp *ipu_dp_get(unsigned int flow)
> +{
> + ? ? ? struct ipu_dp *dp;
> +
> + ? ? ? if (flow > 2)
> + ? ? ? ? ? ? ? return ERR_PTR(-EINVAL);
> +
> + ? ? ? dp = &ipu_dp[flow];
> +
> + ? ? ? if (dp->in_use)
> + ? ? ? ? ? ? ? return ERR_PTR(-EBUSY);
> +
> + ? ? ? dp->in_use = true;
> + ? ? ? dp->flow = ipu_flows[flow];
> +
> + ? ? ? return dp;
> +}
> +EXPORT_SYMBOL(ipu_dp_get);
> +
> +void ipu_dp_put(struct ipu_dp *dp)
> +{
> + ? ? ? dp->in_use = false;
> +}
> +EXPORT_SYMBOL(ipu_dp_put);
> +
> +int ipu_dp_init(struct platform_device *pdev, unsigned long base)
> +{
> + ? ? ? ipu_dp_base = ioremap(base, PAGE_SIZE);
> + ? ? ? if (!ipu_dp_base)
> + ? ? ? ? ? ? ? return -ENOMEM;
> +
> + ? ? ? ipu_dev = &pdev->dev;
> +
> + ? ? ? return 0;
> +}
> +
> +void ipu_dp_exit(struct platform_device *pdev)
> +{
> + ? ? ? ?iounmap(ipu_dp_base);
> +}
> diff --git a/drivers/mfd/imx-ipu-v3/ipu-prv.h b/drivers/mfd/imx-ipu-v3/ipu-prv.h
> new file mode 100644
> index 0000000..9d54ad0
> --- /dev/null
> +++ b/drivers/mfd/imx-ipu-v3/ipu-prv.h
> @@ -0,0 +1,214 @@
> +/*
> + * Copyright (c) 2010 Sascha Hauer <[email protected]>
> + * Copyright (C) 2005-2009 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 __IPU_PRV_H__
> +#define __IPU_PRV_H__
> +
> +#include <linux/types.h>
> +#include <linux/device.h>
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <mach/hardware.h>
> +
> +#define MX51_IPU_CHANNEL_CSI0 ? ? ? ? ? ? ? ? ? 0
> +#define MX51_IPU_CHANNEL_CSI1 ? ? ? ? ? ? ? ? ? 1
> +#define MX51_IPU_CHANNEL_CSI2 ? ? ? ? ? ? ? ? ? 2
> +#define MX51_IPU_CHANNEL_CSI3 ? ? ? ? ? ? ? ? ? 3
> +#define MX51_IPU_CHANNEL_MEM_BG_SYNC ? ? ? ? ? 23
> +#define MX51_IPU_CHANNEL_MEM_FG_SYNC ? ? ? ? ? 27
> +#define MX51_IPU_CHANNEL_MEM_DC_SYNC ? ? ? ? ? 28
> +#define MX51_IPU_CHANNEL_MEM_FG_SYNC_ALPHA ? ? 31
> +#define MX51_IPU_CHANNEL_MEM_DC_ASYNC ? ? ? ? ?41
> +#define MX51_IPU_CHANNEL_ROT_ENC_MEM ? ? ? ? ? 45
> +#define MX51_IPU_CHANNEL_ROT_VF_MEM ? ? ? ? ? ?46
> +#define MX51_IPU_CHANNEL_ROT_PP_MEM ? ? ? ? ? ?47
> +#define MX51_IPU_CHANNEL_ROT_ENC_MEM_OUT ? ? ? 48
> +#define MX51_IPU_CHANNEL_ROT_VF_MEM_OUT ? ? ? ? ? ? ? ?49
> +#define MX51_IPU_CHANNEL_ROT_PP_MEM_OUT ? ? ? ? ? ? ? ?50
> +#define MX51_IPU_CHANNEL_MEM_BG_SYNC_ALPHA ? ? 51
> +
> +#define IPU_DISP0_BASE ? ? ? ? 0x00000000
> +#define IPU_MCU_T_DEFAULT ? ? ?8
> +#define IPU_DISP1_BASE ? ? ? ? (IPU_MCU_T_DEFAULT << 25)
> +#define IPU_CM_REG_BASE ? ? ? ? ? ? ? ?0x1e000000
> +#define IPU_IDMAC_REG_BASE ? ? 0x1e008000
> +#define IPU_ISP_REG_BASE ? ? ? 0x1e010000
> +#define IPU_DP_REG_BASE ? ? ? ? ? ? ? ?0x1e018000
> +#define IPU_IC_REG_BASE ? ? ? ? ? ? ? ?0x1e020000
> +#define IPU_IRT_REG_BASE ? ? ? 0x1e028000
> +#define IPU_CSI0_REG_BASE ? ? ?0x1e030000
> +#define IPU_CSI1_REG_BASE ? ? ?0x1e038000
> +#define IPU_DI0_REG_BASE ? ? ? 0x1e040000
> +#define IPU_DI1_REG_BASE ? ? ? 0x1e048000
> +#define IPU_SMFC_REG_BASE ? ? ?0x1e050000
> +#define IPU_DC_REG_BASE ? ? ? ? ? ? ? ?0x1e058000
> +#define IPU_DMFC_REG_BASE ? ? ?0x1e060000
> +#define IPU_CPMEM_REG_BASE ? ? 0x1f000000
> +#define IPU_LUT_REG_BASE ? ? ? 0x1f020000
> +#define IPU_SRM_REG_BASE ? ? ? 0x1f040000
> +#define IPU_TPM_REG_BASE ? ? ? 0x1f060000
> +#define IPU_DC_TMPL_REG_BASE ? 0x1f080000
> +#define IPU_ISP_TBPR_REG_BASE ?0x1f0c0000
> +#define IPU_VDI_REG_BASE ? ? ? 0x1e068000
> +
> +/* 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))
> +#define IPU_CHA_CUR_BUF(ch) ? ? ? ? ? ?IPU_CM_REG(0x023C + 4 * ((ch) / 32))
> +#define IPU_ALT_CUR_BUF0 ? ? ? ? ? ? ? IPU_CM_REG(0x0244)
> +#define IPU_ALT_CUR_BUF1 ? ? ? ? ? ? ? IPU_CM_REG(0x0248)
> +#define IPU_SRM_STAT ? ? ? ? ? ? ? ? ? IPU_CM_REG(0x024C)
> +#define IPU_PROC_TASK_STAT ? ? ? ? ? ? IPU_CM_REG(0x0250)
> +#define IPU_DISP_TASK_STAT ? ? ? ? ? ? IPU_CM_REG(0x0254)
> +#define IPU_CHA_BUF0_RDY(ch) ? ? ? ? ? IPU_CM_REG(0x0268 + 4 * ((ch) / 32))
> +#define IPU_CHA_BUF1_RDY(ch) ? ? ? ? ? IPU_CM_REG(0x0270 + 4 * ((ch) / 32))
> +#define IPU_ALT_CHA_BUF0_RDY(ch) ? ? ? IPU_CM_REG(0x0278 + 4 * ((ch) / 32))
> +#define IPU_ALT_CHA_BUF1_RDY(ch) ? ? ? IPU_CM_REG(0x0280 + 4 * ((ch) / 32))
> +
> +#define IPU_INT_CTRL(n) ? ? ? ? ? ? ? ?IPU_CM_REG(0x003C + 4 * ((n) - 1))
> +#define IPU_INT_CTRL_IRQ(irq) ?IPU_INT_CTRL(((irq) / 32))
> +#define IPU_INT_STAT_IRQ(irq) ?IPU_INT_STAT(((irq) / 32))
> +#define IPU_INT_STAT(n) ? ? ? ? ? ? ? ?IPU_CM_REG(0x0200 + 4 * ((n) - 1))
> +
> +extern void __iomem *ipu_cm_reg;
> +
> +static inline u32 ipu_cm_read(unsigned offset)
> +{
> + ? ? ? return __raw_readl(ipu_cm_reg + offset);
> +}
> +
> +static inline void ipu_cm_write(u32 value, unsigned offset)
> +{
> + ? ? ? __raw_writel(value, ipu_cm_reg + offset);
> +}
> +
> +#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 ? ? ? ? ? ? IPU_IDMAC_REG(0x0024)
> +#define IDMAC_CH_LOCK_EN_2 ? ? ? ? ? ? IPU_IDMAC_REG(0x0028)
> +#define IDMAC_SUB_ADDR_0 ? ? ? ? ? ? ? IPU_IDMAC_REG(0x002c)
> +#define IDMAC_SUB_ADDR_1 ? ? ? ? ? ? ? IPU_IDMAC_REG(0x0030)
> +#define IDMAC_SUB_ADDR_2 ? ? ? ? ? ? ? IPU_IDMAC_REG(0x0034)
> +#define IDMAC_BAND_EN(ch) ? ? ? ? ? ? ?IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32))
> +#define IDMAC_CHA_BUSY(ch) ? ? ? ? ? ? IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32))
> +
> +extern void __iomem *ipu_idmac_reg;
> +
> +static inline u32 ipu_idmac_read(unsigned offset)
> +{
> + ? ? ? return __raw_readl(ipu_idmac_reg + offset);
> +}
> +
> +static inline void ipu_idmac_write(u32 value, unsigned offset)
> +{
> + ? ? ? __raw_writel(value, ipu_idmac_reg + offset);
> +}
> +
> +#define idmac_idma_is_set(reg, dma) ? ?(ipu_idmac_read(reg(dma)) & idma_mask(dma))
> +#define idma_mask(ch) ? ? ? ? ? ? ? ? ?(1 << (ch & 0x1f))
> +#define ipu_idma_is_set(reg, dma) ? ? ?(ipu_cm_read(reg(dma)) & idma_mask(dma))
> +
> +enum ipu_modules {
> + ? ? ? IPU_CONF_CSI0_EN ? ? ? ? ? ? ? ?= (1 << 0),
> + ? ? ? IPU_CONF_CSI1_EN ? ? ? ? ? ? ? ?= (1 << 1),
> + ? ? ? IPU_CONF_IC_EN ? ? ? ? ? ? ? ? ?= (1 << 2),
> + ? ? ? IPU_CONF_ROT_EN ? ? ? ? ? ? ? ? = (1 << 3),
> + ? ? ? IPU_CONF_ISP_EN ? ? ? ? ? ? ? ? = (1 << 4),
> + ? ? ? IPU_CONF_DP_EN ? ? ? ? ? ? ? ? ?= (1 << 5),
> + ? ? ? IPU_CONF_DI0_EN ? ? ? ? ? ? ? ? = (1 << 6),
> + ? ? ? IPU_CONF_DI1_EN ? ? ? ? ? ? ? ? = (1 << 7),
> + ? ? ? IPU_CONF_SMFC_EN ? ? ? ? ? ? ? ?= (1 << 8),
> + ? ? ? IPU_CONF_DC_EN ? ? ? ? ? ? ? ? ?= (1 << 9),
> + ? ? ? IPU_CONF_DMFC_EN ? ? ? ? ? ? ? ?= (1 << 10),
> +
> + ? ? ? IPU_CONF_VDI_EN ? ? ? ? ? ? ? ? = (1 << 12),
> +
> + ? ? ? IPU_CONF_IDMAC_DIS ? ? ? ? ? ? ?= (1 << 22),
> +
> + ? ? ? IPU_CONF_IC_DMFC_SEL ? ? ? ? ? ?= (1 << 25),
> + ? ? ? IPU_CONF_IC_DMFC_SYNC ? ? ? ? ? = (1 << 26),
> + ? ? ? IPU_CONF_VDI_DMFC_SYNC ? ? ? ? ?= (1 << 27),
> +
> + ? ? ? IPU_CONF_CSI0_DATA_SOURCE ? ? ? = (1 << 28),
> + ? ? ? IPU_CONF_CSI1_DATA_SOURCE ? ? ? = (1 << 29),
> + ? ? ? IPU_CONF_IC_INPUT ? ? ? ? ? ? ? = (1 << 30),
> + ? ? ? IPU_CONF_CSI_SEL ? ? ? ? ? ? ? ?= (1 << 31),
> +};
> +
> +struct ipu_channel {
> + ? ? ? unsigned int num;
> +
> + ? ? ? bool enabled;
> + ? ? ? bool busy;
> +};
> +
> +ipu_color_space_t format_to_colorspace(u32 fmt);
> +bool ipu_pixel_format_has_alpha(u32 fmt);
> +
> +u32 _ipu_channel_status(struct ipu_channel *channel);
> +
> +int _ipu_chan_is_interlaced(struct ipu_channel *channel);
> +
> +int ipu_module_enable(u32 mask);
> +int ipu_module_disable(u32 mask);
> +
> +int ipu_di_init(struct platform_device *pdev, int id, unsigned long base,
> + ? ? ? ? ? ? ? u32 module, struct clk *ipu_clk);
> +void ipu_di_exit(struct platform_device *pdev, int id);
> +
> +int ipu_dmfc_init(struct platform_device *pdev, unsigned long base,
> + ? ? ? ? ? ? ? struct clk *ipu_clk);
> +void ipu_dmfc_exit(struct platform_device *pdev);
> +
> +int ipu_dp_init(struct platform_device *pdev, unsigned long base);
> +void ipu_dp_exit(struct platform_device *pdev);
> +
> +int ipu_dc_init(struct platform_device *pdev, unsigned long base,
> + ? ? ? ? ? ? ? unsigned long template_base);
> +void ipu_dc_exit(struct platform_device *pdev);
> +
> +int ipu_cpmem_init(struct platform_device *pdev, unsigned long base);
> +void ipu_cpmem_exit(struct platform_device *pdev);
> +
> +#endif ? ? ? ? ? ? ? ? ? ? ? ? /* __IPU_PRV_H__ */
> diff --git a/include/linux/mfd/imx-ipu-v3.h b/include/linux/mfd/imx-ipu-v3.h
> new file mode 100644
> index 0000000..0288c51
> --- /dev/null
> +++ b/include/linux/mfd/imx-ipu-v3.h
> @@ -0,0 +1,218 @@
> +/*
> + * Copyright 2005-2009 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
> + */
> +
> +#ifndef __ASM_ARCH_IPU_H__
> +#define __ASM_ARCH_IPU_H__
> +
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +#include <linux/interrupt.h>
> +#include <linux/bitmap.h>
> +
> +/*
> + * 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;
> +
> +/*
> + * IPU Pixel Formats
> + *
> + * Pixel formats are defined with ASCII FOURCC code. The pixel format codes are
> + * the same used by V4L2 API.
> + */
> +
> +/* Generic or Raw Data Formats */
> +#define IPU_PIX_FMT_GENERIC v4l2_fourcc('I', 'P', 'U', '0') ? ?/* IPU Generic Data */
> +#define IPU_PIX_FMT_GENERIC_32 v4l2_fourcc('I', 'P', 'U', '1') /* IPU Generic Data */
> +#define IPU_PIX_FMT_LVDS666 v4l2_fourcc('L', 'V', 'D', '6') ? ?/* IPU Generic Data */
> +#define IPU_PIX_FMT_LVDS888 v4l2_fourcc('L', 'V', 'D', '8') ? ?/* IPU Generic Data */
> +/* RGB Formats */
> +#define IPU_PIX_FMT_RGB332 ?V4L2_PIX_FMT_RGB332 ? ? ? ? ? ? ? ? ? ? ? ?/* ?8 ?RGB-3-3-2 ? ?*/
> +#define IPU_PIX_FMT_RGB555 ?V4L2_PIX_FMT_RGB555 ? ? ? ? ? ? ? ? ? ? ? ?/* 16 ?RGB-5-5-5 ? ?*/
> +#define IPU_PIX_FMT_RGB565 ?V4L2_PIX_FMT_RGB565 ? ? ? ? ? ? ? ? ? ? ? ?/* 1 6 ?RGB-5-6-5 ? */
> +#define IPU_PIX_FMT_RGB666 ?v4l2_fourcc('R', 'G', 'B', '6') ? ?/* 18 ?RGB-6-6-6 ? ?*/
> +#define IPU_PIX_FMT_BGR666 ?v4l2_fourcc('B', 'G', 'R', '6') ? ?/* 18 ?BGR-6-6-6 ? ?*/
> +#define IPU_PIX_FMT_BGR24 ? V4L2_PIX_FMT_BGR24 ? ? ? ? ? ? ? ? /* 24 ?BGR-8-8-8 ? ?*/
> +#define IPU_PIX_FMT_RGB24 ? V4L2_PIX_FMT_RGB24 ? ? ? ? ? ? ? ? /* 24 ?RGB-8-8-8 ? ?*/
> +#define IPU_PIX_FMT_BGR32 ? V4L2_PIX_FMT_BGR32 ? ? ? ? ? ? ? ? /* 32 ?BGR-8-8-8-8 ?*/
> +#define IPU_PIX_FMT_BGRA32 ?v4l2_fourcc('B', 'G', 'R', 'A') ? ?/* 32 ?BGR-8-8-8-8 ?*/
> +#define IPU_PIX_FMT_RGB32 ? V4L2_PIX_FMT_RGB32 ? ? ? ? ? ? ? ? /* 32 ?RGB-8-8-8-8 ?*/
> +#define IPU_PIX_FMT_RGBA32 ?v4l2_fourcc('R', 'G', 'B', 'A') ? ?/* 32 ?RGB-8-8-8-8 ?*/
> +#define IPU_PIX_FMT_ABGR32 ?v4l2_fourcc('A', 'B', 'G', 'R') ? ?/* 32 ?ABGR-8-8-8-8 */
> +/* YUV Interleaved Formats */
> +#define IPU_PIX_FMT_YUYV ? ?V4L2_PIX_FMT_YUYV ? ? ? ? ? ? ? ? ?/* 16 YUV 4:2:2 */
> +#define IPU_PIX_FMT_UYVY ? ?V4L2_PIX_FMT_UYVY ? ? ? ? ? ? ? ? ?/* 16 YUV 4:2:2 */
> +#define IPU_PIX_FMT_Y41P ? ?V4L2_PIX_FMT_Y41P ? ? ? ? ? ? ? ? ?/* 12 YUV 4:1:1 */
> +#define IPU_PIX_FMT_YUV444 ?V4L2_PIX_FMT_YUV444 ? ? ? ? ? ? ? ? ? ? ? ?/* 24 YUV 4:4:4 */
> +/* two planes -- one Y, one Cb + Cr interleaved ?*/
> +#define IPU_PIX_FMT_NV12 ? ?V4L2_PIX_FMT_NV12 ? ? ? ? ? ? ? ? ?/* 12 ?Y/CbCr 4:2:0 ?*/
> +/* YUV Planar Formats */
> +#define IPU_PIX_FMT_GREY ? ?V4L2_PIX_FMT_GREY ? ? ? ? ? ? ? ? ?/* 8 ?Greyscale */
> +#define IPU_PIX_FMT_YVU410P V4L2_PIX_FMT_YVU410P ? ? ? ? ? ? ? /* 9 ?YVU 4:1:0 */
> +#define IPU_PIX_FMT_YUV410P V4L2_PIX_FMT_YUV410P ? ? ? ? ? ? ? /* 9 ?YUV 4:1:0 */
> +#define IPU_PIX_FMT_YVU420P v4l2_fourcc('Y', 'V', '1', '2') ? ?/* 12 YVU 4:2:0 */
> +#define IPU_PIX_FMT_YUV420P v4l2_fourcc('I', '4', '2', '0') ? ?/* 12 YUV 4:2:0 */
> +#define IPU_PIX_FMT_YUV420P2 v4l2_fourcc('Y', 'U', '1', '2') ? /* 12 YUV 4:2:0 */
> +#define IPU_PIX_FMT_YVU422P v4l2_fourcc('Y', 'V', '1', '6') ? ?/* 16 YVU 4:2:2 */
> +#define IPU_PIX_FMT_YUV422P V4L2_PIX_FMT_YUV422P ? ? ? ? ? ? ? /* 16 YUV 4:2:2 */
> +
> +/*
> + * Bitfield of Display Interface signal polarities.
> + */
> +struct ipu_di_signal_cfg {
> + ? ? ? unsigned datamask_en:1;
> + ? ? ? unsigned ext_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;
> +
> + ? ? ? u16 width;
> + ? ? ? u16 height;
> + ? ? ? u32 pixel_fmt;
> + ? ? ? u16 h_start_width;
> + ? ? ? u16 h_sync_width;
> + ? ? ? u16 h_end_width;
> + ? ? ? u16 v_start_width;
> + ? ? ? u16 v_sync_width;
> + ? ? ? u16 v_end_width;
> + ? ? ? u32 v_to_h_sync;
> +};
> +
> +typedef enum {
> + ? ? ? RGB,
> + ? ? ? YCbCr,
> + ? ? ? YUV
> +} ipu_color_space_t;
> +
> +#define IPU_IRQ_EOF(channel) ? ? ? ? ? (channel) ? ? ? ? ? ? ? /* 0 .. 63 */
> +#define IPU_IRQ_NFACK(channel) ? ? ? ? ((channel) + 64) ? ? ? ?/* 64 .. 127 */
> +#define IPU_IRQ_NFB4EOF(channel) ? ? ? ((channel) + 128) ? ? ? /* 128 .. 191 */
> +#define IPU_IRQ_EOS(channel) ? ? ? ? ? ((channel) + 192) ? ? ? /* 192 .. 255 */
> +
> +#define IPU_IRQ_DP_SF_START ? ? ? ? ? ?(448 + 2)
> +#define IPU_IRQ_DP_SF_END ? ? ? ? ? ? ?(448 + 3)
> +#define IPU_IRQ_BG_SF_END ? ? ? ? ? ? ?IPU_IRQ_DP_SF_END,
> +#define IPU_IRQ_DC_FC_0 ? ? ? ? ? ? ? ? ? ? ? ?(448 + 8)
> +#define IPU_IRQ_DC_FC_1 ? ? ? ? ? ? ? ? ? ? ? ?(448 + 9)
> +#define IPU_IRQ_DC_FC_2 ? ? ? ? ? ? ? ? ? ? ? ?(448 + 10)
> +#define IPU_IRQ_DC_FC_3 ? ? ? ? ? ? ? ? ? ? ? ?(448 + 11)
> +#define IPU_IRQ_DC_FC_4 ? ? ? ? ? ? ? ? ? ? ? ?(448 + 12)
> +#define IPU_IRQ_DC_FC_6 ? ? ? ? ? ? ? ? ? ? ? ?(448 + 13)
> +#define IPU_IRQ_VSYNC_PRE_0 ? ? ? ? ? ?(448 + 14)
> +#define IPU_IRQ_VSYNC_PRE_1 ? ? ? ? ? ?(448 + 15)
> +
> +#define IPU_IRQ_COUNT ?(15 * 32)
> +
> +#define DECLARE_IPU_IRQ_BITMAP(name) ? DECLARE_BITMAP(name, IPU_IRQ_COUNT)
> +
> +struct ipu_irq_handler {
> + ? ? ? struct list_head ? ? ? ?list;
> + ? ? ? void ? ? ? ? ? ? ? ? ? ?(*handler)(unsigned long *, void *);
> + ? ? ? void ? ? ? ? ? ? ? ? ? ?*context;
> + ? ? ? DECLARE_IPU_IRQ_BITMAP(ipu_irqs);
> +};
> +
> +int ipu_irq_add_handler(struct ipu_irq_handler *ipuirq);
> +void ipu_irq_remove_handler(struct ipu_irq_handler *handler);
> +int ipu_irq_update_handler(struct ipu_irq_handler *handler,
> + ? ? ? ? ? ? ? unsigned long *bitmap);
> +int ipu_wait_for_interrupt(int interrupt, int timeout_ms);
> +
> +struct ipu_channel;
> +
> +/*
> + * IPU Image DMA Controller (idmac) functions
> + */
> +struct ipu_channel *ipu_idmac_get(unsigned channel);
> +void ipu_idmac_put(struct ipu_channel *);
> +int ipu_idmac_init_channel_buffer(struct ipu_channel *channel,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u32 pixel_fmt,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u16 width, u16 height,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u32 stride,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ipu_rotate_mode_t rot_mode,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u32 u_offset, u32 v_offset, bool interlaced);
> +
> +int ipu_idmac_update_channel_buffer(struct ipu_channel *channel,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u32 bufNum, dma_addr_t phyaddr);
> +
> +int ipu_idmac_enable_channel(struct ipu_channel *channel);
> +int ipu_idmac_disable_channel(struct ipu_channel *channel);
> +
> +void ipu_idmac_set_double_buffer(struct ipu_channel *channel, bool doublebuffer);
> +void ipu_idmac_select_buffer(struct ipu_channel *channel, u32 buf_num);
> +
> +/*
> + * IPU Display Controller (dc) functions
> + */
> +int ipu_dc_init_sync(int dc_chan, int di, bool interlaced, u32 pixel_fmt, u32 width);
> +void ipu_dc_init_async(int dc_chan, int di, bool interlaced);
> +void ipu_dc_enable_channel(u32 dc_chan);
> +void ipu_dc_disable_channel(u32 dc_chan);
> +
> +/*
> + * IPU Display Interface (di) functions
> + */
> +int ipu_di_disable(int disp);
> +int ipu_di_enable(int disp);
> +int ipu_di_init_sync_panel(int disp, u32 pixel_clk,
> + ? ? ? ? ? ? ? struct ipu_di_signal_cfg *sig);
> +
> +/*
> + * IPU Display Multi FIFO Controller (dmfc) functions
> + */
> +struct dmfc_channel;
> +int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc);
> +void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc);
> +int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc, unsigned long bandwidth_mbs);
> +void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc);
> +int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width);
> +struct dmfc_channel *ipu_dmfc_get(int ipu_channel);
> +void ipu_dmfc_put(struct dmfc_channel *dmfc);
> +
> +/*
> + * IPU Display Processor (dp) functions
> + */
> +#define IPU_DP_FLOW_SYNC ? ? ? 0
> +#define IPU_DP_FLOW_ASYNC0 ? ? 1
> +#define IPU_DP_FLOW_ASYNC1 ? ? 2
> +
> +struct ipu_dp *ipu_dp_get(unsigned int flow);
> +void ipu_dp_put(struct ipu_dp *);
> +int ipu_dp_enable_channel(struct ipu_dp *dp);
> +void ipu_dp_disable_channel(struct ipu_dp *dp);
> +void ipu_dp_enable_fg(struct ipu_dp *dp);
> +void ipu_dp_disable_fg(struct ipu_dp *dp);
> +int ipu_dp_setup_channel(struct ipu_dp *dp, u32 in_pixel_fmt,
> + ? ? ? ? ? ? ? ?u32 out_pixel_fmt, int bg);
> +int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos);
> +int ipu_dp_set_color_key(struct ipu_dp *dp, bool enable, u32 colorKey);
> +int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha,
> + ? ? ? ? ? ? ? bool bg_chan);
> +
> +#endif
> --
> 1.7.2.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at ?http://www.tux.org/lkml/
>

2010-12-12 06:13:45

by Liu Ying

[permalink] [raw]
Subject: Re: [PATCH 5/9] Add i.MX5 framebuffer driver

Hello, Sascha,

I have following comments to this patch:
1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.
2) ADC is not supported yet in the framebuffer driver, so please
modify this comment:
> + * Framebuffer Framebuffer Driver for SDC and ADC.
3) 'ipu_dp_set_window_pos()' is called only once in
imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
support to change the overlay framebuffer position. Need a
mechanism/interface for users to change the overlay framebuffer
position.
4) Need to make sure the framebuffer on DP-FG is blanked before the
framebuffer on DP-BG is blanked. Meanwhile, the framebuffer on DP-FG
should be unblanked after the framebuffer on DP-BG is unblanked
5) Need to check the framebuffer on DP-FG doesn't run out of the range
of the framebuffer on DP-BG.
6) I prefer to find the video mode in modedb first, and if we cannot
find the video mode in common video mode data base, we can find a
video mode in custom video mode data base which is defined in platform
data. In this way, we don't need to export common modefb.

Best Regards,
Liu Ying


2010/12/9 Sascha Hauer <[email protected]>:
> This patch adds framebuffer support to the Freescale i.MX SoCs
> equipped with an IPU v3, so far these are the i.MX50/51/53.
>
> This driver has been tested on the i.MX51 babbage board with
> both DVI and analog VGA in different resolutions and color depths.
> It has also been tested on a custom i.MX51 board using a fixed
> resolution panel.
>
> Signed-off-by: Sascha Hauer <[email protected]>
> ---
> ?drivers/video/Kconfig ?| ? 11 +
> ?drivers/video/Makefile | ? ?1 +
> ?drivers/video/mx5fb.c ?| ?846 ++++++++++++++++++++++++++++++++++++++++++++++++
> ?3 files changed, 858 insertions(+), 0 deletions(-)
> ?create mode 100644 drivers/video/mx5fb.c
>
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 27c1fb4..1901915 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -2236,6 +2236,17 @@ config FB_MX3
> ? ? ? ? ?far only synchronous displays are supported. If you plan to use
> ? ? ? ? ?an LCD display with your i.MX31 system, say Y here.
>
> +config FB_MX5
> + ? ? ? tristate "MX5 Framebuffer support"
> + ? ? ? depends on FB && MFD_IMX_IPU_V3
> + ? ? ? select FB_CFB_FILLRECT
> + ? ? ? select FB_CFB_COPYAREA
> + ? ? ? select FB_CFB_IMAGEBLIT
> + ? ? ? select FB_MODE_HELPERS
> + ? ? ? help
> + ? ? ? ? This is a framebuffer device for the i.MX51 LCD Controller. If you
> + ? ? ? ? plan to use an LCD display with your i.MX51 system, say Y here.
> +
> ?config FB_BROADSHEET
> ? ? ? ?tristate "E-Ink Broadsheet/Epson S1D13521 controller support"
> ? ? ? ?depends on FB
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 485e8ed..ad408d2 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -145,6 +145,7 @@ obj-$(CONFIG_FB_BF54X_LQ043) ? ? ? ? ?+= bf54x-lq043fb.o
> ?obj-$(CONFIG_FB_BFIN_LQ035Q1) ? ? += bfin-lq035q1-fb.o
> ?obj-$(CONFIG_FB_BFIN_T350MCQB) ? += bfin-t350mcqb-fb.o
> ?obj-$(CONFIG_FB_MX3) ? ? ? ? ? ? += mx3fb.o
> +obj-$(CONFIG_FB_MX5) ? ? ? ? ? ? += mx5fb.o
> ?obj-$(CONFIG_FB_DA8XX) ? ? ? ? ? += da8xx-fb.o
>
> ?# the test framebuffer is last
> diff --git a/drivers/video/mx5fb.c b/drivers/video/mx5fb.c
> new file mode 100644
> index 0000000..fd9baf4
> --- /dev/null
> +++ b/drivers/video/mx5fb.c
> @@ -0,0 +1,846 @@
> +/*
> + * Copyright 2004-2009 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
> + *
> + * Framebuffer Framebuffer Driver for SDC and ADC.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +#include <linux/fb.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/console.h>
> +#include <linux/mfd/imx-ipu-v3.h>
> +#include <asm/uaccess.h>
> +#include <mach/ipu-v3.h>
> +
> +#define DRIVER_NAME "imx-ipuv3-fb"
> +
> +struct imx_ipu_fb_info {
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? ipu_channel_num;
> + ? ? ? struct ipu_channel ? ? ?*ipu_ch;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? dc;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? ipu_di;
> + ? ? ? u32 ? ? ? ? ? ? ? ? ? ? ipu_di_pix_fmt;
> + ? ? ? u32 ? ? ? ? ? ? ? ? ? ? ipu_in_pix_fmt;
> +
> + ? ? ? u32 ? ? ? ? ? ? ? ? ? ? pseudo_palette[16];
> +
> + ? ? ? struct ipu_dp ? ? ? ? ? *dp;
> + ? ? ? struct dmfc_channel ? ? *dmfc;
> + ? ? ? struct fb_info ? ? ? ? ?*slave;
> + ? ? ? struct fb_info ? ? ? ? ?*master;
> + ? ? ? bool ? ? ? ? ? ? ? ? ? ?enabled;
> +};
> +
> +static int imx_ipu_fb_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->ypanstep = 1;
> +
> + ? ? ? return 0;
> +}
> +
> +static int imx_ipu_fb_map_video_memory(struct fb_info *fbi)
> +{
> + ? ? ? int size;
> +
> + ? ? ? size = fbi->var.yres_virtual * fbi->fix.line_length;
> +
> + ? ? ? if (fbi->screen_base) {
> + ? ? ? ? ? ? ? if (fbi->fix.smem_len >= size)
> + ? ? ? ? ? ? ? ? ? ? ? return 0;
> +
> + ? ? ? ? ? ? ? dma_free_writecombine(fbi->device, fbi->fix.smem_len,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? fbi->screen_base, fbi->fix.smem_start);
> + ? ? ? }
> +
> + ? ? ? fbi->screen_base = dma_alloc_writecombine(fbi->device,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? size,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (dma_addr_t *)&fbi->fix.smem_start,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GFP_DMA);
> + ? ? ? if (fbi->screen_base == 0) {
> + ? ? ? ? ? ? ? dev_err(fbi->device, "Unable to allocate framebuffer memory (%d)\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fbi->fix.smem_len);
> + ? ? ? ? ? ? ? fbi->fix.smem_len = 0;
> + ? ? ? ? ? ? ? fbi->fix.smem_start = 0;
> + ? ? ? ? ? ? ? return -ENOMEM;
> + ? ? ? }
> +
> + ? ? ? fbi->fix.smem_len = size;
> + ? ? ? fbi->screen_size = fbi->fix.smem_len;
> +
> + ? ? ? dev_dbg(fbi->device, "allocated fb @ paddr=0x%08lx, size=%d\n",
> + ? ? ? ? ? ? ? fbi->fix.smem_start, fbi->fix.smem_len);
> +
> + ? ? ? /* Clear the screen */
> + ? ? ? memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
> +
> + ? ? ? return 0;
> +}
> +
> +static void imx_ipu_fb_enable(struct fb_info *fbi)
> +{
> + ? ? ? struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +
> + ? ? ? if (mxc_fbi->enabled)
> + ? ? ? ? ? ? ? return;
> +
> + ? ? ? ipu_di_enable(mxc_fbi->ipu_di);
> + ? ? ? ipu_dmfc_enable_channel(mxc_fbi->dmfc);
> + ? ? ? ipu_idmac_enable_channel(mxc_fbi->ipu_ch);
> + ? ? ? ipu_dc_enable_channel(mxc_fbi->dc);
> + ? ? ? ipu_dp_enable_channel(mxc_fbi->dp);
> + ? ? ? mxc_fbi->enabled = 1;
> +}
> +
> +static void imx_ipu_fb_disable(struct fb_info *fbi)
> +{
> + ? ? ? struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +
> + ? ? ? if (!mxc_fbi->enabled)
> + ? ? ? ? ? ? ? return;
> +
> + ? ? ? ipu_dp_disable_channel(mxc_fbi->dp);
> + ? ? ? ipu_dc_disable_channel(mxc_fbi->dc);
> + ? ? ? ipu_idmac_disable_channel(mxc_fbi->ipu_ch);
> + ? ? ? ipu_dmfc_disable_channel(mxc_fbi->dmfc);
> + ? ? ? ipu_di_disable(mxc_fbi->ipu_di);
> +
> + ? ? ? mxc_fbi->enabled = 0;
> +}
> +
> +static int calc_vref(struct fb_var_screeninfo *var)
> +{
> + ? ? ? unsigned long htotal, vtotal;
> +
> + ? ? ? htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin;
> + ? ? ? vtotal = var->yres + var->lower_margin + var->vsync_len + var->upper_margin;
> +
> + ? ? ? if (!htotal || !vtotal)
> + ? ? ? ? ? ? ? return 60;
> +
> + ? ? ? return PICOS2KHZ(var->pixclock) * 1000 / vtotal / htotal;
> +}
> +
> +static int calc_bandwidth(struct fb_var_screeninfo *var, unsigned int vref)
> +{
> + ? ? ? return var->xres * var->yres * vref;
> +}
> +
> +static int imx_ipu_fb_set_par(struct fb_info *fbi)
> +{
> + ? ? ? int ret;
> + ? ? ? struct ipu_di_signal_cfg sig_cfg;
> + ? ? ? struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> + ? ? ? u32 out_pixel_fmt;
> + ? ? ? int interlaced = 0;
> + ? ? ? struct fb_var_screeninfo *var = &fbi->var;
> + ? ? ? int enabled = mxc_fbi->enabled;
> +
> + ? ? ? dev_dbg(fbi->device, "Reconfiguring framebuffer %dx%d-%d\n",
> + ? ? ? ? ? ? ? fbi->var.xres, fbi->var.yres, fbi->var.bits_per_pixel);
> +
> + ? ? ? if (enabled)
> + ? ? ? ? ? ? ? imx_ipu_fb_disable(fbi);
> +
> + ? ? ? fbi->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
> +
> + ? ? ? var->yres_virtual = var->yres;
> +
> + ? ? ? ret = imx_ipu_fb_map_video_memory(fbi);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? return ret;
> +
> + ? ? ? if (var->vmode & FB_VMODE_INTERLACED)
> + ? ? ? ? ? ? ? interlaced = 1;
> +
> + ? ? ? memset(&sig_cfg, 0, sizeof(sig_cfg));
> + ? ? ? out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
> +
> + ? ? ? if (var->vmode & FB_VMODE_INTERLACED)
> + ? ? ? ? ? ? ? sig_cfg.interlaced = 1;
> + ? ? ? if (var->vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
> + ? ? ? ? ? ? ? sig_cfg.odd_field_first = 1;
> + ? ? ? if (var->sync & FB_SYNC_EXT)
> + ? ? ? ? ? ? ? sig_cfg.ext_clk = 1;
> + ? ? ? if (var->sync & FB_SYNC_HOR_HIGH_ACT)
> + ? ? ? ? ? ? ? sig_cfg.Hsync_pol = 1;
> + ? ? ? if (var->sync & FB_SYNC_VERT_HIGH_ACT)
> + ? ? ? ? ? ? ? sig_cfg.Vsync_pol = 1;
> + ? ? ? if (!(var->sync & FB_SYNC_CLK_LAT_FALL))
> + ? ? ? ? ? ? ? sig_cfg.clk_pol = 1;
> + ? ? ? if (var->sync & FB_SYNC_DATA_INVERT)
> + ? ? ? ? ? ? ? sig_cfg.data_pol = 1;
> + ? ? ? if (!(var->sync & FB_SYNC_OE_LOW_ACT))
> + ? ? ? ? ? ? ? sig_cfg.enable_pol = 1;
> + ? ? ? if (var->sync & FB_SYNC_CLK_IDLE_EN)
> + ? ? ? ? ? ? ? sig_cfg.clkidle_en = 1;
> +
> + ? ? ? dev_dbg(fbi->device, "pixclock = %lu.%03lu MHz\n",
> + ? ? ? ? ? ? ? PICOS2KHZ(var->pixclock) / 1000,
> + ? ? ? ? ? ? ? PICOS2KHZ(var->pixclock) % 1000);
> +
> + ? ? ? sig_cfg.width = var->xres;
> + ? ? ? sig_cfg.height = var->yres;
> + ? ? ? sig_cfg.pixel_fmt = out_pixel_fmt;
> + ? ? ? sig_cfg.h_start_width = var->left_margin;
> + ? ? ? sig_cfg.h_sync_width = var->hsync_len;
> + ? ? ? sig_cfg.h_end_width = var->right_margin;
> + ? ? ? sig_cfg.v_start_width = var->upper_margin;
> + ? ? ? sig_cfg.v_sync_width = var->vsync_len;
> + ? ? ? sig_cfg.v_end_width = var->lower_margin;
> + ? ? ? sig_cfg.v_to_h_sync = 0;
> +
> + ? ? ? if (mxc_fbi->dp) {
> + ? ? ? ? ? ? ? ret = ipu_dp_setup_channel(mxc_fbi->dp, mxc_fbi->ipu_in_pix_fmt,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? out_pixel_fmt, 1);
> + ? ? ? ? ? ? ? if (ret) {
> + ? ? ? ? ? ? ? ? ? ? ? dev_dbg(fbi->device, "initializing display processor failed with %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret);
> + ? ? ? ? ? ? ? ? ? ? ? return ret;
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> +
> + ? ? ? ret = ipu_dc_init_sync(mxc_fbi->dc, mxc_fbi->ipu_di, interlaced,
> + ? ? ? ? ? ? ? ? ? ? ? out_pixel_fmt, fbi->var.xres);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_dbg(fbi->device, "initializing display controller failed with %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret);
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> +
> + ? ? ? ret = ipu_di_init_sync_panel(mxc_fbi->ipu_di,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PICOS2KHZ(var->pixclock) * 1000UL,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &sig_cfg);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_dbg(fbi->device, "initializing panel failed with %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret);
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> +
> + ? ? ? fbi->mode = (struct fb_videomode *)fb_match_mode(var, &fbi->modelist);
> + ? ? ? var->xoffset = var->yoffset = 0;
> +
> + ? ? ? if (fbi->var.vmode & FB_VMODE_INTERLACED)
> + ? ? ? ? ? ? ? interlaced = 1;
> +
> + ? ? ? ret = ipu_idmac_init_channel_buffer(mxc_fbi->ipu_ch,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mxc_fbi->ipu_in_pix_fmt,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? var->xres, var->yres,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fbi->fix.line_length,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IPU_ROTATE_NONE,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fbi->fix.smem_start,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0, 0, interlaced);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_dbg(fbi->device, "init channel buffer failed with %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret);
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> +
> + ? ? ? ret = ipu_dmfc_init_channel(mxc_fbi->dmfc, var->xres);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_dbg(fbi->device, "initializing dmfc channel failed with %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret);
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> +
> + ? ? ? ret = ipu_dmfc_alloc_bandwidth(mxc_fbi->dmfc, calc_bandwidth(var, calc_vref(var)));
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_dbg(fbi->device, "allocating dmfc bandwidth failed with %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret);
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> +
> + ? ? ? if (enabled)
> + ? ? ? ? ? ? ? imx_ipu_fb_enable(fbi);
> +
> + ? ? ? return ret;
> +}
> +
> +/*
> + * These are the bitfields for each
> + * display depth that we support.
> + */
> +struct imxfb_rgb {
> + ? ? ? struct fb_bitfield ? ? ?red;
> + ? ? ? struct fb_bitfield ? ? ?green;
> + ? ? ? struct fb_bitfield ? ? ?blue;
> + ? ? ? struct fb_bitfield ? ? ?transp;
> +};
> +
> +static struct imxfb_rgb def_rgb_8 = {
> + ? ? ? .red ? ?= { .offset = ?5, .length = 3, },
> + ? ? ? .green ?= { .offset = ?2, .length = 3, },
> + ? ? ? .blue ? = { .offset = ?0, .length = 2, },
> + ? ? ? .transp = { .offset = ?0, .length = 0, },
> +};
> +
> +static struct imxfb_rgb def_rgb_16 = {
> + ? ? ? .red ? ?= { .offset = 11, .length = 5, },
> + ? ? ? .green ?= { .offset = ?5, .length = 6, },
> + ? ? ? .blue ? = { .offset = ?0, .length = 5, },
> + ? ? ? .transp = { .offset = ?0, .length = 0, },
> +};
> +
> +static struct imxfb_rgb def_rgb_24 = {
> + ? ? ? .red ? ?= { .offset = 16, .length = 8, },
> + ? ? ? .green ?= { .offset = ?8, .length = 8, },
> + ? ? ? .blue ? = { .offset = ?0, .length = 8, },
> + ? ? ? .transp = { .offset = ?0, .length = 0, },
> +};
> +
> +static struct imxfb_rgb def_rgb_32 = {
> + ? ? ? .red ? ?= { .offset = 16, .length = 8, },
> + ? ? ? .green ?= { .offset = ?8, .length = 8, },
> + ? ? ? .blue ? = { .offset = ?0, .length = 8, },
> + ? ? ? .transp = { .offset = 24, .length = 8, },
> +};
> +
> +/*
> + * Check framebuffer variable parameters and adjust to valid values.
> + *
> + * @param ? ? ? var ? ? ?framebuffer variable parameters
> + *
> + * @param ? ? ? info ? ? framebuffer information pointer
> + */
> +static int imx_ipu_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
> +{
> + ? ? ? struct imx_ipu_fb_info *mxc_fbi = info->par;
> + ? ? ? struct imxfb_rgb *rgb;
> +
> + ? ? ? /* we don't support xpan, force xres_virtual to be equal to xres */
> + ? ? ? var->xres_virtual = var->xres;
> +
> + ? ? ? if (var->yres_virtual < var->yres)
> + ? ? ? ? ? ? ? var->yres_virtual = var->yres;
> +
> + ? ? ? switch (var->bits_per_pixel) {
> + ? ? ? case 8:
> + ? ? ? ? ? ? ? rgb = &def_rgb_8;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 16:
> + ? ? ? ? ? ? ? rgb = &def_rgb_16;
> + ? ? ? ? ? ? ? mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_RGB565;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 24:
> + ? ? ? ? ? ? ? rgb = &def_rgb_24;
> + ? ? ? ? ? ? ? mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR24;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 32:
> + ? ? ? ? ? ? ? rgb = &def_rgb_32;
> + ? ? ? ? ? ? ? mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR32;
> + ? ? ? ? ? ? ? break;
> + ? ? ? default:
> + ? ? ? ? ? ? ? var->bits_per_pixel = 24;
> + ? ? ? ? ? ? ? rgb = &def_rgb_24;
> + ? ? ? ? ? ? ? mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR24;
> + ? ? ? }
> +
> + ? ? ? var->red ? ?= rgb->red;
> + ? ? ? var->green ?= rgb->green;
> + ? ? ? var->blue ? = rgb->blue;
> + ? ? ? var->transp = rgb->transp;
> +
> + ? ? ? return 0;
> +}
> +
> +static inline unsigned int chan_to_field(u_int chan, struct fb_bitfield *bf)
> +{
> + ? ? ? chan &= 0xffff;
> + ? ? ? chan >>= 16 - bf->length;
> + ? ? ? return chan << bf->offset;
> +}
> +
> +static int imx_ipu_fb_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;
> +}
> +
> +static int imx_ipu_fb_blank(int blank, struct fb_info *info)
> +{
> + ? ? ? dev_dbg(info->device, "blank = %d\n", blank);
> +
> + ? ? ? switch (blank) {
> + ? ? ? case FB_BLANK_POWERDOWN:
> + ? ? ? case FB_BLANK_VSYNC_SUSPEND:
> + ? ? ? case FB_BLANK_HSYNC_SUSPEND:
> + ? ? ? case FB_BLANK_NORMAL:
> + ? ? ? ? ? ? ? imx_ipu_fb_disable(info);
> + ? ? ? ? ? ? ? break;
> + ? ? ? case FB_BLANK_UNBLANK:
> + ? ? ? ? ? ? ? imx_ipu_fb_enable(info);
> + ? ? ? ? ? ? ? break;
> + ? ? ? }
> +
> + ? ? ? return 0;
> +}
> +
> +static int imx_ipu_fb_pan_display(struct fb_var_screeninfo *var,
> + ? ? ? ? ? ? ? struct fb_info *info)
> +{
> + ? ? ? struct imx_ipu_fb_info *mxc_fbi = info->par;
> + ? ? ? unsigned long base;
> + ? ? ? int ret;
> +
> + ? ? ? if (info->var.yoffset == var->yoffset)
> + ? ? ? ? ? ? ? return 0; ? ? ? /* No change, do nothing */
> +
> + ? ? ? base = var->yoffset * var->xres_virtual * var->bits_per_pixel / 8;
> + ? ? ? base += info->fix.smem_start;
> +
> + ? ? ? ret = ipu_wait_for_interrupt(IPU_IRQ_EOF(mxc_fbi->ipu_channel_num), 100);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? return ret;
> +
> + ? ? ? if (ipu_idmac_update_channel_buffer(mxc_fbi->ipu_ch, 0, base)) {
> + ? ? ? ? ? ? ? dev_err(info->device,
> + ? ? ? ? ? ? ? ? ? ? ? "Error updating SDC buf to address=0x%08lX\n", base);
> + ? ? ? }
> +
> + ? ? ? info->var.yoffset = var->yoffset;
> +
> + ? ? ? return 0;
> +}
> +
> +static struct fb_ops imx_ipu_fb_ops = {
> + ? ? ? .owner ? ? ? ? ?= THIS_MODULE,
> + ? ? ? .fb_set_par ? ? = imx_ipu_fb_set_par,
> + ? ? ? .fb_check_var ? = imx_ipu_fb_check_var,
> + ? ? ? .fb_setcolreg ? = imx_ipu_fb_setcolreg,
> + ? ? ? .fb_pan_display = imx_ipu_fb_pan_display,
> + ? ? ? .fb_fillrect ? ?= cfb_fillrect,
> + ? ? ? .fb_copyarea ? ?= cfb_copyarea,
> + ? ? ? .fb_imageblit ? = cfb_imageblit,
> + ? ? ? .fb_blank ? ? ? = imx_ipu_fb_blank,
> +};
> +
> +/*
> + * Overlay functions
> + */
> +static int imx_ipu_fb_enable_overlay(struct fb_info *fbi)
> +{
> + ? ? ? struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +
> + ? ? ? ipu_dmfc_enable_channel(mxc_fbi->dmfc);
> + ? ? ? ipu_idmac_enable_channel(mxc_fbi->ipu_ch);
> + ? ? ? ipu_dp_enable_fg(mxc_fbi->dp);
> +
> + ? ? ? return 0;
> +}
> +
> +static int imx_ipu_fb_disable_overlay(struct fb_info *fbi)
> +{
> + ? ? ? struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +
> + ? ? ? ipu_dp_disable_fg(mxc_fbi->dp);
> + ? ? ? ipu_idmac_disable_channel(mxc_fbi->ipu_ch);
> + ? ? ? ipu_dmfc_disable_channel(mxc_fbi->dmfc);
> +
> + ? ? ? return 0;
> +}
> +
> +static int imx_ipu_fb_set_par_overlay(struct fb_info *fbi)
> +{
> + ? ? ? struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> + ? ? ? struct fb_var_screeninfo *var = &fbi->var;
> + ? ? ? struct fb_info *fbi_master = mxc_fbi->master;
> + ? ? ? struct fb_var_screeninfo *var_master = &fbi_master->var;
> + ? ? ? int ret;
> + ? ? ? int interlaced = 0;
> + ? ? ? int enabled = mxc_fbi->enabled;
> +
> + ? ? ? dev_dbg(fbi->device, "Reconfiguring framebuffer %dx%d-%d\n",
> + ? ? ? ? ? ? ? fbi->var.xres, fbi->var.yres, fbi->var.bits_per_pixel);
> +
> + ? ? ? if (enabled)
> + ? ? ? ? ? ? ? imx_ipu_fb_disable_overlay(fbi);
> +
> + ? ? ? fbi->fix.line_length = var->xres_virtual *
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?var->bits_per_pixel / 8;
> +
> + ? ? ? ret = imx_ipu_fb_map_video_memory(fbi);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? return ret;
> +
> + ? ? ? ipu_dp_set_window_pos(mxc_fbi->dp, 64, 64);
> +
> + ? ? ? var->xoffset = var->yoffset = 0;
> +
> + ? ? ? if (var->vmode & FB_VMODE_INTERLACED)
> + ? ? ? ? ? ? ? interlaced = 1;
> +
> + ? ? ? ret = ipu_idmac_init_channel_buffer(mxc_fbi->ipu_ch,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mxc_fbi->ipu_in_pix_fmt,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? var->xres, var->yres,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fbi->fix.line_length,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IPU_ROTATE_NONE,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fbi->fix.smem_start,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0, 0, interlaced);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_dbg(fbi->device, "init channel buffer failed with %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret);
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> +
> + ? ? ? ret = ipu_dmfc_init_channel(mxc_fbi->dmfc, var->xres);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_dbg(fbi->device, "initializing dmfc channel failed with %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret);
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> +
> + ? ? ? ret = ipu_dmfc_alloc_bandwidth(mxc_fbi->dmfc, calc_bandwidth(var, calc_vref(var_master)));
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_dbg(fbi->device, "allocating dmfc bandwidth failed with %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret);
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> +
> + ? ? ? if (enabled)
> + ? ? ? ? ? ? ? imx_ipu_fb_enable_overlay(fbi);
> +
> + ? ? ? return ret;
> +}
> +
> +static int imx_ipu_fb_blank_overlay(int blank, struct fb_info *fbi)
> +{
> + ? ? ? dev_dbg(fbi->device, "blank = %d\n", blank);
> +
> + ? ? ? switch (blank) {
> + ? ? ? case FB_BLANK_POWERDOWN:
> + ? ? ? case FB_BLANK_VSYNC_SUSPEND:
> + ? ? ? case FB_BLANK_HSYNC_SUSPEND:
> + ? ? ? case FB_BLANK_NORMAL:
> + ? ? ? ? ? ? ? imx_ipu_fb_disable_overlay(fbi);
> + ? ? ? ? ? ? ? break;
> + ? ? ? case FB_BLANK_UNBLANK:
> + ? ? ? ? ? ? ? imx_ipu_fb_enable_overlay(fbi);
> + ? ? ? ? ? ? ? break;
> + ? ? ? }
> +
> + ? ? ? return 0;
> +}
> +
> +static struct fb_ops imx_ipu_fb_overlay_ops = {
> + ? ? ? .owner ? ? ? ? ?= THIS_MODULE,
> + ? ? ? .fb_set_par ? ? = imx_ipu_fb_set_par_overlay,
> + ? ? ? .fb_check_var ? = imx_ipu_fb_check_var,
> + ? ? ? .fb_setcolreg ? = imx_ipu_fb_setcolreg,
> + ? ? ? .fb_pan_display = imx_ipu_fb_pan_display,
> + ? ? ? .fb_fillrect ? ?= cfb_fillrect,
> + ? ? ? .fb_copyarea ? ?= cfb_copyarea,
> + ? ? ? .fb_imageblit ? = cfb_imageblit,
> + ? ? ? .fb_blank ? ? ? = imx_ipu_fb_blank_overlay,
> +};
> +
> +static struct fb_info *imx_ipu_fb_init_fbinfo(struct device *dev, struct fb_ops *ops)
> +{
> + ? ? ? struct fb_info *fbi;
> + ? ? ? struct imx_ipu_fb_info *mxc_fbi;
> +
> + ? ? ? fbi = framebuffer_alloc(sizeof(struct imx_ipu_fb_info), dev);
> + ? ? ? if (!fbi)
> + ? ? ? ? ? ? ? return NULL;
> +
> + ? ? ? BUG_ON(fbi->par == NULL);
> + ? ? ? mxc_fbi = fbi->par;
> +
> + ? ? ? fbi->var.activate = FB_ACTIVATE_NOW;
> +
> + ? ? ? fbi->fbops = ops;
> + ? ? ? fbi->flags = FBINFO_FLAG_DEFAULT;
> + ? ? ? fbi->pseudo_palette = mxc_fbi->pseudo_palette;
> +
> + ? ? ? fb_alloc_cmap(&fbi->cmap, 16, 0);
> +
> + ? ? ? return fbi;
> +}
> +
> +static int imx_ipu_fb_init_overlay(struct platform_device *pdev,
> + ? ? ? ? ? ? ? struct fb_info *fbi_master, int ipu_channel)
> +{
> + ? ? ? struct imx_ipu_fb_info *mxc_fbi_master = fbi_master->par;
> + ? ? ? struct fb_info *ovlfbi;
> + ? ? ? struct imx_ipu_fb_info *ovl_mxc_fbi;
> + ? ? ? int ret;
> +
> + ? ? ? ovlfbi = imx_ipu_fb_init_fbinfo(&pdev->dev, &imx_ipu_fb_overlay_ops);
> + ? ? ? if (!ovlfbi)
> + ? ? ? ? ? ? ? return -ENOMEM;
> +
> + ? ? ? ovl_mxc_fbi = ovlfbi->par;
> + ? ? ? ovl_mxc_fbi->ipu_ch = ipu_idmac_get(ipu_channel);
> + ? ? ? ovl_mxc_fbi->dmfc = ipu_dmfc_get(ipu_channel);
> + ? ? ? ovl_mxc_fbi->ipu_di = -1;
> + ? ? ? ovl_mxc_fbi->dp = mxc_fbi_master->dp;
> + ? ? ? ovl_mxc_fbi->master = fbi_master;
> + ? ? ? mxc_fbi_master->slave = ovlfbi;
> +
> + ? ? ? ovlfbi->var.xres = 240;
> + ? ? ? ovlfbi->var.yres = 320;
> + ? ? ? ovlfbi->var.yres_virtual = ovlfbi->var.yres;
> + ? ? ? ovlfbi->var.xres_virtual = ovlfbi->var.xres;
> + ? ? ? imx_ipu_fb_check_var(&ovlfbi->var, ovlfbi);
> + ? ? ? imx_ipu_fb_set_fix(ovlfbi);
> +
> + ? ? ? ret = register_framebuffer(ovlfbi);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? framebuffer_release(ovlfbi);
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> +
> + ? ? ? ipu_dp_set_global_alpha(ovl_mxc_fbi->dp, 1, 0x80, 1);
> + ? ? ? ipu_dp_set_color_key(ovl_mxc_fbi->dp, 0, 0);
> +
> + ? ? ? imx_ipu_fb_set_par_overlay(ovlfbi);
> +
> + ? ? ? return 0;
> +}
> +
> +static void imx_ipu_fb_exit_overlay(struct platform_device *pdev,
> + ? ? ? ? ? ? ? struct fb_info *fbi_master, int ipu_channel)
> +{
> + ? ? ? struct imx_ipu_fb_info *mxc_fbi_master = fbi_master->par;
> + ? ? ? struct fb_info *ovlfbi = mxc_fbi_master->slave;
> + ? ? ? struct imx_ipu_fb_info *ovl_mxc_fbi = ovlfbi->par;
> +
> + ? ? ? imx_ipu_fb_blank_overlay(FB_BLANK_POWERDOWN, ovlfbi);
> +
> + ? ? ? unregister_framebuffer(ovlfbi);
> +
> + ? ? ? ipu_idmac_put(ovl_mxc_fbi->ipu_ch);
> + ? ? ? ipu_dmfc_free_bandwidth(ovl_mxc_fbi->dmfc);
> + ? ? ? ipu_dmfc_put(ovl_mxc_fbi->dmfc);
> +
> + ? ? ? framebuffer_release(ovlfbi);
> +}
> +
> +static int imx_ipu_fb_find_mode(struct fb_info *fbi)
> +{
> + ? ? ? int ret;
> + ? ? ? struct fb_videomode *mode_array;
> + ? ? ? struct fb_modelist *modelist;
> + ? ? ? struct fb_var_screeninfo *var = &fbi->var;
> + ? ? ? int i = 0;
> +
> + ? ? ? list_for_each_entry(modelist, &fbi->modelist, list)
> + ? ? ? ? ? ? ? i++;
> +
> + ? ? ? mode_array = kmalloc(sizeof (struct fb_modelist) * i, GFP_KERNEL);
> + ? ? ? if (!mode_array)
> + ? ? ? ? ? ? ? return -ENOMEM;
> +
> + ? ? ? i = 0;
> + ? ? ? list_for_each_entry(modelist, &fbi->modelist, list)
> + ? ? ? ? ? ? ? mode_array[i++] = modelist->mode;
> +
> + ? ? ? ret = fb_find_mode(&fbi->var, fbi, NULL, mode_array, i, NULL, 16);
> + ? ? ? if (ret == 0)
> + ? ? ? ? ? ? ? return -EINVAL;
> +
> + ? ? ? dev_dbg(fbi->device, "found %dx%d-%d hs:%d:%d:%d vs:%d:%d:%d\n",
> + ? ? ? ? ? ? ? ? ? ? ? var->xres, var->yres, var->bits_per_pixel,
> + ? ? ? ? ? ? ? ? ? ? ? var->hsync_len, var->left_margin, var->right_margin,
> + ? ? ? ? ? ? ? ? ? ? ? var->vsync_len, var->upper_margin, var->lower_margin);
> +
> + ? ? ? kfree(mode_array);
> +
> + ? ? ? return 0;
> +}
> +
> +static int __devinit imx_ipu_fb_probe(struct platform_device *pdev)
> +{
> + ? ? ? struct fb_info *fbi;
> + ? ? ? struct imx_ipu_fb_info *mxc_fbi;
> + ? ? ? struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
> + ? ? ? int ret = 0, i;
> +
> + ? ? ? pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
> +
> + ? ? ? fbi = imx_ipu_fb_init_fbinfo(&pdev->dev, &imx_ipu_fb_ops);
> + ? ? ? if (!fbi)
> + ? ? ? ? ? ? ? return -ENOMEM;
> +
> + ? ? ? mxc_fbi = fbi->par;
> +
> + ? ? ? mxc_fbi->ipu_channel_num = plat_data->ipu_channel_bg;
> + ? ? ? mxc_fbi->dc = plat_data->dc_channel;
> + ? ? ? mxc_fbi->ipu_di_pix_fmt = plat_data->interface_pix_fmt;
> + ? ? ? mxc_fbi->ipu_di = pdev->id;
> +
> + ? ? ? mxc_fbi->ipu_ch = ipu_idmac_get(plat_data->ipu_channel_bg);
> + ? ? ? if (IS_ERR(mxc_fbi->ipu_ch)) {
> + ? ? ? ? ? ? ? ret = PTR_ERR(mxc_fbi->ipu_ch);
> + ? ? ? ? ? ? ? goto failed_request_ipu;
> + ? ? ? }
> +
> + ? ? ? mxc_fbi->dmfc = ipu_dmfc_get(plat_data->ipu_channel_bg);
> + ? ? ? if (IS_ERR(mxc_fbi->ipu_ch)) {
> + ? ? ? ? ? ? ? ret = PTR_ERR(mxc_fbi->ipu_ch);
> + ? ? ? ? ? ? ? goto failed_request_dmfc;
> + ? ? ? }
> +
> + ? ? ? if (plat_data->dp_channel >= 0) {
> + ? ? ? ? ? ? ? mxc_fbi->dp = ipu_dp_get(plat_data->dp_channel);
> + ? ? ? ? ? ? ? if (IS_ERR(mxc_fbi->dp)) {
> + ? ? ? ? ? ? ? ? ? ? ? ret = PTR_ERR(mxc_fbi->ipu_ch);
> + ? ? ? ? ? ? ? ? ? ? ? goto failed_request_dp;
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> +
> + ? ? ? fbi->var.yres_virtual = fbi->var.yres;
> +
> + ? ? ? INIT_LIST_HEAD(&fbi->modelist);
> + ? ? ? for (i = 0; i < plat_data->num_modes; i++)
> + ? ? ? ? ? ? ? fb_add_videomode(&plat_data->modes[i], &fbi->modelist);
> +
> + ? ? ? if (plat_data->flags & IMX_IPU_FB_USE_MODEDB) {
> + ? ? ? ? ? ? ? for (i = 0; i < num_fb_modes; i++)
> + ? ? ? ? ? ? ? ? ? ? ? fb_add_videomode(&fb_modes[i], &fbi->modelist);
> + ? ? ? }
> +
> + ? ? ? imx_ipu_fb_find_mode(fbi);
> +
> + ? ? ? imx_ipu_fb_check_var(&fbi->var, fbi);
> + ? ? ? imx_ipu_fb_set_fix(fbi);
> + ? ? ? ret = register_framebuffer(fbi);
> + ? ? ? if (ret < 0)
> + ? ? ? ? ? ? ? goto failed_register;
> +
> + ? ? ? imx_ipu_fb_set_par(fbi);
> + ? ? ? imx_ipu_fb_blank(FB_BLANK_UNBLANK, fbi);
> +
> + ? ? ? if (plat_data->ipu_channel_fg >= 0 && plat_data->flags & IMX_IPU_FB_USE_OVERLAY)
> + ? ? ? ? ? ? ? imx_ipu_fb_init_overlay(pdev, fbi, plat_data->ipu_channel_fg);
> +
> + ? ? ? platform_set_drvdata(pdev, fbi);
> +
> + ? ? ? return 0;
> +
> +failed_register:
> + ? ? ? if (plat_data->dp_channel >= 0)
> + ? ? ? ? ? ? ? ipu_dp_put(mxc_fbi->dp);
> +failed_request_dp:
> + ? ? ? ipu_dmfc_put(mxc_fbi->dmfc);
> +failed_request_dmfc:
> + ? ? ? ipu_idmac_put(mxc_fbi->ipu_ch);
> +failed_request_ipu:
> + ? ? ? fb_dealloc_cmap(&fbi->cmap);
> + ? ? ? framebuffer_release(fbi);
> +
> + ? ? ? return ret;
> +}
> +
> +static int __devexit imx_ipu_fb_remove(struct platform_device *pdev)
> +{
> + ? ? ? struct fb_info *fbi = platform_get_drvdata(pdev);
> + ? ? ? struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> + ? ? ? struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
> +
> + ? ? ? if (plat_data->ipu_channel_fg >= 0 && plat_data->flags & IMX_IPU_FB_USE_OVERLAY)
> + ? ? ? ? ? ? ? imx_ipu_fb_exit_overlay(pdev, fbi, plat_data->ipu_channel_fg);
> +
> + ? ? ? imx_ipu_fb_blank(FB_BLANK_POWERDOWN, fbi);
> +
> + ? ? ? dma_free_writecombine(fbi->device, fbi->fix.smem_len,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? fbi->screen_base, fbi->fix.smem_start);
> +
> + ? ? ? if (&fbi->cmap)
> + ? ? ? ? ? ? ? fb_dealloc_cmap(&fbi->cmap);
> +
> + ? ? ? unregister_framebuffer(fbi);
> +
> + ? ? ? if (plat_data->dp_channel >= 0)
> + ? ? ? ? ? ? ? ipu_dp_put(mxc_fbi->dp);
> + ? ? ? ipu_dmfc_free_bandwidth(mxc_fbi->dmfc);
> + ? ? ? ipu_dmfc_put(mxc_fbi->dmfc);
> + ? ? ? ipu_idmac_put(mxc_fbi->ipu_ch);
> +
> + ? ? ? framebuffer_release(fbi);
> +
> + ? ? ? return 0;
> +}
> +
> +static struct platform_driver imx_ipu_fb_driver = {
> + ? ? ? .driver = {
> + ? ? ? ? ? ? ? .name = DRIVER_NAME,
> + ? ? ? },
> + ? ? ? .probe = imx_ipu_fb_probe,
> + ? ? ? .remove = __devexit_p(imx_ipu_fb_remove),
> +};
> +
> +static int __init imx_ipu_fb_init(void)
> +{
> + ? ? ? return platform_driver_register(&imx_ipu_fb_driver);
> +}
> +
> +static void __exit imx_ipu_fb_exit(void)
> +{
> + ? ? ? platform_driver_unregister(&imx_ipu_fb_driver);
> +}
> +
> +module_init(imx_ipu_fb_init);
> +module_exit(imx_ipu_fb_exit);
> +
> +MODULE_AUTHOR("Freescale Semiconductor, Inc.");
> +MODULE_DESCRIPTION("i.MX framebuffer driver");
> +MODULE_LICENSE("GPL");
> +MODULE_SUPPORTED_DEVICE("fb");
> --
> 1.7.2.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at ?http://www.tux.org/lkml/
>

2010-12-13 07:31:01

by Lothar Waßmann

[permalink] [raw]
Subject: Re: [PATCH 5/9] Add i.MX5 framebuffer driver

Hi,

Liu Ying writes:
> 6) I prefer to find the video mode in modedb first, and if we cannot
> find the video mode in common video mode data base, we can find a
> video mode in custom video mode data base which is defined in platform
> data. In this way, we don't need to export common modefb.
>
IMO platform specific video modes should take precedence over generic
ones, so the platform data should be evaluated first.


Lothar Wa?mann
--
___________________________________________________________

Ka-Ro electronics GmbH | Pascalstra?e 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Gesch?ftsf?hrer: Matthias Kaussen
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996

http://www.karo-electronics.de | [email protected]
___________________________________________________________

2010-12-13 11:23:50

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 3/9] Add a mfd IPUv3 driver

Hi Liu Ying,

Thanks for looking at the patches.

On Sun, Dec 12, 2010 at 01:21:57PM +0800, Liu Ying wrote:
> Hello, Sascha,
>
> IPU is not embedded in i,MX50 SoC, but in i.MX51/53 SoCs, please
> modify the commit message.
>
> I have the following comments for the files editted respectively:
> 1) ipu-common.c
> - ipu_idmac_get()/ipu_idmac_put() need a mechanism to protect IPU
> IDMAC resources, namely, get rid of potential race condition issue. As
> only framebuffer support is added in your patches, there should be no
> race condition issue now. After IC channels are supported(maybe as DMA
> engine), perhaps, there will be such kind of issue.

ok.

> - ipu_idmac_select_buffer() need to add spinlock to protect
> IPU_CHA_BUFx_RDY registers.

ok.

> - It looks that ipu_uninit_channel() only clears
> IPU_CHA_DB_MODE_SEL register, so why not put it in
> ipu_idmac_disable_channel()?

ok.

> - It looks that ipu_add_client_devices() and your framebuffer
> patch assume /dev/fb0 uses DP_BG, /dev/fb1 uses DP_FG and /dev/fb2
> uses DC.
> But fb1_platform_data->ipu_channel_bg is
> MX51_IPU_CHANNEL_MEM_DC_SYNC, this may make confusion for driver
> readers and it is not easy for code maintenance.

Do you have a better suggestion? fb2 becomes fb1 when overlay support
is not used.

> - It also looks that ipu_add_client_devices() and your framebuffer
> driver assume DP_BG/DP_FG are bound with DI0 and DC is bound with DI1.
> It is ok for babbage board to support this kind of
> configuration, but it may bring some limitation. For example, TVE(TV
> encoder) module embedded in MX51 SoC can only connected with DI1 and
> users may like to use TV as the primary device(support HW overlay),
> and FSL Android BSP does support to use DI1 with LCD as the primary
> device on babbage board.

SO we need to put the display number into the platform data, right?

>
> 2) ipu-cpmem.c
> - In order to improve performance, maybe writing
> ipu_ch_param_addr(ch) directly will be fine, but not using memset()
> and memcpy() in ipu_ch_param_init().

I don't see this function in any performance critical path.

>
> 3) ipu-dc.c
> - Looks '#include <asm/atomic.h>' and '#include
> <linux/spinlock.h>' are unnecessary.
> - Need to call 'ipu_module_disable(IPU_CONF_DC_EN);' somewhere.

ok.

>
> 4) ipu-di.c
> - Looks '#include <asm/atomic.h>' and '#include <linux/delay.h>'
> are unnecessary.

ok.

>
> 5) ipu-dmfc.c
> - Looks '#include <linux/delay.h>' are unnecessary.

ok.

>
> 6) ipu-dp.c
> - Looks '#include <asm/atomic.h>' and '#include <linux/delay.h>'
> are unnecessary.

ok.

>
> 7) ipu-prv.h
> - Looks '#include <linux/interrupt.h>' is unnecessary.
> - Please rename 'MX51_IPU_CHANNEL_xxxx' to be 'IPU_CHANNEL_xxxx',
> IPUv3 channel names do not depend on SoC name.

I was proven wrong each time I believed this.

>
> 8) include/linux/mfd/imx-ipu-v3.h
> - Looks '#include <linux/slab.h>' and '#include
> <linux/interrupt.h>' are unnecessary.

ok.

Sascha

--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2010-12-13 11:35:12

by Liu Ying

[permalink] [raw]
Subject: Re: [PATCH 5/9] Add i.MX5 framebuffer driver

Hello, Lothar Wa?mann,

I think your opinion is reasonable and better.
So, first, we can find the video mode in the video mode data base
defined in platform data, and then, if this fails, we can find it in
common video mode data base.

Best Regards,
Liu Ying

2010/12/13 Lothar Wa?mann <[email protected]>:
> Hi,
>
> Liu Ying writes:
>> 6) I prefer to find the video mode in modedb first, and if we cannot
>> find the video mode in common video mode data base, we can find a
>> video mode in custom video mode data base which is defined in platform
>> data. In this way, we don't need to export common modefb.
>>
> IMO platform specific video modes should take precedence over generic
> ones, so the platform data should be evaluated first.
>
>
> Lothar Wa?mann
> --
> ___________________________________________________________
>
> Ka-Ro electronics GmbH | Pascalstra?e 22 | D - 52076 Aachen
> Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
> Gesch?ftsf?hrer: Matthias Kaussen
> Handelsregistereintrag: Amtsgericht Aachen, HRB 4996
>
> http://www.karo-electronics.de | [email protected]
> ___________________________________________________________
>

2010-12-13 11:38:34

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 5/9] Add i.MX5 framebuffer driver

On Sun, Dec 12, 2010 at 02:13:40PM +0800, Liu Ying wrote:
> Hello, Sascha,
>
> I have following comments to this patch:
> 1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.

ok.

> 2) ADC is not supported yet in the framebuffer driver, so please
> modify this comment:
> > + * Framebuffer Framebuffer Driver for SDC and ADC.

ok.

> 3) 'ipu_dp_set_window_pos()' is called only once in
> imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
> support to change the overlay framebuffer position. Need a
> mechanism/interface for users to change the overlay framebuffer
> position.

Yes. The overlay support is currently only present in the driver because
I didn't want to break it in general. There is no generic overlay API in
the kernel and I didn't want to invent the n+1 API. Currently the
possible use cases of the overlay support are not clear to me. Number
one use case would probably be to display video output, but the
corresponding driver would be a v4l2 driver, so in this case we would
only need an in-kernel API.
Anyway, I have not the resources to implement complete overlay support.
So either we keep it like it is and it more has an example character
or we remove it completely from the driver for now.

> 4) Need to make sure the framebuffer on DP-FG is blanked before the
> framebuffer on DP-BG is blanked. Meanwhile, the framebuffer on DP-FG
> should be unblanked after the framebuffer on DP-BG is unblanked
> 5) Need to check the framebuffer on DP-FG doesn't run out of the range
> of the framebuffer on DP-BG.

I tend to remove the overlay support.

> 6) I prefer to find the video mode in modedb first, and if we cannot
> find the video mode in common video mode data base, we can find a
> video mode in custom video mode data base which is defined in platform
> data. In this way, we don't need to export common modefb.

I exported the modedb to be able to show the different modes under
/sys/class/graphics/fb0/modes and to set it with
/sys/class/graphics/fb0/mode. If you want this there is no way around
exporting it. The ioctl interface for mode setting is independent of the
specific modes anyway.

BTW please make comments specific to specific code inline.

Sascha


--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2010-12-13 11:43:35

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 9/9] ARM i.MX51 babbage: Add framebuffer support

On Sun, Dec 12, 2010 at 09:37:38AM +0800, Liu Ying wrote:
> Hello, Sascha,
>
> I have following 3 comments to this patch:
> 1) I think DISP1_DATx pins need not be set specially, as they keep the
> default register values.

We do not want to depend on default register values in the Linux Kernel.

> 2) Please define 'MX51_PAD_DI_GP4__IPU_DI2_PIN15' in
> arch/arm/plat-mxc/include/mach/iomux-mx51.h, and rename the pin to be
> 'MX51_PAD_DI_GP4__DI2_PIN15', as we name it according to the MX51
> iomux reference manual.

I'm not sure. Normally it's good practice to code the unit into the
name. Otherwise we end up with names like MX51_PAD_xyz__TX which could
be the tx pin of just about any unit.


> 3) It is better to exchange the following two lines or just remove the
> first line:
> + gpio_set_value(GPIO_DVI_RESET, 0);
> + gpio_direction_output(GPIO_DVI_RESET, 0);

ok.

Sascha


--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2010-12-14 04:05:11

by Liu Ying

[permalink] [raw]
Subject: Re: [PATCH 3/9] Add a mfd IPUv3 driver

Hello, Sascha,

Please find my feedback with [LY] embedded.

Best Regards,
Liu Ying

2010/12/13 Sascha Hauer <[email protected]>:
> Hi Liu Ying,
>
> Thanks for looking at the patches.
[LY] You are welcome.
>
> On Sun, Dec 12, 2010 at 01:21:57PM +0800, Liu Ying wrote:
>> Hello, Sascha,
>>
>> IPU is not embedded in i,MX50 SoC, but in i.MX51/53 SoCs, please
>> modify the commit message.
>>
>> I have the following comments for the files editted respectively:
>> 1) ipu-common.c
>> ? ? - ipu_idmac_get()/ipu_idmac_put() need a mechanism to protect IPU
>> IDMAC resources, namely, get rid of potential race condition issue. As
>> only framebuffer support is added in your patches, there should be no
>> race condition issue now. After IC channels are supported(maybe as DMA
>> engine), perhaps, there will be such kind of issue.
>
> ok.
>
>> ? ? - ipu_idmac_select_buffer() need to add spinlock to protect
>> IPU_CHA_BUFx_RDY registers.
>
> ok.
>
>> ? ? - It looks that ipu_uninit_channel() only clears
>> IPU_CHA_DB_MODE_SEL register, so why not put it in
>> ipu_idmac_disable_channel()?
>
> ok.
>
>> ? ? - It looks that ipu_add_client_devices() and your framebuffer
>> patch assume /dev/fb0 uses DP_BG, /dev/fb1 uses DP_FG and /dev/fb2
>> uses DC.
>> ? ? ? But fb1_platform_data->ipu_channel_bg is
>> MX51_IPU_CHANNEL_MEM_DC_SYNC, this may make confusion for driver
>> readers and it is not easy for code maintenance.
>
> Do you have a better suggestion? fb2 becomes fb1 when overlay support
> is not used.
[LY] How about assigning DP-BG to /dev/fb0, DC to /dev/fb1 and DP_FG
to /dev/fb2?
>
>> ? ? - It also looks that ipu_add_client_devices() and your framebuffer
>> driver assume DP_BG/DP_FG are bound with DI0 and DC is bound with DI1.
>> ? ? ? It is ok for babbage board to support this kind of
>> configuration, but it may bring some limitation. For example, TVE(TV
>> encoder) module embedded in MX51 SoC can only connected with DI1 and
>> users may like to use TV as the primary device(support HW overlay),
>> and FSL Android BSP does support to use DI1 with LCD as the primary
>> device on babbage board.
>
> SO we need to put the display number into the platform data, right?
[LY] Yes, I think so. As the primary display port could be DI0 or DI1
on boards like babbage board(two display ports are used), the primary
display number in platform data should be configurable(I'm not sure
whether this can be accepted by community), perhaps, configured by
kernel bootup command line.
>
>>
>> 2) ipu-cpmem.c
>> ? ? - In order to improve performance, maybe writing
>> ipu_ch_param_addr(ch) directly will be fine, but not using memset()
>> and memcpy() in ipu_ch_param_init().
>
> I don't see this function in any performance critical path.
[LY] Yes, currently, this function is not in performance critical
path, so it is ok for me now. However, after IC/IRT channels are
involved, the channels' idmac configuration might be changed from time
to time and frequently, as the channels could be used as dma engine.
>
>>
>> 3) ipu-dc.c
>> ? ? - Looks '#include <asm/atomic.h>' and '#include
>> <linux/spinlock.h>' are unnecessary.
>> ? ? - Need to call 'ipu_module_disable(IPU_CONF_DC_EN);' somewhere.
>
> ok.
>
>>
>> 4) ipu-di.c
>> ? ? - Looks '#include <asm/atomic.h>' and '#include <linux/delay.h>'
>> are unnecessary.
>
> ok.
>
>>
>> 5) ipu-dmfc.c
>> ? ? - Looks '#include <linux/delay.h>' are unnecessary.
>
> ok.
>
>>
>> 6) ipu-dp.c
>> ? ? - Looks '#include <asm/atomic.h>' and '#include <linux/delay.h>'
>> are unnecessary.
>
> ok.
>
>>
>> 7) ipu-prv.h
>> ? ? - Looks '#include <linux/interrupt.h>' is unnecessary.
>> ? ? - Please rename 'MX51_IPU_CHANNEL_xxxx' to be 'IPU_CHANNEL_xxxx',
>> IPUv3 channel names do not depend on SoC name.
>
> I was proven wrong each time I believed this.
[LY] What if IPUv3 will be embedded in more SoCs? Could you please
tell the reason? Thanks.
>
>>
>> 8) include/linux/mfd/imx-ipu-v3.h
>> ? ?- Looks '#include <linux/slab.h>' and '#include
>> <linux/interrupt.h>' are unnecessary.
>
> ok.
>
> Sascha
>
> --
> Pengutronix e.K. ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
> Industrial Linux Solutions ? ? ? ? ? ? ? ? | http://www.pengutronix.de/ ?|
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 ? ?|
> Amtsgericht Hildesheim, HRA 2686 ? ? ? ? ? | Fax: ? +49-5121-206917-5555 |
>

2010-12-14 06:40:13

by Liu Ying

[permalink] [raw]
Subject: Re: [PATCH 5/9] Add i.MX5 framebuffer driver

Hello, Sascha,

Please find my feedback with [LY] embedded.

And, a bug related with this patch:
1) Bootup the babbage board.
2) echo U:640x480p-60 > /sys/class/graphics/fb0/mode
3) cat /sys/class/graphics/fb0/virtual_size, it shows '640,480'.
4) echo 640,960 > /sys/class/graphics/fb0/virtual_size, change virtual
yres to be 960.
5) cat /sys/class/graphics/fb0/virtual_size, it still shows '640,480'.
I think this is caused by 'var->yres_virtual = var->yres;' in custom
fb_set_par() function(Sorry, I cannot make the comment inline this
time, as I don't find the original code within the mail I am
replying). This makes fb0 use only one block of memory whose size is
equal to screen size, so pan display can not really play her role and
screen tearing issue may happen.

Best Regards,
Liu Ying

2010/12/13 Sascha Hauer <[email protected]>:
> On Sun, Dec 12, 2010 at 02:13:40PM +0800, Liu Ying wrote:
>> Hello, Sascha,
>>
>> I have following comments to this patch:
>> 1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.
>
> ok.
>
>> 2) ADC is not supported yet in the framebuffer driver, so please
>> modify this comment:
>> ? ?> + * Framebuffer Framebuffer Driver for SDC and ADC.
>
> ok.
>
>> 3) 'ipu_dp_set_window_pos()' is called only once in
>> imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
>> support to change the overlay framebuffer position. Need a
>> mechanism/interface for users to change the overlay framebuffer
>> position.
>
> Yes. The overlay support is currently only present in the driver because
> I didn't want to break it in general. There is no generic overlay API in
> the kernel and I didn't want to invent the n+1 API. Currently the
> possible use cases of the overlay support are not clear to me. Number
> one use case would probably be to display video output, but the
> corresponding driver would be a v4l2 driver, so in this case we would
> only need an in-kernel API.
> Anyway, I have not the resources to implement complete overlay support.
> So either we keep it like it is and it more has an example character
> or we remove it completely from the driver for now.
>
>> 4) Need to make sure the framebuffer on DP-FG is blanked before the
>> framebuffer on DP-BG is blanked. Meanwhile, the framebuffer on DP-FG
>> should be unblanked after the framebuffer on DP-BG is unblanked
>> 5) Need to check the framebuffer on DP-FG doesn't run out of the range
>> of the framebuffer on DP-BG.
>
> I tend to remove the overlay support.
>
>> 6) I prefer to find the video mode in modedb first, and if we cannot
>> find the video mode in common video mode data base, we can find a
>> video mode in custom video mode data base which is defined in platform
>> data. In this way, we don't need to export common modefb.
>
> I exported the modedb to be able to show the different modes under
> /sys/class/graphics/fb0/modes and to set it with
> /sys/class/graphics/fb0/mode. If you want this there is no way around
> exporting it. The ioctl interface for mode setting is independent of the
> specific modes anyway.
[LY] Just a concern. If a platform choose to add the generic video
mode data base to IPUv3 framebuffer modelist, 'cat
/sys/class/graphics/fb0/modes' will show all the video modes in the
generic data base to users. I am not sure whether showing them to
users means the IPUv3 framebuffer driver acknowledges to support all
of them or not. I tried to do 'echo U:1800x1440p-70 >
/sys/class/graphics/fb0/mode' with the kernel on ipuv3 branch, there
will be a kernel dump(seems it can not allocate enough memory).
>
> BTW please make comments specific to specific code inline.
[LY] Ok.
>
> Sascha
>
>
> --
> Pengutronix e.K. ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
> Industrial Linux Solutions ? ? ? ? ? ? ? ? | http://www.pengutronix.de/ ?|
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 ? ?|
> Amtsgericht Hildesheim, HRA 2686 ? ? ? ? ? | Fax: ? +49-5121-206917-5555 |
>

2010-12-14 06:47:12

by Liu Ying

[permalink] [raw]
Subject: Re: [PATCH 9/9] ARM i.MX51 babbage: Add framebuffer support

2010/12/13 Sascha Hauer <[email protected]>:
> On Sun, Dec 12, 2010 at 09:37:38AM +0800, Liu Ying wrote:
>> Hello, Sascha,
>>
>> I have following 3 comments to this patch:
>> 1) I think DISP1_DATx pins need not be set specially, as they keep the
>> default register values.
>
> We do not want to depend on default register values in the Linux Kernel.
[LY] Ok. Please add disp1 related pins configuration, including
DISP1_HSYNC, DISP1_VSYNC and DISP1_CLK.
It will be fine to add disp2 related pins configuration also.
>
>> 2) Please define 'MX51_PAD_DI_GP4__IPU_DI2_PIN15' in
>> arch/arm/plat-mxc/include/mach/iomux-mx51.h, and rename the pin to be
>> 'MX51_PAD_DI_GP4__DI2_PIN15', as we name it according to the MX51
>> iomux reference manual.
>
> I'm not sure. Normally it's good practice to code the unit into the
> name. Otherwise we end up with names like MX51_PAD_xyz__TX which could
> be the tx pin of just about any unit.
>
>
>> 3) It is better to exchange the following two lines or just remove the
>> first line:
>> + ? ? ? gpio_set_value(GPIO_DVI_RESET, 0);
>> + ? ? ? gpio_direction_output(GPIO_DVI_RESET, 0);
>
> ok.
>
> Sascha
>
>
> --
> Pengutronix e.K. ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
> Industrial Linux Solutions ? ? ? ? ? ? ? ? | http://www.pengutronix.de/ ?|
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 ? ?|
> Amtsgericht Hildesheim, HRA 2686 ? ? ? ? ? | Fax: ? +49-5121-206917-5555 |
>

2010-12-14 08:40:32

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 3/9] Add a mfd IPUv3 driver

On Tue, Dec 14, 2010 at 12:05:07PM +0800, Liu Ying wrote:
> Hello, Sascha,
>
> Please find my feedback with [LY] embedded.
>
> Best Regards,
> Liu Ying
>
> 2010/12/13 Sascha Hauer <[email protected]>:
> > Hi Liu Ying,
> >
> > Thanks for looking at the patches.
> [LY] You are welcome.
> >
> > On Sun, Dec 12, 2010 at 01:21:57PM +0800, Liu Ying wrote:
> >> Hello, Sascha,
> >>
> >> IPU is not embedded in i,MX50 SoC, but in i.MX51/53 SoCs, please
> >> modify the commit message.
> >>
> >> I have the following comments for the files editted respectively:
> >> 1) ipu-common.c
> >> ? ? - ipu_idmac_get()/ipu_idmac_put() need a mechanism to protect IPU
> >> IDMAC resources, namely, get rid of potential race condition issue. As
> >> only framebuffer support is added in your patches, there should be no
> >> race condition issue now. After IC channels are supported(maybe as DMA
> >> engine), perhaps, there will be such kind of issue.
> >
> > ok.
> >
> >> ? ? - ipu_idmac_select_buffer() need to add spinlock to protect
> >> IPU_CHA_BUFx_RDY registers.
> >
> > ok.
> >
> >> ? ? - It looks that ipu_uninit_channel() only clears
> >> IPU_CHA_DB_MODE_SEL register, so why not put it in
> >> ipu_idmac_disable_channel()?
> >
> > ok.
> >
> >> ? ? - It looks that ipu_add_client_devices() and your framebuffer
> >> patch assume /dev/fb0 uses DP_BG, /dev/fb1 uses DP_FG and /dev/fb2
> >> uses DC.
> >> ? ? ? But fb1_platform_data->ipu_channel_bg is
> >> MX51_IPU_CHANNEL_MEM_DC_SYNC, this may make confusion for driver
> >> readers and it is not easy for code maintenance.
> >
> > Do you have a better suggestion? fb2 becomes fb1 when overlay support
> > is not used.
> [LY] How about assigning DP-BG to /dev/fb0, DC to /dev/fb1 and DP_FG
> to /dev/fb2?
> >
> >> ? ? - It also looks that ipu_add_client_devices() and your framebuffer
> >> driver assume DP_BG/DP_FG are bound with DI0 and DC is bound with DI1.
> >> ? ? ? It is ok for babbage board to support this kind of
> >> configuration, but it may bring some limitation. For example, TVE(TV
> >> encoder) module embedded in MX51 SoC can only connected with DI1 and
> >> users may like to use TV as the primary device(support HW overlay),
> >> and FSL Android BSP does support to use DI1 with LCD as the primary
> >> device on babbage board.
> >
> > SO we need to put the display number into the platform data, right?
> [LY] Yes, I think so. As the primary display port could be DI0 or DI1
> on boards like babbage board(two display ports are used), the primary
> display number in platform data should be configurable(I'm not sure
> whether this can be accepted by community), perhaps, configured by
> kernel bootup command line.

Ok, I'll find a solution for this.

> >
> >>
> >> 2) ipu-cpmem.c
> >> ? ? - In order to improve performance, maybe writing
> >> ipu_ch_param_addr(ch) directly will be fine, but not using memset()
> >> and memcpy() in ipu_ch_param_init().
> >
> > I don't see this function in any performance critical path.
> [LY] Yes, currently, this function is not in performance critical
> path, so it is ok for me now. However, after IC/IRT channels are
> involved, the channels' idmac configuration might be changed from time
> to time and frequently, as the channels could be used as dma engine.

We are talking about 60 frames per second at maximum, right? So the
channel would be reconfigured 60 times a second, that's still not
performance critical, at least not if we are talking about a 100 byte or
something memset.

> >
> >>
> >> 3) ipu-dc.c
> >> ? ? - Looks '#include <asm/atomic.h>' and '#include
> >> <linux/spinlock.h>' are unnecessary.
> >> ? ? - Need to call 'ipu_module_disable(IPU_CONF_DC_EN);' somewhere.
> >
> > ok.
> >
> >>
> >> 4) ipu-di.c
> >> ? ? - Looks '#include <asm/atomic.h>' and '#include <linux/delay.h>'
> >> are unnecessary.
> >
> > ok.
> >
> >>
> >> 5) ipu-dmfc.c
> >> ? ? - Looks '#include <linux/delay.h>' are unnecessary.
> >
> > ok.
> >
> >>
> >> 6) ipu-dp.c
> >> ? ? - Looks '#include <asm/atomic.h>' and '#include <linux/delay.h>'
> >> are unnecessary.
> >
> > ok.
> >
> >>
> >> 7) ipu-prv.h
> >> ? ? - Looks '#include <linux/interrupt.h>' is unnecessary.
> >> ? ? - Please rename 'MX51_IPU_CHANNEL_xxxx' to be 'IPU_CHANNEL_xxxx',
> >> IPUv3 channel names do not depend on SoC name.
> >
> > I was proven wrong each time I believed this.
> [LY] What if IPUv3 will be embedded in more SoCs? Could you please
> tell the reason? Thanks.

Then channel assignments will change, I'll promise.

Sascha


--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2010-12-14 08:45:50

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 5/9] Add i.MX5 framebuffer driver

On Tue, Dec 14, 2010 at 02:40:09PM +0800, Liu Ying wrote:
> Hello, Sascha,
>
> Please find my feedback with [LY] embedded.
>
> And, a bug related with this patch:
> 1) Bootup the babbage board.
> 2) echo U:640x480p-60 > /sys/class/graphics/fb0/mode
> 3) cat /sys/class/graphics/fb0/virtual_size, it shows '640,480'.
> 4) echo 640,960 > /sys/class/graphics/fb0/virtual_size, change virtual
> yres to be 960.
> 5) cat /sys/class/graphics/fb0/virtual_size, it still shows '640,480'.
> I think this is caused by 'var->yres_virtual = var->yres;' in custom
> fb_set_par() function(Sorry, I cannot make the comment inline this
> time, as I don't find the original code within the mail I am
> replying). This makes fb0 use only one block of memory whose size is
> equal to screen size, so pan display can not really play her role and
> screen tearing issue may happen.
>
> Best Regards,
> Liu Ying
>
> 2010/12/13 Sascha Hauer <[email protected]>:
> > On Sun, Dec 12, 2010 at 02:13:40PM +0800, Liu Ying wrote:
> >> Hello, Sascha,
> >>
> >> I have following comments to this patch:
> >> 1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.
> >
> > ok.
> >
> >> 2) ADC is not supported yet in the framebuffer driver, so please
> >> modify this comment:
> >> ? ?> + * Framebuffer Framebuffer Driver for SDC and ADC.
> >
> > ok.
> >
> >> 3) 'ipu_dp_set_window_pos()' is called only once in
> >> imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
> >> support to change the overlay framebuffer position. Need a
> >> mechanism/interface for users to change the overlay framebuffer
> >> position.
> >
> > Yes. The overlay support is currently only present in the driver because
> > I didn't want to break it in general. There is no generic overlay API in
> > the kernel and I didn't want to invent the n+1 API. Currently the
> > possible use cases of the overlay support are not clear to me. Number
> > one use case would probably be to display video output, but the
> > corresponding driver would be a v4l2 driver, so in this case we would
> > only need an in-kernel API.
> > Anyway, I have not the resources to implement complete overlay support.
> > So either we keep it like it is and it more has an example character
> > or we remove it completely from the driver for now.
> >
> >> 4) Need to make sure the framebuffer on DP-FG is blanked before the
> >> framebuffer on DP-BG is blanked. Meanwhile, the framebuffer on DP-FG
> >> should be unblanked after the framebuffer on DP-BG is unblanked
> >> 5) Need to check the framebuffer on DP-FG doesn't run out of the range
> >> of the framebuffer on DP-BG.
> >
> > I tend to remove the overlay support.
> >
> >> 6) I prefer to find the video mode in modedb first, and if we cannot
> >> find the video mode in common video mode data base, we can find a
> >> video mode in custom video mode data base which is defined in platform
> >> data. In this way, we don't need to export common modefb.
> >
> > I exported the modedb to be able to show the different modes under
> > /sys/class/graphics/fb0/modes and to set it with
> > /sys/class/graphics/fb0/mode. If you want this there is no way around
> > exporting it. The ioctl interface for mode setting is independent of the
> > specific modes anyway.
> [LY] Just a concern. If a platform choose to add the generic video
> mode data base to IPUv3 framebuffer modelist, 'cat
> /sys/class/graphics/fb0/modes' will show all the video modes in the
> generic data base to users. I am not sure whether showing them to
> users means the IPUv3 framebuffer driver acknowledges to support all
> of them or not. I tried to do 'echo U:1800x1440p-70 >
> /sys/class/graphics/fb0/mode' with the kernel on ipuv3 branch, there
> will be a kernel dump(seems it can not allocate enough memory).

We'll have to increase the dma coherent size (and max zone order) for
these resolutions to work. I have patches for this in my local tree but
decided to wait until some contiguous memory allocator hits the tree.
Also, the fb driver should sanity check the generic modes before adding
them to the list. FOr example the IPU can't do 1920x1200@60. This code
is missing currently.

Sascha


--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2010-12-14 12:00:58

by Chen Jie-B02280

[permalink] [raw]
Subject: RE: [PATCH 3/9] Add a mfd IPUv3 driver

Hi, Sascha,

My colleague Ying has give you many valuable comments, I have few new comments in-line below with [Jason]:

Jason Chen / Chen Jie
NMG / MAD
Freescale Semiconductor (China) Ltd.
2F, Building B, 177#, Bi Bo Rd
Pu Dong New District Shanghai 201203
Tel: 021-28937178
Fax: 021-28937444
E-mail: [email protected]<mailto:[email protected]>
---------- Forwarded message ----------
From: Sascha Hauer <[email protected]<mailto:[email protected]>>
Date: 2010/12/9
Subject: [PATCH 3/9] Add a mfd IPUv3 driver
To: [email protected]<mailto:[email protected]>
Cc: [email protected]<mailto:[email protected]>, [email protected]<mailto:[email protected]>, Zhang Lily-R58066 <[email protected]<mailto:[email protected]>>, Arnaud Patard <[email protected]<mailto:[email protected]>>, Sascha Hauer <[email protected]<mailto:[email protected]>>


The IPU is the Image Processing Unit found on i.MX50/51/53 SoCs. It
features several units for image processing, this patch adds support
for the units needed for Framebuffer support, namely:

- Display Controller (dc)
- Display Interface (di)
- Display Multi Fifo Controller (dmfc)
- Display Processor (dp)
- Image DMA Controller (idmac)

This patch is based on the Freescale driver, but follows a different
approach. The Freescale code implements logical idmac channels and
the handling of the subunits is hidden in common idmac code pathes
in big switch/case statements. This patch instead just provides code
and resource management for the different subunits. The user, in this
case the framebuffer driver, decides how the different units play
together.

The IPU has other units missing in this patch:

- CMOS Sensor Interface (csi)
- Video Deinterlacer (vdi)
- Sensor Multi FIFO Controler (smfc)
- Image Converter (ic)
- Image Rotator (irt)

So expect more files to come in this directory.

Signed-off-by: Sascha Hauer <[email protected]<mailto:[email protected]>>
---
arch/arm/plat-mxc/include/mach/ipu-v3.h | 48 +++
drivers/mfd/Kconfig | 7 +
drivers/mfd/Makefile | 1 +
drivers/mfd/imx-ipu-v3/Makefile | 3 +
drivers/mfd/imx-ipu-v3/ipu-common.c | 661 +++++++++++++++++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-cpmem.c | 608 ++++++++++++++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-dc.c | 362 +++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-di.c | 507 ++++++++++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-dmfc.c | 343 ++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-dp.c | 468 ++++++++++++++++++++++
drivers/mfd/imx-ipu-v3/ipu-prv.h | 214 ++++++++++
include/linux/mfd/imx-ipu-v3.h | 218 ++++++++++
12 files changed, 3440 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/plat-mxc/include/mach/ipu-v3.h
create mode 100644 drivers/mfd/imx-ipu-v3/Makefile
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-common.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-cpmem.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-dc.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-di.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-dmfc.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-dp.c
create mode 100644 drivers/mfd/imx-ipu-v3/ipu-prv.h
create mode 100644 include/linux/mfd/imx-ipu-v3.h

diff --git a/arch/arm/plat-mxc/include/mach/ipu-v3.h b/arch/arm/plat-mxc/include/mach/ipu-v3.h
new file mode 100644
index 0000000..0a6c3e8
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/ipu-v3.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]<mailto:[email protected]>>
+ *
+ * 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 __MACH_IPU_V3_H_
+#define __MACH_IPU_V3_H_
+
+/* IPU specific extensions to struct fb_videomode flag field */
+#define FB_SYNC_OE_LOW_ACT (1 << 8)
+#define FB_SYNC_CLK_LAT_FALL (1 << 9)
+#define FB_SYNC_DATA_INVERT (1 << 10)
+#define FB_SYNC_CLK_IDLE_EN (1 << 11)
+#define FB_SYNC_SHARP_MODE (1 << 12)
+#define FB_SYNC_SWAP_RGB (1 << 13)
+
+struct ipuv3_fb_platform_data {
+ const struct fb_videomode *modes;
+ int num_modes;
+ char *mode_str;
+ u32 interface_pix_fmt;
+
+#define IMX_IPU_FB_USE_MODEDB (1 << 0)
+#define IMX_IPU_FB_USE_OVERLAY (1 << 1)
+ unsigned long flags;
+
+ int ipu_channel_bg;
+ int ipu_channel_fg;
+ int dc_channel;
+ int dp_channel;
+};
+
+struct imx_ipuv3_platform_data {
+ int rev;
+ struct ipuv3_fb_platform_data *fb0_platform_data;
+ struct ipuv3_fb_platform_data *fb1_platform_data;
+};
+
+#endif /* __MACH_IPU_V3_H_ */
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3a1493b..3c81879 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -606,6 +606,13 @@ config MFD_VX855
VIA VX855/VX875 south bridge. You will need to enable the vx855_spi
and/or vx855_gpio drivers for this to do anything useful.

+config MFD_IMX_IPU_V3
+ tristate "Support the Image Processing Unit (IPU) found on the i.MX51"
+ depends on ARCH_MX51
+ select MFD_CORE
+ help
+ Say yes here to support the IPU on i.MX51.
+
endif # MFD_SUPPORT

menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f54b365..7873b13 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -81,3 +81,4 @@ obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o
obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o
obj-$(CONFIG_MFD_VX855) += vx855.o
+obj-$(CONFIG_MFD_IMX_IPU_V3) += imx-ipu-v3/
diff --git a/drivers/mfd/imx-ipu-v3/Makefile b/drivers/mfd/imx-ipu-v3/Makefile
new file mode 100644
index 0000000..ff70fe8
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_MFD_IMX_IPU_V3) += imx-ipu-v3.o
+
+imx-ipu-v3-objs := ipu-common.o ipu-dc.o ipu-di.o ipu-dp.o ipu-dmfc.o ipu-cpmem.o
diff --git a/drivers/mfd/imx-ipu-v3/ipu-common.c b/drivers/mfd/imx-ipu-v3/ipu-common.c
new file mode 100644
index 0000000..e6edb88
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-common.c
@@ -0,0 +1,661 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]<mailto:[email protected]>>
+ * Copyright (C) 2005-2009 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.
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <linux/clk.h>
+#include <linux/list.h>
+#include <mach/common.h>
+#include <linux/mfd/core.h>
+#include <mach/ipu-v3.h>
+
+#include "ipu-prv.h"
+
+static struct clk *ipu_clk;
+static struct device *ipu_dev;
+
+static DEFINE_SPINLOCK(ipu_lock);
+void __iomem *ipu_cm_reg;
+void __iomem *ipu_idmac_reg;
+
+static struct ipu_channel channels[64];
+
+struct ipu_channel *ipu_idmac_get(unsigned num)
+{
+ struct ipu_channel *channel;
+
+ dev_dbg(ipu_dev, "%s %d\n", __func__, num);
+
+ if (num > 63)
+ return ERR_PTR(-ENODEV);
+
+ channel = &channels[num];
+
+ if (channel->busy)
+ return ERR_PTR(-EBUSY);
+
+ channel->busy = 1;
+ channel->num = num;
+
+ clk_enable(ipu_clk);
+
+ return channel;
+}
+EXPORT_SYMBOL(ipu_idmac_get);
+
+void ipu_idmac_put(struct ipu_channel *channel)
+{
+ dev_dbg(ipu_dev, "%s %d\n", __func__, channel->num);
+
+ clk_disable(ipu_clk);
+ channel->busy = 0;
+}
+EXPORT_SYMBOL(ipu_idmac_put);
+
+void ipu_idmac_set_double_buffer(struct ipu_channel *channel, bool doublebuffer)
+{
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&ipu_lock, flags);
+
+ reg = ipu_cm_read(IPU_CHA_DB_MODE_SEL(channel->num));
+ if (doublebuffer)
+ reg |= idma_mask(channel->num);
+ else
+ reg &= ~idma_mask(channel->num);
+ ipu_cm_write(reg, IPU_CHA_DB_MODE_SEL(channel->num));
+
+ spin_unlock_irqrestore(&ipu_lock, flags);
+}
+EXPORT_SYMBOL(ipu_idmac_set_double_buffer);
+
+void ipu_uninit_channel(struct ipu_channel *channel)
+{
+ unsigned long lock_flags;
+ u32 val;
+
+ /* Make sure channel is disabled */
+
+ if (idmac_idma_is_set(IDMAC_CHA_EN, channel->num)) {
+ dev_err(ipu_dev,
+ "Channel %d is not disabled, disable first\n",
+ channel->num);
+ return;
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ /* Reset the double buffer */
+ val = ipu_cm_read(IPU_CHA_DB_MODE_SEL(channel->num));
+ val &= ~idma_mask(channel->num);
+ ipu_cm_write(val, IPU_CHA_DB_MODE_SEL(channel->num));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+EXPORT_SYMBOL(ipu_uninit_channel);
+
+int ipu_module_enable(u32 mask)
+{
+ unsigned long lock_flags;
+ u32 ipu_conf;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ ipu_conf = ipu_cm_read(IPU_CONF);
+ ipu_conf |= mask;
+ ipu_cm_write(ipu_conf, IPU_CONF);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+
+int ipu_module_disable(u32 mask)
+{
+ unsigned long lock_flags;
+ u32 ipu_conf;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ ipu_conf = ipu_cm_read(IPU_CONF);
+ ipu_conf &= ~mask;
+ ipu_cm_write(ipu_conf, IPU_CONF);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+
+void ipu_idmac_select_buffer(struct ipu_channel *channel, u32 buf_num)
+{
+ unsigned int chno = channel->num;
+
+ /* Mark buffer as ready. */
+ if (buf_num == 0)
+ ipu_cm_write(idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
+ else
+ ipu_cm_write(idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
+}
+EXPORT_SYMBOL(ipu_idmac_select_buffer);
+
+int ipu_idmac_enable_channel(struct ipu_channel *channel)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu_lock, flags);
+
+ val = ipu_idmac_read(IDMAC_CHA_EN(channel->num));
+ val |= idma_mask(channel->num);
+ ipu_idmac_write(val, IDMAC_CHA_EN(channel->num));
+
+ spin_unlock_irqrestore(&ipu_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_idmac_enable_channel);
+
+int ipu_idmac_disable_channel(struct ipu_channel *channel)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu_lock, flags);
+
+ /* Disable DMA channel(s) */
+ val = ipu_idmac_read(IDMAC_CHA_EN(channel->num));
+ val &= ~idma_mask(channel->num);
+ ipu_idmac_write(val, IDMAC_CHA_EN(channel->num));
+
+ /* Set channel buffers NOT to be ready */
+ ipu_cm_write(0xf0000000, IPU_GPR); /* write one to clear */
+
+ if (ipu_idma_is_set(IPU_CHA_BUF0_RDY, channel->num)) {
+ ipu_cm_write(idma_mask(channel->num),
+ IPU_CHA_BUF0_RDY(channel->num));
+ }
+ if (ipu_idma_is_set(IPU_CHA_BUF1_RDY, channel->num)) {
+ ipu_cm_write(idma_mask(channel->num),
+ IPU_CHA_BUF1_RDY(channel->num));
+ }
+
+ ipu_cm_write(0x0, IPU_GPR); /* write one to set */
+
+ spin_unlock_irqrestore(&ipu_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_idmac_disable_channel);
+
+static LIST_HEAD(ipu_irq_handlers);
+
+static void ipu_irq_update_irq_mask(void)
+{
+ struct ipu_irq_handler *handler;
+ int i;
+
+ DECLARE_IPU_IRQ_BITMAP(irqs);
+
+ bitmap_zero(irqs, IPU_IRQ_COUNT);
+
+ list_for_each_entry(handler, &ipu_irq_handlers, list)
+ bitmap_or(irqs, irqs, handler->ipu_irqs, IPU_IRQ_COUNT);
+
+ for (i = 0; i < BITS_TO_LONGS(IPU_IRQ_COUNT); i++)
+ ipu_cm_write(irqs[i], IPU_INT_CTRL(i + 1));
+}
+
+int ipu_irq_add_handler(struct ipu_irq_handler *ipuirq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu_lock, flags);
+
+ list_add_tail(&ipuirq->list, &ipu_irq_handlers);
+ ipu_irq_update_irq_mask();
+
+ spin_unlock_irqrestore(&ipu_lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL(ipu_irq_add_handler);
+
+void ipu_irq_remove_handler(struct ipu_irq_handler *handler)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu_lock, flags);
+
+ list_del(&handler->list);
+ ipu_irq_update_irq_mask();
+
+ spin_unlock_irqrestore(&ipu_lock, flags);
+}
+EXPORT_SYMBOL(ipu_irq_remove_handler);
+
+int ipu_irq_update_handler(struct ipu_irq_handler *handler,
+ unsigned long *bitmap)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu_lock, flags);
+
+ bitmap_copy(handler->ipu_irqs, bitmap, IPU_IRQ_COUNT);
+ ipu_irq_update_irq_mask();
+
+ spin_unlock_irqrestore(&ipu_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_irq_update_handler);
+
+static void ipu_completion_handler(unsigned long *bitmask, void *context)
+{
+ struct completion *completion = context;
+
+ complete(completion);
+}
+
+int ipu_wait_for_interrupt(int interrupt, int timeout_ms)
+{
+ struct ipu_irq_handler handler;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ int ret;
+
+ bitmap_zero(handler.ipu_irqs, IPU_IRQ_COUNT);
+ bitmap_set(handler.ipu_irqs, interrupt, 1);
+
+ handler.handler = ipu_completion_handler;
+ handler.context = &completion;
+ ipu_irq_add_handler(&handler);
+
+ ret = wait_for_completion_timeout(&completion,
+ msecs_to_jiffies(timeout_ms));
+
+ ipu_irq_remove_handler(&handler);
+
+ if (ret > 0)
+ ret = 0;
+
+ return ret;
+}
+EXPORT_SYMBOL(ipu_wait_for_interrupt);
+
+static irqreturn_t ipu_irq_handler(int irq, void *desc)
+{
+ DECLARE_IPU_IRQ_BITMAP(status);
+ struct ipu_irq_handler *handler;
+ int i;
+
+ for (i = 0; i < BITS_TO_LONGS(IPU_IRQ_COUNT); i++) {
+ status[i] = ipu_cm_read(IPU_INT_STAT(i + 1));
+ ipu_cm_write(status[i], IPU_INT_STAT(i + 1));
+ }
+
+ list_for_each_entry(handler, &ipu_irq_handlers, list) {
+ DECLARE_IPU_IRQ_BITMAP(tmp);
+ if (bitmap_and(tmp, status, handler->ipu_irqs, IPU_IRQ_COUNT))
+ handler->handler(tmp, handler->context);
+ }
+
+ return IRQ_HANDLED;
+}
+
+ipu_color_space_t format_to_colorspace(u32 fmt)
+{
+ switch (fmt) {
+ case IPU_PIX_FMT_RGB666:
+ case IPU_PIX_FMT_RGB565:
+ case IPU_PIX_FMT_BGR24:
+ case IPU_PIX_FMT_RGB24:
+ 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:
+ return RGB;
+
+ default:
+ return YCbCr;
+ }
+}
+
+static int ipu_reset(void)
+{
+ int timeout = 10000;
+ u32 val;
+
+ /* hard reset the IPU */
+ val = readl(MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR));
+ val |= 1 << 3;
+ writel(val, MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR));
+
+ ipu_cm_write(0x807FFFFF, IPU_MEM_RST);
+
+ while (ipu_cm_read(IPU_MEM_RST) & 0x80000000) {
+ if (!timeout--)
+ return -ETIME;
+ udelay(100);
+ }
+
+ return 0;
+}
+
+/*
+ * The MIPI HSC unit has been removed from the i.MX51 Reference Manual by
+ * the Freescale marketing division. However this did not remove the
+ * hardware from the chip which still needs to be configured...
+ */
+static int __devinit ipu_mipi_setup(void)
+{
+ struct clk *hsc_clk;
+ void __iomem *hsc_addr;
+ int ret = 0;
+
+ hsc_addr = ioremap(MX51_MIPI_HSC_BASE_ADDR, PAGE_SIZE);
+ if (!hsc_addr)
+ return -ENOMEM;
+
+ hsc_clk = clk_get_sys(NULL, "mipi_hsp");
+ if (IS_ERR(hsc_clk)) {
+ ret = PTR_ERR(hsc_clk);
+ goto unmap;
+ }
+ clk_enable(hsc_clk);
+
+ /* setup MIPI module to legacy mode */
+ __raw_writel(0xF00, hsc_addr);
+
+ /* CSI mode: reserved; DI control mode: legacy (from Freescale BSP) */
+ __raw_writel(__raw_readl(hsc_addr + 0x800) | 0x30ff,
+ hsc_addr + 0x800);
+
+ clk_disable(hsc_clk);
+ clk_put(hsc_clk);
+unmap:
+ iounmap(hsc_addr);
+
+ return ret;
+}
+
+static int ipu_submodules_init(struct platform_device *pdev,
+ unsigned long ipu_base, struct clk *ipu_clk)
+{
+ char *unit;
+ int ret;
+
+ ret = ipu_di_init(pdev, 0, ipu_base + IPU_DI0_REG_BASE,
+ IPU_CONF_DI0_EN, ipu_clk);
+ if (ret) {
+ unit = "di0";
+ goto err_di_0;
+ }
+
+ ret = ipu_di_init(pdev, 1, ipu_base + IPU_DI1_REG_BASE,
+ IPU_CONF_DI1_EN, ipu_clk);
+ if (ret) {
+ unit = "di1";
+ goto err_di_1;
+ }
+
+ ret = ipu_dc_init(pdev, ipu_base + IPU_DC_REG_BASE,
+ ipu_base + IPU_DC_TMPL_REG_BASE);
+ if (ret) {
+ unit = "dc_template";
+ goto err_dc;
+ }
+
+ ret = ipu_dmfc_init(pdev, ipu_base + IPU_DMFC_REG_BASE, ipu_clk);
+ if (ret) {
+ unit = "dmfc";
+ goto err_dmfc;
+ }
+
+ ret = ipu_dp_init(pdev, ipu_base + IPU_SRM_REG_BASE);
+ if (ret) {
+ unit = "dp";
+ goto err_dp;
+ }
+
+ ret = ipu_cpmem_init(pdev, ipu_base + IPU_CPMEM_REG_BASE);
+ if (ret) {
+ unit = "cpmem";
+ goto err_cpmem;
+ }
+
+ return 0;
+
+err_cpmem:
+ ipu_dp_exit(pdev);
+err_dp:
+ ipu_dmfc_exit(pdev);
+err_dmfc:
+ ipu_dc_exit(pdev);
+err_dc:
+ ipu_di_exit(pdev, 1);
+err_di_1:
+ ipu_di_exit(pdev, 0);
+err_di_0:
+ dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
+ return ret;
+}
+
+static void ipu_submodules_exit(struct platform_device *pdev,
+ unsigned long ipu_base)
+{
+ ipu_cpmem_exit(pdev);
+ ipu_dp_exit(pdev);
+ ipu_dmfc_exit(pdev);
+ ipu_dc_exit(pdev);
+ ipu_di_exit(pdev, 1);
+ ipu_di_exit(pdev, 0);
+}
+
+static int ipu_add_subdevice_pdata(struct platform_device *pdev,
+ const char *name, int id, void *pdata, size_t pdata_size)
+{
+ struct mfd_cell cell = {
+ .platform_data = pdata,
+ .data_size = pdata_size,
+ };
+
+ cell.name<http://cell.name> = name;
+
+ return mfd_add_devices(&pdev->dev, id, &cell, 1, NULL, 0);
+}
+
+static int ipu_add_client_devices(struct platform_device *pdev)
+{
+ struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data;
+
+ if (plat_data->fb0_platform_data) {
+ plat_data->fb0_platform_data->ipu_channel_bg =
+ MX51_IPU_CHANNEL_MEM_BG_SYNC;
+ plat_data->fb0_platform_data->ipu_channel_fg =
+ MX51_IPU_CHANNEL_MEM_FG_SYNC;
+ plat_data->fb0_platform_data->dc_channel = 5;
+ plat_data->fb0_platform_data->dp_channel = IPU_DP_FLOW_SYNC;
+
+ ipu_add_subdevice_pdata(pdev, "imx-ipuv3-fb", 0,
+ plat_data->fb0_platform_data,
+ sizeof(struct ipuv3_fb_platform_data));
+ }
+
+ if (plat_data->fb1_platform_data) {
+ plat_data->fb1_platform_data->ipu_channel_bg =
+ MX51_IPU_CHANNEL_MEM_DC_SYNC;
+ plat_data->fb1_platform_data->ipu_channel_fg = -1;
+ plat_data->fb1_platform_data->dc_channel = 1;
+ plat_data->fb1_platform_data->dp_channel = -1;
+
+ ipu_add_subdevice_pdata(pdev, "imx-ipuv3-fb", 1,
+ plat_data->fb1_platform_data,
+ sizeof(struct ipuv3_fb_platform_data));
+ }
+
+ return 0;
+}
+
+static int __devinit ipu_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ unsigned long ipu_base;
+ int ret, irq1, irq2;
+
+ spin_lock_init(&ipu_lock);
+
+ ipu_dev = &pdev->dev;
+
+ irq1 = platform_get_irq(pdev, 0);
+ irq2 = platform_get_irq(pdev, 1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res || irq1 < 0 || irq2 < 0)
+ return -ENODEV;
+
+ ipu_base = res->start;
+
+ ipu_cm_reg = ioremap(ipu_base + IPU_CM_REG_BASE, PAGE_SIZE);
+ if (!ipu_cm_reg) {
+ ret = -ENOMEM;
+ goto failed_ioremap1;
+ }
+
+ ipu_idmac_reg = ioremap(ipu_base + IPU_IDMAC_REG_BASE, PAGE_SIZE);
+ if (!ipu_idmac_reg) {
+ ret = -ENOMEM;
+ goto failed_ioremap2;
+ }
+
+ ret = ipu_mipi_setup();
+ if (ret)
+ goto failed_mipi_setup;
+
+ ipu_clk = clk_get(&pdev->dev, "ipu");
+ if (IS_ERR(ipu_clk)) {
+ ret = PTR_ERR(ipu_clk);
+ dev_err(&pdev->dev, "clk_get failed with %d", ret);
+ goto failed_clk_get;
+ }
+
+ clk_enable(ipu_clk);
+
+ ret = request_irq(irq1, ipu_irq_handler, IRQF_DISABLED, pdev->name,
+ &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "request irq %d failed with: %d\n", irq1, ret);
+ goto failed_request_irq1;
+ }
+
+ ret = request_irq(irq2, ipu_irq_handler, IRQF_DISABLED, pdev->name,
+ &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "request irq %d failed with: %d\n", irq2, ret);
+ goto failed_request_irq2;
+ }
+
+ ipu_reset();
+
+ ret = ipu_submodules_init(pdev, ipu_base, ipu_clk);
+ if (ret)
+ goto failed_submodules_init;
+
+ /* Set sync refresh channels as high priority */
+ ipu_idmac_write(0x18800000, IDMAC_CHA_PRI(0));
+
+ ret = ipu_add_client_devices(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "adding client devices failed with %d\n", ret);
+ goto failed_add_clients;
+ }
+
[Jason] add clk_disable(ipu_clk)

+ return 0;
+
+failed_add_clients:
+ ipu_submodules_exit(pdev, ipu_base);
+failed_submodules_init:
+ free_irq(irq2, &pdev->dev);
+failed_request_irq2:
+ free_irq(irq1, &pdev->dev);
+failed_request_irq1:
+ clk_put(ipu_clk);
+failed_clk_get:
+failed_mipi_setup:
+ iounmap(ipu_idmac_reg);
+failed_ioremap2:
+ iounmap(ipu_cm_reg);
+failed_ioremap1:
+
+ return ret;
+}
+
+static int __devexit ipu_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+ unsigned long ipu_base;
+ int irq1, irq2;
+
+ irq1 = platform_get_irq(pdev, 0);
+ irq2 = platform_get_irq(pdev, 1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ipu_base = res->start;
+
+ mfd_remove_devices(&pdev->dev);
+ ipu_submodules_exit(pdev, ipu_base);
+ free_irq(irq2, &pdev->dev);
+ free_irq(irq1, &pdev->dev);
+ clk_disable(ipu_clk);
+ clk_put(ipu_clk);
+ iounmap(ipu_idmac_reg);
+ iounmap(ipu_cm_reg);
+
+ return 0;
+}
+
+static struct platform_driver mxcipu_driver = {
+ .driver = {
+ .name = "imx-ipuv3",
+ },
+ .probe = ipu_probe,
+ .remove = __devexit_p(ipu_remove),
+};
+
+static int __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);
+
+MODULE_DESCRIPTION("i.MX IPU v3 driver");
+MODULE_AUTHOR("Sascha Hauer <[email protected]<mailto:[email protected]>>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/imx-ipu-v3/ipu-cpmem.c b/drivers/mfd/imx-ipu-v3/ipu-cpmem.c
new file mode 100644
index 0000000..587b487
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-cpmem.c
@@ -0,0 +1,608 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]<mailto:[email protected]>>
+ * Copyright (C) 2005-2009 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <linux/types.h>
+#include <linux/bitrev.h>
+#include <linux/io.h>
+
+#include "ipu-prv.h"
+
+#define __F(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size))
+
+#define IPU_FIELD_XV __F(0, 0, 10)
+#define IPU_FIELD_YV __F(0, 10, 9)
+#define IPU_FIELD_XB __F(0, 19, 13)
+#define IPU_FIELD_YB __F(0, 32, 12)
+#define IPU_FIELD_NSB_B __F(0, 44, 1)
+#define IPU_FIELD_CF __F(0, 45, 1)
+#define IPU_FIELD_UBO __F(0, 46, 22)
+#define IPU_FIELD_VBO __F(0, 68, 22)
+#define IPU_FIELD_IOX __F(0, 90, 4)
+#define IPU_FIELD_RDRW __F(0, 94, 1)
+#define IPU_FIELD_SO __F(0, 113, 1)
+#define IPU_FIELD_BNDM __F(0, 114, 3)
+#define IPU_FIELD_BM __F(0, 117, 2)
+#define IPU_FIELD_ROT __F(0, 119, 1)
+#define IPU_FIELD_HF __F(0, 120, 1)
+#define IPU_FIELD_VF __F(0, 121, 1)
+#define IPU_FIELD_THE __F(0, 122, 1)
+#define IPU_FIELD_CAP __F(0, 123, 1)
+#define IPU_FIELD_CAE __F(0, 124, 1)
+#define IPU_FIELD_FW __F(0, 125, 13)
+#define IPU_FIELD_FH __F(0, 138, 12)
+#define IPU_FIELD_EBA0 __F(1, 0, 29)
+#define IPU_FIELD_EBA1 __F(1, 29, 29)
+#define IPU_FIELD_ILO __F(1, 58, 20)
+#define IPU_FIELD_NPB __F(1, 78, 7)
+#define IPU_FIELD_PFS __F(1, 85, 4)
+#define IPU_FIELD_ALU __F(1, 89, 1)
+#define IPU_FIELD_ALBM __F(1, 90, 3)
+#define IPU_FIELD_ID __F(1, 93, 2)
+#define IPU_FIELD_TH __F(1, 95, 7)
+#define IPU_FIELD_SLY __F(1, 102, 14)
+#define IPU_FIELD_WID3 __F(1, 125, 3)
+#define IPU_FIELD_SLUV __F(1, 128, 14)
+#define IPU_FIELD_CRE __F(1, 149, 1)
+
+#define IPU_FIELD_XV __F(0, 0, 10)
+#define IPU_FIELD_YV __F(0, 10, 9)
+#define IPU_FIELD_XB __F(0, 19, 13)
+#define IPU_FIELD_YB __F(0, 32, 12)
+#define IPU_FIELD_NSB_B __F(0, 44, 1)
+#define IPU_FIELD_CF __F(0, 45, 1)
+#define IPU_FIELD_SX __F(0, 46, 12)
+#define IPU_FIELD_SY __F(0, 58, 11)
+#define IPU_FIELD_NS __F(0, 69, 10)
+#define IPU_FIELD_SDX __F(0, 79, 7)
+#define IPU_FIELD_SM __F(0, 86, 10)
+#define IPU_FIELD_SCC __F(0, 96, 1)
+#define IPU_FIELD_SCE __F(0, 97, 1)
+#define IPU_FIELD_SDY __F(0, 98, 7)
+#define IPU_FIELD_SDRX __F(0, 105, 1)
+#define IPU_FIELD_SDRY __F(0, 106, 1)
+#define IPU_FIELD_BPP __F(0, 107, 3)
+#define IPU_FIELD_DEC_SEL __F(0, 110, 2)
+#define IPU_FIELD_DIM __F(0, 112, 1)
+#define IPU_FIELD_SO __F(0, 113, 1)
+#define IPU_FIELD_BNDM __F(0, 114, 3)
+#define IPU_FIELD_BM __F(0, 117, 2)
+#define IPU_FIELD_ROT __F(0, 119, 1)
+#define IPU_FIELD_HF __F(0, 120, 1)
+#define IPU_FIELD_VF __F(0, 121, 1)
+#define IPU_FIELD_THE __F(0, 122, 1)
+#define IPU_FIELD_CAP __F(0, 123, 1)
+#define IPU_FIELD_CAE __F(0, 124, 1)
+#define IPU_FIELD_FW __F(0, 125, 13)
+#define IPU_FIELD_FH __F(0, 138, 12)
+#define IPU_FIELD_EBA0 __F(1, 0, 29)
+#define IPU_FIELD_EBA1 __F(1, 29, 29)
+#define IPU_FIELD_ILO __F(1, 58, 20)
+#define IPU_FIELD_NPB __F(1, 78, 7)
+#define IPU_FIELD_PFS __F(1, 85, 4)
+#define IPU_FIELD_ALU __F(1, 89, 1)
+#define IPU_FIELD_ALBM __F(1, 90, 3)
+#define IPU_FIELD_ID __F(1, 93, 2)
+#define IPU_FIELD_TH __F(1, 95, 7)
+#define IPU_FIELD_SL __F(1, 102, 14)
+#define IPU_FIELD_WID0 __F(1, 116, 3)
+#define IPU_FIELD_WID1 __F(1, 119, 3)
+#define IPU_FIELD_WID2 __F(1, 122, 3)
+#define IPU_FIELD_WID3 __F(1, 125, 3)
+#define IPU_FIELD_OFS0 __F(1, 128, 5)
+#define IPU_FIELD_OFS1 __F(1, 133, 5)
+#define IPU_FIELD_OFS2 __F(1, 138, 5)
+#define IPU_FIELD_OFS3 __F(1, 143, 5)
+#define IPU_FIELD_SXYS __F(1, 148, 1)
+#define IPU_FIELD_CRE __F(1, 149, 1)
+#define IPU_FIELD_DEC_SEL2 __F(1, 150, 1)
+
+static u32 *ipu_cpmem_base;
+static struct device *ipu_dev;
+
+struct ipu_ch_param_word {
+ u32 data[5];
+ u32 res[3];
+};
+
+struct ipu_ch_param {
+ struct ipu_ch_param_word word[2];
+};
+
+static u32 ipu_bytes_per_pixel(u32 fmt)
+{
+ switch (fmt) {
+ case IPU_PIX_FMT_GENERIC: /* generic data */
+ case IPU_PIX_FMT_RGB332:
+ case IPU_PIX_FMT_YUV420P:
+ case IPU_PIX_FMT_YUV422P:
+ return 1;
+
+ case IPU_PIX_FMT_RGB565:
+ case IPU_PIX_FMT_YUYV:
+ case IPU_PIX_FMT_UYVY:
+ return 2;
+
+ case IPU_PIX_FMT_BGR24:
+ case IPU_PIX_FMT_RGB24:
+ return 3;
+
+ 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:
+ return 4;
+
+ default:
+ return 1;
+ }
+}
+
+bool ipu_pixel_format_has_alpha(u32 fmt)
+{
+ switch (fmt) {
+ case IPU_PIX_FMT_RGBA32:
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_ABGR32:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+#define ipu_ch_param_addr(ch) (((struct ipu_ch_param *)ipu_cpmem_base) + (ch))
+
+static inline void ipu_ch_param_set_field(struct ipu_ch_param *base, u32 wbs, u32 v)
+{
+ u32 bit = (wbs >> 8) % 160;
+ u32 size = wbs & 0xff;
+ u32 word = (wbs >> 8) / 160;
+ u32 i = bit / 32;
+ u32 ofs = bit % 32;
+ u32 mask = (1 << size) - 1;
+
+ pr_debug("%s %d %d %d\n", __func__, word, bit , size);
+
+ base->word[word].data[i] &= ~(mask << ofs);
+ base->word[word].data[i] |= v << ofs;
+
+ if ((bit + size - 1) / 32 > i) {
+ base->word[word].data[i + 1] &= ~(v >> (mask ? (32 - ofs) : 0));
+ base->word[word].data[i + 1] |= v >> (ofs ? (32 - ofs) : 0);
+ }
+}
+
+static inline u32 ipu_ch_param_read_field(struct ipu_ch_param *base, u32 wbs)
+{
+ u32 bit = (wbs >> 8) % 160;
+ u32 size = wbs & 0xff;
+ u32 word = (wbs >> 8) / 160;
+ u32 i = bit / 32;
+ u32 ofs = bit % 32;
+ u32 mask = (1 << size) - 1;
+ u32 val = 0;
+
+ pr_debug("%s %d %d %d\n", __func__, word, bit , size);
+
+ val = (base->word[word].data[i] >> ofs) & mask;
+
+ if ((bit + size - 1) / 32 > i) {
+ u32 tmp;
+ tmp = base->word[word].data[i + 1];
+ tmp &= mask >> (ofs ? (32 - ofs) : 0);
+ val |= tmp << (ofs ? (32 - ofs) : 0);
+ }
+
+ return val;
+}
+
+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, IPU_FIELD_WID0, red_width - 1);
+ ipu_ch_param_set_field(p, IPU_FIELD_OFS0, red_offset);
+ /* Setup green width and offset */
+ ipu_ch_param_set_field(p, IPU_FIELD_WID1, green_width - 1);
+ ipu_ch_param_set_field(p, IPU_FIELD_OFS1, green_offset);
+ /* Setup blue width and offset */
+ ipu_ch_param_set_field(p, IPU_FIELD_WID2, blue_width - 1);
+ ipu_ch_param_set_field(p, IPU_FIELD_OFS2, blue_offset);
+ /* Setup alpha width and offset */
+ ipu_ch_param_set_field(p, IPU_FIELD_WID3, alpha_width - 1);
+ ipu_ch_param_set_field(p, IPU_FIELD_OFS3, alpha_offset);
+}
+
+static inline void ipu_ch_param_dump(int ch)
+{
+ struct ipu_ch_param *p = ipu_ch_param_addr(ch);
+ pr_debug("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]);
+ pr_debug("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]);
+ pr_debug("PFS 0x%x\n", ipu_ch_param_read_field(p, IPU_FIELD_PFS));
+ pr_debug("BPP 0x%x\n", ipu_ch_param_read_field(p, IPU_FIELD_BPP));
+ pr_debug("NPB 0x%x\n", ipu_ch_param_read_field(p, IPU_FIELD_NPB));
+
+ pr_debug("FW %d\n", ipu_ch_param_read_field(p, IPU_FIELD_FW));
+ pr_debug("FH %d\n", ipu_ch_param_read_field(p, IPU_FIELD_FH));
+ pr_debug("Stride %d\n", ipu_ch_param_read_field(p, IPU_FIELD_SL));
+
+ pr_debug("Width0 %d+1\n", ipu_ch_param_read_field(p, IPU_FIELD_WID0));
+ pr_debug("Width1 %d+1\n", ipu_ch_param_read_field(p, IPU_FIELD_WID1));
+ pr_debug("Width2 %d+1\n", ipu_ch_param_read_field(p, IPU_FIELD_WID2));
+ pr_debug("Width3 %d+1\n", ipu_ch_param_read_field(p, IPU_FIELD_WID3));
+ pr_debug("Offset0 %d\n", ipu_ch_param_read_field(p, IPU_FIELD_OFS0));
+ pr_debug("Offset1 %d\n", ipu_ch_param_read_field(p, IPU_FIELD_OFS1));
+ pr_debug("Offset2 %d\n", ipu_ch_param_read_field(p, IPU_FIELD_OFS2));
+ pr_debug("Offset3 %d\n", ipu_ch_param_read_field(p, IPU_FIELD_OFS3));
+}
+
+static inline void ipu_ch_param_set_burst_size(u32 ch,
+ u16 burst_pixels)
+{
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_NPB,
+ burst_pixels - 1);
+};
+
+static inline int ipu_ch_param_get_burst_size(u32 ch)
+{
+ return ipu_ch_param_read_field(ipu_ch_param_addr(ch), IPU_FIELD_NPB) + 1;
+};
+
+static inline int ipu_ch_param_get_bpp(u32 ch)
+{
+ return ipu_ch_param_read_field(ipu_ch_param_addr(ch), IPU_FIELD_BPP);
+};
+
+static inline void ipu_ch_param_set_buffer(u32 ch, int bufNum,
+ dma_addr_t phyaddr)
+{
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch),
+ bufNum ? IPU_FIELD_EBA1 : IPU_FIELD_EBA0,
+ phyaddr / 8);
+};
+
+#define IPU_FIELD_ROT_HF_VF __F(0, 119, 3)
+
+static inline void ipu_ch_param_set_rotation(u32 ch,
+ ipu_rotate_mode_t rot)
+{
+ u32 temp_rot = bitrev8(rot) >> 5;
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ROT_HF_VF, temp_rot);
+};
+
+static inline void ipu_ch_param_set_block_mode(u32 ch)
+{
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_BM, 1);
+};
+
+static inline void ipu_ch_param_set_alpha_use_separate_channel(u32 ch,
+ bool option)
+{
+ if (option)
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ALU, 1);
+ else
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ALU, 0);
+};
+
+static inline void ipu_ch_param_set_alpha_condition_read(u32 ch)
+{
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_CRE, 1);
+};
+
+static inline void ipu_ch_param_set_alpha_buffer_memory(u32 ch)
+{
+ int alp_mem_idx;
+
+ 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 correlated channel of local alpha channel\n");
+ return;
+ }
+
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ALBM, alp_mem_idx);
+};
+
+static inline void ipu_ch_param_set_interlaced_scan(u32 ch)
+{
+ u32 stride;
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_SO, 1);
+ stride = ipu_ch_param_read_field(ipu_ch_param_addr(ch), IPU_FIELD_SL) + 1;
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ILO, stride / 8);
+ stride *= 2;
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_SLY, stride - 1);
+};
+
+static inline void ipu_ch_param_set_high_priority(u32 ch)
+{
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_ID, 1);
+};
+
+static inline void ipu_ch_params_set_alpha_width(u32 ch, int alpha_width)
+{
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), IPU_FIELD_WID3,
+ alpha_width - 1);
+}
+
+static int ipu_ch_param_init(int ch,
+ u32 pixel_fmt, u32 width,
+ u32 height, u32 stride,
+ u32 u, u32 v,
+ u32 uv_stride, dma_addr_t addr0,
+ dma_addr_t addr1)
+{
+ u32 u_offset = 0;
+ u32 v_offset = 0;
+ struct ipu_ch_param params;
+
+ memset(&params, 0, sizeof(params));
+
+ ipu_ch_param_set_field(&params, IPU_FIELD_FW, width - 1);
+ ipu_ch_param_set_field(&params, IPU_FIELD_FH, height - 1);
+ ipu_ch_param_set_field(&params, IPU_FIELD_SLY, stride - 1);
+ ipu_ch_param_set_field(&params, IPU_FIELD_EBA0, addr0 >> 3);
+ ipu_ch_param_set_field(&params, IPU_FIELD_EBA1, addr1 >> 3);
+
+ switch (pixel_fmt) {
+ case IPU_PIX_FMT_GENERIC:
+ /* Represents 8-bit Generic data */
+ ipu_ch_param_set_field(&params, IPU_FIELD_BPP, 5); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 6); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 63); /* 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, IPU_FIELD_BPP, 3); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 15); /* burst size */
+
+ ipu_ch_params_set_packing(&params, 5, 0, 6, 5, 5, 11, 8, 16);
+ break;
+ case IPU_PIX_FMT_BGR24:
+ ipu_ch_param_set_field(&params, IPU_FIELD_BPP, 1); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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, IPU_FIELD_BPP, 1); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 19); /* burst size */
+
+ ipu_ch_params_set_packing(&params, 8, 16, 8, 8, 8, 0, 8, 24);
+ break;
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_BGR32:
+ ipu_ch_param_set_field(&params, IPU_FIELD_BPP, 0); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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, IPU_FIELD_BPP, 0); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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, IPU_FIELD_BPP, 0); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 7); /* pix format */
+
+ 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, IPU_FIELD_BPP, 3); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 0xA); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 15); /* burst size */
+ break;
+ case IPU_PIX_FMT_YUYV:
+ ipu_ch_param_set_field(&params, IPU_FIELD_BPP, 3); /* bits/pixel */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 0x8); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 31); /* burst size */
+ break;
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YUV420P:
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 2); /* pix format */
+
+ if (uv_stride < stride / 2)
+ uv_stride = stride / 2;
+
+ u_offset = stride * height;
+ v_offset = u_offset + (uv_stride * height / 2);
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 31); /* burst size */
+ break;
+ case IPU_PIX_FMT_YVU422P:
+ /* BPP & pixel format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 1); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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, IPU_FIELD_PFS, 1); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 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_NV12:
+ /* BPP & pixel format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_PFS, 4); /* pix format */
+ ipu_ch_param_set_field(&params, IPU_FIELD_NPB, 31); /* burst size */
+ uv_stride = stride;
+ u_offset = (u == 0) ? stride * height : u;
+ break;
+ default:
+ dev_err(ipu_dev, "mxc ipu: unimplemented pixel format: %d\n",
+ pixel_fmt);
+ return -EINVAL;
+ }
+ /* set burst size to 16 */
+ if (uv_stride)
+ ipu_ch_param_set_field(&params, IPU_FIELD_SLUV, uv_stride - 1);
+
+ if (u > u_offset)
+ u_offset = u;
+
+ if (v > v_offset)
+ v_offset = v;
+
+ ipu_ch_param_set_field(&params, IPU_FIELD_UBO, u_offset / 8);
+ ipu_ch_param_set_field(&params, IPU_FIELD_VBO, v_offset / 8);
+
+ pr_debug("initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ch));
+ memcpy(ipu_ch_param_addr(ch), &params, sizeof(params));
+ return 0;
+}
+
+/*
+ * This function is called to initialize a buffer for a IPU channel.
+ *
+ * @param channel The IPU channel.
+ *
+ * @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 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
+ */
+int ipu_idmac_init_channel_buffer(struct ipu_channel *channel,
+ u32 pixel_fmt,
+ u16 width, u16 height,
+ u32 stride,
+ ipu_rotate_mode_t rot_mode,
+ dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
+ u32 u, u32 v, bool interlaced)
+{
+ int ret;
+ u32 dma_chan = channel->num;
+
+ if (stride < width * ipu_bytes_per_pixel(pixel_fmt))
+ stride = width * ipu_bytes_per_pixel(pixel_fmt);
+
+ if (stride % 4) {
+ dev_err(ipu_dev,
+ "Stride not 32-bit aligned, stride = %d\n", stride);
+ return -EINVAL;
+ }
+
+ /* Build parameter memory data for DMA channel */
+ ret = ipu_ch_param_init(dma_chan, pixel_fmt, width, height, stride, u, v, 0,
+ phyaddr_0, phyaddr_1);
+ if (ret)
+ return ret;
+
+ if (rot_mode)
+ ipu_ch_param_set_rotation(dma_chan, rot_mode);
+
+ if (interlaced)
+ ipu_ch_param_set_interlaced_scan(dma_chan);
+
+ if (idmac_idma_is_set(IDMAC_CHA_PRI, dma_chan))
+ ipu_ch_param_set_high_priority(dma_chan);
+
+ ipu_ch_param_dump(dma_chan);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_idmac_init_channel_buffer);
+
+int ipu_idmac_update_channel_buffer(struct ipu_channel *channel,
+ u32 buf_num, dma_addr_t phyaddr)
+{
+ u32 dma_chan = channel->num;
+
+ ipu_ch_param_set_buffer(dma_chan, buf_num, phyaddr);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_idmac_update_channel_buffer);
+
+int ipu_cpmem_init(struct platform_device *pdev, unsigned long base)
+{
+ ipu_cpmem_base = ioremap(base, PAGE_SIZE);
+ if (!ipu_cpmem_base)
+ return -ENOMEM;
+ ipu_dev = &pdev->dev;
+ return 0;
+}
+
+void ipu_cpmem_exit(struct platform_device *pdev)
+{
+ iounmap(ipu_cpmem_base);
+}
diff --git a/drivers/mfd/imx-ipu-v3/ipu-dc.c b/drivers/mfd/imx-ipu-v3/ipu-dc.c
new file mode 100644
index 0000000..23ba5d7
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-dc.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]<mailto:[email protected]>>
+ * Copyright (C) 2005-2009 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.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <asm/atomic.h>
+
+#include "ipu-prv.h"
+
+#define ASYNC_SER_WAVE 6
+
+#define DC_DISP_ID_SERIAL 2
+#define DC_DISP_ID_ASYNC 3
+
+#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 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_WR_CH_CONF(ch) (ipu_dc_reg + dc_channels[ch].channel_offset)
+#define DC_WR_CH_ADDR(ch) (ipu_dc_reg + dc_channels[ch].channel_offset + 4)
+#define DC_RL_CH(ch, evt) (ipu_dc_reg + dc_channels[ch].channel_offset + 8 + ((evt) & ~0x1) * 2)
+
+#define DC_GEN (ipu_dc_reg + 0x00D4)
+#define DC_DISP_CONF1(disp) (ipu_dc_reg + 0x00D8 + disp * 4)
+#define DC_DISP_CONF2(disp) (ipu_dc_reg + 0x00E8 + disp * 4)
+#define DC_STAT (ipu_dc_reg + 0x01C8)
+
+#define WROD(lf) (0x18 | (lf << 1))
+
+#define DC_WR_CH_CONF_FIELD_MODE (1 << 9)
+#define DC_WR_CH_CONF_PROG_TYPE_OFFSET 5
+#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5)
+#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2)
+#define DC_WR_CH_CONF_PROG_DISP_ID_OFFSET 3
+#define DC_WR_CH_CONF_PROG_DISP_ID_MASK (3 << 3)
+
+static void __iomem *ipu_dc_reg;
+static void __iomem *ipu_dc_tmpl_reg;
+static struct device *ipu_dev;
+
+struct ipu_dc {
+ unsigned int di; /* The display interface number assigned to this dc channel */
+ unsigned int channel_offset;
+};
+
+static struct ipu_dc dc_channels[10];
+
+static void ipu_dc_link_event(int chan, int event, int addr, int priority)
+{
+ u32 reg;
+
+ reg = __raw_readl(DC_RL_CH(chan, event));
+ reg &= ~(0xFFFF << (16 * (event & 0x1)));
+ reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
+ __raw_writel(reg, DC_RL_CH(chan, event));
+}
+
+static void ipu_dc_write_tmpl(int word, u32 opcode, u32 operand, int map,
+ int wave, int glue, int sync)
+{
+ u32 reg;
+ int stop = 1;
+
+ reg = sync;
+ reg |= (glue << 4);
+ reg |= (++wave << 11);
+ reg |= (++map << 15);
+ reg |= (operand << 20) & 0xFFF00000;
+ __raw_writel(reg, ipu_dc_tmpl_reg + word * 8);
+
+ reg = (operand >> 12);
+ reg |= opcode << 4;
+ reg |= (stop << 9);
+ __raw_writel(reg, ipu_dc_tmpl_reg + word * 8 + 4);
+}
+
+static int ipu_pixfmt_to_map(u32 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;
+ }
+
+ return -EINVAL;
+}
+
+#define SYNC_WAVE 0
+
+int ipu_dc_init_sync(int dc_chan, int di, bool interlaced, u32 pixel_fmt, u32 width)
+{
+ u32 reg = 0, map;
+
+ dc_channels[dc_chan].di = di;
+
+ map = ipu_pixfmt_to_map(pixel_fmt);
+ if (map < 0) {
+ dev_dbg(ipu_dev, "IPU_DISP: No MAP\n");
+ return -EINVAL;
+ }
+
+ if (interlaced) {
+ ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 3);
+ ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 2);
+ ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 1);
+
+ /* Init template microcode */
+ ipu_dc_write_tmpl(0, WROD(0), 0, map, SYNC_WAVE, 0, 8);
+ } else {
+ if (di) {
+ ipu_dc_link_event(dc_chan, DC_EVT_NL, 2, 3);
+ ipu_dc_link_event(dc_chan, DC_EVT_EOL, 3, 2);
+ ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 4, 1);
+ /* Init template microcode */
+ ipu_dc_write_tmpl(2, WROD(0), 0, map, SYNC_WAVE, 8, 5);
+ ipu_dc_write_tmpl(3, WROD(0), 0, map, SYNC_WAVE, 4, 5);
+ ipu_dc_write_tmpl(4, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+ } else {
+ ipu_dc_link_event(dc_chan, DC_EVT_NL, 5, 3);
+ ipu_dc_link_event(dc_chan, DC_EVT_EOL, 6, 2);
+ ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 7, 1);
+ /* Init template microcode */
+ ipu_dc_write_tmpl(5, WROD(0), 0, map, SYNC_WAVE, 8, 5);
+ ipu_dc_write_tmpl(6, WROD(0), 0, map, SYNC_WAVE, 4, 5);
+ ipu_dc_write_tmpl(7, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+ }
+ }
+ ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0);
+ ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0);
+ ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0);
+ ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0);
+ ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0);
+ ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0);
+
+ reg = 0x2;
+ reg |= di << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET;
+ reg |= di << 2;
+ if (interlaced)
+ reg |= DC_WR_CH_CONF_FIELD_MODE;
+
+ __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+
+ __raw_writel(0x00000000, DC_WR_CH_ADDR(dc_chan));
+
+ __raw_writel(0x00000084, DC_GEN);
+
+ __raw_writel(width, DC_DISP_CONF2(di));
+
+ ipu_module_enable(IPU_CONF_DC_EN);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dc_init_sync);
+
+void ipu_dc_init_async(int dc_chan, int di, bool interlaced)
+{
+ u32 reg = 0;
+ dc_channels[dc_chan].di = di;
+ ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0x64, 1);
+ ipu_dc_link_event(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;
+ __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+
+ __raw_writel(0x00000000, DC_WR_CH_ADDR(dc_chan));
+
+ __raw_writel(0x00000084, DC_GEN);
+
+ ipu_module_enable(IPU_CONF_DC_EN);
+}
+EXPORT_SYMBOL(ipu_dc_init_async);
+
+void ipu_dc_enable_channel(u32 dc_chan)
+{
+ int di;
+ u32 reg;
+
+ di = dc_channels[dc_chan].di;
+
+ /* Make sure other DC sync channel is not assigned same DI */
+ reg = __raw_readl(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;
+ __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan));
+ }
+
+ reg = __raw_readl(DC_WR_CH_CONF(dc_chan));
+ reg |= 4 << DC_WR_CH_CONF_PROG_TYPE_OFFSET;
+ __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+}
+EXPORT_SYMBOL(ipu_dc_enable_channel);
+
+void ipu_dc_disable_channel(u32 dc_chan)
+{
+ u32 reg;
+ int irq = 0, ret, timeout = 50;
+
+ if (dc_chan == 1) {
+ irq = IPU_IRQ_DC_FC_1;
+ } else if (dc_chan == 5) {
+ irq = IPU_IRQ_DP_SF_END;
+ } else {
+ return;
+ }
+
+ ret = ipu_wait_for_interrupt(irq, 50);
+ if (ret)
+ return;
+
+ /* Wait for DC triple buffer to empty */
+ if (dc_channels[dc_chan].di == 0)
+ while ((__raw_readl(DC_STAT) & 0x00000002)
+ != 0x00000002) {
+ msleep(2);
+ timeout -= 2;
+ if (timeout <= 0)
+ break;
+ }
+ else if (dc_channels[dc_chan].di == 1)
+ while ((__raw_readl(DC_STAT) & 0x00000020)
+ != 0x00000020) {
+ msleep(2);
+ timeout -= 2;
+ if (timeout <= 0)
+ break;
+ }
+
+ reg = __raw_readl(DC_WR_CH_CONF(dc_chan));
+ reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+ __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+}
+EXPORT_SYMBOL(ipu_dc_disable_channel);
+
+static void ipu_dc_map_config(int map, int byte_num, int offset, int mask)
+{
+ int ptr = map * 3 + byte_num;
+ u32 reg;
+
+ reg = __raw_readl(DC_MAP_CONF_VAL(ptr));
+ reg &= ~(0xffff << (16 * (ptr & 0x1)));
+ reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
+ __raw_writel(reg, DC_MAP_CONF_VAL(ptr));
+
+ reg = __raw_readl(DC_MAP_CONF_PTR(map));
+ reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num)));
+ reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
+ __raw_writel(reg, DC_MAP_CONF_PTR(map));
+}
+
+static void ipu_dc_map_clear(int map)
+{
+ u32 reg = __raw_readl(DC_MAP_CONF_PTR(map));
+ __raw_writel(reg & ~(0xffff << (16 * (map & 0x1))),
+ DC_MAP_CONF_PTR(map));
+}
+
+int ipu_dc_init(struct platform_device *pdev, unsigned long base, unsigned long template_base)
+{
+ static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, 0x78, 0, 0x94, 0xb4};
+ int i;
+
+ ipu_dc_reg = ioremap(base, PAGE_SIZE);
+ if (!ipu_dc_reg)
+ return -ENOMEM;
+
+ ipu_dev = &pdev->dev;
+
+ ipu_dc_tmpl_reg = ioremap(template_base, PAGE_SIZE);
+ if (!ipu_dc_tmpl_reg) {
+ iounmap(ipu_dc_reg);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < 10; i++)
+ dc_channels[i].channel_offset = channel_offsets[i];
+
+ /* IPU_PIX_FMT_RGB24 */
+ ipu_dc_map_clear(0);
+ ipu_dc_map_config(0, 0, 7, 0xff);
+ ipu_dc_map_config(0, 1, 15, 0xff);
+ ipu_dc_map_config(0, 2, 23, 0xff);
+
+ /* IPU_PIX_FMT_RGB666 */
+ ipu_dc_map_clear(1);
+ ipu_dc_map_config(1, 0, 5, 0xfc);
+ ipu_dc_map_config(1, 1, 11, 0xfc);
+ ipu_dc_map_config(1, 2, 17, 0xfc);
+
+ /* IPU_PIX_FMT_YUV444 */
+ ipu_dc_map_clear(2);
+ ipu_dc_map_config(2, 0, 15, 0xff);
+ ipu_dc_map_config(2, 1, 23, 0xff);
+ ipu_dc_map_config(2, 2, 7, 0xff);
+
+ /* IPU_PIX_FMT_RGB565 */
+ ipu_dc_map_clear(3);
+ ipu_dc_map_config(3, 0, 4, 0xf8);
+ ipu_dc_map_config(3, 1, 10, 0xfc);
+ ipu_dc_map_config(3, 2, 15, 0xf8);
+
+ /* IPU_PIX_FMT_LVDS666 */
+ ipu_dc_map_clear(4);
+ ipu_dc_map_config(4, 0, 5, 0xfc);
+ ipu_dc_map_config(4, 1, 13, 0xfc);
+ ipu_dc_map_config(4, 2, 21, 0xfc);
+
+ return 0;
+}
+
+void ipu_dc_exit(struct platform_device *pdev)
+{
+ iounmap(ipu_dc_reg);
+ iounmap(ipu_dc_tmpl_reg);
+}
diff --git a/drivers/mfd/imx-ipu-v3/ipu-di.c b/drivers/mfd/imx-ipu-v3/ipu-di.c
new file mode 100644
index 0000000..73ebd51
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-di.c
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]<mailto:[email protected]>>
+ * Copyright (C) 2005-2009 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.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <linux/platform_device.h>
+#include <asm/atomic.h>
+
+#include "ipu-prv.h"
+
+#define SYNC_WAVE 0
+
+#define DC_DISP_ID_SYNC(di) (di)
+
+struct ipu_di {
+ void __iomem *base;
+ int id;
+ u32 module;
+ struct clk *clk;
+ struct clk *ipu_clk;
+ bool external_clk;
+};
+
+static struct ipu_di dis[2];
+
+static DEFINE_MUTEX(di_mutex);
+static struct device *ipu_dev;
+
+struct di_sync_config {
+ 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;
+};
+
+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 = 0,
+ DI_SYNC_CLK = 1,
+ DI_SYNC_INT_HSYNC = 2,
+ DI_SYNC_HSYNC = 3,
+ DI_SYNC_VSYNC = 4,
+ DI_SYNC_DE = 6,
+};
+
+#define DI_GENERAL 0x0000
+#define DI_BS_CLKGEN0 0x0004
+#define DI_BS_CLKGEN1 0x0008
+#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1))
+#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1))
+#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2))
+#define DI_SYNC_AS_GEN 0x0054
+#define DI_DW_GEN(gen) (0x0058 + 4 * (gen))
+#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set)))
+#define DI_SER_CONF 0x015c
+#define DI_SSC 0x0160
+#define DI_POL 0x0164
+#define DI_AW0 0x0168
+#define DI_AW1 0x016c
+#define DI_SCR_CONF 0x0170
+#define DI_STAT 0x0174
+
+#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19)
+#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16)
+#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3)
+#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0)
+
+#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29)
+#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25)
+#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12)
+#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9)
+#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16)
+#define DI_SW_GEN1_CNT_UP(x) (x)
+#define DI_SW_GEN1_AUTO_RELOAD (0x10000000)
+
+#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24
+#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16
+
+#define DI_GEN_DI_CLK_EXT (1 << 20)
+#define DI_GEN_POLARITY_1 (1 << 0)
+#define DI_GEN_POLARITY_2 (1 << 1)
+#define DI_GEN_POLARITY_3 (1 << 2)
+#define DI_GEN_POLARITY_4 (1 << 3)
+#define DI_GEN_POLARITY_5 (1 << 4)
+#define DI_GEN_POLARITY_6 (1 << 5)
+#define DI_GEN_POLARITY_7 (1 << 6)
+#define DI_GEN_POLARITY_8 (1 << 7)
+
+#define DI_POL_DRDY_DATA_POLARITY (1 << 7)
+#define DI_POL_DRDY_POLARITY_15 (1 << 4)
+
+#define DI_VSYNC_SEL_OFFSET 13
+
+#define DI0_COUNTER_RELEASE (1 << 24)
+#define DI1_COUNTER_RELEASE (1 << 25)
+
+static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset)
+{
+ return __raw_readl(di->base + offset);
+}
+
+static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset)
+{
+ __raw_writel(value, di->base + offset);
+}
+
+static void ipu_di_data_wave_config(struct ipu_di *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(di, reg, DI_DW_GEN(wave_gen));
+}
+
+static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin, int set,
+ int up, int down)
+{
+ u32 reg;
+
+ reg = ipu_di_read(di, DI_DW_GEN(wave_gen));
+ reg &= ~(0x3 << (di_pin * 2));
+ reg |= set << (di_pin * 2);
+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen));
+
+ ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set));
+}
+
+static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, int count)
+{
+ u32 reg;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ struct di_sync_config *c = &config[i];
+ int wave_gen = i + 1;
+
+ pr_debug("%s %d\n", __func__, wave_gen);
+ if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) || (c->repeat_count >= 0x1000) ||
+ (c->cnt_up >= 0x400) || (c->cnt_down >= 0x400)) {
+ dev_err(ipu_dev, "DI%d counters out of range.\n", di->id);
+ return;
+ }
+
+ reg = DI_SW_GEN0_RUN_COUNT(c->run_count) |
+ DI_SW_GEN0_RUN_SRC(c->run_src) |
+ DI_SW_GEN0_OFFSET_COUNT(c->offset_count) |
+ DI_SW_GEN0_OFFSET_SRC(c->offset_src);
+ ipu_di_write(di, reg, DI_SW_GEN0(wave_gen));
+
+ reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) |
+ DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) |
+ DI_SW_GEN1_CNT_POL_TRIGGER_SRC(c->cnt_polarity_trigger_src) |
+ DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) |
+ DI_SW_GEN1_CNT_DOWN(c->cnt_down) |
+ DI_SW_GEN1_CNT_UP(c->cnt_up);
+
+ if (c->repeat_count == 0) {
+ /* Enable auto reload */
+ reg |= DI_SW_GEN1_AUTO_RELOAD;
+ }
+
+ ipu_di_write(di, reg, DI_SW_GEN1(wave_gen));
+
+ reg = ipu_di_read(di, DI_STP_REP(wave_gen));
+ reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1)));
+ reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1));
+ ipu_di_write(di, reg, DI_STP_REP(wave_gen));
+ }
+}
+
+static void ipu_di_sync_config_interlaced(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
+{
+ u32 h_total = sig->width + sig->h_sync_width + sig->h_start_width + sig->h_end_width;
+ u32 v_total = sig->height + sig->v_sync_width + sig->v_start_width + sig->v_end_width;
+ u32 reg;
+ struct di_sync_config cfg[] = {
+ {
+ .run_count = h_total / 2 - 1,
+ .run_src = DI_SYNC_CLK,
+ }, {
+ .run_count = h_total - 11,
+ .run_src = DI_SYNC_CLK,
+ .cnt_down = 4,
+ }, {
+ .run_count = v_total * 2 - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = 1,
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = 4,
+ }, {
+ .run_count = v_total / 2 - 1,
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = sig->v_start_width,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = 2,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ }, {
+ .run_src = DI_SYNC_HSYNC,
+ .repeat_count = sig->height / 2,
+ .cnt_clr_src = 4,
+ }, {
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_HSYNC,
+ }, {
+ .run_count = v_total / 2 - 1,
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = 9,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = 2,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ }, {
+ .run_src = DI_SYNC_CLK,
+ .offset_count = sig->h_start_width,
+ .offset_src = DI_SYNC_CLK,
+ .repeat_count = sig->width,
+ .cnt_clr_src = 5,
+ }, {
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = v_total / 2,
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_clr_src = DI_SYNC_HSYNC,
+ .cnt_down = 4,
+ }
+ };
+
+ ipu_di_sync_config(di, cfg, ARRAY_SIZE(cfg));
+
+ /* set gentime select and tag sel */
+ reg = ipu_di_read(di, DI_SW_GEN1(9));
+ reg &= 0x1FFFFFFF;
+ reg |= (3 - 1) << 29 | 0x00008000;
+ ipu_di_write(di, reg, DI_SW_GEN1(9));
+
+ ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);
+}
+
+static void ipu_di_sync_config_noninterlaced(struct ipu_di *di,
+ struct ipu_di_signal_cfg *sig, int div)
+{
+ u32 h_total = sig->width + sig->h_sync_width + sig->h_start_width +
+ sig->h_end_width;
+ u32 v_total = sig->height + sig->v_sync_width + sig->v_start_width +
+ sig->v_end_width + (sig->v_end_width < 2 ? 1 : 0);
+ struct di_sync_config cfg[] = {
+ {
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ } , {
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ .offset_count = div * sig->v_to_h_sync,
+ .offset_src = DI_SYNC_CLK,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_CLK,
+ .cnt_down = sig->h_sync_width * 2,
+ } , {
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = sig->v_sync_width * 2,
+ } , {
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = sig->v_sync_width + sig->v_start_width,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = sig->height,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ } , {
+ .run_src = DI_SYNC_CLK,
+ .offset_count = sig->h_sync_width + sig->h_start_width,
+ .offset_src = DI_SYNC_CLK,
+ .repeat_count = sig->width,
+ .cnt_clr_src = 5,
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ },
+ };
+
+ ipu_di_write(di, v_total - 1, DI_SCR_CONF);
+ ipu_di_sync_config(di, cfg, ARRAY_SIZE(cfg));
+}
+
+int ipu_di_init_sync_panel(int disp, u32 pixel_clk, struct ipu_di_signal_cfg *sig)
+{
+ struct ipu_di *di = &dis[disp];
+ u32 reg;
+ u32 disp_gen, di_gen, vsync_cnt;
+ u32 div;
+ u32 h_total, v_total;
+ struct clk *di_clk;
+
+ dev_dbg(ipu_dev, "disp %d: panel size = %d x %d\n",
+ disp, sig->width, sig->height);
+
+ if (disp > 1)
+ return -EINVAL;
+
+ if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0))
+ return -EINVAL;
+
+ h_total = sig->width + sig->h_sync_width + sig->h_start_width + sig->h_end_width;
+ v_total = sig->height + sig->v_sync_width + sig->v_start_width + sig->v_end_width;
+
+ mutex_lock(&di_mutex);
+
+ /* Init clocking */
+ if (sig->ext_clk) {
+ di->external_clk = true;
+ di_clk = di->clk;
+ } else {
+ di->external_clk = false;
+ di_clk = di->ipu_clk;
+ }
+
+ /*
+ * Calculate divider
+ * Fractional part is 4 bits,
+ * so simply multiply by 2^4 to get fractional part.
+ */
+ div = (clk_get_rate(di_clk) * 16) / pixel_clk;
+ if (div < 0x10) /* Min DI disp clock divider is 1 */
+ div = 0x10;
+
+ disp_gen = ipu_cm_read(IPU_DISP_GEN);
+ disp_gen &= disp ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE;
+ ipu_cm_write(disp_gen, IPU_DISP_GEN);
+
+ ipu_di_write(di, div, DI_BS_CLKGEN0);
+
+ /* Setup pixel clock timing */
+ /* Down time is half of period */
+ ipu_di_write(di, (div / 16) << 16, DI_BS_CLKGEN1);
+
+ ipu_di_data_wave_config(di, SYNC_WAVE, div / 16 - 1, div / 16 - 1);
+ ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div / 16 * 2);
+
+ div = div / 16; /* Now divider is integer portion */
+
+ di_gen = 0;
+ if (sig->ext_clk)
+ di_gen |= DI_GEN_DI_CLK_EXT;
+
+ if (sig->interlaced) {
+ ipu_di_sync_config_interlaced(di, sig);
+
+ /* set y_sel = 1 */
+ di_gen |= 0x10000000;
+ di_gen |= DI_GEN_POLARITY_5;
+ di_gen |= DI_GEN_POLARITY_8;
+
+ vsync_cnt = 7;
+
+ if (sig->Hsync_pol)
+ di_gen |= DI_GEN_POLARITY_3;
+ if (sig->Vsync_pol)
+ di_gen |= DI_GEN_POLARITY_2;
+ } else {
+ ipu_di_sync_config_noninterlaced(di, sig, div);
+
+ vsync_cnt = 3;
+
+ if (sig->Hsync_pol)
+ di_gen |= DI_GEN_POLARITY_2;
+ if (sig->Vsync_pol)
+ di_gen |= DI_GEN_POLARITY_3;
+ }
+
+ ipu_di_write(di, di_gen, DI_GENERAL);
+ ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002,
+ DI_SYNC_AS_GEN);
+
+ reg = ipu_di_read(di, 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(di, reg, DI_POL);
+
+ mutex_unlock(&di_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_di_init_sync_panel);
+
+int ipu_di_enable(int disp)
+{
+ struct ipu_di *di = &dis[disp];
+ u32 reg;
+
+ reg = ipu_cm_read(IPU_DISP_GEN);
+ if (disp)
+ reg |= DI1_COUNTER_RELEASE;
+ else
+ reg |= DI0_COUNTER_RELEASE;
+ ipu_cm_write(reg, IPU_DISP_GEN);
+
+ if (di->external_clk)
+ clk_enable(di->clk);
+
+ ipu_module_enable(di->module);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_di_enable);
+
+int ipu_di_disable(int disp)
+{
+ struct ipu_di *di = &dis[disp];
+ u32 reg;
+
+ ipu_module_disable(di->module);
+
+ if (di->external_clk)
+ clk_disable(di->clk);
+
+ reg = ipu_cm_read(IPU_DISP_GEN);
+ if (disp)
+ reg &= ~DI1_COUNTER_RELEASE;
+ else
+ reg &= ~DI0_COUNTER_RELEASE;
+ ipu_cm_write(reg, IPU_DISP_GEN);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_di_disable);
+
+int ipu_di_init(struct platform_device *pdev, int id, unsigned long base,
+ u32 module, struct clk *ipu_clk)
+{
+ char *clkid;
+
+ if (id > 1)
+ return -EINVAL;
+
+ if (id)
+ clkid = "di1";
+ else
+ clkid = "di0";
+
+ ipu_dev = &pdev->dev;
+
+ dis[id].clk = clk_get(&pdev->dev, clkid);
+ dis[id].module = module;
+ dis[id].id = id;
+ dis[id].ipu_clk = ipu_clk;
+ dis[id].base = ioremap(base, PAGE_SIZE);
+ if (!dis[id].base)
+ return -ENOMEM;
+
+ /* Set MCU_T to divide MCU access window into 2 */
+ ipu_cm_write(0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN);
+
+ return 0;
+}
+
+void ipu_di_exit(struct platform_device *pdev, int id)
+{
+ clk_put(dis[id].clk);
+ iounmap(dis[id].base);
+}
diff --git a/drivers/mfd/imx-ipu-v3/ipu-dmfc.c b/drivers/mfd/imx-ipu-v3/ipu-dmfc.c
new file mode 100644
index 0000000..25782a7
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-dmfc.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]<mailto:[email protected]>>
+ * Copyright (C) 2005-2009 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.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/imx-ipu-v3.h>
+
+#include "ipu-prv.h"
+
+#define DMFC_RD_CHAN 0x0000
+#define DMFC_WR_CHAN 0x0004
+#define DMFC_WR_CHAN_DEF 0x0008
+#define DMFC_DP_CHAN 0x000c
+#define DMFC_DP_CHAN_DEF 0x0010
+#define DMFC_GENERAL1 0x0014
+#define DMFC_GENERAL2 0x0018
+#define DMFC_IC_CTRL 0x001c
+#define DMFC_STAT 0x0020
+
+#define DMFC_WR_CHAN_1_28 0
+#define DMFC_WR_CHAN_2_41 8
+#define DMFC_WR_CHAN_1C_42 16
+#define DMFC_WR_CHAN_2C_43 24
+
+#define DMFC_DP_CHAN_5B_23 0
+#define DMFC_DP_CHAN_5F_27 8
+#define DMFC_DP_CHAN_6B_24 16
+#define DMFC_DP_CHAN_6F_29 24
+
+#define DMFC_FIFO_SIZE_64 (3 << 3)
+#define DMFC_FIFO_SIZE_128 (2 << 3)
+#define DMFC_FIFO_SIZE_256 (1 << 3)
+#define DMFC_FIFO_SIZE_512 (0 << 3)
+
+#define DMFC_SEGMENT(x) ((x & 0x7) << 0)
+#define DMFC_BURSTSIZE_32 (0 << 6)
+#define DMFC_BURSTSIZE_16 (1 << 6)
+#define DMFC_BURSTSIZE_8 (2 << 6)
+#define DMFC_BURSTSIZE_4 (3 << 6)
+
+static struct device *ipu_dev;
+
+struct dmfc_channel {
+ int ipu_channel;
+ unsigned long channel_reg;
+ unsigned long shift;
+ unsigned eot_shift;
+ unsigned slots;
+ unsigned max_fifo_lines;
+ unsigned slotmask;
+ unsigned segment;
+};
+
+static struct dmfc_channel dmfcs[] = {
+ {
+ .ipu_channel = 23,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_5B_23,
+ .eot_shift = 20,
+ .max_fifo_lines = 3,
+ }, {
+ .ipu_channel = 24,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_6B_24,
+ .eot_shift = 22,
+ .max_fifo_lines = 1,
+ }, {
+ .ipu_channel = 27,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_5F_27,
+ .eot_shift = 21,
+ .max_fifo_lines = 2,
+ }, {
+ .ipu_channel = 28,
+ .channel_reg = DMFC_WR_CHAN,
+ .shift = DMFC_WR_CHAN_1_28,
+ .eot_shift = 16,
+ .max_fifo_lines = 2,
+ }, {
+ .ipu_channel = 29,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_6F_29,
+ .eot_shift = 23,
+ .max_fifo_lines = 1,
+ },
+};
+
+#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcs)
+
+static int dmfc_use_count;
+static void __iomem *dmfc_regs;
+static unsigned long dmfc_bandwidth_per_slot;
+static DEFINE_MUTEX(dmfc_mutex);
+
+int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc)
+{
+ mutex_lock(&dmfc_mutex);
+
+ if (!dmfc_use_count)
+ ipu_module_enable(IPU_CONF_DMFC_EN);
+
+ dmfc_use_count++;
+
+ mutex_unlock(&dmfc_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dmfc_enable_channel);
+
+void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
+{
+ mutex_lock(&dmfc_mutex);
+
+ dmfc_use_count--;
+
+ if (!dmfc_use_count)
+ ipu_module_disable(IPU_CONF_DMFC_EN);
+
+ if (dmfc_use_count < 0)
+ dmfc_use_count = 0;
+
+ mutex_unlock(&dmfc_mutex);
+}
+EXPORT_SYMBOL(ipu_dmfc_disable_channel);
+
+static int ipu_dmfc_setup_channel(struct dmfc_channel *dmfc, int slots, int segment)
+{
+ u32 val, field;
+
+ dev_dbg(ipu_dev, "dmfc: using %d slots starting from segment %d for IPU channel %d\n",
+ slots, segment, dmfc->ipu_channel);
+
+ if (!dmfc)
+ return -EINVAL;
+
+ switch (slots) {
+ case 1:
+ field = DMFC_FIFO_SIZE_64;
+ break;
+ case 2:
+ field = DMFC_FIFO_SIZE_128;
+ break;
+ case 4:
+ field = DMFC_FIFO_SIZE_256;
+ break;
+ case 8:
+ field = DMFC_FIFO_SIZE_512;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ field |= DMFC_SEGMENT(segment) | DMFC_BURSTSIZE_8;
+
+ val = readl(dmfc_regs + dmfc->channel_reg);
+
+ val &= ~(0xff << dmfc->shift);
+ val |= field << dmfc->shift;
+
+ writel(val, dmfc_regs + dmfc->channel_reg);
+
+ dmfc->slots = slots;
+ dmfc->segment = segment;
+ dmfc->slotmask = ((1 << slots) - 1) << segment;
+
+ return 0;
+}
+
+static int dmfc_bandwidth_to_slots(unsigned long bandwidth)
+{
+ int slots = 1;
+
+ while (slots * dmfc_bandwidth_per_slot < bandwidth)
+ slots *= 2;
+
+ return slots;
+}
+
+static int dmfc_find_slots(int slots)
+{
+ unsigned slotmask_need, slotmask_used = 0;
+ int i, segment = 0;
+
+ slotmask_need = (1 << slots) - 1;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ slotmask_used |= dmfcs[i].slotmask;
+
+ while (slotmask_need <= 0xff) {
+ if (!(slotmask_used & slotmask_need))
+ return segment;
+
+ slotmask_need <<= 1;
+ segment++;
+ }
+
+ return -EBUSY;
+}
+
+void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc)
+{
+ int i;
+
+ dev_dbg(ipu_dev, "dmfc: freeing %d slots starting from segment %d\n",
+ dmfc->slots, dmfc->segment);
+
+ mutex_lock(&dmfc_mutex);
+
+ if (!dmfc->slots)
+ goto out;
+
+ dmfc->slotmask = 0;
+ dmfc->slots = 0;
+ dmfc->segment = 0;
+
+ for (i = 0; i < ARRAY_SIZE(dmfcs); i++)
+ dmfcs[i].slotmask = 0;
+
+ for (i = 0; i < ARRAY_SIZE(dmfcs); i++) {
+ if (dmfcs[i].slots > 0) {
+ dmfcs[i].segment = dmfc_find_slots(dmfcs[i].slots);
+ dmfcs[i].slotmask = ((1 << dmfcs[i].slots) - 1) << dmfcs[i].segment;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dmfcs); i++) {
+ if (dmfcs[i].slots > 0)
+ ipu_dmfc_setup_channel(&dmfcs[i], dmfcs[i].slots, dmfcs[i].segment);
+ }
+out:
+ mutex_unlock(&dmfc_mutex);
+}
+EXPORT_SYMBOL(ipu_dmfc_free_bandwidth);
+
+int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
+ unsigned long bandwidth_pixel_per_second)
+{
+ int slots = dmfc_bandwidth_to_slots(bandwidth_pixel_per_second);
+ int segment = 0;
+
+ dev_dbg(ipu_dev, "dmfc: trying to allocate %ldMpixel/s for IPU channel %d\n",
+ bandwidth_pixel_per_second / 1000000, dmfc->ipu_channel);
+
+ ipu_dmfc_free_bandwidth(dmfc);
+
+ mutex_lock(&dmfc_mutex);
+
+ if (slots > 8)
+ return -EBUSY;
[Jason] mutex_unlock before return

+
+ segment = dmfc_find_slots(slots);
+ if (segment < 0)
+ return -EBUSY;
[Jason] mutex_unlock before return

+
+ ipu_dmfc_setup_channel(dmfc, slots, segment);
+
+ mutex_unlock(&dmfc_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dmfc_alloc_bandwidth);
+
+int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width)
+{
+ u32 dmfc_gen1;
+
+ dmfc_gen1 = readl(dmfc_regs + DMFC_GENERAL1);
+
+ if ((dmfc->slots * 64 * 4) / width > dmfc->max_fifo_lines)
+ dmfc_gen1 |= 1 << dmfc->eot_shift;
+ else
+ dmfc_gen1 &= ~(1 << dmfc->eot_shift);
+
+ writel(dmfc_gen1, dmfc_regs + DMFC_GENERAL1);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dmfc_init_channel);
+
+struct dmfc_channel *ipu_dmfc_get(int ipu_channel)
+{
+ int i;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ if (dmfcs[i].ipu_channel == ipu_channel)
+ return &dmfcs[i];
+ return NULL;
+}
+EXPORT_SYMBOL(ipu_dmfc_get);
+
+void ipu_dmfc_put(struct dmfc_channel *dmfc)
+{
+ ipu_dmfc_free_bandwidth(dmfc);
+}
+EXPORT_SYMBOL(ipu_dmfc_put);
+
+int ipu_dmfc_init(struct platform_device *pdev, unsigned long base,
+ struct clk *ipu_clk)
+{
+ dmfc_regs = ioremap(base, PAGE_SIZE);
+
+ if (!dmfc_regs)
+ return -ENOMEM;
+
+ ipu_dev = &pdev->dev;
+
+ writel(0x0, dmfc_regs + DMFC_WR_CHAN);
+ writel(0x0, dmfc_regs + DMFC_DP_CHAN);
+
+ /*
+ * We have a total bandwidth of clkrate * 4pixel divided
+ * into 8 slots.
+ */
+ dmfc_bandwidth_per_slot = clk_get_rate(ipu_clk) / 4;
+
+ dev_dbg(ipu_dev, "dmfc: 8 slots with %ldMpixel/s bandwidth each\n",
+ dmfc_bandwidth_per_slot / 1000000);
+
+ writel(0x202020f6, dmfc_regs + DMFC_WR_CHAN_DEF);
+ writel(0x2020f6f6, dmfc_regs + DMFC_DP_CHAN_DEF);
+ writel(0x00000003, dmfc_regs + DMFC_GENERAL1);
+
+ return 0;
+}
+
+void ipu_dmfc_exit(struct platform_device *pdev)
+{
+ iounmap(dmfc_regs);
+}
diff --git a/drivers/mfd/imx-ipu-v3/ipu-dp.c b/drivers/mfd/imx-ipu-v3/ipu-dp.c
new file mode 100644
index 0000000..505107d
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-dp.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]<mailto:[email protected]>>
+ * Copyright (C) 2005-2009 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.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <linux/err.h>
+#include <asm/atomic.h>
+
+#include "ipu-prv.h"
+
+#define DP_SYNC 0
+#define DP_ASYNC0 0x60
+#define DP_ASYNC1 0xBC
+
+#define DP_COM_CONF(flow) (ipu_dp_base + flow)
+#define DP_GRAPH_WIND_CTRL(flow) (ipu_dp_base + 0x0004 + flow)
+#define DP_FG_POS(flow) (ipu_dp_base + 0x0008 + flow)
+#define DP_CSC_A_0(flow) (ipu_dp_base + 0x0044 + flow)
+#define DP_CSC_A_1(flow) (ipu_dp_base + 0x0048 + flow)
+#define DP_CSC_A_2(flow) (ipu_dp_base + 0x004C + flow)
+#define DP_CSC_A_3(flow) (ipu_dp_base + 0x0050 + flow)
+#define DP_CSC_0(flow) (ipu_dp_base + 0x0054 + flow)
+#define DP_CSC_1(flow) (ipu_dp_base + 0x0058 + flow)
+
+#define DP_COM_CONF_FG_EN (1 << 0)
+#define DP_COM_CONF_GWSEL (1 << 1)
+#define DP_COM_CONF_GWAM (1 << 2)
+#define DP_COM_CONF_GWCKE (1 << 3)
+#define DP_COM_CONF_CSC_DEF_MASK (3 << 8)
+#define DP_COM_CONF_CSC_DEF_OFFSET 8
+#define DP_COM_CONF_CSC_DEF_FG (3 << 8)
+#define DP_COM_CONF_CSC_DEF_BG (2 << 8)
+#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8)
+
+struct ipu_dp {
+ u32 flow;
+ bool in_use;
+};
+
+static struct ipu_dp ipu_dp[3];
+static struct device *ipu_dev;
+
+static u32 ipu_flows[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
+
+enum csc_type_t {
+ RGB2YUV = 0,
+ YUV2RGB,
+ RGB2RGB,
+ YUV2YUV,
+ CSC_NONE,
+ CSC_NUM
+};
+
+static void __iomem *ipu_dp_base;
+static int dp_use_count;
+static DEFINE_MUTEX(dp_mutex);
+
+/* Y = R * .299 + G * .587 + B * .114;
+ U = R * -.169 + G * -.332 + B * .500 + 128.;
+ V = R * .500 + G * -.419 + B * -.0813 + 128.;*/
+static const int rgb2ycbcr_coeff[5][3] = {
+ { 153, 301, 58, },
+ { -87, -170, 0x0100, },
+ { 0x100, -215, -42, },
+ { 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 */
+};
+
+/* Please keep S0, S1 and S2 as 0x2 by using this conversion */
+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;
+}
+
+struct dp_csc_param_t {
+ int mode;
+ void *coeff;
+};
+
+/*
+ * 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, },
+ }
+};
+
+static enum csc_type_t fg_csc_type = CSC_NONE, bg_csc_type = CSC_NONE;
+
+static int color_key_4rgb = 1;
+
+int ipu_dp_set_color_key(struct ipu_dp *dp, bool enable, u32 color_key)
+{
+ u32 reg;
+ int y, u, v;
+ int red, green, blue;
+
+ mutex_lock(&dp_mutex);
+
+ color_key_4rgb = 1;
+ /* Transform color key from rgb to yuv if CSC is enabled */
+ if (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) ||
+ ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) ||
+ ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) ||
+ ((fg_csc_type == YUV2RGB) && (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;
+
+ color_key_4rgb = 0;
+
+ dev_dbg(ipu_dev, "color key change to yuv fmt 0x%x\n", color_key);
+ }
+
+ if (enable) {
+ reg = __raw_readl(DP_GRAPH_WIND_CTRL(dp->flow)) & 0xFF000000L;
+ __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL(dp->flow));
+
+ reg = __raw_readl(DP_COM_CONF(dp->flow));
+ __raw_writel(reg | DP_COM_CONF_GWCKE, DP_COM_CONF(dp->flow));
+ } else {
+ reg = __raw_readl(DP_COM_CONF(dp->flow));
+ __raw_writel(reg & ~DP_COM_CONF_GWCKE, DP_COM_CONF(dp->flow));
+ }
+
+ reg = ipu_cm_read(IPU_SRM_PRI2) | 0x8;
+ ipu_cm_write(reg, IPU_SRM_PRI2);
+
+ mutex_unlock(&dp_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dp_set_color_key);
+
+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha, bool bg_chan)
+{
+ u32 reg;
+
+ mutex_lock(&dp_mutex);
+
+ reg = __raw_readl(DP_COM_CONF(dp->flow));
+ if (bg_chan)
+ reg &= ~DP_COM_CONF_GWSEL;
+ else
+ reg |= DP_COM_CONF_GWSEL;
+ __raw_writel(reg, DP_COM_CONF(dp->flow));
+
+ if (enable) {
+ reg = __raw_readl(DP_GRAPH_WIND_CTRL(dp->flow)) & 0x00FFFFFFL;
+ __raw_writel(reg | ((u32) alpha << 24),
+ DP_GRAPH_WIND_CTRL(dp->flow));
+
+ reg = __raw_readl(DP_COM_CONF(dp->flow));
+ __raw_writel(reg | DP_COM_CONF_GWAM, DP_COM_CONF(dp->flow));
+ } else {
+ reg = __raw_readl(DP_COM_CONF(dp->flow));
+ __raw_writel(reg & ~DP_COM_CONF_GWAM, DP_COM_CONF(dp->flow));
+ }
+
+ reg = ipu_cm_read(IPU_SRM_PRI2) | 0x8;
+ ipu_cm_write(reg, IPU_SRM_PRI2);
+
+ mutex_unlock(&dp_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dp_set_global_alpha);
+
+int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
+{
+ u32 reg;
+
+ mutex_lock(&dp_mutex);
+
+ __raw_writel((x_pos << 16) | y_pos, DP_FG_POS(dp->flow));
+
+ reg = ipu_cm_read(IPU_SRM_PRI2);
+ reg |= 0x8;
+ ipu_cm_write(reg, IPU_SRM_PRI2);
+
+ mutex_unlock(&dp_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dp_set_window_pos);
+
+#define mask_a(a) ((u32)(a) & 0x3FF)
+#define mask_b(b) ((u32)(b) & 0x3FFF)
+
+void __ipu_dp_csc_setup(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 = __raw_readl(DP_COM_CONF(dp));
+ reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+ reg |= dp_csc_param.mode;
+ __raw_writel(reg, DP_COM_CONF(dp));
+ }
+
+ coeff = dp_csc_param.coeff;
+
+ if (coeff) {
+ __raw_writel(mask_a((*coeff)[0][0]) |
+ (mask_a((*coeff)[0][1]) << 16), DP_CSC_A_0(dp));
+ __raw_writel(mask_a((*coeff)[0][2]) |
+ (mask_a((*coeff)[1][0]) << 16), DP_CSC_A_1(dp));
+ __raw_writel(mask_a((*coeff)[1][1]) |
+ (mask_a((*coeff)[1][2]) << 16), DP_CSC_A_2(dp));
+ __raw_writel(mask_a((*coeff)[2][0]) |
+ (mask_a((*coeff)[2][1]) << 16), DP_CSC_A_3(dp));
+ __raw_writel(mask_a((*coeff)[2][2]) |
+ (mask_b((*coeff)[3][0]) << 16) |
+ ((*coeff)[4][0] << 30), DP_CSC_0(dp));
+ __raw_writel(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_SRM_PRI2) | 0x8;
+ ipu_cm_write(reg, IPU_SRM_PRI2);
+ }
+}
+
+int ipu_dp_setup_channel(struct ipu_dp *dp, u32 in_pixel_fmt,
+ u32 out_pixel_fmt, int bg)
+{
+ int in_fmt, out_fmt;
+ enum csc_type_t *csc_type;
+ u32 reg;
+
+ if (bg)
+ csc_type = &bg_csc_type;
+ else
+ csc_type = &fg_csc_type;
+
+ in_fmt = format_to_colorspace(in_pixel_fmt);
+ out_fmt = format_to_colorspace(out_pixel_fmt);
+
+ if (in_fmt == RGB) {
+ if (out_fmt == RGB)
+ *csc_type = RGB2RGB;
+ else
+ *csc_type = RGB2YUV;
+ } else {
+ if (out_fmt == RGB)
+ *csc_type = YUV2RGB;
+ else
+ *csc_type = YUV2YUV;
+ }
+
+ /* Transform color key from rgb to yuv if CSC is enabled */
+ reg = __raw_readl(DP_COM_CONF(dp->flow));
+ if (color_key_4rgb && (reg & DP_COM_CONF_GWCKE) &&
+ (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) ||
+ ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) ||
+ ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) ||
+ ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB)))) {
+ int red, green, blue;
+ int y, u, v;
+ u32 color_key = __raw_readl(DP_GRAPH_WIND_CTRL(dp->flow)) & 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 = __raw_readl(DP_GRAPH_WIND_CTRL(dp->flow)) & 0xFF000000L;
+ __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL(dp->flow));
+ color_key_4rgb = 0;
+
+ dev_dbg(ipu_dev, "_ipu_dp_init color key change to yuv fmt 0x%x!\n", color_key);
+ }
+
+ __ipu_dp_csc_setup(dp->flow, dp_csc_array[bg_csc_type][fg_csc_type], true);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dp_setup_channel);
+
+int ipu_dp_enable_channel(struct ipu_dp *dp)
+{
+ mutex_lock(&dp_mutex);
+
+ if (!dp_use_count)
+ ipu_module_enable(IPU_CONF_DP_EN);
+
+ dp_use_count++;
+
+ mutex_unlock(&dp_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dp_enable_channel);
+
+void ipu_dp_disable_channel(struct ipu_dp *dp)
+{
+ mutex_lock(&dp_mutex);
+
+ dp_use_count--;
+
+ if (!dp_use_count)
+ ipu_module_disable(IPU_CONF_DP_EN);
+
+ if (dp_use_count < 0)
+ dp_use_count = 0;
+
+ mutex_unlock(&dp_mutex);
+}
+EXPORT_SYMBOL(ipu_dp_disable_channel);
+
+void ipu_dp_enable_fg(struct ipu_dp *dp)
+{
+ u32 reg;
+
+ /* Enable FG channel */
+ reg = __raw_readl(DP_COM_CONF(DP_SYNC));
+ __raw_writel(reg | DP_COM_CONF_FG_EN, DP_COM_CONF(DP_SYNC));
+
+ reg = ipu_cm_read(IPU_SRM_PRI2);
+ reg |= 0x8;
+ ipu_cm_write(reg, IPU_SRM_PRI2);
+}
+EXPORT_SYMBOL(ipu_dp_enable_fg);
+
+void ipu_dp_disable_fg(struct ipu_dp *dp)
+{
+ u32 reg, csc;
+
+ reg = __raw_readl(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;
+ __raw_writel(reg, DP_COM_CONF(DP_SYNC));
+
+ reg = ipu_cm_read(IPU_SRM_PRI2) | 0x8;
+ ipu_cm_write(reg, IPU_SRM_PRI2);
+}
+EXPORT_SYMBOL(ipu_dp_disable_fg);
+
+struct ipu_dp *ipu_dp_get(unsigned int flow)
+{
+ struct ipu_dp *dp;
+
+ if (flow > 2)
+ return ERR_PTR(-EINVAL);
+
+ dp = &ipu_dp[flow];
+
+ if (dp->in_use)
+ return ERR_PTR(-EBUSY);
+
+ dp->in_use = true;
+ dp->flow = ipu_flows[flow];
+
+ return dp;
+}
+EXPORT_SYMBOL(ipu_dp_get);
+
+void ipu_dp_put(struct ipu_dp *dp)
+{
+ dp->in_use = false;
+}
+EXPORT_SYMBOL(ipu_dp_put);
+
+int ipu_dp_init(struct platform_device *pdev, unsigned long base)
+{
+ ipu_dp_base = ioremap(base, PAGE_SIZE);
+ if (!ipu_dp_base)
+ return -ENOMEM;
+
+ ipu_dev = &pdev->dev;
+
+ return 0;
+}
+
+void ipu_dp_exit(struct platform_device *pdev)
+{
+ iounmap(ipu_dp_base);
+}
diff --git a/drivers/mfd/imx-ipu-v3/ipu-prv.h b/drivers/mfd/imx-ipu-v3/ipu-prv.h
new file mode 100644
index 0000000..9d54ad0
--- /dev/null
+++ b/drivers/mfd/imx-ipu-v3/ipu-prv.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <[email protected]<mailto:[email protected]>>
+ * Copyright (C) 2005-2009 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 __IPU_PRV_H__
+#define __IPU_PRV_H__
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <mach/hardware.h>
+
+#define MX51_IPU_CHANNEL_CSI0 0
+#define MX51_IPU_CHANNEL_CSI1 1
+#define MX51_IPU_CHANNEL_CSI2 2
+#define MX51_IPU_CHANNEL_CSI3 3
+#define MX51_IPU_CHANNEL_MEM_BG_SYNC 23
+#define MX51_IPU_CHANNEL_MEM_FG_SYNC 27
+#define MX51_IPU_CHANNEL_MEM_DC_SYNC 28
+#define MX51_IPU_CHANNEL_MEM_FG_SYNC_ALPHA 31
+#define MX51_IPU_CHANNEL_MEM_DC_ASYNC 41
+#define MX51_IPU_CHANNEL_ROT_ENC_MEM 45
+#define MX51_IPU_CHANNEL_ROT_VF_MEM 46
+#define MX51_IPU_CHANNEL_ROT_PP_MEM 47
+#define MX51_IPU_CHANNEL_ROT_ENC_MEM_OUT 48
+#define MX51_IPU_CHANNEL_ROT_VF_MEM_OUT 49
+#define MX51_IPU_CHANNEL_ROT_PP_MEM_OUT 50
+#define MX51_IPU_CHANNEL_MEM_BG_SYNC_ALPHA 51
+
+#define IPU_DISP0_BASE 0x00000000
+#define IPU_MCU_T_DEFAULT 8
+#define IPU_DISP1_BASE (IPU_MCU_T_DEFAULT << 25)
+#define IPU_CM_REG_BASE 0x1e000000
+#define IPU_IDMAC_REG_BASE 0x1e008000
+#define IPU_ISP_REG_BASE 0x1e010000
+#define IPU_DP_REG_BASE 0x1e018000
+#define IPU_IC_REG_BASE 0x1e020000
+#define IPU_IRT_REG_BASE 0x1e028000
+#define IPU_CSI0_REG_BASE 0x1e030000
+#define IPU_CSI1_REG_BASE 0x1e038000
+#define IPU_DI0_REG_BASE 0x1e040000
+#define IPU_DI1_REG_BASE 0x1e048000
+#define IPU_SMFC_REG_BASE 0x1e050000
+#define IPU_DC_REG_BASE 0x1e058000
+#define IPU_DMFC_REG_BASE 0x1e060000
+#define IPU_CPMEM_REG_BASE 0x1f000000
+#define IPU_LUT_REG_BASE 0x1f020000
+#define IPU_SRM_REG_BASE 0x1f040000
+#define IPU_TPM_REG_BASE 0x1f060000
+#define IPU_DC_TMPL_REG_BASE 0x1f080000
+#define IPU_ISP_TBPR_REG_BASE 0x1f0c0000
+#define IPU_VDI_REG_BASE 0x1e068000
+
+/* 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))
+#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32))
+#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244)
+#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248)
+#define IPU_SRM_STAT IPU_CM_REG(0x024C)
+#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250)
+#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254)
+#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32))
+#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32))
+#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32))
+#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32))
+
+#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * ((n) - 1))
+#define IPU_INT_CTRL_IRQ(irq) IPU_INT_CTRL(((irq) / 32))
+#define IPU_INT_STAT_IRQ(irq) IPU_INT_STAT(((irq) / 32))
+#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * ((n) - 1))
+
+extern void __iomem *ipu_cm_reg;
+
+static inline u32 ipu_cm_read(unsigned offset)
+{
+ return __raw_readl(ipu_cm_reg + offset);
+}
+
+static inline void ipu_cm_write(u32 value, unsigned offset)
+{
+ __raw_writel(value, ipu_cm_reg + offset);
+}
+
+#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 IPU_IDMAC_REG(0x0024)
+#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028)
+#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c)
+#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030)
+#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034)
+#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32))
+#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32))
+
+extern void __iomem *ipu_idmac_reg;
+
+static inline u32 ipu_idmac_read(unsigned offset)
+{
+ return __raw_readl(ipu_idmac_reg + offset);
+}
+
+static inline void ipu_idmac_write(u32 value, unsigned offset)
+{
+ __raw_writel(value, ipu_idmac_reg + offset);
+}
+
+#define idmac_idma_is_set(reg, dma) (ipu_idmac_read(reg(dma)) & idma_mask(dma))
+#define idma_mask(ch) (1 << (ch & 0x1f))
+#define ipu_idma_is_set(reg, dma) (ipu_cm_read(reg(dma)) & idma_mask(dma))
+
+enum ipu_modules {
+ IPU_CONF_CSI0_EN = (1 << 0),
+ IPU_CONF_CSI1_EN = (1 << 1),
+ IPU_CONF_IC_EN = (1 << 2),
+ IPU_CONF_ROT_EN = (1 << 3),
+ IPU_CONF_ISP_EN = (1 << 4),
+ IPU_CONF_DP_EN = (1 << 5),
+ IPU_CONF_DI0_EN = (1 << 6),
+ IPU_CONF_DI1_EN = (1 << 7),
+ IPU_CONF_SMFC_EN = (1 << 8),
+ IPU_CONF_DC_EN = (1 << 9),
+ IPU_CONF_DMFC_EN = (1 << 10),
+
+ IPU_CONF_VDI_EN = (1 << 12),
+
+ IPU_CONF_IDMAC_DIS = (1 << 22),
+
+ IPU_CONF_IC_DMFC_SEL = (1 << 25),
+ IPU_CONF_IC_DMFC_SYNC = (1 << 26),
+ IPU_CONF_VDI_DMFC_SYNC = (1 << 27),
+
+ IPU_CONF_CSI0_DATA_SOURCE = (1 << 28),
+ IPU_CONF_CSI1_DATA_SOURCE = (1 << 29),
+ IPU_CONF_IC_INPUT = (1 << 30),
+ IPU_CONF_CSI_SEL = (1 << 31),
+};
+
+struct ipu_channel {
+ unsigned int num;
+
+ bool enabled;
+ bool busy;
+};
+
+ipu_color_space_t format_to_colorspace(u32 fmt);
+bool ipu_pixel_format_has_alpha(u32 fmt);
+
+u32 _ipu_channel_status(struct ipu_channel *channel);
+
+int _ipu_chan_is_interlaced(struct ipu_channel *channel);
+
+int ipu_module_enable(u32 mask);
+int ipu_module_disable(u32 mask);
+
+int ipu_di_init(struct platform_device *pdev, int id, unsigned long base,
+ u32 module, struct clk *ipu_clk);
+void ipu_di_exit(struct platform_device *pdev, int id);
+
+int ipu_dmfc_init(struct platform_device *pdev, unsigned long base,
+ struct clk *ipu_clk);
+void ipu_dmfc_exit(struct platform_device *pdev);
+
+int ipu_dp_init(struct platform_device *pdev, unsigned long base);
+void ipu_dp_exit(struct platform_device *pdev);
+
+int ipu_dc_init(struct platform_device *pdev, unsigned long base,
+ unsigned long template_base);
+void ipu_dc_exit(struct platform_device *pdev);
+
+int ipu_cpmem_init(struct platform_device *pdev, unsigned long base);
+void ipu_cpmem_exit(struct platform_device *pdev);
+
+#endif /* __IPU_PRV_H__ */
diff --git a/include/linux/mfd/imx-ipu-v3.h b/include/linux/mfd/imx-ipu-v3.h
new file mode 100644
index 0000000..0288c51
--- /dev/null
+++ b/include/linux/mfd/imx-ipu-v3.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2005-2009 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
+ */
+
+#ifndef __ASM_ARCH_IPU_H__
+#define __ASM_ARCH_IPU_H__
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/interrupt.h>
+#include <linux/bitmap.h>
+
+/*
+ * 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;
+
+/*
+ * IPU Pixel Formats
+ *
+ * Pixel formats are defined with ASCII FOURCC code. The pixel format codes are
+ * the same used by V4L2 API.
+ */
+
+/* Generic or Raw Data Formats */
+#define IPU_PIX_FMT_GENERIC v4l2_fourcc('I', 'P', 'U', '0') /* IPU Generic Data */
+#define IPU_PIX_FMT_GENERIC_32 v4l2_fourcc('I', 'P', 'U', '1') /* IPU Generic Data */
+#define IPU_PIX_FMT_LVDS666 v4l2_fourcc('L', 'V', 'D', '6') /* IPU Generic Data */
+#define IPU_PIX_FMT_LVDS888 v4l2_fourcc('L', 'V', 'D', '8') /* IPU Generic Data */
+/* RGB Formats */
+#define IPU_PIX_FMT_RGB332 V4L2_PIX_FMT_RGB332 /* 8 RGB-3-3-2 */
+#define IPU_PIX_FMT_RGB555 V4L2_PIX_FMT_RGB555 /* 16 RGB-5-5-5 */
+#define IPU_PIX_FMT_RGB565 V4L2_PIX_FMT_RGB565 /* 1 6 RGB-5-6-5 */
+#define IPU_PIX_FMT_RGB666 v4l2_fourcc('R', 'G', 'B', '6') /* 18 RGB-6-6-6 */
+#define IPU_PIX_FMT_BGR666 v4l2_fourcc('B', 'G', 'R', '6') /* 18 BGR-6-6-6 */
+#define IPU_PIX_FMT_BGR24 V4L2_PIX_FMT_BGR24 /* 24 BGR-8-8-8 */
+#define IPU_PIX_FMT_RGB24 V4L2_PIX_FMT_RGB24 /* 24 RGB-8-8-8 */
+#define IPU_PIX_FMT_BGR32 V4L2_PIX_FMT_BGR32 /* 32 BGR-8-8-8-8 */
+#define IPU_PIX_FMT_BGRA32 v4l2_fourcc('B', 'G', 'R', 'A') /* 32 BGR-8-8-8-8 */
+#define IPU_PIX_FMT_RGB32 V4L2_PIX_FMT_RGB32 /* 32 RGB-8-8-8-8 */
+#define IPU_PIX_FMT_RGBA32 v4l2_fourcc('R', 'G', 'B', 'A') /* 32 RGB-8-8-8-8 */
+#define IPU_PIX_FMT_ABGR32 v4l2_fourcc('A', 'B', 'G', 'R') /* 32 ABGR-8-8-8-8 */
+/* YUV Interleaved Formats */
+#define IPU_PIX_FMT_YUYV V4L2_PIX_FMT_YUYV /* 16 YUV 4:2:2 */
+#define IPU_PIX_FMT_UYVY V4L2_PIX_FMT_UYVY /* 16 YUV 4:2:2 */
+#define IPU_PIX_FMT_Y41P V4L2_PIX_FMT_Y41P /* 12 YUV 4:1:1 */
+#define IPU_PIX_FMT_YUV444 V4L2_PIX_FMT_YUV444 /* 24 YUV 4:4:4 */
+/* two planes -- one Y, one Cb + Cr interleaved */
+#define IPU_PIX_FMT_NV12 V4L2_PIX_FMT_NV12 /* 12 Y/CbCr 4:2:0 */
+/* YUV Planar Formats */
+#define IPU_PIX_FMT_GREY V4L2_PIX_FMT_GREY /* 8 Greyscale */
+#define IPU_PIX_FMT_YVU410P V4L2_PIX_FMT_YVU410P /* 9 YVU 4:1:0 */
+#define IPU_PIX_FMT_YUV410P V4L2_PIX_FMT_YUV410P /* 9 YUV 4:1:0 */
+#define IPU_PIX_FMT_YVU420P v4l2_fourcc('Y', 'V', '1', '2') /* 12 YVU 4:2:0 */
+#define IPU_PIX_FMT_YUV420P v4l2_fourcc('I', '4', '2', '0') /* 12 YUV 4:2:0 */
+#define IPU_PIX_FMT_YUV420P2 v4l2_fourcc('Y', 'U', '1', '2') /* 12 YUV 4:2:0 */
+#define IPU_PIX_FMT_YVU422P v4l2_fourcc('Y', 'V', '1', '6') /* 16 YVU 4:2:2 */
+#define IPU_PIX_FMT_YUV422P V4L2_PIX_FMT_YUV422P /* 16 YUV 4:2:2 */
+
+/*
+ * Bitfield of Display Interface signal polarities.
+ */
+struct ipu_di_signal_cfg {
+ unsigned datamask_en:1;
+ unsigned ext_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;
+
+ u16 width;
+ u16 height;
+ u32 pixel_fmt;
+ u16 h_start_width;
+ u16 h_sync_width;
+ u16 h_end_width;
+ u16 v_start_width;
+ u16 v_sync_width;
+ u16 v_end_width;
+ u32 v_to_h_sync;
+};
+
+typedef enum {
+ RGB,
+ YCbCr,
+ YUV
+} ipu_color_space_t;
+
+#define IPU_IRQ_EOF(channel) (channel) /* 0 .. 63 */
+#define IPU_IRQ_NFACK(channel) ((channel) + 64) /* 64 .. 127 */
+#define IPU_IRQ_NFB4EOF(channel) ((channel) + 128) /* 128 .. 191 */
+#define IPU_IRQ_EOS(channel) ((channel) + 192) /* 192 .. 255 */
+
+#define IPU_IRQ_DP_SF_START (448 + 2)
+#define IPU_IRQ_DP_SF_END (448 + 3)
+#define IPU_IRQ_BG_SF_END IPU_IRQ_DP_SF_END,
+#define IPU_IRQ_DC_FC_0 (448 + 8)
+#define IPU_IRQ_DC_FC_1 (448 + 9)
+#define IPU_IRQ_DC_FC_2 (448 + 10)
+#define IPU_IRQ_DC_FC_3 (448 + 11)
+#define IPU_IRQ_DC_FC_4 (448 + 12)
+#define IPU_IRQ_DC_FC_6 (448 + 13)
+#define IPU_IRQ_VSYNC_PRE_0 (448 + 14)
+#define IPU_IRQ_VSYNC_PRE_1 (448 + 15)
+
+#define IPU_IRQ_COUNT (15 * 32)
+
+#define DECLARE_IPU_IRQ_BITMAP(name) DECLARE_BITMAP(name, IPU_IRQ_COUNT)
+
+struct ipu_irq_handler {
+ struct list_head list;
+ void (*handler)(unsigned long *, void *);
+ void *context;
+ DECLARE_IPU_IRQ_BITMAP(ipu_irqs);
+};
+
+int ipu_irq_add_handler(struct ipu_irq_handler *ipuirq);
+void ipu_irq_remove_handler(struct ipu_irq_handler *handler);
+int ipu_irq_update_handler(struct ipu_irq_handler *handler,
+ unsigned long *bitmap);
+int ipu_wait_for_interrupt(int interrupt, int timeout_ms);
+
+struct ipu_channel;
+
+/*
+ * IPU Image DMA Controller (idmac) functions
+ */
+struct ipu_channel *ipu_idmac_get(unsigned channel);
+void ipu_idmac_put(struct ipu_channel *);
+int ipu_idmac_init_channel_buffer(struct ipu_channel *channel,
+ u32 pixel_fmt,
+ u16 width, u16 height,
+ u32 stride,
+ ipu_rotate_mode_t rot_mode,
+ dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
+ u32 u_offset, u32 v_offset, bool interlaced);
+
+int ipu_idmac_update_channel_buffer(struct ipu_channel *channel,
+ u32 bufNum, dma_addr_t phyaddr);
+
+int ipu_idmac_enable_channel(struct ipu_channel *channel);
+int ipu_idmac_disable_channel(struct ipu_channel *channel);
+
+void ipu_idmac_set_double_buffer(struct ipu_channel *channel, bool doublebuffer);
+void ipu_idmac_select_buffer(struct ipu_channel *channel, u32 buf_num);
+
+/*
+ * IPU Display Controller (dc) functions
+ */
+int ipu_dc_init_sync(int dc_chan, int di, bool interlaced, u32 pixel_fmt, u32 width);
+void ipu_dc_init_async(int dc_chan, int di, bool interlaced);
+void ipu_dc_enable_channel(u32 dc_chan);
+void ipu_dc_disable_channel(u32 dc_chan);
+
+/*
+ * IPU Display Interface (di) functions
+ */
+int ipu_di_disable(int disp);
+int ipu_di_enable(int disp);
+int ipu_di_init_sync_panel(int disp, u32 pixel_clk,
+ struct ipu_di_signal_cfg *sig);
+
+/*
+ * IPU Display Multi FIFO Controller (dmfc) functions
+ */
+struct dmfc_channel;
+int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc);
+void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc);
+int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc, unsigned long bandwidth_mbs);
+void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc);
+int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width);
+struct dmfc_channel *ipu_dmfc_get(int ipu_channel);
+void ipu_dmfc_put(struct dmfc_channel *dmfc);
+
+/*
+ * IPU Display Processor (dp) functions
+ */
+#define IPU_DP_FLOW_SYNC 0
+#define IPU_DP_FLOW_ASYNC0 1
+#define IPU_DP_FLOW_ASYNC1 2
+
+struct ipu_dp *ipu_dp_get(unsigned int flow);
+void ipu_dp_put(struct ipu_dp *);
+int ipu_dp_enable_channel(struct ipu_dp *dp);
+void ipu_dp_disable_channel(struct ipu_dp *dp);
+void ipu_dp_enable_fg(struct ipu_dp *dp);
+void ipu_dp_disable_fg(struct ipu_dp *dp);
+int ipu_dp_setup_channel(struct ipu_dp *dp, u32 in_pixel_fmt,
+ u32 out_pixel_fmt, int bg);
+int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos);
+int ipu_dp_set_color_key(struct ipu_dp *dp, bool enable, u32 colorKey);
+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha,
+ bool bg_chan);
+
+#endif
--
1.7.2.3


Attachments:
winmail.dat (202.58 kB)

2010-12-14 12:36:51

by Chen Jie-B02280

[permalink] [raw]
Subject: RE: [PATCH 5/9] Add i.MX5 framebuffer driver

Hi, Sascha,

Few comments inline with [Jason]

Jason Chen / Chen Jie
NMG / MAD
Freescale Semiconductor (China) Ltd.
2F, Building B, 177#, Bi Bo Rd
Pu Dong New District Shanghai 201203
Tel: 021-28937178
Fax: 021-28937444
E-mail: [email protected]<mailto:[email protected]>

From: Jason Chen [mailto:[email protected]]
Sent: Tuesday, December 14, 2010 3:35 PM
To: Chen Jie-B02280
Subject: Fwd: [PATCH 5/9] Add i.MX5 framebuffer driver


---------- Forwarded message ----------
From: Liu Ying <[email protected]<mailto:[email protected]>>
Date: 2010/12/12
Subject: Re: [PATCH 5/9] Add i.MX5 framebuffer driver
To: Sascha Hauer <[email protected]<mailto:[email protected]>>
Cc: [email protected]<mailto:[email protected]>, [email protected]<mailto:[email protected]>, [email protected]<mailto:[email protected]>, Zhang Lily-R58066 <[email protected]<mailto:[email protected]>>, Arnaud Patard <[email protected]<mailto:[email protected]>>


Hello, Sascha,

I have following comments to this patch:
1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.
2) ADC is not supported yet in the framebuffer driver, so please
modify this comment:
> + * Framebuffer Framebuffer Driver for SDC and ADC.
3) 'ipu_dp_set_window_pos()' is called only once in
imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
support to change the overlay framebuffer position. Need a
mechanism/interface for users to change the overlay framebuffer
position.
[Jason] DP-FG should be one fb device, sequence ioctl should be added after it, like global alpha , color key etc.

4) Need to make sure the framebuffer on DP-FG is blanked before the
framebuffer on DP-BG is blanked. Meanwhile, the framebuffer on DP-FG
should be unblanked after the framebuffer on DP-BG is unblanked
5) Need to check the framebuffer on DP-FG doesn't run out of the range
of the framebuffer on DP-BG.
6) I prefer to find the video mode in modedb first, and if we cannot
find the video mode in common video mode data base, we can find a
video mode in custom video mode data base which is defined in platform
data. In this way, we don't need to export common modefb.

Best Regards,
Liu Ying


2010/12/9 Sascha Hauer <[email protected]<mailto:[email protected]>>:
> This patch adds framebuffer support to the Freescale i.MX SoCs
> equipped with an IPU v3, so far these are the i.MX50/51/53.
>
> This driver has been tested on the i.MX51 babbage board with
> both DVI and analog VGA in different resolutions and color depths.
> It has also been tested on a custom i.MX51 board using a fixed
> resolution panel.
>
> Signed-off-by: Sascha Hauer <[email protected]<mailto:[email protected]>>
> ---
> drivers/video/Kconfig | 11 +
> drivers/video/Makefile | 1 +
> drivers/video/mx5fb.c | 846 ++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 858 insertions(+), 0 deletions(-)
> create mode 100644 drivers/video/mx5fb.c
>
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 27c1fb4..1901915 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -2236,6 +2236,17 @@ config FB_MX3
> far only synchronous displays are supported. If you plan to use
> an LCD display with your i.MX31 system, say Y here.
>
> +config FB_MX5
> + tristate "MX5 Framebuffer support"
> + depends on FB && MFD_IMX_IPU_V3
> + select FB_CFB_FILLRECT
> + select FB_CFB_COPYAREA
> + select FB_CFB_IMAGEBLIT
> + select FB_MODE_HELPERS
> + help
> + This is a framebuffer device for the i.MX51 LCD Controller. If you
> + plan to use an LCD display with your i.MX51 system, say Y here.
> +
> config FB_BROADSHEET
> tristate "E-Ink Broadsheet/Epson S1D13521 controller support"
> depends on FB
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 485e8ed..ad408d2 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -145,6 +145,7 @@ obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o
> obj-$(CONFIG_FB_BFIN_LQ035Q1) += bfin-lq035q1-fb.o
> obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o
> obj-$(CONFIG_FB_MX3) += mx3fb.o
> +obj-$(CONFIG_FB_MX5) += mx5fb.o
> obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
>
> # the test framebuffer is last
> diff --git a/drivers/video/mx5fb.c b/drivers/video/mx5fb.c
> new file mode 100644
> index 0000000..fd9baf4
> --- /dev/null
> +++ b/drivers/video/mx5fb.c
> @@ -0,0 +1,846 @@
> +/*
> + * Copyright 2004-2009 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
> + *
> + * Framebuffer Framebuffer Driver for SDC and ADC.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +#include <linux/fb.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/console.h>
> +#include <linux/mfd/imx-ipu-v3.h>
> +#include <asm/uaccess.h>
> +#include <mach/ipu-v3.h>
> +
> +#define DRIVER_NAME "imx-ipuv3-fb"
> +
> +struct imx_ipu_fb_info {
> + int ipu_channel_num;
> + struct ipu_channel *ipu_ch;
> + int dc;
> + int ipu_di;
> + u32 ipu_di_pix_fmt;
> + u32 ipu_in_pix_fmt;
> +
> + u32 pseudo_palette[16];
> +
> + struct ipu_dp *dp;
> + struct dmfc_channel *dmfc;
> + struct fb_info *slave;
> + struct fb_info *master;
> + bool enabled;
> +};
> +
> +static int imx_ipu_fb_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->ypanstep = 1;
> +
> + return 0;
> +}
> +
> +static int imx_ipu_fb_map_video_memory(struct fb_info *fbi)
> +{
> + int size;
> +
> + size = fbi->var.yres_virtual * fbi->fix.line_length;
> +
> + if (fbi->screen_base) {
> + if (fbi->fix.smem_len >= size)
> + return 0;
> +
> + dma_free_writecombine(fbi->device, fbi->fix.smem_len,
> + fbi->screen_base, fbi->fix.smem_start);
> + }
> +
> + fbi->screen_base = dma_alloc_writecombine(fbi->device,
> + size,
> + (dma_addr_t *)&fbi->fix.smem_start,
> + GFP_DMA);
> + if (fbi->screen_base == 0) {
> + dev_err(fbi->device, "Unable to allocate framebuffer memory (%d)\n",
> + fbi->fix.smem_len);
> + fbi->fix.smem_len = 0;
> + fbi->fix.smem_start = 0;
> + return -ENOMEM;
> + }
> +
> + fbi->fix.smem_len = size;
> + fbi->screen_size = fbi->fix.smem_len;
> +
> + dev_dbg(fbi->device, "allocated fb @ paddr=0x%08lx, size=%d\n",
> + fbi->fix.smem_start, fbi->fix.smem_len);
> +
> + /* Clear the screen */
> + memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
> +
> + return 0;
> +}
> +
> +static void imx_ipu_fb_enable(struct fb_info *fbi)
> +{
> + struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +
> + if (mxc_fbi->enabled)
> + return;
> +
> + ipu_di_enable(mxc_fbi->ipu_di);
> + ipu_dmfc_enable_channel(mxc_fbi->dmfc);
> + ipu_idmac_enable_channel(mxc_fbi->ipu_ch);
> + ipu_dc_enable_channel(mxc_fbi->dc);
> + ipu_dp_enable_channel(mxc_fbi->dp);
> + mxc_fbi->enabled = 1;
> +}
> +
> +static void imx_ipu_fb_disable(struct fb_info *fbi)
> +{
> + struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +
> + if (!mxc_fbi->enabled)
> + return;
> +
> + ipu_dp_disable_channel(mxc_fbi->dp);
> + ipu_dc_disable_channel(mxc_fbi->dc);
> + ipu_idmac_disable_channel(mxc_fbi->ipu_ch);
> + ipu_dmfc_disable_channel(mxc_fbi->dmfc);
> + ipu_di_disable(mxc_fbi->ipu_di);
> +
> + mxc_fbi->enabled = 0;
> +}
> +
> +static int calc_vref(struct fb_var_screeninfo *var)
> +{
> + unsigned long htotal, vtotal;
> +
> + htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin;
> + vtotal = var->yres + var->lower_margin + var->vsync_len + var->upper_margin;
> +
> + if (!htotal || !vtotal)
> + return 60;
> +
> + return PICOS2KHZ(var->pixclock) * 1000 / vtotal / htotal;
> +}
> +
> +static int calc_bandwidth(struct fb_var_screeninfo *var, unsigned int vref)
> +{
> + return var->xres * var->yres * vref;
> +}
> +
> +static int imx_ipu_fb_set_par(struct fb_info *fbi)
> +{
> + int ret;
> + struct ipu_di_signal_cfg sig_cfg;
> + struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> + u32 out_pixel_fmt;
> + int interlaced = 0;
> + struct fb_var_screeninfo *var = &fbi->var;
> + int enabled = mxc_fbi->enabled;
> +
> + dev_dbg(fbi->device, "Reconfiguring framebuffer %dx%d-%d\n",
> + fbi->var.xres, fbi->var.yres, fbi->var.bits_per_pixel);
> +
> + if (enabled)
> + imx_ipu_fb_disable(fbi);
> +
> + fbi->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
> +
> + var->yres_virtual = var->yres;
> +
> + ret = imx_ipu_fb_map_video_memory(fbi);
> + if (ret)
> + return ret;
> +
> + if (var->vmode & FB_VMODE_INTERLACED)
> + interlaced = 1;
> +
> + memset(&sig_cfg, 0, sizeof(sig_cfg));
> + out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
> +
> + if (var->vmode & FB_VMODE_INTERLACED)
> + sig_cfg.interlaced = 1;
> + if (var->vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
> + sig_cfg.odd_field_first = 1;
> + if (var->sync & FB_SYNC_EXT)
> + sig_cfg.ext_clk = 1;
[Jason] FB_SYNC_EXT has not be used in FSL kernel mainline, it represent SYNC ext, should not be flag of ext clk. Some application for example X-server could not recognize it.

> + if (var->sync & FB_SYNC_HOR_HIGH_ACT)
> + sig_cfg.Hsync_pol = 1;
> + if (var->sync & FB_SYNC_VERT_HIGH_ACT)
> + sig_cfg.Vsync_pol = 1;
> + if (!(var->sync & FB_SYNC_CLK_LAT_FALL))
> + sig_cfg.clk_pol = 1;
> + if (var->sync & FB_SYNC_DATA_INVERT)
> + sig_cfg.data_pol = 1;
> + if (!(var->sync & FB_SYNC_OE_LOW_ACT))
> + sig_cfg.enable_pol = 1;
> + if (var->sync & FB_SYNC_CLK_IDLE_EN)
> + sig_cfg.clkidle_en = 1;
> +
> + dev_dbg(fbi->device, "pixclock = %lu.%03lu MHz\n",
> + PICOS2KHZ(var->pixclock) / 1000,
> + PICOS2KHZ(var->pixclock) % 1000);
> +
> + sig_cfg.width = var->xres;
> + sig_cfg.height = var->yres;
> + sig_cfg.pixel_fmt = out_pixel_fmt;
> + sig_cfg.h_start_width = var->left_margin;
> + sig_cfg.h_sync_width = var->hsync_len;
> + sig_cfg.h_end_width = var->right_margin;
> + sig_cfg.v_start_width = var->upper_margin;
> + sig_cfg.v_sync_width = var->vsync_len;
> + sig_cfg.v_end_width = var->lower_margin;
> + sig_cfg.v_to_h_sync = 0;
> +
> + if (mxc_fbi->dp) {
> + ret = ipu_dp_setup_channel(mxc_fbi->dp, mxc_fbi->ipu_in_pix_fmt,
> + out_pixel_fmt, 1);
> + if (ret) {
> + dev_dbg(fbi->device, "initializing display processor failed with %d\n",
> + ret);
> + return ret;
> + }
> + }
> +
> + ret = ipu_dc_init_sync(mxc_fbi->dc, mxc_fbi->ipu_di, interlaced,
> + out_pixel_fmt, fbi->var.xres);
> + if (ret) {
> + dev_dbg(fbi->device, "initializing display controller failed with %d\n",
> + ret);
> + return ret;
> + }
> +
> + ret = ipu_di_init_sync_panel(mxc_fbi->ipu_di,
> + PICOS2KHZ(var->pixclock) * 1000UL,
> + &sig_cfg);
> + if (ret) {
> + dev_dbg(fbi->device, "initializing panel failed with %d\n",
> + ret);
> + return ret;
> + }
> +
> + fbi->mode = (struct fb_videomode *)fb_match_mode(var, &fbi->modelist);
> + var->xoffset = var->yoffset = 0;
> +
> + if (fbi->var.vmode & FB_VMODE_INTERLACED)
> + interlaced = 1;
> +
> + ret = ipu_idmac_init_channel_buffer(mxc_fbi->ipu_ch,
> + mxc_fbi->ipu_in_pix_fmt,
> + var->xres, var->yres,
> + fbi->fix.line_length,
> + IPU_ROTATE_NONE,
> + fbi->fix.smem_start,
> + 0,
> + 0, 0, interlaced);
> + if (ret) {
> + dev_dbg(fbi->device, "init channel buffer failed with %d\n",
> + ret);
> + return ret;
> + }
> +
> + ret = ipu_dmfc_init_channel(mxc_fbi->dmfc, var->xres);
> + if (ret) {
> + dev_dbg(fbi->device, "initializing dmfc channel failed with %d\n",
> + ret);
> + return ret;
> + }
> +
> + ret = ipu_dmfc_alloc_bandwidth(mxc_fbi->dmfc, calc_bandwidth(var, calc_vref(var)));
> + if (ret) {
> + dev_dbg(fbi->device, "allocating dmfc bandwidth failed with %d\n",
> + ret);
> + return ret;
> + }
> +
> + if (enabled)
> + imx_ipu_fb_enable(fbi);
> +
> + return ret;
> +}
> +
> +/*
> + * These are the bitfields for each
> + * display depth that we support.
> + */
> +struct imxfb_rgb {
> + struct fb_bitfield red;
> + struct fb_bitfield green;
> + struct fb_bitfield blue;
> + struct fb_bitfield transp;
> +};
> +
> +static struct imxfb_rgb def_rgb_8 = {
> + .red = { .offset = 5, .length = 3, },
> + .green = { .offset = 2, .length = 3, },
> + .blue = { .offset = 0, .length = 2, },
> + .transp = { .offset = 0, .length = 0, },
> +};
> +
> +static struct imxfb_rgb def_rgb_16 = {
> + .red = { .offset = 11, .length = 5, },
> + .green = { .offset = 5, .length = 6, },
> + .blue = { .offset = 0, .length = 5, },
> + .transp = { .offset = 0, .length = 0, },
> +};
> +
> +static struct imxfb_rgb def_rgb_24 = {
> + .red = { .offset = 16, .length = 8, },
> + .green = { .offset = 8, .length = 8, },
> + .blue = { .offset = 0, .length = 8, },
> + .transp = { .offset = 0, .length = 0, },
> +};
> +
> +static struct imxfb_rgb def_rgb_32 = {
> + .red = { .offset = 16, .length = 8, },
> + .green = { .offset = 8, .length = 8, },
> + .blue = { .offset = 0, .length = 8, },
> + .transp = { .offset = 24, .length = 8, },
> +};
> +
> +/*
> + * Check framebuffer variable parameters and adjust to valid values.
> + *
> + * @param var framebuffer variable parameters
> + *
> + * @param info framebuffer information pointer
> + */
> +static int imx_ipu_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
> +{
> + struct imx_ipu_fb_info *mxc_fbi = info->par;
> + struct imxfb_rgb *rgb;
> +
> + /* we don't support xpan, force xres_virtual to be equal to xres */
> + var->xres_virtual = var->xres;
> +
> + if (var->yres_virtual < var->yres)
> + var->yres_virtual = var->yres;
> +
> + switch (var->bits_per_pixel) {
> + case 8:
> + rgb = &def_rgb_8;
> + break;
> + case 16:
> + rgb = &def_rgb_16;
> + mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_RGB565;
> + break;
> + case 24:
> + rgb = &def_rgb_24;
> + mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR24;
> + break;
> + case 32:
> + rgb = &def_rgb_32;
> + mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR32;
> + break;
> + default:
> + var->bits_per_pixel = 24;
> + rgb = &def_rgb_24;
> + mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR24;
> + }
> +
> + var->red = rgb->red;
> + var->green = rgb->green;
> + var->blue = rgb->blue;
> + var->transp = rgb->transp;
> +
> + return 0;
> +}
> +
> +static inline unsigned int chan_to_field(u_int chan, struct fb_bitfield *bf)
> +{
> + chan &= 0xffff;
> + chan >>= 16 - bf->length;
> + return chan << bf->offset;
> +}
> +
> +static int imx_ipu_fb_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;
> +}
> +
> +static int imx_ipu_fb_blank(int blank, struct fb_info *info)
> +{
> + dev_dbg(info->device, "blank = %d\n", blank);
> +
> + switch (blank) {
> + case FB_BLANK_POWERDOWN:
> + case FB_BLANK_VSYNC_SUSPEND:
> + case FB_BLANK_HSYNC_SUSPEND:
> + case FB_BLANK_NORMAL:
> + imx_ipu_fb_disable(info);
> + break;
> + case FB_BLANK_UNBLANK:
> + imx_ipu_fb_enable(info);
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int imx_ipu_fb_pan_display(struct fb_var_screeninfo *var,
> + struct fb_info *info)
> +{
> + struct imx_ipu_fb_info *mxc_fbi = info->par;
> + unsigned long base;
> + int ret;
> +
> + if (info->var.yoffset == var->yoffset)
> + return 0; /* No change, do nothing */
> +
> + base = var->yoffset * var->xres_virtual * var->bits_per_pixel / 8;
> + base += info->fix.smem_start;
> +
> + ret = ipu_wait_for_interrupt(IPU_IRQ_EOF(mxc_fbi->ipu_channel_num), 100);
> + if (ret)
> + return ret;
> +
> + if (ipu_idmac_update_channel_buffer(mxc_fbi->ipu_ch, 0, base)) {
> + dev_err(info->device,
> + "Error updating SDC buf to address=0x%08lX\n", base);
> + }
[Jason] It's better to enable double -buffer for fb which could avoid tearing issue.

> +
> + info->var.yoffset = var->yoffset;
> +
> + return 0;
> +}
> +
> +static struct fb_ops imx_ipu_fb_ops = {
> + .owner = THIS_MODULE,
> + .fb_set_par = imx_ipu_fb_set_par,
> + .fb_check_var = imx_ipu_fb_check_var,
> + .fb_setcolreg = imx_ipu_fb_setcolreg,
> + .fb_pan_display = imx_ipu_fb_pan_display,
> + .fb_fillrect = cfb_fillrect,
> + .fb_copyarea = cfb_copyarea,
> + .fb_imageblit = cfb_imageblit,
> + .fb_blank = imx_ipu_fb_blank,
> +};
> +
> +/*
> + * Overlay functions
> + */
> +static int imx_ipu_fb_enable_overlay(struct fb_info *fbi)
> +{
> + struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +
> + ipu_dmfc_enable_channel(mxc_fbi->dmfc);
> + ipu_idmac_enable_channel(mxc_fbi->ipu_ch);
> + ipu_dp_enable_fg(mxc_fbi->dp);
> +
> + return 0;
> +}
> +
> +static int imx_ipu_fb_disable_overlay(struct fb_info *fbi)
> +{
> + struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +
> + ipu_dp_disable_fg(mxc_fbi->dp);
> + ipu_idmac_disable_channel(mxc_fbi->ipu_ch);
> + ipu_dmfc_disable_channel(mxc_fbi->dmfc);
> +
> + return 0;
> +}
> +
> +static int imx_ipu_fb_set_par_overlay(struct fb_info *fbi)
> +{
> + struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> + struct fb_var_screeninfo *var = &fbi->var;
> + struct fb_info *fbi_master = mxc_fbi->master;
> + struct fb_var_screeninfo *var_master = &fbi_master->var;
> + int ret;
> + int interlaced = 0;
> + int enabled = mxc_fbi->enabled;
> +
> + dev_dbg(fbi->device, "Reconfiguring framebuffer %dx%d-%d\n",
> + fbi->var.xres, fbi->var.yres, fbi->var.bits_per_pixel);
> +
> + if (enabled)
> + imx_ipu_fb_disable_overlay(fbi);
> +
> + fbi->fix.line_length = var->xres_virtual *
> + var->bits_per_pixel / 8;
> +
> + ret = imx_ipu_fb_map_video_memory(fbi);
> + if (ret)
> + return ret;
> +
> + ipu_dp_set_window_pos(mxc_fbi->dp, 64, 64);
> +
> + var->xoffset = var->yoffset = 0;
> +
> + if (var->vmode & FB_VMODE_INTERLACED)
> + interlaced = 1;
> +
> + ret = ipu_idmac_init_channel_buffer(mxc_fbi->ipu_ch,
> + mxc_fbi->ipu_in_pix_fmt,
> + var->xres, var->yres,
> + fbi->fix.line_length,
> + IPU_ROTATE_NONE,
> + fbi->fix.smem_start,
> + 0,
> + 0, 0, interlaced);
> + if (ret) {
> + dev_dbg(fbi->device, "init channel buffer failed with %d\n",
> + ret);
> + return ret;
> + }
> +
> + ret = ipu_dmfc_init_channel(mxc_fbi->dmfc, var->xres);
> + if (ret) {
> + dev_dbg(fbi->device, "initializing dmfc channel failed with %d\n",
> + ret);
> + return ret;
> + }
> +
> + ret = ipu_dmfc_alloc_bandwidth(mxc_fbi->dmfc, calc_bandwidth(var, calc_vref(var_master)));
> + if (ret) {
> + dev_dbg(fbi->device, "allocating dmfc bandwidth failed with %d\n",
> + ret);
> + return ret;
> + }
> +
> + if (enabled)
> + imx_ipu_fb_enable_overlay(fbi);
> +
> + return ret;
> +}
> +
> +static int imx_ipu_fb_blank_overlay(int blank, struct fb_info *fbi)
> +{
> + dev_dbg(fbi->device, "blank = %d\n", blank);
> +
> + switch (blank) {
> + case FB_BLANK_POWERDOWN:
> + case FB_BLANK_VSYNC_SUSPEND:
> + case FB_BLANK_HSYNC_SUSPEND:
> + case FB_BLANK_NORMAL:
> + imx_ipu_fb_disable_overlay(fbi);
> + break;
> + case FB_BLANK_UNBLANK:
> + imx_ipu_fb_enable_overlay(fbi);
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static struct fb_ops imx_ipu_fb_overlay_ops = {
> + .owner = THIS_MODULE,
> + .fb_set_par = imx_ipu_fb_set_par_overlay,
> + .fb_check_var = imx_ipu_fb_check_var,
> + .fb_setcolreg = imx_ipu_fb_setcolreg,
> + .fb_pan_display = imx_ipu_fb_pan_display,
> + .fb_fillrect = cfb_fillrect,
> + .fb_copyarea = cfb_copyarea,
> + .fb_imageblit = cfb_imageblit,
> + .fb_blank = imx_ipu_fb_blank_overlay,
> +};
> +
> +static struct fb_info *imx_ipu_fb_init_fbinfo(struct device *dev, struct fb_ops *ops)
> +{
> + struct fb_info *fbi;
> + struct imx_ipu_fb_info *mxc_fbi;
> +
> + fbi = framebuffer_alloc(sizeof(struct imx_ipu_fb_info), dev);
> + if (!fbi)
> + return NULL;
> +
> + BUG_ON(fbi->par == NULL);
> + mxc_fbi = fbi->par;
> +
> + fbi->var.activate = FB_ACTIVATE_NOW;
> +
> + fbi->fbops = ops;
> + fbi->flags = FBINFO_FLAG_DEFAULT;
> + fbi->pseudo_palette = mxc_fbi->pseudo_palette;
> +
> + fb_alloc_cmap(&fbi->cmap, 16, 0);
> +
> + return fbi;
> +}
> +
> +static int imx_ipu_fb_init_overlay(struct platform_device *pdev,
> + struct fb_info *fbi_master, int ipu_channel)
> +{
> + struct imx_ipu_fb_info *mxc_fbi_master = fbi_master->par;
> + struct fb_info *ovlfbi;
> + struct imx_ipu_fb_info *ovl_mxc_fbi;
> + int ret;
> +
> + ovlfbi = imx_ipu_fb_init_fbinfo(&pdev->dev, &imx_ipu_fb_overlay_ops);
> + if (!ovlfbi)
> + return -ENOMEM;
> +
> + ovl_mxc_fbi = ovlfbi->par;
> + ovl_mxc_fbi->ipu_ch = ipu_idmac_get(ipu_channel);
> + ovl_mxc_fbi->dmfc = ipu_dmfc_get(ipu_channel);
> + ovl_mxc_fbi->ipu_di = -1;
> + ovl_mxc_fbi->dp = mxc_fbi_master->dp;
> + ovl_mxc_fbi->master = fbi_master;
> + mxc_fbi_master->slave = ovlfbi;
> +
> + ovlfbi->var.xres = 240;
> + ovlfbi->var.yres = 320;
> + ovlfbi->var.yres_virtual = ovlfbi->var.yres;
> + ovlfbi->var.xres_virtual = ovlfbi->var.xres;
> + imx_ipu_fb_check_var(&ovlfbi->var, ovlfbi);
> + imx_ipu_fb_set_fix(ovlfbi);
> +
> + ret = register_framebuffer(ovlfbi);
> + if (ret) {
> + framebuffer_release(ovlfbi);
> + return ret;
> + }
> +
> + ipu_dp_set_global_alpha(ovl_mxc_fbi->dp, 1, 0x80, 1);
> + ipu_dp_set_color_key(ovl_mxc_fbi->dp, 0, 0);
> +
> + imx_ipu_fb_set_par_overlay(ovlfbi);
> +
> + return 0;
> +}
> +
> +static void imx_ipu_fb_exit_overlay(struct platform_device *pdev,
> + struct fb_info *fbi_master, int ipu_channel)
> +{
> + struct imx_ipu_fb_info *mxc_fbi_master = fbi_master->par;
> + struct fb_info *ovlfbi = mxc_fbi_master->slave;
> + struct imx_ipu_fb_info *ovl_mxc_fbi = ovlfbi->par;
> +
> + imx_ipu_fb_blank_overlay(FB_BLANK_POWERDOWN, ovlfbi);
> +
> + unregister_framebuffer(ovlfbi);
> +
> + ipu_idmac_put(ovl_mxc_fbi->ipu_ch);
> + ipu_dmfc_free_bandwidth(ovl_mxc_fbi->dmfc);
> + ipu_dmfc_put(ovl_mxc_fbi->dmfc);
> +
> + framebuffer_release(ovlfbi);
> +}
> +
> +static int imx_ipu_fb_find_mode(struct fb_info *fbi)
> +{
> + int ret;
> + struct fb_videomode *mode_array;
> + struct fb_modelist *modelist;
> + struct fb_var_screeninfo *var = &fbi->var;
> + int i = 0;
> +
> + list_for_each_entry(modelist, &fbi->modelist, list)
> + i++;
> +
> + mode_array = kmalloc(sizeof (struct fb_modelist) * i, GFP_KERNEL);
> + if (!mode_array)
> + return -ENOMEM;
> +
> + i = 0;
> + list_for_each_entry(modelist, &fbi->modelist, list)
> + mode_array[i++] = modelist->mode;
> +
> + ret = fb_find_mode(&fbi->var, fbi, NULL, mode_array, i, NULL, 16);
> + if (ret == 0)
> + return -EINVAL;
> +
> + dev_dbg(fbi->device, "found %dx%d-%d hs:%d:%d:%d vs:%d:%d:%d\n",
> + var->xres, var->yres, var->bits_per_pixel,
> + var->hsync_len, var->left_margin, var->right_margin,
> + var->vsync_len, var->upper_margin, var->lower_margin);
> +
> + kfree(mode_array);
> +
> + return 0;
> +}
> +
> +static int __devinit imx_ipu_fb_probe(struct platform_device *pdev)
> +{
> + struct fb_info *fbi;
> + struct imx_ipu_fb_info *mxc_fbi;
> + struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
> + int ret = 0, i;
> +
> + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
> +
> + fbi = imx_ipu_fb_init_fbinfo(&pdev->dev, &imx_ipu_fb_ops);
> + if (!fbi)
> + return -ENOMEM;
> +
> + mxc_fbi = fbi->par;
> +
> + mxc_fbi->ipu_channel_num = plat_data->ipu_channel_bg;
> + mxc_fbi->dc = plat_data->dc_channel;
> + mxc_fbi->ipu_di_pix_fmt = plat_data->interface_pix_fmt;
> + mxc_fbi->ipu_di = pdev->id;
> +
> + mxc_fbi->ipu_ch = ipu_idmac_get(plat_data->ipu_channel_bg);
> + if (IS_ERR(mxc_fbi->ipu_ch)) {
> + ret = PTR_ERR(mxc_fbi->ipu_ch);
> + goto failed_request_ipu;
> + }
> +
> + mxc_fbi->dmfc = ipu_dmfc_get(plat_data->ipu_channel_bg);
> + if (IS_ERR(mxc_fbi->ipu_ch)) {
> + ret = PTR_ERR(mxc_fbi->ipu_ch);
> + goto failed_request_dmfc;
> + }
> +
> + if (plat_data->dp_channel >= 0) {
> + mxc_fbi->dp = ipu_dp_get(plat_data->dp_channel);
> + if (IS_ERR(mxc_fbi->dp)) {
> + ret = PTR_ERR(mxc_fbi->ipu_ch);
> + goto failed_request_dp;
> + }
> + }
> +
> + fbi->var.yres_virtual = fbi->var.yres;
> +
> + INIT_LIST_HEAD(&fbi->modelist);
> + for (i = 0; i < plat_data->num_modes; i++)
> + fb_add_videomode(&plat_data->modes[i], &fbi->modelist);
> +
> + if (plat_data->flags & IMX_IPU_FB_USE_MODEDB) {
> + for (i = 0; i < num_fb_modes; i++)
> + fb_add_videomode(&fb_modes[i], &fbi->modelist);
> + }
> +
> + imx_ipu_fb_find_mode(fbi);
> +
> + imx_ipu_fb_check_var(&fbi->var, fbi);
> + imx_ipu_fb_set_fix(fbi);
> + ret = register_framebuffer(fbi);
> + if (ret < 0)
> + goto failed_register;
> +
> + imx_ipu_fb_set_par(fbi);
> + imx_ipu_fb_blank(FB_BLANK_UNBLANK, fbi);
> +
> + if (plat_data->ipu_channel_fg >= 0 && plat_data->flags & IMX_IPU_FB_USE_OVERLAY)
> + imx_ipu_fb_init_overlay(pdev, fbi, plat_data->ipu_channel_fg);
> +
> + platform_set_drvdata(pdev, fbi);
> +
> + return 0;
> +
> +failed_register:
> + if (plat_data->dp_channel >= 0)
> + ipu_dp_put(mxc_fbi->dp);
> +failed_request_dp:
> + ipu_dmfc_put(mxc_fbi->dmfc);
> +failed_request_dmfc:
> + ipu_idmac_put(mxc_fbi->ipu_ch);
> +failed_request_ipu:
> + fb_dealloc_cmap(&fbi->cmap);
> + framebuffer_release(fbi);
> +
> + return ret;
> +}
> +
> +static int __devexit imx_ipu_fb_remove(struct platform_device *pdev)
> +{
> + struct fb_info *fbi = platform_get_drvdata(pdev);
> + struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> + struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
> +
> + if (plat_data->ipu_channel_fg >= 0 && plat_data->flags & IMX_IPU_FB_USE_OVERLAY)
> + imx_ipu_fb_exit_overlay(pdev, fbi, plat_data->ipu_channel_fg);
> +
> + imx_ipu_fb_blank(FB_BLANK_POWERDOWN, fbi);
> +
> + dma_free_writecombine(fbi->device, fbi->fix.smem_len,
> + fbi->screen_base, fbi->fix.smem_start);
> +
> + if (&fbi->cmap)
> + fb_dealloc_cmap(&fbi->cmap);
> +
> + unregister_framebuffer(fbi);
> +
> + if (plat_data->dp_channel >= 0)
> + ipu_dp_put(mxc_fbi->dp);
> + ipu_dmfc_free_bandwidth(mxc_fbi->dmfc);
> + ipu_dmfc_put(mxc_fbi->dmfc);
> + ipu_idmac_put(mxc_fbi->ipu_ch);
> +
> + framebuffer_release(fbi);
> +
> + return 0;
> +}
> +
> +static struct platform_driver imx_ipu_fb_driver = {
> + .driver = {
> + .name = DRIVER_NAME,
> + },
> + .probe = imx_ipu_fb_probe,
> + .remove = __devexit_p(imx_ipu_fb_remove),
> +};
> +
> +static int __init imx_ipu_fb_init(void)
> +{
> + return platform_driver_register(&imx_ipu_fb_driver);
> +}
> +
> +static void __exit imx_ipu_fb_exit(void)
> +{
> + platform_driver_unregister(&imx_ipu_fb_driver);
> +}
> +
> +module_init(imx_ipu_fb_init);
> +module_exit(imx_ipu_fb_exit);
> +
> +MODULE_AUTHOR("Freescale Semiconductor, Inc.");
> +MODULE_DESCRIPTION("i.MX framebuffer driver");
> +MODULE_LICENSE("GPL");
> +MODULE_SUPPORTED_DEVICE("fb");
> --
> 1.7.2.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]<mailto:[email protected]>
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>


Attachments:
winmail.dat (66.70 kB)

2010-12-14 13:13:31

by Liu Ying

[permalink] [raw]
Subject: Re: [PATCH 3/9] Add a mfd IPUv3 driver

2010/12/14 Sascha Hauer <[email protected]>:
> On Tue, Dec 14, 2010 at 12:05:07PM +0800, Liu Ying wrote:
>> Hello, Sascha,
>>
>> Please find my feedback with [LY] embedded.
>>
>> Best Regards,
>> Liu Ying
>>
>> 2010/12/13 Sascha Hauer <[email protected]>:
>> > Hi Liu Ying,
>> >
>> > Thanks for looking at the patches.
>> [LY] You are welcome.
>> >
>> > On Sun, Dec 12, 2010 at 01:21:57PM +0800, Liu Ying wrote:
>> >> Hello, Sascha,
>> >>
>> >> IPU is not embedded in i,MX50 SoC, but in i.MX51/53 SoCs, please
>> >> modify the commit message.
>> >>
>> >> I have the following comments for the files editted respectively:
>> >> 1) ipu-common.c
>> >> ? ? - ipu_idmac_get()/ipu_idmac_put() need a mechanism to protect IPU
>> >> IDMAC resources, namely, get rid of potential race condition issue. As
>> >> only framebuffer support is added in your patches, there should be no
>> >> race condition issue now. After IC channels are supported(maybe as DMA
>> >> engine), perhaps, there will be such kind of issue.
>> >
>> > ok.
>> >
>> >> ? ? - ipu_idmac_select_buffer() need to add spinlock to protect
>> >> IPU_CHA_BUFx_RDY registers.
>> >
>> > ok.
>> >
>> >> ? ? - It looks that ipu_uninit_channel() only clears
>> >> IPU_CHA_DB_MODE_SEL register, so why not put it in
>> >> ipu_idmac_disable_channel()?
>> >
>> > ok.
>> >
>> >> ? ? - It looks that ipu_add_client_devices() and your framebuffer
>> >> patch assume /dev/fb0 uses DP_BG, /dev/fb1 uses DP_FG and /dev/fb2
>> >> uses DC.
>> >> ? ? ? But fb1_platform_data->ipu_channel_bg is
>> >> MX51_IPU_CHANNEL_MEM_DC_SYNC, this may make confusion for driver
>> >> readers and it is not easy for code maintenance.
>> >
>> > Do you have a better suggestion? fb2 becomes fb1 when overlay support
>> > is not used.
>> [LY] How about assigning DP-BG to /dev/fb0, DC to /dev/fb1 and DP_FG
>> to /dev/fb2?
>> >
>> >> ? ? - It also looks that ipu_add_client_devices() and your framebuffer
>> >> driver assume DP_BG/DP_FG are bound with DI0 and DC is bound with DI1.
>> >> ? ? ? It is ok for babbage board to support this kind of
>> >> configuration, but it may bring some limitation. For example, TVE(TV
>> >> encoder) module embedded in MX51 SoC can only connected with DI1 and
>> >> users may like to use TV as the primary device(support HW overlay),
>> >> and FSL Android BSP does support to use DI1 with LCD as the primary
>> >> device on babbage board.
>> >
>> > SO we need to put the display number into the platform data, right?
>> [LY] Yes, I think so. As the primary display port could be DI0 or DI1
>> on boards like babbage board(two display ports are used), the primary
>> display number in platform data should be configurable(I'm not sure
>> whether this can be accepted by community), perhaps, configured by
>> kernel bootup command line.
>
> Ok, I'll find a solution for this.
>
>> >
>> >>
>> >> 2) ipu-cpmem.c
>> >> ? ? - In order to improve performance, maybe writing
>> >> ipu_ch_param_addr(ch) directly will be fine, but not using memset()
>> >> and memcpy() in ipu_ch_param_init().
>> >
>> > I don't see this function in any performance critical path.
>> [LY] Yes, currently, this function is not in performance critical
>> path, so it is ok for me now. However, after IC/IRT channels are
>> involved, the channels' idmac configuration might be changed from time
>> to time and frequently, as the channels could be used as dma engine.
>
> We are talking about 60 frames per second at maximum, right? So the
> channel would be reconfigured 60 times a second, that's still not
> performance critical, at least not if we are talking about a 100 byte or
> something memset.
>
You are right. Take processing VGA(640x480) frames for example, IC
channel's input bandwidth is 100MPixel/sec and output 50MPixel/sec,
the framerate can theoritically reach 162fps, then there will be about
162fps x 2 IDMACs x 2(memset/memcpy) x100Byte = 63KByte/sec memory
accessing overhead.
>> >
>> >>
>> >> 3) ipu-dc.c
>> >> ? ? - Looks '#include <asm/atomic.h>' and '#include
>> >> <linux/spinlock.h>' are unnecessary.
>> >> ? ? - Need to call 'ipu_module_disable(IPU_CONF_DC_EN);' somewhere.
>> >
>> > ok.
>> >
>> >>
>> >> 4) ipu-di.c
>> >> ? ? - Looks '#include <asm/atomic.h>' and '#include <linux/delay.h>'
>> >> are unnecessary.
>> >
>> > ok.
>> >
>> >>
>> >> 5) ipu-dmfc.c
>> >> ? ? - Looks '#include <linux/delay.h>' are unnecessary.
>> >
>> > ok.
>> >
>> >>
>> >> 6) ipu-dp.c
>> >> ? ? - Looks '#include <asm/atomic.h>' and '#include <linux/delay.h>'
>> >> are unnecessary.
>> >
>> > ok.
>> >
>> >>
>> >> 7) ipu-prv.h
>> >> ? ? - Looks '#include <linux/interrupt.h>' is unnecessary.
>> >> ? ? - Please rename 'MX51_IPU_CHANNEL_xxxx' to be 'IPU_CHANNEL_xxxx',
>> >> IPUv3 channel names do not depend on SoC name.
>> >
>> > I was proven wrong each time I believed this.
>> [LY] What if IPUv3 will be embedded in more SoCs? Could you please
>> tell the reason? Thanks.
>
> Then channel assignments will change, I'll promise.

i.MX53 SoC doesn't change IPUv3 channel assignments.
>
> Sascha
--
Liu Ying

2010-12-14 13:23:33

by Liu Ying

[permalink] [raw]
Subject: Re: [PATCH 5/9] Add i.MX5 framebuffer driver

2010/12/14 Sascha Hauer <[email protected]>:
> On Tue, Dec 14, 2010 at 02:40:09PM +0800, Liu Ying wrote:
>> Hello, Sascha,
>>
>> Please find my feedback with [LY] embedded.
>>
>> And, a bug related with this patch:
>> 1) Bootup the babbage board.
>> 2) echo U:640x480p-60 > /sys/class/graphics/fb0/mode
>> 3) cat /sys/class/graphics/fb0/virtual_size, it shows '640,480'.
>> 4) echo 640,960 > /sys/class/graphics/fb0/virtual_size, change virtual
>> yres to be 960.
>> 5) cat /sys/class/graphics/fb0/virtual_size, it still shows '640,480'.
>> I think this is caused by 'var->yres_virtual = var->yres;' in custom
>> fb_set_par() function(Sorry, I cannot make the comment inline this
>> time, as I don't find the original code within the mail I am
>> replying). This makes fb0 use only one block of memory whose size is
>> equal to screen size, so pan display can not really play her role and
>> screen tearing issue may happen.

Sascha,

Just would like to know if you've caught this bug.

>>
>> Best Regards,
>> Liu Ying
>>
>> 2010/12/13 Sascha Hauer <[email protected]>:
>> > On Sun, Dec 12, 2010 at 02:13:40PM +0800, Liu Ying wrote:
>> >> Hello, Sascha,
>> >>
>> >> I have following comments to this patch:
>> >> 1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.
>> >
>> > ok.
>> >
>> >> 2) ADC is not supported yet in the framebuffer driver, so please
>> >> modify this comment:
>> >> ? ?> + * Framebuffer Framebuffer Driver for SDC and ADC.
>> >
>> > ok.
>> >
>> >> 3) 'ipu_dp_set_window_pos()' is called only once in
>> >> imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
>> >> support to change the overlay framebuffer position. Need a
>> >> mechanism/interface for users to change the overlay framebuffer
>> >> position.
>> >
>> > Yes. The overlay support is currently only present in the driver because
>> > I didn't want to break it in general. There is no generic overlay API in
>> > the kernel and I didn't want to invent the n+1 API. Currently the
>> > possible use cases of the overlay support are not clear to me. Number
>> > one use case would probably be to display video output, but the
>> > corresponding driver would be a v4l2 driver, so in this case we would
>> > only need an in-kernel API.
>> > Anyway, I have not the resources to implement complete overlay support.
>> > So either we keep it like it is and it more has an example character
>> > or we remove it completely from the driver for now.
>> >
>> >> 4) Need to make sure the framebuffer on DP-FG is blanked before the
>> >> framebuffer on DP-BG is blanked. Meanwhile, the framebuffer on DP-FG
>> >> should be unblanked after the framebuffer on DP-BG is unblanked
>> >> 5) Need to check the framebuffer on DP-FG doesn't run out of the range
>> >> of the framebuffer on DP-BG.
>> >
>> > I tend to remove the overlay support.
>> >
>> >> 6) I prefer to find the video mode in modedb first, and if we cannot
>> >> find the video mode in common video mode data base, we can find a
>> >> video mode in custom video mode data base which is defined in platform
>> >> data. In this way, we don't need to export common modefb.
>> >
>> > I exported the modedb to be able to show the different modes under
>> > /sys/class/graphics/fb0/modes and to set it with
>> > /sys/class/graphics/fb0/mode. If you want this there is no way around
>> > exporting it. The ioctl interface for mode setting is independent of the
>> > specific modes anyway.
>> [LY] ?Just a concern. If a platform choose to add the generic video
>> mode data base to IPUv3 framebuffer modelist, 'cat
>> /sys/class/graphics/fb0/modes' will show all the video modes in the
>> generic data base to users. I am not sure whether showing them to
>> users means the IPUv3 framebuffer driver acknowledges to support all
>> of them or not. I tried to do 'echo U:1800x1440p-70 >
>> /sys/class/graphics/fb0/mode' with the kernel on ipuv3 branch, there
>> will be a kernel dump(seems it can not allocate enough memory).
>
> We'll have to increase the dma coherent size (and max zone order) for
> these resolutions to work. I have patches for this in my local tree but
> decided to wait until some contiguous memory allocator hits the tree.
> Also, the fb driver should sanity check the generic modes before adding
> them to the list. FOr example the IPU can't do 1920x1200@60. This code
> is missing currently.
>
> Sascha
>
>
> --
> Pengutronix e.K. ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
> Industrial Linux Solutions ? ? ? ? ? ? ? ? | http://www.pengutronix.de/ ?|
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 ? ?|
> Amtsgericht Hildesheim, HRA 2686 ? ? ? ? ? | Fax: ? +49-5121-206917-5555 |
>



--
Best Regards,
Liu Ying

2010-12-15 11:18:36

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 5/9] Add i.MX5 framebuffer driver

On Tue, Dec 14, 2010 at 09:23:30PM +0800, Liu Ying wrote:
> 2010/12/14 Sascha Hauer <[email protected]>:
> > On Tue, Dec 14, 2010 at 02:40:09PM +0800, Liu Ying wrote:
> >> Hello, Sascha,
> >>
> >> Please find my feedback with [LY] embedded.
> >>
> >> And, a bug related with this patch:
> >> 1) Bootup the babbage board.
> >> 2) echo U:640x480p-60 > /sys/class/graphics/fb0/mode
> >> 3) cat /sys/class/graphics/fb0/virtual_size, it shows '640,480'.
> >> 4) echo 640,960 > /sys/class/graphics/fb0/virtual_size, change virtual
> >> yres to be 960.
> >> 5) cat /sys/class/graphics/fb0/virtual_size, it still shows '640,480'.
> >> I think this is caused by 'var->yres_virtual = var->yres;' in custom
> >> fb_set_par() function(Sorry, I cannot make the comment inline this
> >> time, as I don't find the original code within the mail I am
> >> replying). This makes fb0 use only one block of memory whose size is
> >> equal to screen size, so pan display can not really play her role and
> >> screen tearing issue may happen.
>
> Sascha,
>
> Just would like to know if you've caught this bug.

Yes, it will be fixed in the next series. It was the framebuffer driver
forcing yres_virtual to yres in set_par.

Sascha

>
> >>
> >> Best Regards,
> >> Liu Ying
> >>
> >> 2010/12/13 Sascha Hauer <[email protected]>:
> >> > On Sun, Dec 12, 2010 at 02:13:40PM +0800, Liu Ying wrote:
> >> >> Hello, Sascha,
> >> >>
> >> >> I have following comments to this patch:
> >> >> 1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.
> >> >
> >> > ok.
> >> >
> >> >> 2) ADC is not supported yet in the framebuffer driver, so please
> >> >> modify this comment:
> >> >> ? ?> + * Framebuffer Framebuffer Driver for SDC and ADC.
> >> >
> >> > ok.
> >> >
> >> >> 3) 'ipu_dp_set_window_pos()' is called only once in
> >> >> imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
> >> >> support to change the overlay framebuffer position. Need a
> >> >> mechanism/interface for users to change the overlay framebuffer
> >> >> position.
> >> >
> >> > Yes. The overlay support is currently only present in the driver because
> >> > I didn't want to break it in general. There is no generic overlay API in
> >> > the kernel and I didn't want to invent the n+1 API. Currently the
> >> > possible use cases of the overlay support are not clear to me. Number
> >> > one use case would probably be to display video output, but the
> >> > corresponding driver would be a v4l2 driver, so in this case we would
> >> > only need an in-kernel API.
> >> > Anyway, I have not the resources to implement complete overlay support.
> >> > So either we keep it like it is and it more has an example character
> >> > or we remove it completely from the driver for now.
> >> >
> >> >> 4) Need to make sure the framebuffer on DP-FG is blanked before the
> >> >> framebuffer on DP-BG is blanked. Meanwhile, the framebuffer on DP-FG
> >> >> should be unblanked after the framebuffer on DP-BG is unblanked
> >> >> 5) Need to check the framebuffer on DP-FG doesn't run out of the range
> >> >> of the framebuffer on DP-BG.
> >> >
> >> > I tend to remove the overlay support.
> >> >
> >> >> 6) I prefer to find the video mode in modedb first, and if we cannot
> >> >> find the video mode in common video mode data base, we can find a
> >> >> video mode in custom video mode data base which is defined in platform
> >> >> data. In this way, we don't need to export common modefb.
> >> >
> >> > I exported the modedb to be able to show the different modes under
> >> > /sys/class/graphics/fb0/modes and to set it with
> >> > /sys/class/graphics/fb0/mode. If you want this there is no way around
> >> > exporting it. The ioctl interface for mode setting is independent of the
> >> > specific modes anyway.
> >> [LY] ?Just a concern. If a platform choose to add the generic video
> >> mode data base to IPUv3 framebuffer modelist, 'cat
> >> /sys/class/graphics/fb0/modes' will show all the video modes in the
> >> generic data base to users. I am not sure whether showing them to
> >> users means the IPUv3 framebuffer driver acknowledges to support all
> >> of them or not. I tried to do 'echo U:1800x1440p-70 >
> >> /sys/class/graphics/fb0/mode' with the kernel on ipuv3 branch, there
> >> will be a kernel dump(seems it can not allocate enough memory).
> >
> > We'll have to increase the dma coherent size (and max zone order) for
> > these resolutions to work. I have patches for this in my local tree but
> > decided to wait until some contiguous memory allocator hits the tree.
> > Also, the fb driver should sanity check the generic modes before adding
> > them to the list. FOr example the IPU can't do 1920x1200@60. This code
> > is missing currently.
> >
> > Sascha
> >
> >
> > --
> > Pengutronix e.K. ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
> > Industrial Linux Solutions ? ? ? ? ? ? ? ? | http://www.pengutronix.de/ ?|
> > Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 ? ?|
> > Amtsgericht Hildesheim, HRA 2686 ? ? ? ? ? | Fax: ? +49-5121-206917-5555 |
> >
>
>
>
> --
> Best Regards,
> Liu Ying
>

--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2010-12-15 14:40:16

by Sascha Hauer

[permalink] [raw]
Subject: Re: [PATCH 5/9] Add i.MX5 framebuffer driver

On Tue, Dec 14, 2010 at 12:38:08PM +0000, Chen Jie-B02280 wrote:
> Hi, Sascha,
>
> Few comments inline with [Jason]

Please consider switching to a sane mailer which is able to quote correctly.

>
> I have following comments to this patch:
> 1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.
> 2) ADC is not supported yet in the framebuffer driver, so please
> modify this comment:
> > + * Framebuffer Framebuffer Driver for SDC and ADC.
> 3) 'ipu_dp_set_window_pos()' is called only once in
> imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
> support to change the overlay framebuffer position. Need a
> mechanism/interface for users to change the overlay framebuffer
> position.
> [Jason] DP-FG should be one fb device, sequence ioctl should be added
> after it, like global alpha , color key etc.

As said before, I have no interest in making the overlay fully
functional atm. So either we'll leave it here for reference if someone
ever tries to implement it properly or I'll remove it completely.

> > +static int imx_ipu_fb_set_par(struct fb_info *fbi)
> > +{
> > + int ret;
> > + struct ipu_di_signal_cfg sig_cfg;
> > + struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> > + u32 out_pixel_fmt;
> > + int interlaced = 0;
> > + struct fb_var_screeninfo *var = &fbi->var;
> > + int enabled = mxc_fbi->enabled;
> > +
> > + dev_dbg(fbi->device, "Reconfiguring framebuffer %dx%d-%d\n",
> > + fbi->var.xres, fbi->var.yres, fbi->var.bits_per_pixel);
> > +
> > + if (enabled)
> > + imx_ipu_fb_disable(fbi);
> > +
> > + fbi->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
> > +
> > + var->yres_virtual = var->yres;
> > +
> > + ret = imx_ipu_fb_map_video_memory(fbi);
> > + if (ret)
> > + return ret;
> > +
> > + if (var->vmode & FB_VMODE_INTERLACED)
> > + interlaced = 1;
> > +
> > + memset(&sig_cfg, 0, sizeof(sig_cfg));
> > + out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
> > +
> > + if (var->vmode & FB_VMODE_INTERLACED)
> > + sig_cfg.interlaced = 1;
> > + if (var->vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
> > + sig_cfg.odd_field_first = 1;
> > + if (var->sync & FB_SYNC_EXT)
> > + sig_cfg.ext_clk = 1;
> [Jason] FB_SYNC_EXT has not be used in FSL kernel mainline, it
> represent SYNC ext, should not be flag of ext clk. Some application
> for example X-server could not recognize it.

Ok, I'll remove it.


> > +static int imx_ipu_fb_pan_display(struct fb_var_screeninfo *var,
> > + struct fb_info *info)
> > +{
> > + struct imx_ipu_fb_info *mxc_fbi = info->par;
> > + unsigned long base;
> > + int ret;
> > +
> > + if (info->var.yoffset == var->yoffset)
> > + return 0; /* No change, do nothing */
> > +
> > + base = var->yoffset * var->xres_virtual * var->bits_per_pixel / 8;
> > + base += info->fix.smem_start;
> > +
> > + ret = ipu_wait_for_interrupt(IPU_IRQ_EOF(mxc_fbi->ipu_channel_num), 100);
> > + if (ret)
> > + return ret;
> > +
> > + if (ipu_idmac_update_channel_buffer(mxc_fbi->ipu_ch, 0, base)) {
> > + dev_err(info->device,
> > + "Error updating SDC buf to address=0x%08lX\n", base);
> > + }
> [Jason] It's better to enable double -buffer for fb which could avoid tearing issue.

There is no tearing as the switching is done during vsync.


--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2010-12-15 15:40:08

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 1/9] ARM i.MX51: Add ipu clock support

On Thursday 09 December 2010, Sascha Hauer wrote:
> +static int clk_ipu_enable(struct clk *clk)
> +{
> + u32 reg;
> +
> + _clk_ccgr_enable(clk);
> +
> + /* Enable handshake with IPU when certain clock rates are changed */
> + reg = __raw_readl(MXC_CCM_CCDR);
> + reg &= ~MXC_CCM_CCDR_IPU_HS_MASK;
> + __raw_writel(reg, MXC_CCM_CCDR);
> +
> + /* Enable handshake with IPU when LPM is entered */
> + reg = __raw_readl(MXC_CCM_CLPCR);
> + reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
> + __raw_writel(reg, MXC_CCM_CLPCR);
> +
> + return 0;
> +}

Why __raw_readl?

The regular accessor function for I/O registers is readl, which handles
the access correctly with regard to atomicity, I/O ordering and byteorder.

Arnd

2010-12-15 15:50:03

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 6/9] ARM i.MX51: Add IPU device support

On Thursday 09 December 2010, Sascha Hauer wrote:
> +#define imx51_add_ipuv3(pdata) \
> + imx_add_ipuv3(&imx51_ipuv3_data, pdata)

This looks like a pointless abstraction, it does not make
the code smaller or easier to read. I know it's sometimes
tempting to use macros, but in most cases, you should try
not to.

> +#define imx51_ipuv3_data_entry_single(soc) \
> + { \
> + .iobase = soc ## _IPU_CTRL_BASE_ADDR, \
> + .irq_err = soc ## _INT_IPU_ERR, \
> + .irq = soc ## _INT_IPU_SYN, \
> + }
> +
> +#ifdef CONFIG_SOC_IMX51
> +const struct imx_ipuv3_data imx51_ipuv3_data __initconst =
> + imx51_ipuv3_data_entry_single(MX51);
> +#endif /* ifdef CONFIG_SOC_IMX35 */

This looks really strange: You define a macro that is used
in only a single place, and the only user is a data structure
that references data from other files and is used elsewhere
as well.

Avoiding the macro would make it easier to grep for the use
of the identifiers.

If you put the data structure in the place where it is used,
you can avoid the #ifdef and make it static.

Also, the comment on the #endif does not match the #if.

Arnd

2010-12-15 16:25:49

by Arnaud Patard

[permalink] [raw]
Subject: Re: [PATCH 6/9] ARM i.MX51: Add IPU device support

Arnd Bergmann <[email protected]> writes:

Hi,

> On Thursday 09 December 2010, Sascha Hauer wrote:
>> +#define imx51_add_ipuv3(pdata) \
>> + imx_add_ipuv3(&imx51_ipuv3_data, pdata)
>
> This looks like a pointless abstraction, it does not make
> the code smaller or easier to read. I know it's sometimes
> tempting to use macros, but in most cases, you should try
> not to.
>

it's how things have been handled atm in the imx code. I don't have
any preference on this at all but at least either we go on with it
or we get rid of all theses #defines. It's a matter of consistency.
The thing is that I would consider removing the imx*add* stuff to be
a cleanup and should be done in a different patch, not in a patchset
adding IPU support for imx51.

Anyway, let's wait for Sascha's point of view, he knows the imx stuff
far better than me.

Arnaud

2010-12-15 16:29:48

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 6/9] ARM i.MX51: Add IPU device support

On Wednesday 15 December 2010, Arnaud Patard wrote:
> The thing is that I would consider removing the imx*add* stuff to be
> a cleanup and should be done in a different patch, not in a patchset
> adding IPU support for imx51.

Yes, that sounds reasonable.

Arnd

2010-12-15 16:35:14

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH 1/9] ARM i.MX51: Add ipu clock support

On Wed, Dec 15, 2010 at 04:40:03PM +0100, Arnd Bergmann wrote:
> On Thursday 09 December 2010, Sascha Hauer wrote:
> > +static int clk_ipu_enable(struct clk *clk)
> > +{
> > + u32 reg;
> > +
> > + _clk_ccgr_enable(clk);
> > +
> > + /* Enable handshake with IPU when certain clock rates are changed */
> > + reg = __raw_readl(MXC_CCM_CCDR);
> > + reg &= ~MXC_CCM_CCDR_IPU_HS_MASK;
> > + __raw_writel(reg, MXC_CCM_CCDR);
> > +
> > + /* Enable handshake with IPU when LPM is entered */
> > + reg = __raw_readl(MXC_CCM_CLPCR);
> > + reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
> > + __raw_writel(reg, MXC_CCM_CLPCR);
> > +
> > + return 0;
> > +}
>
> Why __raw_readl?
>
> The regular accessor function for I/O registers is readl, which handles
> the access correctly with regard to atomicity, I/O ordering and byteorder.

There's no possibility of those two being mis-ordered - they will be in
program order whatever.

What isn't guaranteed is the ordering between I/O accesses (accesses to
device memory) and SDRAM accesses (normal memory) which can pass each other
without additional barriers. Memory accesses can pass I/O accesses.

So, (eg), if you're writing to a register which causes the hardware to
begin reading DMA descriptors from an area allocated from dma_alloc_coherent(),
you need a barrier to ensure that writes to the dma_alloc_coherent() are
visible to the hardware before you write the enable register.

If you don't need normal vs device access ordering, using readl_relaxed()/
writel_relaxed() is preferred, and avoids the (apparantly rather high)
performance overhead of having to issue barriers all the way down to the
L2 cache.

Lastly, I don't see where atomicity comes into it - __raw_writel vs writel
have the same atomicity. Both are single access atomic provided they're
naturally aligned. Misaligned device accesses are not predictable.

2010-12-15 16:50:29

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 1/9] ARM i.MX51: Add ipu clock support

On Wednesday 15 December 2010, Russell King - ARM Linux wrote:
> > The regular accessor function for I/O registers is readl, which handles
> > the access correctly with regard to atomicity, I/O ordering and byteorder.
>
> There's no possibility of those two being mis-ordered - they will be in
> program order whatever.
>
> What isn't guaranteed is the ordering between I/O accesses (accesses to
> device memory) and SDRAM accesses (normal memory) which can pass each other
> without additional barriers. Memory accesses can pass I/O accesses.

Yes, that's what I meant.

> If you don't need normal vs device access ordering, using readl_relaxed()/
> writel_relaxed() is preferred, and avoids the (apparantly rather high)
> performance overhead of having to issue barriers all the way down to the
> L2 cache.

Well, my point was that the authors should choose their I/O accessors
carefully. Using __raw_writel() without any explanations is a rather
bad default, it's not designed for that. Using writel() as a default
is usually a good choice, as we can assume it to do the right thing.

writel_relaxed() is also good where appropriate, because it tells
the reader that the driver author has thought about the I/O (vs. code)
ordering and concluded that it's safe to do.

> Lastly, I don't see where atomicity comes into it - __raw_writel vs writel
> have the same atomicity. Both are single access atomic provided they're
> naturally aligned. Misaligned device accesses are not predictable.

This is just what gcc turns it into today. In theory, a future gcc or
a future cpu might change that. If you mark a pointer as
'__attribute__((packed))', it probably already does, even for aligned
pointers, while it does not when using writel_{,relaxed}. The point is
that __raw_* means just that -- we don't give any guarantees on what
happens on the bus, so people should not use it.

Arnd

2010-12-15 17:13:13

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH 1/9] ARM i.MX51: Add ipu clock support

On Wed, Dec 15, 2010 at 05:49:59PM +0100, Arnd Bergmann wrote:
> On Wednesday 15 December 2010, Russell King - ARM Linux wrote:
> > Lastly, I don't see where atomicity comes into it - __raw_writel vs writel
> > have the same atomicity. Both are single access atomic provided they're
> > naturally aligned. Misaligned device accesses are not predictable.
>
> This is just what gcc turns it into today. In theory, a future gcc or
> a future cpu might change that. If you mark a pointer as
> '__attribute__((packed))', it probably already does, even for aligned
> pointers, while it does not when using writel_{,relaxed}. The point is
> that __raw_* means just that -- we don't give any guarantees on what
> happens on the bus, so people should not use it.

No. It does give guarantees on what happens on the bus. The kind of
pointer you pass in has no bearing on what happens on the bus because it's
casted away immediately.

__raw_writel(v, ptr) doesn't just do *(ptr) = v - that doesn't work when
ptr is void. Instead, it has to do a cast to ensure that we have a valid
C dereference (void pointers are illegal to dereference):

#define __raw_writel(v,a) (__chk_io_ptr(a), \
*(volatile unsigned int __force *)(a) = (v))

Doesn't matter if 'a' was marked as packed or not - that's all casted away.
That's not something that should ever change - otherwise we'll all be
screwed as you could never cast away pointer attributes.

It _may_ possible that the compiler decides that accessing an 'unsigned int'
will not be done as a single word load, in which case we have exactly the
same problems with writel() too.

And in any case, if __raw_writel() was as you're suggesting, then it would
serve absolutely no purpose at all.

2010-12-16 02:05:36

by Chen Jie-B02280

[permalink] [raw]
Subject: RE: [PATCH 5/9] Add i.MX5 framebuffer driver

Hi, Sascha,



Jason Chen / Chen Jie
NMG / MAD
Freescale Semiconductor (China) Ltd.
2F, Building B, 177#, Bi Bo Rd
Pu Dong New District Shanghai?201203
Tel:???? 021-28937178?
Fax:???? 021-28937444
E-mail:[email protected]


-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of [email protected]
Sent: Wednesday, December 15, 2010 10:39 PM
To: Chen Jie-B02280
Cc: [email protected]; [email protected]; [email protected]; Zhang Lily-R58066; [email protected]
Subject: Re: [PATCH 5/9] Add i.MX5 framebuffer driver

On Tue, Dec 14, 2010 at 12:38:08PM +0000, Chen Jie-B02280 wrote:
> Hi, Sascha,
>
> Few comments inline with [Jason]

Please consider switching to a sane mailer which is able to quote correctly.

>
> I have following comments to this patch:
> 1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.
> 2) ADC is not supported yet in the framebuffer driver, so please
> modify this comment:
> > + * Framebuffer Framebuffer Driver for SDC and ADC.
> 3) 'ipu_dp_set_window_pos()' is called only once in
> imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
> support to change the overlay framebuffer position. Need a
> mechanism/interface for users to change the overlay framebuffer
> position.
> [Jason] DP-FG should be one fb device, sequence ioctl should be added
> after it, like global alpha , color key etc.

As said before, I have no interest in making the overlay fully functional atm. So either we'll leave it here for reference if someone ever tries to implement it properly or I'll remove it completely.
[Jason] Ok, then pls keep it as reference.

> > +static int imx_ipu_fb_set_par(struct fb_info *fbi) {
> > + int ret;
> > + struct ipu_di_signal_cfg sig_cfg;
> > + struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> > + u32 out_pixel_fmt;
> > + int interlaced = 0;
> > + struct fb_var_screeninfo *var = &fbi->var;
> > + int enabled = mxc_fbi->enabled;
> > +
> > + dev_dbg(fbi->device, "Reconfiguring framebuffer %dx%d-%d\n",
> > + fbi->var.xres, fbi->var.yres,
> > + fbi->var.bits_per_pixel);
> > +
> > + if (enabled)
> > + imx_ipu_fb_disable(fbi);
> > +
> > + fbi->fix.line_length = var->xres_virtual *
> > + var->bits_per_pixel / 8;
> > +
> > + var->yres_virtual = var->yres;
> > +
> > + ret = imx_ipu_fb_map_video_memory(fbi);
> > + if (ret)
> > + return ret;
> > +
> > + if (var->vmode & FB_VMODE_INTERLACED)
> > + interlaced = 1;
> > +
> > + memset(&sig_cfg, 0, sizeof(sig_cfg));
> > + out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
> > +
> > + if (var->vmode & FB_VMODE_INTERLACED)
> > + sig_cfg.interlaced = 1;
> > + if (var->vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
> > + sig_cfg.odd_field_first = 1;
> > + if (var->sync & FB_SYNC_EXT)
> > + sig_cfg.ext_clk = 1;
> [Jason] FB_SYNC_EXT has not be used in FSL kernel mainline, it
> represent SYNC ext, should not be flag of ext clk. Some application
> for example X-server could not recognize it.

Ok, I'll remove it.


> > +static int imx_ipu_fb_pan_display(struct fb_var_screeninfo *var,
> > + struct fb_info *info) {
> > + struct imx_ipu_fb_info *mxc_fbi = info->par;
> > + unsigned long base;
> > + int ret;
> > +
> > + if (info->var.yoffset == var->yoffset)
> > + return 0; /* No change, do nothing */
> > +
> > + base = var->yoffset * var->xres_virtual * var->bits_per_pixel / 8;
> > + base += info->fix.smem_start;
> > +
> > + ret = ipu_wait_for_interrupt(IPU_IRQ_EOF(mxc_fbi->ipu_channel_num), 100);
> > + if (ret)
> > + return ret;
> > +
> > + if (ipu_idmac_update_channel_buffer(mxc_fbi->ipu_ch, 0, base)) {
> > + dev_err(info->device,
> > + "Error updating SDC buf to address=0x%08lX\n", base);
> > + }
> [Jason] It's better to enable double -buffer for fb which could avoid tearing issue.

There is no tearing as the switching is done during vsync.
[Jason] Yes, you are right.


--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |