* [PATCH 7/7] ARM i.MX51 babbage: Add framebuffer support
From: Sascha Hauer @ 2011-02-16 14:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297865452-32181-1-git-send-email-s.hauer@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
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 de4fa992f..89e71da 100644
--- a/arch/arm/mach-mx5/Kconfig
+++ b/arch/arm/mach-mx5/Kconfig
@@ -42,6 +42,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 1d231e8..c5170e8 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__GPIO4_24,
MX51_PAD_CSPI1_SS1__GPIO4_25,
+
+ /* Display */
+ MX51_PAD_DISPB2_SER_DIN__GPIO3_5,
+ MX51_PAD_DISPB2_SER_DIO__GPIO3_6,
+ MX51_PAD_NANDF_D12__GPIO3_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__GPIO4_14,
};
/* Serial ports */
@@ -346,6 +383,23 @@ 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,
+ .display = 0,
+};
+
+static struct ipuv3_fb_platform_data babbage_fb1_data = {
+ .interface_pix_fmt = IPU_PIX_FMT_RGB565,
+ .flags = IMX_IPU_FB_USE_MODEDB,
+ .display = 1,
+};
+
+static struct imx_ipuv3_platform_data ipu_data = {
+ .fb_head0_platform_data = &babbage_fb0_data,
+ .fb_head1_platform_data = &babbage_fb1_data,
+};
+
/*
* Board specific initialization.
*/
@@ -392,6 +446,26 @@ 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_direction_output(GPIO_DVI_RESET, 0);
+ /* DVI Power-down */
+ gpio_request(GPIO_DVI_PWRDN, "dvi pwdn");
+ gpio_direction_output(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
^ permalink raw reply related
* [PATCH 6/7] ARM i.MX5: increase dma consistent size for IPU support
From: Sascha Hauer @ 2011-02-16 14:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297865452-32181-1-git-send-email-s.hauer@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/plat-mxc/include/mach/memory.h | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/arch/arm/plat-mxc/include/mach/memory.h b/arch/arm/plat-mxc/include/mach/memory.h
index 8386140..81b4158 100644
--- a/arch/arm/plat-mxc/include/mach/memory.h
+++ b/arch/arm/plat-mxc/include/mach/memory.h
@@ -43,7 +43,8 @@
# endif
#endif
-#if defined(CONFIG_MX3_VIDEO)
+#if defined(CONFIG_MX3_VIDEO) || defined(CONFIG_MFD_IMX_IPU_V3) || \
+ defined(CONFIG_MFD_IMX_IPU_V3_MODULE)
/*
* 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
^ permalink raw reply related
* [PATCH 5/7] ARM i.MX5: Allow to increase max zone order
From: Sascha Hauer @ 2011-02-16 14:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297865452-32181-1-git-send-email-s.hauer@pengutronix.de>
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 <s.hauer@pengutronix.de>
---
arch/arm/Kconfig | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 26d45e5..e25c04e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1451,8 +1451,8 @@ config HW_PERF_EVENTS
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
^ permalink raw reply related
* [PATCH 4/7] ARM i.MX51: Add IPU device support
From: Sascha Hauer @ 2011-02-16 14:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297865452-32181-1-git-send-email-s.hauer@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
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 7fff485..1bd73b3 100644
--- a/arch/arm/mach-mx5/devices-imx51.h
+++ b/arch/arm/mach-mx5/devices-imx51.h
@@ -55,3 +55,7 @@ extern const struct imx_mxc_pwm_data imx51_mxc_pwm_data[] __initconst;
extern const struct imx_imx_keypad_data imx51_imx_keypad_data __initconst;
#define imx51_add_imx_keypad(pdata) \
imx_add_imx_keypad(&imx51_imx_keypad_data, pdata)
+
+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 b9ab1d5..f99317e 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..2c6b913
--- /dev/null
+++ b/arch/arm/plat-mxc/devices/platform-imx_ipuv3.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 Pengutronix
+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+#include <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_512M - 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
^ permalink raw reply related
* [PATCH 3/7] Add i.MX5 framebuffer driver
From: Sascha Hauer @ 2011-02-16 14:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297865452-32181-1-git-send-email-s.hauer@pengutronix.de>
This patch adds framebuffer support to the Freescale i.MX SoCs
equipped with an IPU v3, so far these are the i.MX51/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 <s.hauer@pengutronix.de>
Cc: linux-kernel at vger.kernel.org
Cc: linux-fbdev at vger.kernel.org
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Samuel Ortiz <sameo@linux.intel.com>
---
drivers/video/Kconfig | 11 +
drivers/video/Makefile | 1 +
drivers/video/mx5fb.c | 925 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 937 insertions(+), 0 deletions(-)
create mode 100644 drivers/video/mx5fb.c
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index ffdb37a..70f8490 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2344,6 +2344,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 f4921ab..64f6913 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -152,6 +152,7 @@ obj-$(CONFIG_FB_BFIN_LQ035Q1) += bfin-lq035q1-fb.o
obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o
obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o
obj-$(CONFIG_FB_MX3) += mx3fb.o
+obj-$(CONFIG_FB_MX5) += mx5fb.o
obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
obj-$(CONFIG_MFD_IMX_IPU_V3) += imx-ipu-v3/
diff --git a/drivers/video/mx5fb.c b/drivers/video/mx5fb.c
new file mode 100644
index 0000000..af11f86
--- /dev/null
+++ b/drivers/video/mx5fb.c
@@ -0,0 +1,925 @@
+/*
+ * 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
+ */
+
+#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 di_no;
+ u32 ipu_di_pix_fmt;
+ u32 ipu_in_pix_fmt;
+
+ u32 pseudo_palette[16];
+
+ struct ipu_dp *dp;
+ struct dmfc_channel *dmfc;
+ struct ipu_di *di;
+ struct fb_info *slave;
+ struct fb_info *master;
+ bool enabled;
+
+ /* overlay specific fields */
+ int ovlxres, ovlyres;
+ int usage;
+};
+
+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;
+ else
+ return -ENOMEM;
+ }
+
+ 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->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->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;
+
+ 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->di_no, 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->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 void imx_ipu_fb_enable_overlay(struct fb_info *fbi);
+static void imx_ipu_fb_disable_overlay(struct fb_info *fbi);
+
+static int imx_ipu_fb_blank(int blank, struct fb_info *info)
+{
+ struct imx_ipu_fb_info *mxc_fbi = info->par;
+
+ 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:
+ if (mxc_fbi->slave)
+ imx_ipu_fb_disable_overlay(mxc_fbi->slave);
+ imx_ipu_fb_disable(info);
+ break;
+ case FB_BLANK_UNBLANK:
+ imx_ipu_fb_enable(info);
+ if (mxc_fbi->slave)
+ imx_ipu_fb_enable_overlay(mxc_fbi->slave);
+ 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 void imx_ipu_fb_enable_overlay(struct fb_info *ovl)
+{
+ struct imx_ipu_fb_info *mxc_ovl = ovl->par;
+
+ if (mxc_ovl->enabled)
+ return;
+
+ ipu_dmfc_enable_channel(mxc_ovl->dmfc);
+ ipu_idmac_enable_channel(mxc_ovl->ipu_ch);
+ ipu_dp_enable_fg(mxc_ovl->dp);
+ mxc_ovl->enabled = 1;
+}
+
+static void imx_ipu_fb_disable_overlay(struct fb_info *ovl)
+{
+ struct imx_ipu_fb_info *mxc_ovl = ovl->par;
+
+ if (!mxc_ovl->enabled)
+ return;
+
+ ipu_dp_disable_fg(mxc_ovl->dp);
+ ipu_wait_for_interrupt(451, 100);
+ ipu_idmac_disable_channel(mxc_ovl->ipu_ch);
+ ipu_dmfc_disable_channel(mxc_ovl->dmfc);
+ mxc_ovl->enabled = 0;
+}
+
+#define NONSTD_TO_XPOS(x) (((x) >> 0) & 0xfff)
+#define NONSTD_TO_YPOS(x) (((x) >> 12) & 0xfff)
+#define NONSTD_TO_ALPHA(x) (((x) >> 24) & 0xff)
+
+static int imx_ipu_fb_check_var_overlay(struct fb_var_screeninfo *var,
+ struct fb_info *ovl)
+{
+ struct imx_ipu_fb_info *mxc_ovl = ovl->par;
+ struct fb_info *fbi_master = mxc_ovl->master;
+ struct fb_var_screeninfo *var_master = &fbi_master->var;
+ int ret;
+ static int xpos, ypos;
+
+ xpos = NONSTD_TO_XPOS(var->nonstd);
+ ypos = NONSTD_TO_YPOS(var->nonstd);
+
+ ret = imx_ipu_fb_check_var(var, ovl);
+ if (ret)
+ return ret;
+
+ if (var->xres + xpos > var_master->xres)
+ return -EINVAL;
+ if (var->yres + ypos > var_master->yres)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int imx_ipu_fb_set_par_overlay(struct fb_info *ovl)
+{
+ struct imx_ipu_fb_info *mxc_ovl = ovl->par;
+ struct fb_var_screeninfo *var = &ovl->var;
+ struct fb_info *fbi_master = mxc_ovl->master;
+ struct imx_ipu_fb_info *mxc_fbi_master = fbi_master->par;
+ struct fb_var_screeninfo *var_master = &fbi_master->var;
+ int ret;
+ int interlaced = 0;
+ int xpos, ypos, alpha;
+ int resolution_change;
+
+ dev_dbg(ovl->device, "Reconfiguring framebuffer %dx%d-%d\n",
+ ovl->var.xres, ovl->var.yres, ovl->var.bits_per_pixel);
+
+ resolution_change = mxc_ovl->ovlxres != var->xres ||
+ mxc_ovl->ovlyres != var->yres;
+
+ if (mxc_ovl->enabled && resolution_change)
+ imx_ipu_fb_disable_overlay(ovl);
+
+ ovl->fix.line_length = var->xres_virtual *
+ var->bits_per_pixel / 8;
+
+ xpos = NONSTD_TO_XPOS(var->nonstd);
+ ypos = NONSTD_TO_YPOS(var->nonstd);
+ alpha = NONSTD_TO_ALPHA(var->nonstd);
+
+ if (resolution_change) {
+ ret = imx_ipu_fb_map_video_memory(ovl);
+ if (ret)
+ return ret;
+ }
+
+ if (!resolution_change && mxc_ovl->enabled)
+ ipu_wait_for_interrupt(IPU_IRQ_EOF(mxc_fbi_master->ipu_channel_num), 100);
+
+ ipu_dp_set_window_pos(mxc_ovl->dp, xpos, ypos);
+ ipu_dp_set_global_alpha(mxc_ovl->dp, 1, alpha, 1);
+
+ var->xoffset = var->yoffset = 0;
+
+ if (resolution_change) {
+ if (var->vmode & FB_VMODE_INTERLACED)
+ interlaced = 1;
+
+ ret = ipu_idmac_init_channel_buffer(mxc_ovl->ipu_ch,
+ mxc_ovl->ipu_in_pix_fmt,
+ var->xres, var->yres,
+ ovl->fix.line_length,
+ IPU_ROTATE_NONE,
+ ovl->fix.smem_start,
+ 0,
+ 0, 0, interlaced);
+ if (ret) {
+ dev_dbg(ovl->device, "init channel buffer failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ipu_dmfc_init_channel(mxc_ovl->dmfc, var->xres);
+ if (ret) {
+ dev_dbg(ovl->device, "initializing dmfc channel failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ipu_dmfc_alloc_bandwidth(mxc_ovl->dmfc, calc_bandwidth(var, calc_vref(var_master)));
+ if (ret) {
+ dev_dbg(ovl->device, "allocating dmfc bandwidth failed with %d\n",
+ ret);
+ return ret;
+ }
+ mxc_ovl->ovlxres = var->xres;
+ mxc_ovl->ovlyres = var->yres;
+ }
+
+ if (mxc_fbi_master->enabled)
+ imx_ipu_fb_enable_overlay(ovl);
+
+ return ret;
+}
+
+static int imx_overlayfb_open(struct fb_info *ovl, int user)
+{
+ struct imx_ipu_fb_info *mxc_ovl = ovl->par;
+
+ if (mxc_ovl->usage++ == 0)
+ printk("enable ovl\n");
+
+ return 0;
+}
+
+static int imx_overlayfb_release(struct fb_info *ovl, int user)
+{
+ struct imx_ipu_fb_info *mxc_ovl = ovl->par;
+
+ if (--mxc_ovl->usage == 0) {
+ printk("disable ovl\n");
+
+ if (ovl->screen_base)
+ dma_free_writecombine(ovl->device, ovl->fix.smem_len,
+ ovl->screen_base, ovl->fix.smem_start);
+ ovl->fix.smem_len = 0;
+ ovl->fix.smem_start = 0;
+ ovl->screen_base = NULL;
+ mxc_ovl->ovlxres = 0;
+ mxc_ovl->ovlyres = 0;
+ }
+
+ 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_overlay,
+ .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_open = imx_overlayfb_open,
+ .fb_release = imx_overlayfb_release,
+};
+
+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 *ovl;
+ struct imx_ipu_fb_info *mxc_ovl;
+ int ret;
+
+ ovl = imx_ipu_fb_init_fbinfo(&pdev->dev, &imx_ipu_fb_overlay_ops);
+ if (!ovl)
+ return -ENOMEM;
+
+ mxc_ovl = ovl->par;
+ mxc_ovl->ipu_ch = ipu_idmac_get(ipu_channel);
+ mxc_ovl->dmfc = ipu_dmfc_get(ipu_channel);
+ mxc_ovl->di = NULL;
+ mxc_ovl->dp = mxc_fbi_master->dp;
+ mxc_ovl->master = fbi_master;
+ mxc_fbi_master->slave = ovl;
+
+ imx_ipu_fb_check_var(&ovl->var, ovl);
+ imx_ipu_fb_set_fix(ovl);
+
+ ret = register_framebuffer(ovl);
+ if (ret) {
+ framebuffer_release(ovl);
+ return ret;
+ }
+
+ ipu_dp_set_global_alpha(mxc_ovl->dp, 0, 0, 1);
+ ipu_dp_set_color_key(mxc_ovl->dp, 1, 0x434343);
+
+ 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 *ovl = mxc_fbi_master->slave;
+ struct imx_ipu_fb_info *mxc_ovl = ovl->par;
+
+ if (mxc_ovl->enabled)
+ imx_ipu_fb_disable_overlay(ovl);
+
+ unregister_framebuffer(ovl);
+
+ ipu_idmac_put(mxc_ovl->ipu_ch);
+ ipu_dmfc_free_bandwidth(mxc_ovl->dmfc);
+ ipu_dmfc_put(mxc_ovl->dmfc);
+
+ framebuffer_release(ovl);
+}
+
+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->di_no = plat_data->display;
+
+ 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;
+ }
+ }
+
+ mxc_fbi->di = ipu_di_get(plat_data->display);
+ if (IS_ERR(mxc_fbi->di)) {
+ ret = PTR_ERR(mxc_fbi->di);
+ goto failed_request_di;
+ }
+
+ 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:
+ ipu_di_put(mxc_fbi->di);
+failed_request_di:
+ 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_di_put(mxc_fbi->di);
+ 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
^ permalink raw reply related
* [PATCH 2/7] fb: export fb mode db table
From: Sascha Hauer @ 2011-02-16 14:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297865452-32181-1-git-send-email-s.hauer@pengutronix.de>
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 <s.hauer@pengutronix.de>
Cc: linux-kernel at vger.kernel.org
Cc: linux-fbdev at vger.kernel.org
Cc: Paul Mundt <lethal@linux-sh.org>
---
drivers/video/modedb.c | 8 ++++++--
include/linux/fb.h | 3 +++
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
index 48c3ea8..942b44d 100644
--- a/drivers/video/modedb.c
+++ b/drivers/video/modedb.c
@@ -36,8 +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, 0,
FB_VMODE_NONINTERLACED },
@@ -291,6 +290,11 @@ static const struct fb_videomode modedb[] = {
0, FB_VMODE_NONINTERLACED },
};
+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 cea_modes[64] = {
/* #1: 640x480p at 59.94/60Hz */
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 68ba85a..41b0ce1 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -1154,6 +1154,9 @@ extern const char *fb_mode_option;
extern const struct fb_videomode vesa_modes[];
extern const struct fb_videomode cea_modes[64];
+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
^ permalink raw reply related
* [PATCH 1/7] Add a mfd IPUv3 driver
From: Sascha Hauer @ 2011-02-16 14:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297865452-32181-1-git-send-email-s.hauer@pengutronix.de>
The IPU is the Image Processing Unit found on i.MX51/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 <s.hauer@pengutronix.de>
Cc: linux-kernel at vger.kernel.org
Cc: linux-fbdev at vger.kernel.org
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Samuel Ortiz <sameo@linux.intel.com>
---
arch/arm/plat-mxc/include/mach/ipu-v3.h | 49 +++
drivers/video/Kconfig | 2 +
drivers/video/Makefile | 1 +
drivers/video/imx-ipu-v3/Makefile | 3 +
drivers/video/imx-ipu-v3/ipu-common.c | 708 +++++++++++++++++++++++++++++++
drivers/video/imx-ipu-v3/ipu-cpmem.c | 612 ++++++++++++++++++++++++++
drivers/video/imx-ipu-v3/ipu-dc.c | 364 ++++++++++++++++
drivers/video/imx-ipu-v3/ipu-di.c | 550 ++++++++++++++++++++++++
drivers/video/imx-ipu-v3/ipu-dmfc.c | 355 ++++++++++++++++
drivers/video/imx-ipu-v3/ipu-dp.c | 476 +++++++++++++++++++++
drivers/video/imx-ipu-v3/ipu-prv.h | 216 ++++++++++
include/linux/mfd/imx-ipu-v3.h | 219 ++++++++++
12 files changed, 3555 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/plat-mxc/include/mach/ipu-v3.h
create mode 100644 drivers/video/imx-ipu-v3/Makefile
create mode 100644 drivers/video/imx-ipu-v3/ipu-common.c
create mode 100644 drivers/video/imx-ipu-v3/ipu-cpmem.c
create mode 100644 drivers/video/imx-ipu-v3/ipu-dc.c
create mode 100644 drivers/video/imx-ipu-v3/ipu-di.c
create mode 100644 drivers/video/imx-ipu-v3/ipu-dmfc.c
create mode 100644 drivers/video/imx-ipu-v3/ipu-dp.c
create mode 100644 drivers/video/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..f8900b9
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/ipu-v3.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * 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;
+ int display;
+};
+
+struct imx_ipuv3_platform_data {
+ int rev;
+ struct ipuv3_fb_platform_data *fb_head0_platform_data;
+ struct ipuv3_fb_platform_data *fb_head1_platform_data;
+};
+
+#endif /* __MACH_IPU_V3_H_ */
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 6bafb51b..ffdb37a 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -26,6 +26,8 @@ source "drivers/gpu/drm/Kconfig"
source "drivers/gpu/stub/Kconfig"
+source "drivers/video/imx-ipu-v3/Kconfig"
+
config VGASTATE
tristate
default n
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 8c8fabd..f4921ab 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -153,6 +153,7 @@ obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o
obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o
obj-$(CONFIG_FB_MX3) += mx3fb.o
obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
+obj-$(CONFIG_MFD_IMX_IPU_V3) += imx-ipu-v3/
# the test framebuffer is last
obj-$(CONFIG_FB_VIRTUAL) += vfb.o
diff --git a/drivers/video/imx-ipu-v3/Makefile b/drivers/video/imx-ipu-v3/Makefile
new file mode 100644
index 0000000..ff70fe8
--- /dev/null
+++ b/drivers/video/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/video/imx-ipu-v3/ipu-common.c b/drivers/video/imx-ipu-v3/ipu-common.c
new file mode 100644
index 0000000..d96245f
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipu-common.c
@@ -0,0 +1,708 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * 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);
+static DEFINE_MUTEX(ipu_channel_lock);
+void __iomem *ipu_cm_reg;
+void __iomem *ipu_idmac_reg;
+
+static int ipu_use_count;
+
+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);
+
+ mutex_lock(&ipu_channel_lock);
+
+ channel = &channels[num];
+
+ if (channel->busy) {
+ channel = ERR_PTR(-EBUSY);
+ goto out;
+ }
+
+ channel->busy = 1;
+ channel->num = num;
+
+out:
+ mutex_unlock(&ipu_channel_lock);
+
+ 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);
+
+ mutex_lock(&ipu_channel_lock);
+
+ channel->busy = 0;
+
+ mutex_unlock(&ipu_channel_lock);
+}
+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);
+
+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;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu_lock, flags);
+
+ /* 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));
+
+ spin_unlock_irqrestore(&ipu_lock, flags);
+}
+EXPORT_SYMBOL(ipu_idmac_select_buffer);
+
+int ipu_idmac_enable_channel(struct ipu_channel *channel)
+{
+ u32 val;
+ unsigned long flags;
+
+ ipu_get();
+
+ 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 */
+
+ /* 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, flags);
+
+ ipu_put();
+
+ 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);
+
+ ipu_cm_write(1 << (interrupt % 32), IPU_INT_STAT(interrupt / 32 + 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;
+ else
+ ret = -ETIMEDOUT;
+
+ 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;
+}
+
+void ipu_get(void)
+{
+ mutex_lock(&ipu_channel_lock);
+
+ ipu_use_count++;
+
+ if (ipu_use_count == 1)
+ clk_enable(ipu_clk);
+
+ mutex_unlock(&ipu_channel_lock);
+}
+
+void ipu_put(void)
+{
+ mutex_lock(&ipu_channel_lock);
+
+ ipu_use_count--;
+
+ if (ipu_use_count == 0)
+ clk_disable(ipu_clk);
+
+ if (ipu_use_count < 0) {
+ dev_err(ipu_dev, "ipu use count < 0\n");
+ ipu_use_count = 0;
+ }
+
+ mutex_unlock(&ipu_channel_lock);
+}
+
+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;
+ struct ipuv3_fb_platform_data *fbdata;
+
+ fbdata = plat_data->fb_head0_platform_data;
+ if (fbdata) {
+ fbdata->ipu_channel_bg =
+ MX51_IPU_CHANNEL_MEM_BG_SYNC;
+ fbdata->ipu_channel_fg =
+ MX51_IPU_CHANNEL_MEM_FG_SYNC;
+ fbdata->dc_channel = 5;
+ fbdata->dp_channel = IPU_DP_FLOW_SYNC;
+
+ ipu_add_subdevice_pdata(pdev, "imx-ipuv3-fb", 0,
+ fbdata, sizeof(*fbdata));
+ }
+
+ fbdata = plat_data->fb_head1_platform_data;
+ if (fbdata) {
+ fbdata->ipu_channel_bg =
+ MX51_IPU_CHANNEL_MEM_DC_SYNC;
+ fbdata->ipu_channel_fg = -1;
+ fbdata->dc_channel = 1;
+ fbdata->dp_channel = -1;
+
+ ipu_add_subdevice_pdata(pdev, "imx-ipuv3-fb", 1,
+ fbdata, sizeof(*fbdata));
+ }
+
+ return 0;
+}
+
+static int __devinit ipu_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ unsigned long ipu_base;
+ int ret, irq1, irq2;
+
+ /* There can be only one */
+ if (ipu_dev)
+ return -EBUSY;
+
+ 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;
+ }
+
+ ipu_get();
+
+ 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;
+ }
+
+ ipu_put();
+
+ 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);
+ ipu_put();
+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);
+ iounmap(ipu_idmac_reg);
+ iounmap(ipu_cm_reg);
+
+ if (ipu_use_count != 0) {
+ dev_err(ipu_dev, "unbalanced use count: %d\n", ipu_use_count);
+ clk_disable(ipu_clk);
+ }
+
+ clk_put(ipu_clk);
+ ipu_dev = NULL;
+
+ 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 <s.hauer@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/imx-ipu-v3/ipu-cpmem.c b/drivers/video/imx-ipu-v3/ipu-cpmem.c
new file mode 100644
index 0000000..faff5ee
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipu-cpmem.c
@@ -0,0 +1,612 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * 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(¶ms, 0, sizeof(params));
+
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_FW, width - 1);
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_FH, height - 1);
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_SLY, stride - 1);
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_EBA0, addr0 >> 3);
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_EBA1, addr1 >> 3);
+
+ switch (pixel_fmt) {
+ case IPU_PIX_FMT_GENERIC:
+ /* Represents 8-bit Generic data */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_BPP, 5); /* bits/pixel */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_PFS, 6); /* pix format */
+ ipu_ch_param_set_field(¶ms, 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(¶ms, IPU_FIELD_BPP, 3); /* bits/pixel */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_NPB, 15); /* burst size */
+
+ ipu_ch_params_set_packing(¶ms, 5, 0, 6, 5, 5, 11, 8, 16);
+ break;
+ case IPU_PIX_FMT_BGR24:
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_BPP, 1); /* bits/pixel */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_NPB, 19); /* burst size */
+
+ ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24);
+ break;
+ case IPU_PIX_FMT_RGB24:
+ case IPU_PIX_FMT_YUV444:
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_BPP, 1); /* bits/pixel */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_NPB, 19); /* burst size */
+
+ ipu_ch_params_set_packing(¶ms, 8, 16, 8, 8, 8, 0, 8, 24);
+ break;
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_BGR32:
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_BPP, 0); /* bits/pixel */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_NPB, 15); /* burst size */
+
+ ipu_ch_params_set_packing(¶ms, 8, 8, 8, 16, 8, 24, 8, 0);
+ break;
+ case IPU_PIX_FMT_RGBA32:
+ case IPU_PIX_FMT_RGB32:
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_BPP, 0); /* bits/pixel */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_PFS, 7); /* pix format */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_NPB, 15); /* burst size */
+
+ ipu_ch_params_set_packing(¶ms, 8, 24, 8, 16, 8, 8, 8, 0);
+ break;
+ case IPU_PIX_FMT_ABGR32:
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_BPP, 0); /* bits/pixel */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_PFS, 7); /* pix format */
+
+ ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24);
+ break;
+ case IPU_PIX_FMT_UYVY:
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_BPP, 3); /* bits/pixel */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_PFS, 0xA); /* pix format */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_NPB, 15); /* burst size */
+ break;
+ case IPU_PIX_FMT_YUYV:
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_BPP, 3); /* bits/pixel */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_PFS, 0x8); /* pix format */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_NPB, 31); /* burst size */
+ break;
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YUV420P:
+ ipu_ch_param_set_field(¶ms, 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(¶ms, IPU_FIELD_NPB, 31); /* burst size */
+ break;
+ case IPU_PIX_FMT_YVU422P:
+ /* BPP & pixel format */
+ ipu_ch_param_set_field(¶ms, IPU_FIELD_PFS, 1); /* pix format */
+ ipu_ch_param_set_field(¶ms, 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(¶ms, IPU_FIELD_PFS, 1); /* pix format */
+ ipu_ch_param_set_field(¶ms, 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(¶ms, IPU_FIELD_PFS, 4); /* pix format */
+ ipu_ch_param_set_field(¶ms, 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(¶ms, 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(¶ms, IPU_FIELD_UBO, u_offset / 8);
+ ipu_ch_param_set_field(¶ms, 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), ¶ms, 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 = 0;
+ 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;
+ }
+
+ ipu_get();
+
+ /* 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)
+ goto out;
+
+ 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);
+out:
+ ipu_put();
+
+ return ret;
+}
+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/video/imx-ipu-v3/ipu-dc.c b/drivers/video/imx-ipu-v3/ipu-dc.c
new file mode 100644
index 0000000..b8c601e
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipu-dc.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * 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 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;
+ }
+
+ ipu_get();
+
+ 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);
+
+ ipu_put();
+
+ 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/video/imx-ipu-v3/ipu-di.c b/drivers/video/imx-ipu-v3/ipu-di.c
new file mode 100644
index 0000000..62e3d01
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipu-di.c
@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * 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/io.h>
+#include <linux/err.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <linux/platform_device.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;
+ bool inuse;
+ bool initialized;
+};
+
+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(struct ipu_di *di, u32 pixel_clk, struct ipu_di_signal_cfg *sig)
+{
+ 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",
+ di->id, sig->width, sig->height);
+
+ 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);
+ ipu_get();
+
+ /* 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 &= di->id ? ~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);
+
+ ipu_put();
+ mutex_unlock(&di_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_di_init_sync_panel);
+
+int ipu_di_enable(struct ipu_di *di)
+{
+ u32 reg;
+
+ ipu_get();
+ reg = ipu_cm_read(IPU_DISP_GEN);
+ if (di->id)
+ 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(struct ipu_di *di)
+{
+ u32 reg;
+
+ ipu_module_disable(di->module);
+ ipu_put();
+
+ if (di->external_clk)
+ clk_disable(di->clk);
+
+ reg = ipu_cm_read(IPU_DISP_GEN);
+ if (di->id)
+ reg &= ~DI1_COUNTER_RELEASE;
+ else
+ reg &= ~DI0_COUNTER_RELEASE;
+ ipu_cm_write(reg, IPU_DISP_GEN);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_di_disable);
+
+static DEFINE_MUTEX(ipu_di_lock);
+
+struct ipu_di *ipu_di_get(int disp)
+{
+ struct ipu_di *di;
+
+ if (disp > 1)
+ return ERR_PTR(-EINVAL);
+
+ di = &dis[disp];
+
+ mutex_lock(&ipu_di_lock);
+
+ if (!di->initialized) {
+ di = ERR_PTR(-ENOSYS);
+ goto out;
+ }
+
+ if (di->inuse) {
+ di = ERR_PTR(-EBUSY);
+ goto out;
+ }
+
+ di->inuse = true;
+out:
+ mutex_unlock(&ipu_di_lock);
+
+ return di;
+}
+EXPORT_SYMBOL(ipu_di_get);
+
+void ipu_di_put(struct ipu_di *di)
+{
+ mutex_lock(&ipu_di_lock);
+
+ di->inuse = false;
+
+ mutex_unlock(&ipu_di_lock);
+}
+EXPORT_SYMBOL(ipu_di_put);
+
+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);
+ dis[id].initialized = true;
+ dis[id].inuse = false;
+ 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);
+ dis[id].initialized = false;
+}
diff --git a/drivers/video/imx-ipu-v3/ipu-dmfc.c b/drivers/video/imx-ipu-v3/ipu-dmfc.c
new file mode 100644
index 0000000..3d47a76
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipu-dmfc.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * 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/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);
+ ipu_get();
+
+ 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;
+
+ ipu_put();
+ 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, ret = 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);
+
+ ipu_get();
+ mutex_lock(&dmfc_mutex);
+
+ if (slots > 8) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ segment = dmfc_find_slots(slots);
+ if (segment < 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ipu_dmfc_setup_channel(dmfc, slots, segment);
+
+out:
+ ipu_put();
+ mutex_unlock(&dmfc_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipu_dmfc_alloc_bandwidth);
+
+int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width)
+{
+ u32 dmfc_gen1;
+
+ ipu_get();
+
+ 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);
+
+ ipu_put();
+
+ 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/video/imx-ipu-v3/ipu-dp.c b/drivers/video/imx-ipu-v3/ipu-dp.c
new file mode 100644
index 0000000..57a7fec
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipu-dp.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * 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/io.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <linux/err.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;
+
+ ipu_get();
+
+ 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);
+
+ ipu_put();
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_dp_setup_channel);
+
+int ipu_dp_enable_channel(struct ipu_dp *dp)
+{
+ mutex_lock(&dp_mutex);
+ ipu_get();
+
+ 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;
+
+ ipu_put();
+ 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;
+
+ ipu_get();
+
+ 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);
+
+ ipu_put();
+}
+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/video/imx-ipu-v3/ipu-prv.h b/drivers/video/imx-ipu-v3/ipu-prv.h
new file mode 100644
index 0000000..339b554
--- /dev/null
+++ b/drivers/video/imx-ipu-v3/ipu-prv.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * 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/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);
+
+void ipu_get(void);
+void ipu_put(void);
+
+#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..7243360
--- /dev/null
+++ b/include/linux/mfd/imx-ipu-v3.h
@@ -0,0 +1,219 @@
+/*
+ * 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@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/types.h>
+#include <linux/videodev2.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
+ */
+struct ipu_di;
+struct ipu_di *ipu_di_get(int disp);
+void ipu_di_put(struct ipu_di *);
+int ipu_di_disable(struct ipu_di *);
+int ipu_di_enable(struct ipu_di *);
+int ipu_di_init_sync_panel(struct ipu_di *, 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
^ permalink raw reply related
* [PATCH v3] Add i.MX51/53 IPU framebuffer support
From: Sascha Hauer @ 2011-02-16 14:10 UTC (permalink / raw)
To: linux-arm-kernel
The following is the third version of the series adding ipu support
for the i.MX51/53.
Changes since last version:
- The IPU core driver now sits in drivers/video instead of drivers/mfd.
It still needs mfd support, so it has a select on MFD_SUPPORT and
MFD_CORE.
- The fb driver does not reallocate memory anymore. This is broken since
there is a risk that some userspace process still has the memory mapped
while we reallocate it which leads to bad results.
Sascha Hauer (7):
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/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 | 49 ++
arch/arm/plat-mxc/include/mach/memory.h | 3 +-
drivers/video/Kconfig | 13 +
drivers/video/Makefile | 2 +
drivers/video/imx-ipu-v3/Makefile | 3 +
drivers/video/imx-ipu-v3/ipu-common.c | 708 +++++++++++++++++
drivers/video/imx-ipu-v3/ipu-cpmem.c | 612 +++++++++++++++
drivers/video/imx-ipu-v3/ipu-dc.c | 364 +++++++++
drivers/video/imx-ipu-v3/ipu-di.c | 550 ++++++++++++++
drivers/video/imx-ipu-v3/ipu-dmfc.c | 355 +++++++++
drivers/video/imx-ipu-v3/ipu-dp.c | 476 ++++++++++++
drivers/video/imx-ipu-v3/ipu-prv.h | 216 ++++++
drivers/video/modedb.c | 8 +-
drivers/video/mx5fb.c | 925 +++++++++++++++++++++++
include/linux/fb.h | 3 +
include/linux/mfd/imx-ipu-v3.h | 219 ++++++
24 files changed, 4646 insertions(+), 5 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/video/imx-ipu-v3/Makefile
create mode 100644 drivers/video/imx-ipu-v3/ipu-common.c
create mode 100644 drivers/video/imx-ipu-v3/ipu-cpmem.c
create mode 100644 drivers/video/imx-ipu-v3/ipu-dc.c
create mode 100644 drivers/video/imx-ipu-v3/ipu-di.c
create mode 100644 drivers/video/imx-ipu-v3/ipu-dmfc.c
create mode 100644 drivers/video/imx-ipu-v3/ipu-dp.c
create mode 100644 drivers/video/imx-ipu-v3/ipu-prv.h
create mode 100644 drivers/video/mx5fb.c
create mode 100644 include/linux/mfd/imx-ipu-v3.h
^ permalink raw reply
* ALSA issue on DA850/OMAP-L138/AM18x
From: Christophe Aeschlimann @ 2011-02-16 14:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <B85A65D85D7EB246BE421B3FB0FBB59302484734E0@dbde02.ent.ti.com>
Hi Sudhakar,
On 18.01.2011 05:43, Rajashekhara, Sudhakar wrote:
[...]
> With the above fixes, arecord and aplay does not work for the first time.
> Couple of times I get the below error:
>
> root at da850-omapl138-evm:~# arecord -f dat | aplay -f dat
> arecord: main:608: audio open error: Invalid argument
> aplay: playback:2297: read error
> root at da850-omapl138-evm:~# arecord -f dat | aplay -f dat
> aplay: main:608: audio open error: Invalid argument
> Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 48000 Hz,
Stereo
>
> Third time arecord and aplay work normally.
>
> Has anyone seen such issues on DA850 or any other platform?
>
> I am currently debugging this issue. I'll submit the above patch to
community
> once the issue of fixed.
I'm facing the same issue here.
Did you make any progress on that one ?
> Regards,
> Sudhakar
Regards,
Christophe
^ permalink raw reply
* [PATCH v2] OMAP: PM: DMA: Enable runtime pm
From: G, Manjunath Kondaiah @ 2011-02-16 14:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87ipwm1daa.fsf@ti.com>
Hi Kevin,
On Mon, Feb 14, 2011 at 02:06:53PM -0800, Kevin Hilman wrote:
> "G, Manjunath Kondaiah" <manjugk@ti.com> writes:
>
> > From: Manjunath G Kondaiah <manjugk@ti.com>
> >
> > Enable runtime pm and use pm_runtime_get_sync and pm_runtime_put_autosuspend
> > for OMAP DMA driver.
> >
> > The DMA driver uses auto suspend feature of runtime pm framework through
> > which the clock gets disabled automatically if there is no activity for
> > more than one second.
> >
> > Testing:
> > Compile: omap1_defconfig and omap2plus_defconfig
> > Boot: OMAP1710(H3), OMAP2420(H4), OMAP3630(Zoom3), OMAP4(Blaze)
>
> The normal DMA tests should also be run on these platforms. Based on
> the above, I can't tell any DMA tests were run. Based on my tests,
> this isn't working for chained xfers.
>
> Using the runtime PM sysfs interface, you can check the runtime status
> of the device:
>
> # cat /sys/devices/platform/omap/omap_dma_system.0/power/runtime_status
>
> It should show 'active' during transfer, and after timeout expires it
> will show 'suspended'.
>
> Doing some tests using my dmatest module:
>
> git://gitorious.org/omap-test/dmatest.git
>
> I noticed that it gets stuck in 'active' and never gets suspended when I
> used DMA channel linking (load module using 'linking=1' as load-time option)
>
> I'm not sure exactly why, but I will guess that the reason is that there
> is an imbalance in get/put calls when using chaining, since 'get' is
> only called once upon omap_start_dma() but 'put' is called for every
> channel in the callback.
Even I noticed this after running chaining test case and checking
runtime status. But, I am wondering even with 'active' runtime status,
the core hits off and retention.
The complete log which has all the sequences of running chaining tests,
enabling off mode and checking runtime status is available at:
http://pastebin.com/YEHMEXUP
Though I agree on the point that, it is mismatch with get/put calls with
DMA chaining, I still need to analyze this in detail.
The other thing which is not considered here is, the get_sync is called
inside start_dma only(request_dma will call get_sync and put after the
getting requested channel). After request_dma and start_dma, there are
API's called by user(dma_set_params, priority etc) which also require
get_sync since those API's will access configuration registers. I am
wondering if have get_sync and put in all the API's, this might result
in over loading.
>
> > On zoom3 core retention is tested with following steps:
> > echo 1 > /debug/pm_debug/sleep_while_idle
> > echo 1 > /debug/pm_debug/enable_off_mode
> > echo 5 > /sys/devices/platform/omap/omap_uart.0/sleep_timeout
> > echo 5 > /sys/devices/platform/omap/omap_uart.1/sleep_timeout
> > echo 5 > /sys/devices/platform/omap/omap_uart.2/sleep_timeout
> > echo 5 > /sys/devices/platform/omap/omap_uart.3/sleep_timeout
> >
> > It is observed that(on pm branch), core retention count gets increasing if the
> > board is left idle for more than 5 seconds. However, it doesnot enter off mode
> > (even without DMA runtime changes).
>
> What silicon rev is on your Zoom3?
It's 3630 ES1.0.
> Mainline kernels now disable core off-mode for 3630 revs < ES2.1 due to erratum
>i583.
>
> If this happens, you should see something like this on the console:
>
> Core OFF disabled due to errata i583
>
We can observe above message in mainline after enabling cpu idle in
omap2plus_defconfig.
I switched to zoom2 and able to hit core retention and
off mode with mainline.
-Manjunath
^ permalink raw reply
* [PATCH] ARM: gic: use handle_fasteoi_irq for SPIs
From: Rabin Vincent @ 2011-02-16 14:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <-4413647205110644369@unknownmsgid>
On Wed, Feb 16, 2011 at 18:39, Will Deacon <will.deacon@arm.com> wrote:
>> On Mon, Feb 14, 2011 at 20:56, Will Deacon <will.deacon@arm.com> wrote:
>> > This patch modifies the GIC code to use handle_fasteoi_irq for handling
>> > interrupts, which only requires us to signal EOI to the CPU interface
>> > when handling is complete. Cascaded IRQ handling is also updated so that
>> > EOI is signalled after handling.
>>
>> Several of the platforms using the GIC also have GPIO code which uses
>> set_irq_chained_handler(). ?I think you will have to modify all of
>> these to call irq_eoi() appropriately and not the other functions.
>> Some of these will also likely be used with other interrupt handlers
>> than the GIC, though.
>
> Hmm, I had a quick look at some platforms that do this (mach-dove and
> plat-spear) and I don't see what the problem is. They use their own irq_chip
> structures, with their own function pointers, so this doesn't seem to relate
> to the GIC at all. What am I missing?!
The chained handlers are usually installed on GIC interrupts. So, when
a chained handler does something like this
desc->irq_data.chip->irq_unmask(&desc->irq_data);
the desc->irq_data.chip refers to the gic_chip. These handlers are
written with the knowledge of what flow handler the GIC uses and what
functions it implements, so when you change that, the chained handler
code will not work correctly, and they'll need to be updated just like
you've updated the cascade IRQ handler.
In fact, I think that 846afbd1 ("GIC: Dont disable INT in ack callback")
has broken not just GIC cascading interrupts but assumptions in several
of these chained handlers, since several of them seem to have been
written assuming (invalidly) that irq_ack() masks the interrupt, but
this is no longer the case with the GIC after that commit.
^ permalink raw reply
* [PATCH] mx3fb: Fix parameter to sdc_init_panel
From: Guennadi Liakhovetski @ 2011-02-16 13:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201102141306.43739.alexander.stein@systec-electronic.com>
On Mon, 14 Feb 2011, Alexander Stein wrote:
> On Monday 14 February 2011, 08:57:04 Guennadi Liakhovetski wrote:
> > On Mon, 14 Feb 2011, Alexander Stein wrote:
> > > On Thursday 10 February 2011, 14:01:17 Alexander Stein wrote:
> > > > On Thursday 10 February 2011, 13:48:23 Guennadi Liakhovetski wrote:
> > > > > > I don't know which display you used, but I guess your right_margin
> > > > > > (and lower_margin) needs to be adjusted.
> > > > >
> > > > > Ok, agree. But then you need to adjust all current users and have
> > > > > them tested... Otherwise, of course, you can just adjust your
> > > > > platform panel data to work with the current calculations, even
> > > > > though in principle your patch seems to be doing the right thing
> > > > > (tm), according to the manual. But if applied as is without fixing
> > > > > all the users, it can very well break them...
> > > >
> > > > Ok, I've just seen there are some model defines in mx3fb.c which would
> > > > also be affected.
> > > > I'll try to find all mx3fb users and fix their videomodes and repost a
> > > > new patch.
> > >
> > > Uh, this seems next to impossible for me to handle it my own. I checked
> > > some users of the mx3fb but I cant say which ones will be affected and
> > > which ones not (e.g. without any hsync/vsync, on data enable).
> > > I've not idea how to fix it then.
> >
> > Well, you can, of course, adjust parameters for your platform to be able
> > to run with this incorrect parameter definition...
>
> This is just an ugly work-around. Depending on the settings you may even get
> an overflow resulting in a not-working display at all.
Sure, I know, that this is a work-around. But I don't see options apart
from
1. fix the driver and maybe a couple of platforms, that we have at hand
and hope, that others will fix theirs or shout loud enough, when they
discover, that their platforms are broken
2. fix the driver and all users and try to get as many of them as we can
to test our fixes
3. leave the driver and work around the bug in all new platforms
Choose your poison;)
Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
^ permalink raw reply
* [PATCH 0/3] OMAP2+ hwmod fixes
From: Rajendra Nayak @ 2011-02-16 13:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4D5BCC10.2060301@ti.com>
Hi Benoit,
> -----Original Message-----
> From: Cousson, Benoit [mailto:b-cousson at ti.com]
> Sent: Wednesday, February 16, 2011 6:37 PM
> To: Nayak, Rajendra
> Cc: linux-omap at vger.kernel.org; paul at pwsan.com;
linux-arm-kernel at lists.infradead.org
> Subject: Re: [PATCH 0/3] OMAP2+ hwmod fixes
>
> Hi Rajendra,
>
> On 2/16/2011 1:11 PM, Nayak, Rajendra wrote:
> > This series fixes some hwmod api return values
> > and also adds some state checks.
> > The hwmod iterator functions are made to
> > continue and not break if one of the
> > callback functions ends up with an error.
>
> By doing that, you change the behavior of this function.
> I'm not sure I fully understand why.
> Could you elaborate on the use case?
Since these functions iterate over all hwmods
calling a common callback function, there might
be cases wherein the callback function for *some*
hwmods might fail. For instance, if you run through
all hwmods and try to clear the context registers
for all of them, for some hwmods which might
not have context registers the callback function
might return a -EINVAL, however that should not
stop you from attempting to clear the context
registers for the rest of the hwmods which have
them and abort the function midway, no?
This is more hypothetical, however the real usecase
that prompted me with this patch was when I
had some wrong state check in _setup function,
and the iterator would stop with the first failure
and not even attempt to setup the rest of the
hwmods.
>
> To avoid that behavior in the past, I was just returning
> 0 in case of failure to avoid stopping the iteration.
> It looks like you do not want to stop the iteration but still
> retrieve the error.
> I do not see in this series what you plan to do with the
> error at the end of the iteration.
Most users of these iterators would just use the non-zero
return value to throw an error/warning out stating there
were failures running through all the callback functions.
That does not change with this patch, and they can still
do it.
Regards,
Rajendra
>
> Thanks,
> Benoit
^ permalink raw reply
* [PATCH] ARM: gic: use handle_fasteoi_irq for SPIs
From: Will Deacon @ 2011-02-16 13:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTik-0SaVbtiXZ-iefvvgLq=7KqRTGi7XPLPEKx85@mail.gmail.com>
Hi Rabin,
> On Mon, Feb 14, 2011 at 20:56, Will Deacon <will.deacon@arm.com> wrote:
> > Currently, the gic uses handle_level_irq for handling SPIs (Shared
> > Peripheral Interrupts), requiring active interrupts to be masked at
> > the distributor level during IRQ handling.
> >
> > On a virtualised system, only the CPU interfaces are virtualised in
> > hardware. Accesses to the distributor must be trapped by the hypervisor,
> > adding latency to the critical interrupt path in Linux.
> >
> > This patch modifies the GIC code to use handle_fasteoi_irq for handling
> > interrupts, which only requires us to signal EOI to the CPU interface
> > when handling is complete. Cascaded IRQ handling is also updated so that
> > EOI is signalled after handling.
>
> Several of the platforms using the GIC also have GPIO code which uses
> set_irq_chained_handler(). I think you will have to modify all of
> these to call irq_eoi() appropriately and not the other functions.
> Some of these will also likely be used with other interrupt handlers
> than the GIC, though.
Hmm, I had a quick look at some platforms that do this (mach-dove and
plat-spear) and I don't see what the problem is. They use their own irq_chip
structures, with their own function pointers, so this doesn't seem to relate
to the GIC at all. What am I missing?!
Cheers,
Will
^ permalink raw reply
* [PATCH 0/3] OMAP2+ hwmod fixes
From: Cousson, Benoit @ 2011-02-16 13:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297858285-7056-1-git-send-email-rnayak@ti.com>
Hi Rajendra,
On 2/16/2011 1:11 PM, Nayak, Rajendra wrote:
> This series fixes some hwmod api return values
> and also adds some state checks.
> The hwmod iterator functions are made to
> continue and not break if one of the
> callback functions ends up with an error.
By doing that, you change the behavior of this function.
I'm not sure I fully understand why.
Could you elaborate on the use case?
To avoid that behavior in the past, I was just returning
0 in case of failure to avoid stopping the iteration.
It looks like you do not want to stop the iteration but still
retrieve the error.
I do not see in this series what you plan to do with the
error at the end of the iteration.
Thanks,
Benoit
^ permalink raw reply
* [PATCH] collie: do actually pass locomo_info to locomo driver
From: Andrea Adami @ 2011-02-16 13:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1296775934-9903-1-git-send-email-dbaryshkov@gmail.com>
On Fri, Feb 4, 2011 at 12:32 AM, Dmitry Eremin-Solenikov
<dbaryshkov@gmail.com> wrote:
> locomo_info isn't actually used as a platform_data on collie platform:
> ?arm/mach-sa1100/collie.c:237: warning: ?locomo_info? defined but not used
>
> So locomo driver doesn't setup IRQs correctly. Pass locomo_info to the
> driver.
>
> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> Cc: stable at kernel.org
> ---
> ?arch/arm/mach-sa1100/collie.c | ? ?3 +++
> ?1 files changed, 3 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c
> index d43c5ef..bd3e1bf 100644
> --- a/arch/arm/mach-sa1100/collie.c
> +++ b/arch/arm/mach-sa1100/collie.c
> @@ -241,6 +241,9 @@ static struct locomo_platform_data locomo_info = {
> ?struct platform_device collie_locomo_device = {
> ? ? ? ?.name ? ? ? ? ? = "locomo",
> ? ? ? ?.id ? ? ? ? ? ? = 0,
> + ? ? ? .dev ? ? ? ? ? ?= {
> + ? ? ? ? ? ? ? .platform_data ?= &locomo_info,
> + ? ? ? },
> ? ? ? ?.num_resources ?= ARRAY_SIZE(locomo_resources),
> ? ? ? ?.resource ? ? ? = locomo_resources,
> ?};
> --
> 1.7.2.3
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
The issue is confirmed (see log http://pastebin.com/se3BMrmU )
#locomokbd: Can't get irq for keyboard
#locomokbd: probe of locomo-keyboard failed with error -22
Please apply this trivial fix, thx.
Tested on real hw by OpenEmbedded developers.
Regards
Andrea
^ permalink raw reply
* [PATCH 0/4] ARM: add support for hw-breakpoints [v4]
From: Ulrich Weigand @ 2011-02-16 13:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <000001cbcdd1$03a70ed0$0af52c70$@deacon@arm.com>
"Will Deacon" <will.deacon@arm.com> wrote on 02/16/2011 12:59:49 PM:
> > There seems to be a bug in the gdb patch... There should probably
> be a separate loop for wpts.
> >
> > for (i = 0; i < info->bp_count; i++)
> > > {
> > > if (arm_hwbp_control_is_enabled (p->bpts[i].control))
> > > arm_linux_insert_hw_breakpoint1 (p->bpts + i, tid, 0);
> > > if (arm_hwbp_control_is_enabled (p->wpts[i].control))
> > > arm_linux_insert_hw_breakpoint1 (p->wpts + i, tid, 1);
> > > }
>
> Ulrich Weigand (added to CC) is dealing with the GDB patches, so he
> will have more idea about this than me. Last I heard, the testsuite
> was passing so I guess this has been fixed if it was a problem.
Yes, this is indeed a bug (it only occurs sporadically, since its
effect is to access random memory), and it should be fixed by
using two separate loops.
That's one of the changes I've had to make as well. I'll post an
updated patch set including all the changes required to get a clean
testsuite run shortly ...
Mit freundlichen Gruessen / Best Regards
Ulrich Weigand
--
Dr. Ulrich Weigand | Phone: +49-7031/16-3727
STSM, GNU compiler and toolchain for Linux on System z and Cell/B.E.
IBM Deutschland Research & Development GmbH
Vorsitzender des Aufsichtsrats: Martin Jetter | Gesch?ftsf?hrung: Dirk
Wittkopp
Sitz der Gesellschaft: B?blingen | Registergericht: Amtsgericht
Stuttgart, HRB 243294
^ permalink raw reply
* [PATCH 16/17] mc13xxx: mfd_cell is now implicitly available to drivers
From: Uwe Kleine-König @ 2011-02-16 12:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4D5BC38E.1060009@freescale.com>
On Wed, Feb 16, 2011 at 10:31:10AM -0200, Fabio Estevam wrote:
> Hi Uwe,
>
> On 2/16/2011 7:41 AM, Uwe Kleine-K?nig wrote:
> ...
> >> /* MC13783 */
> >> -static struct mc13783_platform_data mc13783_pdata __initdata = {
> > isn't using __initdata here broken already before your patch? Was
> > introduced in c67a3e09. Fabio?
>
> I followed the same approach as in mx31_3ds.c file.
>
> > But in general I'd prefer to keep as much __initdata as possible because
> > this occupies less memory when using a multi-machine kernel.
>
> Ok, is the __initdata usage correct then? If not, I can change it. Just let me know.
I guess that if you compile mc13xxx support as module it won't work.
Try that, and if it breaks, send a patch fixing mx31_3ds.c, too.
Uwe
--
Pengutronix e.K. | Uwe Kleine-K?nig |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply
* [PATCH V4] OMAP3: PM: Set/clear T2 bit for Smartreflex on TWL
From: Gulati, Shweta @ 2011-02-16 12:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110216143112.6f756a94.jhnikula@gmail.com>
Hi,
On Wed, Feb 16, 2011 at 6:01 PM, Jarkko Nikula <jhnikula@gmail.com> wrote:
> On Wed, 16 Feb 2011 17:24:30 +0530
> "Gulati, Shweta" <shweta.gulati@ti.com> wrote:
>
>> > Note proof of concept patch only. I omitted the comments and don't do
>> > explicit SR disable and I'd clean up the error paths in twl4030_power_init
>> > a bit before this (e.g. printing error codes). Not sure either is the
>> > twl4030-power.c right place for this or core.
>> You missed commit log which says that "T2 bit is required to enable I2C_SR
>> path of voltage control" it is not at all enabling SR, voltage scale
>> APIs VPforceupdate/ VCbypass
>> needs this path to be enabled.
>> And calling APIs twl_i2c_read/write in driver codebase does n't ensure correct
>> ordering of flag changes and twl_read/write.
>
> Ah, yeah. I forgot to comment that I tried shortly also to run this
> enable code from workqueue ~60 s after bootup and indeed SR didn't work
> if those autocomp sysfs nodes were set before setting the TWL SR bit and
> I believe same holds for voltage scaling too as you say.
>
> What I'm thinking is there actually need for some higher level
> control for these that quarantees the right order independently from
> when each module are initialized.
Yes there is a need.
Voltage control for TWL uses different ways VMODE/I2C1/I2C_SR
Most of the OMAP3+ boards uses I2C_SR, for this to function properly
SR_BIT on TWL side needs to be enabled but there could be some boards
that use VMODE method of scaling, for those boards this bit has to be
disabled which can be done from board file that is why a flag is set to make
sure that API 'omap3_twl_set_sr_bit()' is called once and all initializations
are done in order so SR bit is set/cleared aptly.
> --
> Jarkko
>
--
Thanks,
Regards,
Shweta
^ permalink raw reply
* [PATCH 1/5] ARM: smp: Select local timers vs dummy timer support runtime
From: Santosh Shilimkar @ 2011-02-16 12:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297510187-31547-2-git-send-email-santosh.shilimkar@ti.com>
Russell,
> -----Original Message-----
> From: Santosh Shilimkar [mailto:santosh.shilimkar at ti.com]
> Sent: Saturday, February 12, 2011 5:00 PM
> To: linux-omap at vger.kernel.org
> Cc: khilman at ti.com; linux-arm-kernel at lists.infradead.org;
> tony at atomide.com; Santosh Shilimkar; Russell King; David Brown;
> Daniel Walker; Bryan Huntsman; Kukjin Kim; Paul Mundt; Magnus Damm;
> Colin Cross; Erik Gilling; Srinidhi Kasagar; Linus Walleij
> Subject: [PATCH 1/5] ARM: smp: Select local timers vs dummy timer
> support runtime
>
> The current code support of dummy timers in absence of local
> timer is compile time. This is an attempt to convert it to runtime
> so that on few SOC version if the local timers aren't supported
> kernel can switch to dummy timers. OMAP4430 ES1.0 does suffer from
> this limitation.
>
> This patch should not have any functional impact on affected
> files.
>
> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: David Brown <davidb@codeaurora.org>
> Cc: Daniel Walker <dwalker@codeaurora.org>
> Cc: Bryan Huntsman <bryanh@codeaurora.org>
> Cc: Tony Lindgren <tony@atomide.com>
> Cc: Kukjin Kim <kgene.kim@samsung.com>
> Cc: Paul Mundt <lethal@linux-sh.org>
> Cc: Magnus Damm <magnus.damm@gmail.com>
> Cc: Colin Cross <ccross@android.com>
> Cc: Erik Gilling <konkers@android.com>
> Cc: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
> Cc: Linus Walleij <linus.walleij@stericsson.com>
> ---
Any comments on this one?
So far David has acked this for mach-msm.
> arch/arm/include/asm/localtimer.h | 8 +++++++-
> arch/arm/kernel/smp.c | 7 +++----
> arch/arm/mach-msm/timer.c | 3 ++-
> arch/arm/mach-omap2/timer-mpu.c | 3 ++-
> arch/arm/mach-realview/localtimer.c | 3 ++-
> arch/arm/mach-s5pv310/localtimer.c | 3 ++-
> arch/arm/mach-shmobile/localtimer.c | 3 ++-
> arch/arm/mach-tegra/localtimer.c | 3 ++-
> arch/arm/mach-ux500/localtimer.c | 3 ++-
> arch/arm/mach-vexpress/localtimer.c | 3 ++-
> 10 files changed, 26 insertions(+), 13 deletions(-)
>
> diff --git a/arch/arm/include/asm/localtimer.h
> b/arch/arm/include/asm/localtimer.h
> index 6bc63ab..080d74f 100644
> --- a/arch/arm/include/asm/localtimer.h
> +++ b/arch/arm/include/asm/localtimer.h
> @@ -44,8 +44,14 @@ int local_timer_ack(void);
> /*
> * Setup a local timer interrupt for a CPU.
> */
> -void local_timer_setup(struct clock_event_device *);
> +int local_timer_setup(struct clock_event_device *);
>
> +#else
> +
> +static inline int local_timer_setup(struct clock_event_device *evt)
> +{
> + return -ENXIO;
> +}
> #endif
>
> #endif
> diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
> index 4539ebc..7b9cc53 100644
> --- a/arch/arm/kernel/smp.c
> +++ b/arch/arm/kernel/smp.c
> @@ -474,13 +474,12 @@ static void smp_timer_broadcast(const struct
> cpumask *mask)
> #define smp_timer_broadcast NULL
> #endif
>
> -#ifndef CONFIG_LOCAL_TIMERS
> static void broadcast_timer_set_mode(enum clock_event_mode mode,
> struct clock_event_device *evt)
> {
> }
>
> -static void local_timer_setup(struct clock_event_device *evt)
> +static void dummy_timer_setup(struct clock_event_device *evt)
> {
> evt->name = "dummy_timer";
> evt->features = CLOCK_EVT_FEAT_ONESHOT |
> @@ -492,7 +491,6 @@ static void local_timer_setup(struct
> clock_event_device *evt)
>
> clockevents_register_device(evt);
> }
> -#endif
>
> void __cpuinit percpu_timer_setup(void)
> {
> @@ -502,7 +500,8 @@ void __cpuinit percpu_timer_setup(void)
> evt->cpumask = cpumask_of(cpu);
> evt->broadcast = smp_timer_broadcast;
>
> - local_timer_setup(evt);
> + if (local_timer_setup(evt))
> + dummy_timer_setup(evt);
> }
>
> #ifdef CONFIG_HOTPLUG_CPU
> diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
> index c105d28..ae85aa9 100644
> --- a/arch/arm/mach-msm/timer.c
> +++ b/arch/arm/mach-msm/timer.c
> @@ -255,7 +255,7 @@ static void __init msm_timer_init(void)
> }
>
> #ifdef CONFIG_SMP
> -void __cpuinit local_timer_setup(struct clock_event_device *evt)
> +int __cpuinit local_timer_setup(struct clock_event_device *evt)
> {
> struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER];
>
> @@ -287,6 +287,7 @@ void __cpuinit local_timer_setup(struct
> clock_event_device *evt)
> gic_enable_ppi(clock->irq.irq);
>
> clockevents_register_device(evt);
> + return 0;
> }
>
> inline int local_timer_ack(void)
> diff --git a/arch/arm/mach-omap2/timer-mpu.c b/arch/arm/mach-
> omap2/timer-mpu.c
> index 954682e..09c73dc 100644
> --- a/arch/arm/mach-omap2/timer-mpu.c
> +++ b/arch/arm/mach-omap2/timer-mpu.c
> @@ -26,9 +26,10 @@
> /*
> * Setup the local clock events for a CPU.
> */
> -void __cpuinit local_timer_setup(struct clock_event_device *evt)
> +int __cpuinit local_timer_setup(struct clock_event_device *evt)
> {
> evt->irq = OMAP44XX_IRQ_LOCALTIMER;
> twd_timer_setup(evt);
> + return 0;
> }
>
> diff --git a/arch/arm/mach-realview/localtimer.c b/arch/arm/mach-
> realview/localtimer.c
> index 60b4e11..aca29ce 100644
> --- a/arch/arm/mach-realview/localtimer.c
> +++ b/arch/arm/mach-realview/localtimer.c
> @@ -19,8 +19,9 @@
> /*
> * Setup the local clock events for a CPU.
> */
> -void __cpuinit local_timer_setup(struct clock_event_device *evt)
> +int __cpuinit local_timer_setup(struct clock_event_device *evt)
> {
> evt->irq = IRQ_LOCALTIMER;
> twd_timer_setup(evt);
> + return 0;
> }
> diff --git a/arch/arm/mach-s5pv310/localtimer.c b/arch/arm/mach-
> s5pv310/localtimer.c
> index 2784036..8239c6a 100644
> --- a/arch/arm/mach-s5pv310/localtimer.c
> +++ b/arch/arm/mach-s5pv310/localtimer.c
> @@ -18,8 +18,9 @@
> /*
> * Setup the local clock events for a CPU.
> */
> -void __cpuinit local_timer_setup(struct clock_event_device *evt)
> +int __cpuinit local_timer_setup(struct clock_event_device *evt)
> {
> evt->irq = IRQ_LOCALTIMER;
> twd_timer_setup(evt);
> + return 0;
> }
> diff --git a/arch/arm/mach-shmobile/localtimer.c b/arch/arm/mach-
> shmobile/localtimer.c
> index 2111c28..ad9ccc9 100644
> --- a/arch/arm/mach-shmobile/localtimer.c
> +++ b/arch/arm/mach-shmobile/localtimer.c
> @@ -18,8 +18,9 @@
> /*
> * Setup the local clock events for a CPU.
> */
> -void __cpuinit local_timer_setup(struct clock_event_device *evt)
> +int __cpuinit local_timer_setup(struct clock_event_device *evt)
> {
> evt->irq = 29;
> twd_timer_setup(evt);
> + return 0;
> }
> diff --git a/arch/arm/mach-tegra/localtimer.c b/arch/arm/mach-
> tegra/localtimer.c
> index f81ca7c..e91d681 100644
> --- a/arch/arm/mach-tegra/localtimer.c
> +++ b/arch/arm/mach-tegra/localtimer.c
> @@ -18,8 +18,9 @@
> /*
> * Setup the local clock events for a CPU.
> */
> -void __cpuinit local_timer_setup(struct clock_event_device *evt)
> +int __cpuinit local_timer_setup(struct clock_event_device *evt)
> {
> evt->irq = IRQ_LOCALTIMER;
> twd_timer_setup(evt);
> + return 0;
> }
> diff --git a/arch/arm/mach-ux500/localtimer.c b/arch/arm/mach-
> ux500/localtimer.c
> index 2288f6a..5ba1133 100644
> --- a/arch/arm/mach-ux500/localtimer.c
> +++ b/arch/arm/mach-ux500/localtimer.c
> @@ -21,8 +21,9 @@
> /*
> * Setup the local clock events for a CPU.
> */
> -void __cpuinit local_timer_setup(struct clock_event_device *evt)
> +int __cpuinit local_timer_setup(struct clock_event_device *evt)
> {
> evt->irq = IRQ_LOCALTIMER;
> twd_timer_setup(evt);
> + return 0;
> }
> diff --git a/arch/arm/mach-vexpress/localtimer.c b/arch/arm/mach-
> vexpress/localtimer.c
> index c0e3a59..e5adbfa 100644
> --- a/arch/arm/mach-vexpress/localtimer.c
> +++ b/arch/arm/mach-vexpress/localtimer.c
> @@ -19,8 +19,9 @@
> /*
> * Setup the local clock events for a CPU.
> */
> -void __cpuinit local_timer_setup(struct clock_event_device *evt)
> +int __cpuinit local_timer_setup(struct clock_event_device *evt)
> {
> evt->irq = IRQ_LOCALTIMER;
> twd_timer_setup(evt);
> + return 0;
> }
> --
> 1.6.0.4
^ permalink raw reply
* [PATCH V2] i2c: add driver for Freescale i.MX28
From: Wolfram Sang @ 2011-02-16 12:39 UTC (permalink / raw)
To: linux-arm-kernel
Currently only supporting the PIOQUEUE-mode, because DMA-support for
this platform is not yet in mainline. When it becomes available and
support has been added to this driver, it will also be suitable for
i.MX23 and STMP3xxx.
Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Shawn Guo <shawn.guo@freescale.com>
---
Changes since V1:
* remove interruptible from completion (lots of problems with signals)
* add modalias
* resource failure now returning -ENOENT instead of -ENODEV
* improve the comments, explaining functions and TODOs
* add kerneldoc
* fix bad strncpy
* drop __raw-accessors
Note about mxs_i2c_wait_for_data():
I tried twice to use a waitqueue instead, but the interrupt for the wake did not
work as I expected. Definately not proud of it, but at leasts it works for now.
drivers/i2c/busses/Kconfig | 10 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-mxs.c | 412 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 423 insertions(+), 0 deletions(-)
create mode 100644 drivers/i2c/busses/i2c-mxs.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 3a6321c..25e2dc7 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -452,6 +452,16 @@ config I2C_MV64XXX
This driver can also be built as a module. If so, the module
will be called i2c-mv64xxx.
+config I2C_MXS
+ tristate "Freescale i.MX28 I2C interface"
+ depends on SOC_IMX28
+ help
+ Say Y here if you want to use the I2C bus controller on
+ the Freescale i.MX28 processors.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-mxs.
+
config I2C_NOMADIK
tristate "ST-Ericsson Nomadik/Ux500 I2C Controller"
depends on PLAT_NOMADIK
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 84cb16a..f415132 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
+obj-$(CONFIG_I2C_MXS) += i2c-mxs.o
obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o
obj-$(CONFIG_I2C_NUC900) += i2c-nuc900.o
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c
new file mode 100644
index 0000000..8022e23
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -0,0 +1,412 @@
+/*
+ * Freescale MXS I2C bus driver
+ *
+ * Copyright (C) 2011 Wolfram Sang, Pengutronix e.K.
+ *
+ * based on a (non-working) driver which was:
+ *
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * TODO: add dma-support if platform-support for it is available
+ *
+ * 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.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+
+#include <mach/common.h>
+
+#define DRIVER_NAME "mxs-i2c"
+
+#define MXS_I2C_CTRL0 (0x00)
+#define MXS_I2C_CTRL0_SET (0x04)
+
+#define MXS_I2C_CTRL0_SFTRST 0x80000000
+#define MXS_I2C_CTRL0_SEND_NAK_ON_LAST 0x02000000
+#define MXS_I2C_CTRL0_RETAIN_CLOCK 0x00200000
+#define MXS_I2C_CTRL0_POST_SEND_STOP 0x00100000
+#define MXS_I2C_CTRL0_PRE_SEND_START 0x00080000
+#define MXS_I2C_CTRL0_MASTER_MODE 0x00020000
+#define MXS_I2C_CTRL0_DIRECTION 0x00010000
+#define MXS_I2C_CTRL0_XFER_COUNT(v) ((v) & 0x0000FFFF)
+
+#define MXS_I2C_CTRL1 (0x40)
+#define MXS_I2C_CTRL1_SET (0x44)
+#define MXS_I2C_CTRL1_CLR (0x48)
+
+#define MXS_I2C_CTRL1_BUS_FREE_IRQ 0x80
+#define MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ 0x40
+#define MXS_I2C_CTRL1_NO_SLAVE_ACK_IRQ 0x20
+#define MXS_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ 0x10
+#define MXS_I2C_CTRL1_EARLY_TERM_IRQ 0x08
+#define MXS_I2C_CTRL1_MASTER_LOSS_IRQ 0x04
+#define MXS_I2C_CTRL1_SLAVE_STOP_IRQ 0x02
+#define MXS_I2C_CTRL1_SLAVE_IRQ 0x01
+
+#define MXS_I2C_IRQ_MASK (MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ | \
+ MXS_I2C_CTRL1_NO_SLAVE_ACK_IRQ | \
+ MXS_I2C_CTRL1_EARLY_TERM_IRQ | \
+ MXS_I2C_CTRL1_MASTER_LOSS_IRQ | \
+ MXS_I2C_CTRL1_SLAVE_STOP_IRQ | \
+ MXS_I2C_CTRL1_SLAVE_IRQ)
+
+#define MXS_I2C_QUEUECTRL (0x60)
+#define MXS_I2C_QUEUECTRL_SET (0x64)
+#define MXS_I2C_QUEUECTRL_CLR (0x68)
+
+#define MXS_I2C_QUEUECTRL_QUEUE_RUN 0x20
+#define MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE 0x04
+
+#define MXS_I2C_QUEUESTAT (0x70)
+#define MXS_I2C_QUEUESTAT_RD_QUEUE_EMPTY 0x00002000
+
+#define MXS_I2C_QUEUECMD (0x80)
+
+#define MXS_I2C_QUEUEDATA (0x90)
+
+#define MXS_I2C_DATA (0xa0)
+
+
+#define MXS_CMD_I2C_SELECT (MXS_I2C_CTRL0_RETAIN_CLOCK | \
+ MXS_I2C_CTRL0_PRE_SEND_START | \
+ MXS_I2C_CTRL0_MASTER_MODE | \
+ MXS_I2C_CTRL0_DIRECTION | \
+ MXS_I2C_CTRL0_XFER_COUNT(1))
+
+#define MXS_CMD_I2C_WRITE (MXS_I2C_CTRL0_PRE_SEND_START | \
+ MXS_I2C_CTRL0_MASTER_MODE | \
+ MXS_I2C_CTRL0_DIRECTION)
+
+#define MXS_CMD_I2C_READ (MXS_I2C_CTRL0_SEND_NAK_ON_LAST | \
+ MXS_I2C_CTRL0_MASTER_MODE)
+
+/**
+ * struct mxs_i2c_dev - per device, private MXS-I2C data
+ *
+ * @dev: driver model device node
+ * @regs: IO registers pointer
+ * @cmd_complete: completion object for transaction wait
+ * @cmd_err: error code for last transaction
+ * @adapter: i2c subsystem adapter node
+ */
+struct mxs_i2c_dev {
+ struct device *dev;
+ void __iomem *regs;
+ struct completion cmd_complete;
+ u32 cmd_err;
+ struct i2c_adapter adapter;
+};
+
+/*
+ * TODO: check if calls to here are really needed. If not, we could get rid of
+ * mxs_reset_block and the mach-dependency. Needs an I2C analyzer, probably.
+ */
+static void mxs_i2c_reset(struct mxs_i2c_dev *i2c)
+{
+ mxs_reset_block(i2c->regs);
+ writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET);
+}
+
+static void mxs_i2c_pioq_setup_read(struct mxs_i2c_dev *i2c, u8 addr, int len,
+ int flags)
+{
+ u32 data;
+
+ writel(MXS_CMD_I2C_SELECT, i2c->regs + MXS_I2C_QUEUECMD);
+
+ data = (addr << 1) | I2C_SMBUS_READ;
+ writel(data, i2c->regs + MXS_I2C_DATA);
+
+ data = MXS_CMD_I2C_READ | MXS_I2C_CTRL0_XFER_COUNT(len) | flags;
+ writel(data, i2c->regs + MXS_I2C_QUEUECMD);
+}
+
+static void mxs_i2c_pioq_setup_write(struct mxs_i2c_dev *i2c,
+ u8 addr, u8 *buf, int len, int flags)
+{
+ u32 data;
+ int i, shifts_left;
+
+ data = MXS_CMD_I2C_WRITE | MXS_I2C_CTRL0_XFER_COUNT(len + 1) | flags;
+ writel(data, i2c->regs + MXS_I2C_QUEUECMD);
+
+ /*
+ * We have to copy the slave address (u8) and buffer (arbitrary number
+ * of u8) into the data register (u32). To achieve that, the u8 are put
+ * into the MSBs of 'data' which is then shifted for the next u8. When
+ * apropriate, 'data' is written to MXS_I2C_DATA. So, the first u32
+ * looks like this:
+ *
+ * 3 2 1 0
+ * 10987654|32109876|54321098|76543210
+ * --------+--------+--------+--------
+ * buffer+2|buffer+1|buffer+0|slave_addr
+ */
+
+ data = ((addr << 1) | I2C_SMBUS_WRITE) << 24;
+
+ for (i = 0; i < len; i++) {
+ data >>= 8;
+ data |= buf[i] << 24;
+ if ((i & 3) == 2)
+ writel(data, i2c->regs + MXS_I2C_DATA);
+ }
+
+ /* Write out the remaining bytes if any */
+ shifts_left = 24 - (i & 3) * 8;
+ if (shifts_left)
+ writel(data >> shifts_left, i2c->regs + MXS_I2C_DATA);
+}
+
+/*
+ * TODO: should be replaceable with a waitqueue and RD_QUEUE_IRQ (setting the
+ * rd_threshold to 1). Couldn't get this to work, though.
+ */
+static int mxs_i2c_wait_for_data(struct mxs_i2c_dev *i2c)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ while (readl(i2c->regs + MXS_I2C_QUEUESTAT)
+ & MXS_I2C_QUEUESTAT_RD_QUEUE_EMPTY) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ cond_resched();
+ }
+
+ return 0;
+}
+
+static int mxs_i2c_finish_read(struct mxs_i2c_dev *i2c, u8 *buf, int len)
+{
+ u32 data;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if ((i & 3) == 0) {
+ if (mxs_i2c_wait_for_data(i2c))
+ return -ETIMEDOUT;
+ data = readl(i2c->regs + MXS_I2C_QUEUEDATA);
+ }
+ buf[i] = data & 0xff;
+ data >>= 8;
+ }
+
+ return 0;
+}
+
+/*
+ * Low level master read/write transaction.
+ */
+static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
+ int stop)
+{
+ struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap);
+ int ret;
+ int flags;
+
+ init_completion(&i2c->cmd_complete);
+
+ dev_dbg(i2c->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
+ msg->addr, msg->len, msg->flags, stop);
+
+ if (msg->len == 0)
+ return -EINVAL;
+
+ flags = stop ? MXS_I2C_CTRL0_POST_SEND_STOP : 0;
+
+ if (msg->flags & I2C_M_RD)
+ mxs_i2c_pioq_setup_read(i2c, msg->addr, msg->len, flags);
+ else
+ mxs_i2c_pioq_setup_write(i2c, msg->addr, msg->buf, msg->len,
+ flags);
+
+ writel(MXS_I2C_QUEUECTRL_QUEUE_RUN,
+ i2c->regs + MXS_I2C_QUEUECTRL_SET);
+
+ ret = wait_for_completion_timeout(&i2c->cmd_complete,
+ msecs_to_jiffies(1000));
+ if (ret == 0)
+ goto timeout;
+
+ if ((!i2c->cmd_err) && (msg->flags & I2C_M_RD)) {
+ ret = mxs_i2c_finish_read(i2c, msg->buf, msg->len);
+ if (ret)
+ goto timeout;
+ }
+
+ if (i2c->cmd_err == -ENXIO)
+ mxs_i2c_reset(i2c);
+
+ dev_dbg(i2c->dev, "Done with err=%d\n", i2c->cmd_err);
+
+ return i2c->cmd_err;
+
+timeout:
+ dev_dbg(i2c->dev, "Timeout!\n");
+ mxs_i2c_reset(i2c);
+ return -ETIMEDOUT;
+}
+
+static int mxs_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+ int num)
+{
+ int i;
+ int err;
+
+ for (i = 0; i < num; i++) {
+ err = mxs_i2c_xfer_msg(adap, &msgs[i], i == (num - 1));
+ if (err)
+ return err;
+ }
+
+ return num;
+}
+
+static u32 mxs_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id)
+{
+ struct mxs_i2c_dev *i2c = dev_id;
+ u32 stat = readl(i2c->regs + MXS_I2C_CTRL1) & MXS_I2C_IRQ_MASK;
+
+ if (!stat)
+ return IRQ_NONE;
+
+ if (stat & MXS_I2C_CTRL1_NO_SLAVE_ACK_IRQ)
+ i2c->cmd_err = -ENXIO;
+ else if (stat & (MXS_I2C_CTRL1_EARLY_TERM_IRQ |
+ MXS_I2C_CTRL1_MASTER_LOSS_IRQ |
+ MXS_I2C_CTRL1_SLAVE_STOP_IRQ | MXS_I2C_CTRL1_SLAVE_IRQ))
+ /* MXS_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ is only for slaves */
+ i2c->cmd_err = -EIO;
+ else
+ i2c->cmd_err = 0;
+
+ complete(&i2c->cmd_complete);
+
+ writel(stat, i2c->regs + MXS_I2C_CTRL1_CLR);
+ return IRQ_HANDLED;
+}
+
+static const struct i2c_algorithm mxs_i2c_algo = {
+ .master_xfer = mxs_i2c_xfer,
+ .functionality = mxs_i2c_func,
+};
+
+static int __devinit mxs_i2c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mxs_i2c_dev *i2c;
+ struct i2c_adapter *adap;
+ struct resource *res;
+ resource_size_t res_size;
+ int err, irq;
+
+ i2c = devm_kzalloc(dev, sizeof(struct mxs_i2c_dev), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOENT;
+
+ res_size = resource_size(res);
+ if (!devm_request_mem_region(dev, res->start, res_size, res->name))
+ return -EBUSY;
+
+ i2c->regs = devm_ioremap_nocache(dev, res->start, res_size);
+ if (!i2c->regs)
+ return -EBUSY;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ err = devm_request_irq(dev, irq, mxs_i2c_isr, 0, dev_name(dev), i2c);
+ if (err)
+ return err;
+
+ i2c->dev = dev;
+ platform_set_drvdata(pdev, i2c);
+
+ /* Do reset to enforce correct startup after pinmuxing */
+ mxs_i2c_reset(i2c);
+ writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE,
+ i2c->regs + MXS_I2C_QUEUECTRL_SET);
+
+ adap = &i2c->adapter;
+ strlcpy(adap->name, "MXS I2C adapter", sizeof(adap->name));
+ adap->owner = THIS_MODULE;
+ adap->algo = &mxs_i2c_algo;
+ adap->dev.parent = dev;
+ adap->nr = pdev->id;
+ i2c_set_adapdata(adap, i2c);
+ err = i2c_add_numbered_adapter(adap);
+ if (err) {
+ dev_err(dev, "Failed to add adapter (%d)\n", err);
+ writel(MXS_I2C_CTRL0_SFTRST,
+ i2c->regs + MXS_I2C_CTRL0_SET);
+ return err;
+ }
+
+ return 0;
+}
+
+static int __devexit mxs_i2c_remove(struct platform_device *pdev)
+{
+ struct mxs_i2c_dev *i2c = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = i2c_del_adapter(&i2c->adapter);
+ if (ret)
+ return -EBUSY;
+
+ writel(MXS_I2C_QUEUECTRL_QUEUE_RUN,
+ i2c->regs + MXS_I2C_QUEUECTRL_CLR);
+ writel(MXS_I2C_CTRL0_SFTRST, i2c->regs + MXS_I2C_CTRL0_SET);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver mxs_i2c_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(mxs_i2c_remove),
+};
+
+static int __init mxs_i2c_init(void)
+{
+ return platform_driver_probe(&mxs_i2c_driver, mxs_i2c_probe);
+}
+subsys_initcall(mxs_i2c_init);
+
+static void __exit mxs_i2c_exit(void)
+{
+ platform_driver_unregister(&mxs_i2c_driver);
+}
+module_exit(mxs_i2c_exit);
+
+MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_DESCRIPTION("MXS I2C Bus Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
--
1.7.2.3
^ permalink raw reply related
* [PATCH] ARM: errata: pl310 cache sync operation may be faulty
From: Catalin Marinas @ 2011-02-16 12:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110216123410.GA6267@bnru02>
On Wed, 2011-02-16 at 12:34 +0000, Srinidhi KASAGAR wrote:
> From 7a1fa2f8ec106bda4f940f5c9478ec16b2de6846 Mon Sep 17 00:00:00 2001
> From: srinidhi kasagar <srinidhi.kasagar@stericsson.com>
> Date: Mon, 14 Feb 2011 17:07:06 +0530
> Subject: [PATCH] ARM: errata: pl310 cache sync operation may be faulty
>
> The effect of cache sync operation is to drain the store
> buffer and wait for all internal buffers to be empty. In
> normal conditions, store buffer is able to merge the
> normal memory writes within its 32-byte data buffers.
> Due to this erratum present in r3p0, the effect of cache
> sync operation on the store buffer still remains when
> the operation completes. This means that the store buffer
> is always asked to drain and this prevents it from merging
> any further writes.
>
> This can severely affect performance on the write traffic
> esp. on Normal memory NC one.
>
> The proposed workaround is to replace the normal offset of
> cache sync operation(0x730) by another offset targeting an
> unmapped PL310 register 0x740.
>
> Signed-off-by: srinidhi kasagar <srinidhi.kasagar@stericsson.com>
> Acked-by: Linus Walleij <linus.walleij@stericsson.com>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
^ permalink raw reply
* [PATCH 1/3] OMAP2+: hwmod: Avoid setup if clock lookup failed
From: Sergei Shtylyov @ 2011-02-16 12:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297858285-7056-2-git-send-email-rnayak@ti.com>
Hello.
On 16.02.2011 15:11, Rajendra Nayak wrote:
> Add a hwmod state check in the _setup function
> to avoid setting up hwmods' for which clock
> lookup has failed.
> Signed-off-by: Rajendra Nayak<rnayak@ti.com>
[...]
> diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
> index e282e35..cd9dcde 100644
> --- a/arch/arm/mach-omap2/omap_hwmod.c
> +++ b/arch/arm/mach-omap2/omap_hwmod.c
> @@ -1362,6 +1362,12 @@ static int _setup(struct omap_hwmod *oh, void *data)
> int i, r;
> u8 postsetup_state;
>
> + if (oh->_state != _HWMOD_STATE_CLKS_INITED) {
> + WARN(1, "omap_hwmod: %s: _setup failed as one or more"
You forgot space bafore " -- "moreclock" will be printed.
> + "clock lookups' have failed\n", oh->name);
Why there's apostrophe here?
WBR, Sergei
^ permalink raw reply
* [PATCH] ARM: errata: pl310 cache sync operation may be faulty
From: Srinidhi KASAGAR @ 2011-02-16 12:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTi=svL42XJE-61H16B-jgwwu-+pXjS3QcMKNJN6Z@mail.gmail.com>
On Wed, Feb 16, 2011 at 11:32:12 +0100, Catalin Marinas wrote:
> On 16 February 2011 06:03, Srinidhi KASAGAR
> <srinidhi.kasagar@stericsson.com> wrote:
> > On Tue, Feb 15, 2011 at 12:34:22 +0100, Russell King - ARM Linux wrote:
> >> On Tue, Feb 15, 2011 at 04:48:03PM +0530, srinidhi kasagar wrote:
> >> > +#ifdef ARM_ERRATA_753970
> >> > +#define L2X0_DUMMY_REG ?0x740
> >> > + ? /* write to an unmmapped register */
> >> > + ? writel_relaxed(0, base + L2X0_DUMMY_REG);
> >> > + ? cache_wait(base + L2X0_CACHE_SYNC, 1);
> >> > +#else
> >> > ? ? writel_relaxed(0, base + L2X0_CACHE_SYNC);
> >> > ? ? cache_wait(base + L2X0_CACHE_SYNC, 1);
> >> > +#endif
> >>
> >> So why wrap cache_wait() up in that horrible ifdef as well - and why not
> >> put the dummy register definition along side the other register definitions?
> [...]
> > --- a/arch/arm/mm/cache-l2x0.c
> > +++ b/arch/arm/mm/cache-l2x0.c
> > @@ -49,8 +49,14 @@ static inline void cache_wait(void __iomem *reg, unsigned long mask)
> > ?static inline void cache_sync(void)
> > ?{
> > ? ? ? ?void __iomem *base = l2x0_base;
> > +
> > +#ifdef CONFIG_ARM_ERRATA_753970
> > + ? ? ? /* write to an unmmapped register */
> > + ? ? ? writel_relaxed(0, base + L2X0_DUMMY_REG);
> > +#else
> > ? ? ? ?writel_relaxed(0, base + L2X0_CACHE_SYNC);
> > ? ? ? ?cache_wait(base + L2X0_CACHE_SYNC, 1);
> > +#endif
> > ?}
>
> You could still leave cache_wait() after #endif, even though it is a
> no-op. I think it is clearer that the erratum workaround only targets
> the sync.
updated patch below.
^ permalink raw reply
* [PATCH 3/5] ARM: l2x0: Errata fix for flush by Way operation can cause data corruption
From: Santosh Shilimkar @ 2011-02-16 12:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4dfaffa99292bf8e36791ea9a68de75e@mail.gmail.com>
Catalin,
> -----Original Message-----
> From: Santosh Shilimkar [mailto:santosh.shilimkar at ti.com]
> Sent: Tuesday, February 15, 2011 12:44 PM
> To: linux-arm-kernel at lists.infradead.org; Andrei Warkentin
> Cc: linux-omap at vger.kernel.org; Kevin Hilman; tony at atomide.com;
> Catalin Marinas
> Subject: RE: [PATCH 3/5] ARM: l2x0: Errata fix for flush by Way
> operation can cause data corruption
>
> > -----Original Message-----
> > From: Santosh Shilimkar [mailto:santosh.shilimkar at ti.com]
> > Sent: Monday, February 14, 2011 10:39 AM
> > To: Andrei Warkentin
> > Cc: linux-omap at vger.kernel.org; Kevin Hilman; tony at atomide.com;
> > linux-arm-kernel at lists.infradead.org; Catalin Marinas
> > Subject: RE: [PATCH 3/5] ARM: l2x0: Errata fix for flush by Way
> > operation can cause data corruption
> >
>
> [....]
>
> > > ...
> > I understood that from first comment. But I am not in favor
> > of polluting common ARM files with SOC specific #ifdeffery.
> > We have gone over this when first errata support
> > was added for PL310
> >
> > I have a better way to handle this scenario.
> > Expect an updated patch for this.
> >
>
> Below is the updated version which should remove any
> OMAP dependency on these errata's. Attached same.
>
> ----
> From: Santosh Shilimkar <santosh.shilimkar@ti.com>
> Date: Fri, 14 Jan 2011 14:16:04 +0530
> Subject: [v2 PATCH 3/5] ARM: l2x0: Errata fix for flush by Way
> operation
> can cause data corruption
>
> PL310 implements the Clean & Invalidate by Way L2 cache maintenance
> operation (offset 0x7FC). This operation runs in background so that
> PL310 can handle normal accesses while it is in progress. Under very
> rare circumstances, due to this erratum, write data can be lost when
> PL310 treats a cacheable write transaction during a Clean &
> Invalidate
> by Way operation.
>
> Workaround:
> Disable Write-Back and Cache Linefill (Debug Control Register)
> Clean & Invalidate by Way (0x7FC)
> Re-enable Write-Back and Cache Linefill (Debug Control Register)
>
> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> ---
Ack , Nak ?
> arch/arm/Kconfig | 13 ++++++++++++-
> arch/arm/include/asm/outercache.h | 1 +
> arch/arm/mach-omap2/Kconfig | 3 +++
> arch/arm/mach-omap2/omap4-common.c | 7 +++++++
> arch/arm/mm/cache-l2x0.c | 28 +++++++++++++++----------
> ---
> 5 files changed, 38 insertions(+), 14 deletions(-)
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 5cff165..ebadd95 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1140,7 +1140,7 @@ config ARM_ERRATA_742231
>
> config PL310_ERRATA_588369
> bool "Clean & Invalidate maintenance operations do not
> invalidate
> clean lines"
> - depends on CACHE_L2X0 && ARCH_OMAP4
> + depends on CACHE_L2X0 && CACHE_PL310
> help
> The PL310 L2 cache controller implements three types of
> Clean &
> Invalidate maintenance operations: by Physical Address
> @@ -1177,6 +1177,17 @@ config ARM_ERRATA_743622
> visible impact on the overall performance or power
> consumption
> of the
> processor.
>
> +config PL310_ERRATA_727915
> + bool "Background Clean & Invalidate by Way operation can cause
> data corruption"
> + depends on CACHE_L2X0 && CACHE_PL310
> + help
> + PL310 implements the Clean & Invalidate by Way L2 cache
> maintenance
> + operation (offset 0x7FC). This operation runs in background
> so
> that
> + PL310 can handle normal accesses while it is in progress.
> Under
> very
> + rare circumstances, due to this erratum, write data can be
> lost
> when
> + PL310 treats a cacheable write transaction during a Clean &
> + Invalidate by Way operation Note that this errata uses Texas
> + Instrument's secure monitor api to implement the work
> around.
> endmenu
>
> source "arch/arm/common/Kconfig"
> diff --git a/arch/arm/include/asm/outercache.h
> b/arch/arm/include/asm/outercache.h
> index fc19009..348d513 100644
> --- a/arch/arm/include/asm/outercache.h
> +++ b/arch/arm/include/asm/outercache.h
> @@ -31,6 +31,7 @@ struct outer_cache_fns {
> #ifdef CONFIG_OUTER_CACHE_SYNC
> void (*sync)(void);
> #endif
> + void (*set_debug)(unsigned long);
> };
>
> #ifdef CONFIG_OUTER_CACHE
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-
> omap2/Kconfig
> index f285dd7..fd11ab4 100644
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -45,7 +45,10 @@ config ARCH_OMAP4
> select CPU_V7
> select ARM_GIC
> select LOCAL_TIMERS
> + select CACHE_L2X0
> + select CACHE_PL310
> select PL310_ERRATA_588369
> + select PL310_ERRATA_727915
> select ARM_ERRATA_720789
> select ARCH_HAS_OPP
> select PM_OPP if PM
> diff --git a/arch/arm/mach-omap2/omap4-common.c
> b/arch/arm/mach-omap2/omap4-common.c
> index 1926864..9ef8c29 100644
> --- a/arch/arm/mach-omap2/omap4-common.c
> +++ b/arch/arm/mach-omap2/omap4-common.c
> @@ -52,6 +52,12 @@ static void omap4_l2x0_disable(void)
> omap_smc1(0x102, 0x0);
> }
>
> +static void omap4_l2x0_set_debug(unsigned long val)
> +{
> + /* Program PL310 L2 Cache controller debug register */
> + omap_smc1(0x100, val);
> +}
> +
> static int __init omap_l2_cache_init(void)
> {
> u32 aux_ctrl = 0;
> @@ -99,6 +105,7 @@ static int __init omap_l2_cache_init(void)
> * specific one
> */
> outer_cache.disable = omap4_l2x0_disable;
> + outer_cache.set_debug = omap4_l2x0_set_debug;
>
> return 0;
> }
> diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
> index 170c9bb..a8caee4 100644
> --- a/arch/arm/mm/cache-l2x0.c
> +++ b/arch/arm/mm/cache-l2x0.c
> @@ -67,18 +67,22 @@ static inline void l2x0_inv_line(unsigned long
> addr)
> writel_relaxed(addr, base + L2X0_INV_LINE_PA);
> }
>
> -#ifdef CONFIG_PL310_ERRATA_588369
> +#if defined(CONFIG_PL310_ERRATA_588369) ||
> defined(CONFIG_PL310_ERRATA_727915)
> static void debug_writel(unsigned long val)
> {
> - extern void omap_smc1(u32 fn, u32 arg);
> -
> - /*
> - * Texas Instrument secure monitor api to modify the
> - * PL310 Debug Control Register.
> - */
> - omap_smc1(0x100, val);
> + if (outer_cache.set_debug)
> + outer_cache.set_debug(val);
> + else
> + writel(val, l2x0_base + L2X0_DEBUG_CTRL);
> +}
> +#else
> +/* Optimised out for non-errata case */
> +static inline void debug_writel(unsigned long val)
> +{
> }
> +#endif
>
> +#ifdef CONFIG_PL310_ERRATA_588369
> static inline void l2x0_flush_line(unsigned long addr)
> {
> void __iomem *base = l2x0_base;
> @@ -91,11 +95,6 @@ static inline void l2x0_flush_line(unsigned long
> addr)
> }
> #else
>
> -/* Optimised out for non-errata case */
> -static inline void debug_writel(unsigned long val)
> -{
> -}
> -
> static inline void l2x0_flush_line(unsigned long addr)
> {
> void __iomem *base = l2x0_base;
> @@ -119,9 +118,11 @@ static void l2x0_flush_all(void)
>
> /* clean all ways */
> spin_lock_irqsave(&l2x0_lock, flags);
> + debug_writel(0x03);
> writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_INV_WAY);
> cache_wait_way(l2x0_base + L2X0_CLEAN_INV_WAY, l2x0_way_mask);
> cache_sync();
> + debug_writel(0x00);
> spin_unlock_irqrestore(&l2x0_lock, flags);
> }
>
> @@ -329,6 +330,7 @@ void __init l2x0_init(void __iomem *base, __u32
> aux_val, __u32 aux_mask)
> outer_cache.flush_all = l2x0_flush_all;
> outer_cache.inv_all = l2x0_inv_all;
> outer_cache.disable = l2x0_disable;
> + outer_cache.set_debug = NULL;
>
> printk(KERN_INFO "%s cache controller enabled\n", type);
> printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL
> 0x%08x,
> Cache size: %d B\n",
> --
> 1.6.0.4
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox