aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/imx-drm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/imx-drm')
-rw-r--r--drivers/staging/imx-drm/Kconfig35
-rw-r--r--drivers/staging/imx-drm/Makefile9
-rw-r--r--drivers/staging/imx-drm/TODO22
-rw-r--r--drivers/staging/imx-drm/imx-drm-core.c884
-rw-r--r--drivers/staging/imx-drm/imx-drm.h58
-rw-r--r--drivers/staging/imx-drm/imx-fb.c47
-rw-r--r--drivers/staging/imx-drm/imx-fbdev.c74
-rw-r--r--drivers/staging/imx-drm/ipu-v3/Makefile3
-rw-r--r--drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h318
-rw-r--r--drivers/staging/imx-drm/ipu-v3/ipu-common.c1143
-rw-r--r--drivers/staging/imx-drm/ipu-v3/ipu-dc.c372
-rw-r--r--drivers/staging/imx-drm/ipu-v3/ipu-di.c700
-rw-r--r--drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c408
-rw-r--r--drivers/staging/imx-drm/ipu-v3/ipu-dp.c336
-rw-r--r--drivers/staging/imx-drm/ipu-v3/ipu-prv.h206
-rw-r--r--drivers/staging/imx-drm/ipuv3-crtc.c579
-rw-r--r--drivers/staging/imx-drm/parallel-display.c261
17 files changed, 5455 insertions, 0 deletions
diff --git a/drivers/staging/imx-drm/Kconfig b/drivers/staging/imx-drm/Kconfig
new file mode 100644
index 000000000000..14b4449df234
--- /dev/null
+++ b/drivers/staging/imx-drm/Kconfig
@@ -0,0 +1,35 @@
+config DRM_IMX
+ tristate "DRM Support for Freescale i.MX"
+ select DRM_KMS_HELPER
+ select DRM_GEM_CMA_HELPER
+ select DRM_KMS_CMA_HELPER
+ depends on DRM && ARCH_MXC
+ help
+ enable i.MX graphics support
+
+config DRM_IMX_FB_HELPER
+ tristate "provide legacy framebuffer /dev/fb0"
+ select DRM_KMS_CMA_HELPER
+ depends on DRM_IMX
+ help
+ The DRM framework can provide a legacy /dev/fb0 framebuffer
+ for your device. This is necessary to get a framebuffer console
+ and also for appplications using the legacy framebuffer API
+
+config DRM_IMX_PARALLEL_DISPLAY
+ tristate "Support for parallel displays"
+ depends on DRM_IMX
+
+config DRM_IMX_IPUV3_CORE
+ tristate "IPUv3 core support"
+ depends on DRM_IMX
+ help
+ Choose this if you have a i.MX5/6 system and want
+ to use the IPU. This option only enables IPU base
+ support.
+
+config DRM_IMX_IPUV3
+ tristate "DRM Support for i.MX IPUv3"
+ depends on DRM_IMX
+ help
+ Choose this if you have a i.MX5 or i.MX6 processor.
diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile
new file mode 100644
index 000000000000..83a9056546e6
--- /dev/null
+++ b/drivers/staging/imx-drm/Makefile
@@ -0,0 +1,9 @@
+
+imxdrm-objs := imx-drm-core.o imx-fb.o
+
+obj-$(CONFIG_DRM_IMX) += imxdrm.o
+
+obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += parallel-display.o
+obj-$(CONFIG_DRM_IMX_FB_HELPER) += imx-fbdev.o
+obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/
+obj-$(CONFIG_DRM_IMX_IPUV3) += ipuv3-crtc.o
diff --git a/drivers/staging/imx-drm/TODO b/drivers/staging/imx-drm/TODO
new file mode 100644
index 000000000000..e52adc44e607
--- /dev/null
+++ b/drivers/staging/imx-drm/TODO
@@ -0,0 +1,22 @@
+TODO:
+- get DRM Maintainer review for this code
+- Factor out more code to common helper functions
+- decide where to put the base driver. It is not specific to a subsystem
+ and would be used by DRM/KMS and media/V4L2
+- convert irq driver to irq_domain_add_linear
+
+Missing features (not necessarily for moving out of staging):
+
+- Add KMS plane support for CRTC driver
+- Add LDB (LVDS Display Bridge) support
+- Add i.MX6 HDMI support
+- Add support for IC (Image converter)
+- Add support for CSI (CMOS Sensor interface)
+- Add support for VDIC (Video Deinterlacer)
+
+Many work-in-progress patches for the above features exist. Contact
+Sascha Hauer <kernel@pengutronix.de> if you are interested in working
+on a specific feature.
+
+Please send any patches to Greg Kroah-Hartman <gregkh@linuxfoundation.org> and
+Sascha Hauer <kernel@pengutronix.de>
diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c
new file mode 100644
index 000000000000..1913199ba16e
--- /dev/null
+++ b/drivers/staging/imx-drm/imx-drm-core.c
@@ -0,0 +1,884 @@
+/*
+ * Freescale i.MX drm driver
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix
+ *
+ * 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/device.h>
+#include <linux/platform_device.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/fb.h>
+#include <linux/module.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "imx-drm.h"
+
+#define MAX_CRTC 4
+
+struct crtc_cookie {
+ void *cookie;
+ int id;
+ struct list_head list;
+};
+
+struct imx_drm_device {
+ struct drm_device *drm;
+ struct device *dev;
+ struct list_head crtc_list;
+ struct list_head encoder_list;
+ struct list_head connector_list;
+ struct mutex mutex;
+ int references;
+ int pipes;
+ struct drm_fbdev_cma *fbhelper;
+};
+
+struct imx_drm_crtc {
+ struct drm_crtc *crtc;
+ struct list_head list;
+ struct imx_drm_device *imxdrm;
+ int pipe;
+ struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs;
+ struct module *owner;
+ struct crtc_cookie cookie;
+};
+
+struct imx_drm_encoder {
+ struct drm_encoder *encoder;
+ struct list_head list;
+ struct module *owner;
+ struct list_head possible_crtcs;
+};
+
+struct imx_drm_connector {
+ struct drm_connector *connector;
+ struct list_head list;
+ struct module *owner;
+};
+
+static int imx_drm_driver_firstopen(struct drm_device *drm)
+{
+ if (!imx_drm_device_get())
+ return -EINVAL;
+
+ return 0;
+}
+
+static void imx_drm_driver_lastclose(struct drm_device *drm)
+{
+ struct imx_drm_device *imxdrm = drm->dev_private;
+
+ if (imxdrm->fbhelper)
+ drm_fbdev_cma_restore_mode(imxdrm->fbhelper);
+
+ imx_drm_device_put();
+}
+
+static int imx_drm_driver_unload(struct drm_device *drm)
+{
+ struct imx_drm_device *imxdrm = drm->dev_private;
+
+ drm_mode_config_cleanup(imxdrm->drm);
+ drm_kms_helper_poll_fini(imxdrm->drm);
+
+ return 0;
+}
+
+/*
+ * We don't care at all for crtc numbers, but the core expects the
+ * crtcs to be numbered
+ */
+static struct imx_drm_crtc *imx_drm_crtc_by_num(struct imx_drm_device *imxdrm,
+ int num)
+{
+ struct imx_drm_crtc *imx_drm_crtc;
+
+ list_for_each_entry(imx_drm_crtc, &imxdrm->crtc_list, list)
+ if (imx_drm_crtc->pipe == num)
+ return imx_drm_crtc;
+ return NULL;
+}
+
+int imx_drm_crtc_panel_format(struct drm_crtc *crtc, u32 encoder_type,
+ u32 interface_pix_fmt)
+{
+ struct imx_drm_device *imxdrm = crtc->dev->dev_private;
+ struct imx_drm_crtc *imx_crtc;
+ struct imx_drm_crtc_helper_funcs *helper;
+
+ mutex_lock(&imxdrm->mutex);
+
+ list_for_each_entry(imx_crtc, &imxdrm->crtc_list, list)
+ if (imx_crtc->crtc == crtc)
+ goto found;
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return -EINVAL;
+found:
+ mutex_unlock(&imxdrm->mutex);
+
+ helper = &imx_crtc->imx_drm_helper_funcs;
+ if (helper->set_interface_pix_fmt)
+ return helper->set_interface_pix_fmt(crtc,
+ encoder_type, interface_pix_fmt);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_drm_crtc_panel_format);
+
+int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc)
+{
+ return drm_vblank_get(imx_drm_crtc->imxdrm->drm, imx_drm_crtc->pipe);
+}
+EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_get);
+
+void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc)
+{
+ drm_vblank_put(imx_drm_crtc->imxdrm->drm, imx_drm_crtc->pipe);
+}
+EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_put);
+
+void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc)
+{
+ drm_handle_vblank(imx_drm_crtc->imxdrm->drm, imx_drm_crtc->pipe);
+}
+EXPORT_SYMBOL_GPL(imx_drm_handle_vblank);
+
+static int imx_drm_enable_vblank(struct drm_device *drm, int crtc)
+{
+ struct imx_drm_device *imxdrm = drm->dev_private;
+ struct imx_drm_crtc *imx_drm_crtc;
+ int ret;
+
+ imx_drm_crtc = imx_drm_crtc_by_num(imxdrm, crtc);
+ if (!imx_drm_crtc)
+ return -EINVAL;
+
+ if (!imx_drm_crtc->imx_drm_helper_funcs.enable_vblank)
+ return -ENOSYS;
+
+ ret = imx_drm_crtc->imx_drm_helper_funcs.enable_vblank(
+ imx_drm_crtc->crtc);
+
+ return ret;
+}
+
+static void imx_drm_disable_vblank(struct drm_device *drm, int crtc)
+{
+ struct imx_drm_device *imxdrm = drm->dev_private;
+ struct imx_drm_crtc *imx_drm_crtc;
+
+ imx_drm_crtc = imx_drm_crtc_by_num(imxdrm, crtc);
+ if (!imx_drm_crtc)
+ return;
+
+ if (!imx_drm_crtc->imx_drm_helper_funcs.disable_vblank)
+ return;
+
+ imx_drm_crtc->imx_drm_helper_funcs.disable_vblank(imx_drm_crtc->crtc);
+}
+
+static const struct file_operations imx_drm_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_gem_cma_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+ .read = drm_read,
+ .llseek = noop_llseek,
+};
+
+static struct imx_drm_device *imx_drm_device;
+
+static struct imx_drm_device *__imx_drm_device(void)
+{
+ return imx_drm_device;
+}
+
+struct drm_device *imx_drm_device_get(void)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_encoder *enc;
+ struct imx_drm_connector *con;
+ struct imx_drm_crtc *crtc;
+
+ mutex_lock(&imxdrm->mutex);
+
+ list_for_each_entry(enc, &imxdrm->encoder_list, list) {
+ if (!try_module_get(enc->owner)) {
+ dev_err(imxdrm->dev, "could not get module %s\n",
+ module_name(enc->owner));
+ goto unwind_enc;
+ }
+ }
+
+ list_for_each_entry(con, &imxdrm->connector_list, list) {
+ if (!try_module_get(con->owner)) {
+ dev_err(imxdrm->dev, "could not get module %s\n",
+ module_name(con->owner));
+ goto unwind_con;
+ }
+ }
+
+ list_for_each_entry(crtc, &imxdrm->crtc_list, list) {
+ if (!try_module_get(crtc->owner)) {
+ dev_err(imxdrm->dev, "could not get module %s\n",
+ module_name(crtc->owner));
+ goto unwind_crtc;
+ }
+ }
+
+ imxdrm->references++;
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return imxdrm->drm;
+
+unwind_crtc:
+ list_for_each_entry_continue_reverse(crtc, &imxdrm->crtc_list, list)
+ module_put(crtc->owner);
+unwind_con:
+ list_for_each_entry_continue_reverse(con, &imxdrm->connector_list, list)
+ module_put(con->owner);
+unwind_enc:
+ list_for_each_entry_continue_reverse(enc, &imxdrm->encoder_list, list)
+ module_put(enc->owner);
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return NULL;
+
+}
+EXPORT_SYMBOL_GPL(imx_drm_device_get);
+
+void imx_drm_device_put(void)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_encoder *enc;
+ struct imx_drm_connector *con;
+ struct imx_drm_crtc *crtc;
+
+ mutex_lock(&imxdrm->mutex);
+
+ list_for_each_entry(crtc, &imxdrm->crtc_list, list)
+ module_put(crtc->owner);
+
+ list_for_each_entry(con, &imxdrm->connector_list, list)
+ module_put(con->owner);
+
+ list_for_each_entry(enc, &imxdrm->encoder_list, list)
+ module_put(enc->owner);
+
+ imxdrm->references--;
+
+ mutex_unlock(&imxdrm->mutex);
+}
+EXPORT_SYMBOL_GPL(imx_drm_device_put);
+
+static int drm_mode_group_reinit(struct drm_device *dev)
+{
+ struct drm_mode_group *group = &dev->primary->mode_group;
+ uint32_t *id_list = group->id_list;
+ int ret;
+
+ ret = drm_mode_group_init_legacy_group(dev, group);
+ if (ret < 0)
+ return ret;
+
+ kfree(id_list);
+ return 0;
+}
+
+/*
+ * register an encoder to the drm core
+ */
+static int imx_drm_encoder_register(struct imx_drm_encoder *imx_drm_encoder)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+
+ INIT_LIST_HEAD(&imx_drm_encoder->possible_crtcs);
+
+ drm_encoder_init(imxdrm->drm, imx_drm_encoder->encoder,
+ imx_drm_encoder->encoder->funcs,
+ imx_drm_encoder->encoder->encoder_type);
+
+ drm_mode_group_reinit(imxdrm->drm);
+
+ return 0;
+}
+
+/*
+ * unregister an encoder from the drm core
+ */
+static void imx_drm_encoder_unregister(struct imx_drm_encoder
+ *imx_drm_encoder)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+
+ drm_encoder_cleanup(imx_drm_encoder->encoder);
+
+ drm_mode_group_reinit(imxdrm->drm);
+}
+
+/*
+ * register a connector to the drm core
+ */
+static int imx_drm_connector_register(
+ struct imx_drm_connector *imx_drm_connector)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+
+ drm_connector_init(imxdrm->drm, imx_drm_connector->connector,
+ imx_drm_connector->connector->funcs,
+ imx_drm_connector->connector->connector_type);
+ drm_mode_group_reinit(imxdrm->drm);
+
+ return drm_sysfs_connector_add(imx_drm_connector->connector);
+}
+
+/*
+ * unregister a connector from the drm core
+ */
+static void imx_drm_connector_unregister(
+ struct imx_drm_connector *imx_drm_connector)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+
+ drm_sysfs_connector_remove(imx_drm_connector->connector);
+ drm_connector_cleanup(imx_drm_connector->connector);
+
+ drm_mode_group_reinit(imxdrm->drm);
+}
+
+/*
+ * register a crtc to the drm core
+ */
+static int imx_drm_crtc_register(struct imx_drm_crtc *imx_drm_crtc)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ int ret;
+
+ drm_crtc_init(imxdrm->drm, imx_drm_crtc->crtc,
+ imx_drm_crtc->imx_drm_helper_funcs.crtc_funcs);
+ ret = drm_mode_crtc_set_gamma_size(imx_drm_crtc->crtc, 256);
+ if (ret)
+ return ret;
+
+ drm_crtc_helper_add(imx_drm_crtc->crtc,
+ imx_drm_crtc->imx_drm_helper_funcs.crtc_helper_funcs);
+
+ drm_mode_group_reinit(imxdrm->drm);
+
+ return 0;
+}
+
+/*
+ * Called by the CRTC driver when all CRTCs are registered. This
+ * puts all the pieces together and initializes the driver.
+ * Once this is called no more CRTCs can be registered since
+ * the drm core has hardcoded the number of crtcs in several
+ * places.
+ */
+static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ int ret;
+
+ imxdrm->drm = drm;
+
+ drm->dev_private = imxdrm;
+
+ /*
+ * enable drm irq mode.
+ * - with irq_enabled = 1, we can use the vblank feature.
+ *
+ * P.S. note that we wouldn't use drm irq handler but
+ * just specific driver own one instead because
+ * drm framework supports only one irq handler and
+ * drivers can well take care of their interrupts
+ */
+ drm->irq_enabled = 1;
+
+ drm_mode_config_init(drm);
+ imx_drm_mode_config_init(drm);
+
+ mutex_lock(&imxdrm->mutex);
+
+ drm_kms_helper_poll_init(imxdrm->drm);
+
+ /* setup the grouping for the legacy output */
+ ret = drm_mode_group_init_legacy_group(imxdrm->drm,
+ &imxdrm->drm->primary->mode_group);
+ if (ret)
+ goto err_init;
+
+ ret = drm_vblank_init(imxdrm->drm, MAX_CRTC);
+ if (ret)
+ goto err_init;
+
+ /*
+ * with vblank_disable_allowed = 1, vblank interrupt will be disabled
+ * by drm timer once a current process gives up ownership of
+ * vblank event.(after drm_vblank_put function is called)
+ */
+ imxdrm->drm->vblank_disable_allowed = 1;
+
+ ret = 0;
+
+err_init:
+ mutex_unlock(&imxdrm->mutex);
+
+ return ret;
+}
+
+static void imx_drm_update_possible_crtcs(void)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_crtc *imx_drm_crtc;
+ struct imx_drm_encoder *enc;
+ struct crtc_cookie *cookie;
+
+ list_for_each_entry(enc, &imxdrm->encoder_list, list) {
+ u32 possible_crtcs = 0;
+
+ list_for_each_entry(cookie, &enc->possible_crtcs, list) {
+ list_for_each_entry(imx_drm_crtc, &imxdrm->crtc_list, list) {
+ if (imx_drm_crtc->cookie.cookie == cookie->cookie &&
+ imx_drm_crtc->cookie.id == cookie->id) {
+ possible_crtcs |= 1 << imx_drm_crtc->pipe;
+ }
+ }
+ }
+ enc->encoder->possible_crtcs = possible_crtcs;
+ enc->encoder->possible_clones = possible_crtcs;
+ }
+}
+
+/*
+ * imx_drm_add_crtc - add a new crtc
+ *
+ * The return value if !NULL is a cookie for the caller to pass to
+ * imx_drm_remove_crtc later.
+ */
+int imx_drm_add_crtc(struct drm_crtc *crtc,
+ struct imx_drm_crtc **new_crtc,
+ const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs,
+ struct module *owner, void *cookie, int id)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_crtc *imx_drm_crtc;
+ const struct drm_crtc_funcs *crtc_funcs;
+ int ret;
+
+ mutex_lock(&imxdrm->mutex);
+
+ if (imxdrm->references) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
+
+ imx_drm_crtc = kzalloc(sizeof(*imx_drm_crtc), GFP_KERNEL);
+ if (!imx_drm_crtc) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs;
+ imx_drm_crtc->pipe = imxdrm->pipes++;
+ imx_drm_crtc->cookie.cookie = cookie;
+ imx_drm_crtc->cookie.id = id;
+
+ crtc_funcs = imx_drm_helper_funcs->crtc_funcs;
+
+ imx_drm_crtc->crtc = crtc;
+ imx_drm_crtc->imxdrm = imxdrm;
+
+ imx_drm_crtc->owner = owner;
+
+ list_add_tail(&imx_drm_crtc->list, &imxdrm->crtc_list);
+
+ *new_crtc = imx_drm_crtc;
+
+ ret = imx_drm_crtc_register(imx_drm_crtc);
+ if (ret)
+ goto err_register;
+
+ imx_drm_update_possible_crtcs();
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return 0;
+
+err_register:
+ kfree(imx_drm_crtc);
+err_alloc:
+err_busy:
+ mutex_unlock(&imxdrm->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_drm_add_crtc);
+
+/*
+ * imx_drm_remove_crtc - remove a crtc
+ */
+int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc)
+{
+ struct imx_drm_device *imxdrm = imx_drm_crtc->imxdrm;
+
+ mutex_lock(&imxdrm->mutex);
+
+ drm_crtc_cleanup(imx_drm_crtc->crtc);
+
+ list_del(&imx_drm_crtc->list);
+
+ drm_mode_group_reinit(imxdrm->drm);
+
+ mutex_unlock(&imxdrm->mutex);
+
+ kfree(imx_drm_crtc);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_drm_remove_crtc);
+
+/*
+ * imx_drm_add_encoder - add a new encoder
+ */
+int imx_drm_add_encoder(struct drm_encoder *encoder,
+ struct imx_drm_encoder **newenc, struct module *owner)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_encoder *imx_drm_encoder;
+ int ret;
+
+ mutex_lock(&imxdrm->mutex);
+
+ if (imxdrm->references) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
+
+ imx_drm_encoder = kzalloc(sizeof(*imx_drm_encoder), GFP_KERNEL);
+ if (!imx_drm_encoder) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ imx_drm_encoder->encoder = encoder;
+ imx_drm_encoder->owner = owner;
+
+ ret = imx_drm_encoder_register(imx_drm_encoder);
+ if (ret) {
+ kfree(imx_drm_encoder);
+ ret = -ENOMEM;
+ goto err_register;
+ }
+
+ list_add_tail(&imx_drm_encoder->list, &imxdrm->encoder_list);
+
+ *newenc = imx_drm_encoder;
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return 0;
+
+err_register:
+ kfree(imx_drm_encoder);
+err_alloc:
+err_busy:
+ mutex_unlock(&imxdrm->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_drm_add_encoder);
+
+int imx_drm_encoder_add_possible_crtcs(
+ struct imx_drm_encoder *imx_drm_encoder,
+ struct device_node *np)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct of_phandle_args args;
+ struct crtc_cookie *c;
+ int ret = 0;
+ int i;
+
+ if (!list_empty(&imx_drm_encoder->possible_crtcs))
+ return -EBUSY;
+
+ for (i = 0; !ret; i++) {
+ ret = of_parse_phandle_with_args(np, "crtcs",
+ "#crtc-cells", i, &args);
+ if (ret < 0)
+ break;
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ of_node_put(args.np);
+ return -ENOMEM;
+ }
+
+ c->cookie = args.np;
+ c->id = args.args_count > 0 ? args.args[0] : 0;
+
+ of_node_put(args.np);
+
+ mutex_lock(&imxdrm->mutex);
+
+ list_add_tail(&c->list, &imx_drm_encoder->possible_crtcs);
+
+ mutex_unlock(&imxdrm->mutex);
+ }
+
+ imx_drm_update_possible_crtcs();
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_drm_encoder_add_possible_crtcs);
+
+int imx_drm_encoder_get_mux_id(struct imx_drm_encoder *imx_drm_encoder,
+ struct drm_crtc *crtc)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_crtc *imx_crtc;
+ int i = 0;
+
+ mutex_lock(&imxdrm->mutex);
+
+ list_for_each_entry(imx_crtc, &imxdrm->crtc_list, list) {
+ if (imx_crtc->crtc == crtc)
+ goto found;
+ i++;
+ }
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return -EINVAL;
+found:
+ mutex_unlock(&imxdrm->mutex);
+
+ return i;
+}
+
+/*
+ * imx_drm_remove_encoder - remove an encoder
+ */
+int imx_drm_remove_encoder(struct imx_drm_encoder *imx_drm_encoder)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct crtc_cookie *c, *tmp;
+
+ mutex_lock(&imxdrm->mutex);
+
+ imx_drm_encoder_unregister(imx_drm_encoder);
+
+ list_del(&imx_drm_encoder->list);
+
+ list_for_each_entry_safe(c, tmp, &imx_drm_encoder->possible_crtcs,
+ list)
+ kfree(c);
+
+ mutex_unlock(&imxdrm->mutex);
+
+ kfree(imx_drm_encoder);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_drm_remove_encoder);
+
+/*
+ * imx_drm_add_connector - add a connector
+ */
+int imx_drm_add_connector(struct drm_connector *connector,
+ struct imx_drm_connector **new_con,
+ struct module *owner)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_connector *imx_drm_connector;
+ int ret;
+
+ mutex_lock(&imxdrm->mutex);
+
+ if (imxdrm->references) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
+
+ imx_drm_connector = kzalloc(sizeof(*imx_drm_connector), GFP_KERNEL);
+ if (!imx_drm_connector) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ imx_drm_connector->connector = connector;
+ imx_drm_connector->owner = owner;
+
+ ret = imx_drm_connector_register(imx_drm_connector);
+ if (ret)
+ goto err_register;
+
+ list_add_tail(&imx_drm_connector->list, &imxdrm->connector_list);
+
+ *new_con = imx_drm_connector;
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return 0;
+
+err_register:
+ kfree(imx_drm_connector);
+err_alloc:
+err_busy:
+ mutex_unlock(&imxdrm->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_drm_add_connector);
+
+void imx_drm_fb_helper_set(struct drm_fbdev_cma *fbdev_helper)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+
+ imxdrm->fbhelper = fbdev_helper;
+}
+EXPORT_SYMBOL_GPL(imx_drm_fb_helper_set);
+
+/*
+ * imx_drm_remove_connector - remove a connector
+ */
+int imx_drm_remove_connector(struct imx_drm_connector *imx_drm_connector)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+
+ mutex_lock(&imxdrm->mutex);
+
+ imx_drm_connector_unregister(imx_drm_connector);
+
+ list_del(&imx_drm_connector->list);
+
+ mutex_unlock(&imxdrm->mutex);
+
+ kfree(imx_drm_connector);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_drm_remove_connector);
+
+static struct drm_ioctl_desc imx_drm_ioctls[] = {
+ /* none so far */
+};
+
+static struct drm_driver imx_drm_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM,
+ .load = imx_drm_driver_load,
+ .unload = imx_drm_driver_unload,
+ .firstopen = imx_drm_driver_firstopen,
+ .lastclose = imx_drm_driver_lastclose,
+ .gem_free_object = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+ .dumb_create = drm_gem_cma_dumb_create,
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+ .dumb_destroy = drm_gem_cma_dumb_destroy,
+
+ .get_vblank_counter = drm_vblank_count,
+ .enable_vblank = imx_drm_enable_vblank,
+ .disable_vblank = imx_drm_disable_vblank,
+ .ioctls = imx_drm_ioctls,
+ .num_ioctls = ARRAY_SIZE(imx_drm_ioctls),
+ .fops = &imx_drm_driver_fops,
+ .name = "imx-drm",
+ .desc = "i.MX DRM graphics",
+ .date = "20120507",
+ .major = 1,
+ .minor = 0,
+ .patchlevel = 0,
+};
+
+static int imx_drm_platform_probe(struct platform_device *pdev)
+{
+ imx_drm_device->dev = &pdev->dev;
+
+ return drm_platform_init(&imx_drm_driver, pdev);
+}
+
+static int imx_drm_platform_remove(struct platform_device *pdev)
+{
+ drm_platform_exit(&imx_drm_driver, pdev);
+
+ return 0;
+}
+
+static struct platform_driver imx_drm_pdrv = {
+ .probe = imx_drm_platform_probe,
+ .remove = __devexit_p(imx_drm_platform_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "imx-drm",
+ },
+};
+
+static struct platform_device *imx_drm_pdev;
+
+static int __init imx_drm_init(void)
+{
+ int ret;
+
+ imx_drm_device = kzalloc(sizeof(*imx_drm_device), GFP_KERNEL);
+ if (!imx_drm_device)
+ return -ENOMEM;
+
+ mutex_init(&imx_drm_device->mutex);
+ INIT_LIST_HEAD(&imx_drm_device->crtc_list);
+ INIT_LIST_HEAD(&imx_drm_device->connector_list);
+ INIT_LIST_HEAD(&imx_drm_device->encoder_list);
+
+ imx_drm_pdev = platform_device_register_simple("imx-drm", -1, NULL, 0);
+ if (!imx_drm_pdev) {
+ ret = -EINVAL;
+ goto err_pdev;
+ }
+
+ imx_drm_pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32),
+
+ ret = platform_driver_register(&imx_drm_pdrv);
+ if (ret)
+ goto err_pdrv;
+
+ return 0;
+
+err_pdrv:
+ platform_device_unregister(imx_drm_pdev);
+err_pdev:
+ kfree(imx_drm_device);
+
+ return ret;
+}
+
+static void __exit imx_drm_exit(void)
+{
+ platform_device_unregister(imx_drm_pdev);
+ platform_driver_unregister(&imx_drm_pdrv);
+
+ kfree(imx_drm_device);
+}
+
+module_init(imx_drm_init);
+module_exit(imx_drm_exit);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("i.MX drm driver core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h
new file mode 100644
index 000000000000..ae28a490c445
--- /dev/null
+++ b/drivers/staging/imx-drm/imx-drm.h
@@ -0,0 +1,58 @@
+#ifndef _IMX_DRM_H_
+#define _IMX_DRM_H_
+
+struct imx_drm_crtc;
+struct drm_fbdev_cma;
+
+struct imx_drm_crtc_helper_funcs {
+ int (*enable_vblank)(struct drm_crtc *crtc);
+ void (*disable_vblank)(struct drm_crtc *crtc);
+ int (*set_interface_pix_fmt)(struct drm_crtc *crtc, u32 encoder_type,
+ u32 pix_fmt);
+ const struct drm_crtc_helper_funcs *crtc_helper_funcs;
+ const struct drm_crtc_funcs *crtc_funcs;
+};
+
+int imx_drm_add_crtc(struct drm_crtc *crtc,
+ struct imx_drm_crtc **new_crtc,
+ const struct imx_drm_crtc_helper_funcs *imx_helper_funcs,
+ struct module *owner, void *cookie, int id);
+int imx_drm_remove_crtc(struct imx_drm_crtc *);
+int imx_drm_init_drm(struct platform_device *pdev,
+ int preferred_bpp);
+int imx_drm_exit_drm(void);
+
+int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc);
+void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc);
+void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc);
+
+struct imx_drm_encoder;
+int imx_drm_add_encoder(struct drm_encoder *encoder,
+ struct imx_drm_encoder **new_enc,
+ struct module *owner);
+int imx_drm_remove_encoder(struct imx_drm_encoder *);
+
+struct imx_drm_connector;
+int imx_drm_add_connector(struct drm_connector *connector,
+ struct imx_drm_connector **new_con,
+ struct module *owner);
+int imx_drm_remove_connector(struct imx_drm_connector *);
+
+void imx_drm_mode_config_init(struct drm_device *drm);
+
+struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb);
+
+struct drm_device *imx_drm_device_get(void);
+void imx_drm_device_put(void);
+int imx_drm_crtc_panel_format(struct drm_crtc *crtc, u32 encoder_type,
+ u32 interface_pix_fmt);
+void imx_drm_fb_helper_set(struct drm_fbdev_cma *fbdev_helper);
+
+struct device_node;
+
+int imx_drm_encoder_get_mux_id(struct imx_drm_encoder *imx_drm_encoder,
+ struct drm_crtc *crtc);
+int imx_drm_encoder_add_possible_crtcs(struct imx_drm_encoder *imx_drm_encoder,
+ struct device_node *np);
+
+#endif /* _IMX_DRM_H_ */
diff --git a/drivers/staging/imx-drm/imx-fb.c b/drivers/staging/imx-drm/imx-fb.c
new file mode 100644
index 000000000000..03a7b4e14f67
--- /dev/null
+++ b/drivers/staging/imx-drm/imx-fb.c
@@ -0,0 +1,47 @@
+/*
+ * i.MX drm driver
+ *
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ *
+ * Based on Samsung Exynos code
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *
+ * 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/module.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "imx-drm.h"
+
+static struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
+ .fb_create = drm_fb_cma_create,
+};
+
+void imx_drm_mode_config_init(struct drm_device *dev)
+{
+ dev->mode_config.min_width = 64;
+ dev->mode_config.min_height = 64;
+
+ /*
+ * set max width and height as default value(4096x4096).
+ * this value would be used to check framebuffer size limitation
+ * at drm_mode_addfb().
+ */
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
+
+ dev->mode_config.funcs = &imx_drm_mode_config_funcs;
+}
diff --git a/drivers/staging/imx-drm/imx-fbdev.c b/drivers/staging/imx-drm/imx-fbdev.c
new file mode 100644
index 000000000000..8331739c3d08
--- /dev/null
+++ b/drivers/staging/imx-drm/imx-fbdev.c
@@ -0,0 +1,74 @@
+/*
+ * i.MX drm driver
+ *
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ *
+ * Based on Samsung Exynos code
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *
+ * 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/module.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "imx-drm.h"
+
+#define MAX_CONNECTOR 4
+#define PREFERRED_BPP 16
+
+static struct drm_fbdev_cma *fbdev_cma;
+
+static int legacyfb_depth = 16;
+
+module_param(legacyfb_depth, int, 0444);
+
+static int __init imx_fb_helper_init(void)
+{
+ struct drm_device *drm = imx_drm_device_get();
+
+ if (!drm)
+ return -EINVAL;
+
+ if (legacyfb_depth != 16 && legacyfb_depth != 32) {
+ pr_warn("i.MX legacyfb: invalid legacyfb_depth setting. defaulting to 16bpp\n");
+ legacyfb_depth = 16;
+ }
+
+ fbdev_cma = drm_fbdev_cma_init(drm, legacyfb_depth,
+ drm->mode_config.num_crtc, MAX_CONNECTOR);
+
+ if (IS_ERR(fbdev_cma)) {
+ imx_drm_device_put();
+ return PTR_ERR(fbdev_cma);
+ }
+
+ imx_drm_fb_helper_set(fbdev_cma);
+
+ return 0;
+}
+
+static void __exit imx_fb_helper_exit(void)
+{
+ imx_drm_fb_helper_set(NULL);
+ drm_fbdev_cma_fini(fbdev_cma);
+ imx_drm_device_put();
+}
+
+late_initcall(imx_fb_helper_init);
+module_exit(imx_fb_helper_exit);
+
+MODULE_DESCRIPTION("Freescale i.MX legacy fb driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/imx-drm/ipu-v3/Makefile b/drivers/staging/imx-drm/ipu-v3/Makefile
new file mode 100644
index 000000000000..28ed72e98a96
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += imx-ipu-v3.o
+
+imx-ipu-v3-objs := ipu-common.o ipu-dc.o ipu-di.o ipu-dp.o ipu-dmfc.o
diff --git a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h
new file mode 100644
index 000000000000..74158dd73758
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef __DRM_IPU_H__
+#define __DRM_IPU_H__
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/bitmap.h>
+#include <linux/fb.h>
+#include <linux/videodev2.h>
+
+struct ipu_soc;
+
+enum ipuv3_type {
+ IPUV3EX,
+ IPUV3M,
+ IPUV3H,
+};
+
+/*
+ * Bitfield of Display Interface signal polarities.
+ */
+struct ipu_di_signal_cfg {
+ unsigned datamask_en: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;
+ unsigned long pixelclock;
+#define IPU_DI_CLKMODE_SYNC (1 << 0)
+#define IPU_DI_CLKMODE_EXT (1 << 1)
+ unsigned long clkflags;
+};
+
+enum ipu_color_space {
+ IPUV3_COLORSPACE_RGB,
+ IPUV3_COLORSPACE_YUV,
+ IPUV3_COLORSPACE_UNKNOWN,
+};
+
+struct ipuv3_channel;
+
+enum ipu_channel_irq {
+ IPU_IRQ_EOF = 0,
+ IPU_IRQ_NFACK = 64,
+ IPU_IRQ_NFB4EOF = 128,
+ IPU_IRQ_EOS = 192,
+};
+
+int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
+ enum ipu_channel_irq irq);
+
+#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)
+
+/*
+ * IPU Image DMA Controller (idmac) functions
+ */
+struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned channel);
+void ipu_idmac_put(struct ipuv3_channel *);
+
+int ipu_idmac_enable_channel(struct ipuv3_channel *channel);
+int ipu_idmac_disable_channel(struct ipuv3_channel *channel);
+
+void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
+ bool doublebuffer);
+void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num);
+
+/*
+ * IPU Display Controller (dc) functions
+ */
+struct ipu_dc;
+struct ipu_di;
+struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel);
+void ipu_dc_put(struct ipu_dc *dc);
+int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
+ u32 pixel_fmt, u32 width);
+void ipu_dc_enable_channel(struct ipu_dc *dc);
+void ipu_dc_disable_channel(struct ipu_dc *dc);
+
+/*
+ * IPU Display Interface (di) functions
+ */
+struct ipu_di *ipu_di_get(struct ipu_soc *ipu, 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_get_num(struct ipu_di *);
+int ipu_di_init_sync_panel(struct ipu_di *, 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, int burstsize);
+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(struct ipu_soc *ipu, int ipuv3_channel);
+void ipu_dmfc_put(struct dmfc_channel *dmfc);
+
+/*
+ * IPU Display Processor (dp) functions
+ */
+#define IPU_DP_FLOW_SYNC_BG 0
+#define IPU_DP_FLOW_SYNC_FG 1
+#define IPU_DP_FLOW_ASYNC0_BG 2
+#define IPU_DP_FLOW_ASYNC0_FG 3
+#define IPU_DP_FLOW_ASYNC1_BG 4
+#define IPU_DP_FLOW_ASYNC1_FG 5
+
+struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, 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);
+int ipu_dp_setup_channel(struct ipu_dp *dp,
+ enum ipu_color_space in, enum ipu_color_space out);
+int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos);
+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha,
+ bool bg_chan);
+
+#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size))
+
+#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22)
+#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22)
+#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4)
+#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1)
+#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1)
+#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14)
+#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14)
+
+#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10)
+#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9)
+#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13)
+#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12)
+#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1)
+#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1)
+#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12)
+#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11)
+#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10)
+#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7)
+#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10)
+#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1)
+#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1)
+#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7)
+#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1)
+#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1)
+#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3)
+#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2)
+#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1)
+#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3)
+#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2)
+#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1)
+#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1)
+#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1)
+#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1)
+#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1)
+#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1)
+#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13)
+#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12)
+#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29)
+#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29)
+#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20)
+#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7)
+#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4)
+#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1)
+#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3)
+#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2)
+#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7)
+#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14)
+#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3)
+#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3)
+#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3)
+#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3)
+#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5)
+#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5)
+#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5)
+#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5)
+#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1)
+#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1)
+#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1)
+
+struct ipu_cpmem_word {
+ u32 data[5];
+ u32 res[3];
+};
+
+struct ipu_ch_param {
+ struct ipu_cpmem_word word[2];
+};
+
+void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v);
+u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs);
+struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel);
+void ipu_ch_param_dump(struct ipu_ch_param __iomem *p);
+
+static inline void ipu_ch_param_zero(struct ipu_ch_param __iomem *p)
+{
+ int i;
+ void __iomem *base = p;
+
+ for (i = 0; i < sizeof(*p) / sizeof(u32); i++)
+ writel(0, base + i * sizeof(u32));
+}
+
+static inline void ipu_cpmem_set_buffer(struct ipu_ch_param __iomem *p,
+ int bufnum, dma_addr_t buf)
+{
+ if (bufnum)
+ ipu_ch_param_write_field(p, IPU_FIELD_EBA1, buf >> 3);
+ else
+ ipu_ch_param_write_field(p, IPU_FIELD_EBA0, buf >> 3);
+}
+
+static inline void ipu_cpmem_set_resolution(struct ipu_ch_param __iomem *p,
+ int xres, int yres)
+{
+ ipu_ch_param_write_field(p, IPU_FIELD_FW, xres - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_FH, yres - 1);
+}
+
+static inline void ipu_cpmem_set_stride(struct ipu_ch_param __iomem *p,
+ int stride)
+{
+ ipu_ch_param_write_field(p, IPU_FIELD_SLY, stride - 1);
+}
+
+void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel);
+
+struct ipu_rgb {
+ struct fb_bitfield red;
+ struct fb_bitfield green;
+ struct fb_bitfield blue;
+ struct fb_bitfield transp;
+ int bits_per_pixel;
+};
+
+struct ipu_image {
+ struct v4l2_pix_format pix;
+ struct v4l2_rect rect;
+ dma_addr_t phys;
+};
+
+int ipu_cpmem_set_format_passthrough(struct ipu_ch_param __iomem *p,
+ int width);
+
+int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *,
+ struct ipu_rgb *rgb);
+
+static inline void ipu_cpmem_interlaced_scan(struct ipu_ch_param *p,
+ int stride)
+{
+ ipu_ch_param_write_field(p, IPU_FIELD_SO, 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_ILO, stride / 8);
+ ipu_ch_param_write_field(p, IPU_FIELD_SLY, (stride * 2) - 1);
+};
+
+void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format,
+ int stride, int height);
+void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p,
+ u32 pixel_format, int stride, int u_offset, int v_offset);
+int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 pixelformat);
+int ipu_cpmem_set_image(struct ipu_ch_param __iomem *cpmem,
+ struct ipu_image *image);
+
+enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat);
+
+static inline void ipu_cpmem_set_burstsize(struct ipu_ch_param __iomem *p,
+ int burstsize)
+{
+ ipu_ch_param_write_field(p, IPU_FIELD_NPB, burstsize - 1);
+};
+
+struct ipu_client_platformdata {
+ int di;
+ int dc;
+ int dp;
+ int dmfc;
+ int dma[2];
+};
+
+#endif /* __DRM_IPU_H__ */
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c
new file mode 100644
index 000000000000..f381960f42b0
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c
@@ -0,0 +1,1143 @@
+/*
+ * 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/module.h>
+#include <linux/export.h>
+#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/clk.h>
+#include <linux/list.h>
+#include <linux/irq.h>
+#include <linux/of_device.h>
+#include <asm/mach/irq.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
+{
+ return readl(ipu->cm_reg + offset);
+}
+
+static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
+{
+ writel(value, ipu->cm_reg + offset);
+}
+
+static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset)
+{
+ return readl(ipu->idmac_reg + offset);
+}
+
+static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value,
+ unsigned offset)
+{
+ writel(value, ipu->idmac_reg + offset);
+}
+
+void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
+{
+ u32 val;
+
+ val = ipu_cm_read(ipu, IPU_SRM_PRI2);
+ val |= 0x8;
+ ipu_cm_write(ipu, val, IPU_SRM_PRI2);
+}
+EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update);
+
+struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+
+ return ipu->cpmem_base + channel->num;
+}
+EXPORT_SYMBOL_GPL(ipu_get_cpmem);
+
+void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ struct ipu_ch_param __iomem *p = ipu_get_cpmem(channel);
+ u32 val;
+
+ if (ipu->ipu_type == IPUV3EX)
+ ipu_ch_param_write_field(p, IPU_FIELD_ID, 1);
+
+ val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(channel->num));
+ val |= 1 << (channel->num % 32);
+ ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(channel->num));
+};
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority);
+
+void ipu_ch_param_write_field(struct ipu_ch_param __iomem *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;
+ u32 val;
+
+ pr_debug("%s %d %d %d\n", __func__, word, bit , size);
+
+ val = readl(&base->word[word].data[i]);
+ val &= ~(mask << ofs);
+ val |= v << ofs;
+ writel(val, &base->word[word].data[i]);
+
+ if ((bit + size - 1) / 32 > i) {
+ val = readl(&base->word[word].data[i + 1]);
+ val &= ~(mask >> (ofs ? (32 - ofs) : 0));
+ val |= v >> (ofs ? (32 - ofs) : 0);
+ writel(val, &base->word[word].data[i + 1]);
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_ch_param_write_field);
+
+u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *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 = (readl(&base->word[word].data[i]) >> ofs) & mask;
+
+ if ((bit + size - 1) / 32 > i) {
+ u32 tmp;
+ tmp = readl(&base->word[word].data[i + 1]);
+ tmp &= mask >> (ofs ? (32 - ofs) : 0);
+ val |= tmp << (ofs ? (32 - ofs) : 0);
+ }
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(ipu_ch_param_read_field);
+
+int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *p,
+ struct ipu_rgb *rgb)
+{
+ int bpp = 0, npb = 0, ro, go, bo, to;
+
+ ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset;
+ go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset;
+ bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset;
+ to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset;
+
+ ipu_ch_param_write_field(p, IPU_FIELD_WID0, rgb->red.length - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS0, ro);
+ ipu_ch_param_write_field(p, IPU_FIELD_WID1, rgb->green.length - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS1, go);
+ ipu_ch_param_write_field(p, IPU_FIELD_WID2, rgb->blue.length - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS2, bo);
+
+ if (rgb->transp.length) {
+ ipu_ch_param_write_field(p, IPU_FIELD_WID3,
+ rgb->transp.length - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS3, to);
+ } else {
+ ipu_ch_param_write_field(p, IPU_FIELD_WID3, 7);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS3,
+ rgb->bits_per_pixel);
+ }
+
+ switch (rgb->bits_per_pixel) {
+ case 32:
+ bpp = 0;
+ npb = 15;
+ break;
+ case 24:
+ bpp = 1;
+ npb = 19;
+ break;
+ case 16:
+ bpp = 3;
+ npb = 31;
+ break;
+ case 8:
+ bpp = 5;
+ npb = 63;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp);
+ ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb);
+ ipu_ch_param_write_field(p, IPU_FIELD_PFS, 7); /* rgb mode */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb);
+
+int ipu_cpmem_set_format_passthrough(struct ipu_ch_param __iomem *p,
+ int width)
+{
+ int bpp = 0, npb = 0;
+
+ switch (width) {
+ case 32:
+ bpp = 0;
+ npb = 15;
+ break;
+ case 24:
+ bpp = 1;
+ npb = 19;
+ break;
+ case 16:
+ bpp = 3;
+ npb = 31;
+ break;
+ case 8:
+ bpp = 5;
+ npb = 63;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp);
+ ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb);
+ ipu_ch_param_write_field(p, IPU_FIELD_PFS, 6); /* raw mode */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough);
+
+void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p,
+ u32 pixel_format, int stride, int u_offset, int v_offset)
+{
+ switch (pixel_format) {
+ case V4L2_PIX_FMT_YUV420:
+ ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_UBO, u_offset / 8);
+ ipu_ch_param_write_field(p, IPU_FIELD_VBO, v_offset / 8);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full);
+
+void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format,
+ int stride, int height)
+{
+ int u_offset, v_offset;
+ int uv_stride = 0;
+
+ switch (pixel_format) {
+ case V4L2_PIX_FMT_YUV420:
+ uv_stride = stride / 2;
+ u_offset = stride * height;
+ v_offset = u_offset + (uv_stride * height / 2);
+ ipu_cpmem_set_yuv_planar_full(p, V4L2_PIX_FMT_YUV420, stride,
+ u_offset, v_offset);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar);
+
+static struct ipu_rgb def_rgb_32 = {
+ .red = { .offset = 16, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 0, .length = 8, },
+ .transp = { .offset = 24, .length = 8, },
+ .bits_per_pixel = 32,
+};
+
+static struct ipu_rgb def_bgr_32 = {
+ .red = { .offset = 16, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 0, .length = 8, },
+ .transp = { .offset = 24, .length = 8, },
+ .bits_per_pixel = 32,
+};
+
+static struct ipu_rgb def_rgb_24 = {
+ .red = { .offset = 0, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 16, .length = 8, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 24,
+};
+
+static struct ipu_rgb def_bgr_24 = {
+ .red = { .offset = 16, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 0, .length = 8, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 24,
+};
+
+static struct ipu_rgb def_rgb_16 = {
+ .red = { .offset = 11, .length = 5, },
+ .green = { .offset = 5, .length = 6, },
+ .blue = { .offset = 0, .length = 5, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 16,
+};
+
+#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y))
+#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \
+ (pix->width * (y) / 4) + (x) / 2)
+#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \
+ (pix->width * pix->height / 4) + \
+ (pix->width * (y) / 4) + (x) / 2)
+
+int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 pixelformat)
+{
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ /* pix format */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 2);
+ /* burst size */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 63);
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ /* bits/pixel */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3);
+ /* pix format */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0xA);
+ /* burst size */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ /* bits/pixel */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3);
+ /* pix format */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0x8);
+ /* burst size */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ ipu_cpmem_set_format_rgb(cpmem, &def_rgb_32);
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ ipu_cpmem_set_format_rgb(cpmem, &def_rgb_16);
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ ipu_cpmem_set_format_rgb(cpmem, &def_bgr_32);
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ ipu_cpmem_set_format_rgb(cpmem, &def_rgb_24);
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ ipu_cpmem_set_format_rgb(cpmem, &def_bgr_24);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt);
+
+int ipu_cpmem_set_image(struct ipu_ch_param __iomem *cpmem,
+ struct ipu_image *image)
+{
+ struct v4l2_pix_format *pix = &image->pix;
+ int y_offset, u_offset, v_offset;
+
+ pr_debug("%s: resolution: %dx%d stride: %d\n",
+ __func__, pix->width, pix->height,
+ pix->bytesperline);
+
+ ipu_cpmem_set_resolution(cpmem, image->rect.width,
+ image->rect.height);
+ ipu_cpmem_set_stride(cpmem, pix->bytesperline);
+
+ ipu_cpmem_set_fmt(cpmem, pix->pixelformat);
+
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ y_offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
+ u_offset = U_OFFSET(pix, image->rect.left,
+ image->rect.top) - y_offset;
+ v_offset = V_OFFSET(pix, image->rect.left,
+ image->rect.top) - y_offset;
+
+ ipu_cpmem_set_yuv_planar_full(cpmem, pix->pixelformat,
+ pix->bytesperline, u_offset, v_offset);
+ ipu_cpmem_set_buffer(cpmem, 0, image->phys + y_offset);
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ ipu_cpmem_set_buffer(cpmem, 0, image->phys +
+ image->rect.left * 2 +
+ image->rect.top * image->pix.bytesperline);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ ipu_cpmem_set_buffer(cpmem, 0, image->phys +
+ image->rect.left * 4 +
+ image->rect.top * image->pix.bytesperline);
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ ipu_cpmem_set_buffer(cpmem, 0, image->phys +
+ image->rect.left * 2 +
+ image->rect.top * image->pix.bytesperline);
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ ipu_cpmem_set_buffer(cpmem, 0, image->phys +
+ image->rect.left * 3 +
+ image->rect.top * image->pix.bytesperline);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_image);
+
+enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
+{
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YVYU:
+ return IPUV3_COLORSPACE_YUV;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_RGB565:
+ return IPUV3_COLORSPACE_RGB;
+ default:
+ return IPUV3_COLORSPACE_UNKNOWN;
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
+
+struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
+{
+ struct ipuv3_channel *channel;
+
+ dev_dbg(ipu->dev, "%s %d\n", __func__, num);
+
+ if (num > 63)
+ return ERR_PTR(-ENODEV);
+
+ mutex_lock(&ipu->channel_lock);
+
+ channel = &ipu->channel[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_GPL(ipu_idmac_get);
+
+void ipu_idmac_put(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+
+ 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_GPL(ipu_idmac_put);
+
+#define idma_mask(ch) (1 << (ch & 0x1f))
+
+void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
+ bool doublebuffer)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
+ if (doublebuffer)
+ reg |= idma_mask(channel->num);
+ else
+ reg &= ~idma_mask(channel->num);
+ ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
+
+int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
+{
+ unsigned long lock_flags;
+ u32 val;
+
+ spin_lock_irqsave(&ipu->lock, lock_flags);
+
+ val = ipu_cm_read(ipu, IPU_DISP_GEN);
+
+ if (mask & IPU_CONF_DI0_EN)
+ val |= IPU_DI0_COUNTER_RELEASE;
+ if (mask & IPU_CONF_DI1_EN)
+ val |= IPU_DI1_COUNTER_RELEASE;
+
+ ipu_cm_write(ipu, val, IPU_DISP_GEN);
+
+ val = ipu_cm_read(ipu, IPU_CONF);
+ val |= mask;
+ ipu_cm_write(ipu, val, IPU_CONF);
+
+ spin_unlock_irqrestore(&ipu->lock, lock_flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_module_enable);
+
+int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
+{
+ unsigned long lock_flags;
+ u32 val;
+
+ spin_lock_irqsave(&ipu->lock, lock_flags);
+
+ val = ipu_cm_read(ipu, IPU_CONF);
+ val &= ~mask;
+ ipu_cm_write(ipu, val, IPU_CONF);
+
+ val = ipu_cm_read(ipu, IPU_DISP_GEN);
+
+ if (mask & IPU_CONF_DI0_EN)
+ val &= ~IPU_DI0_COUNTER_RELEASE;
+ if (mask & IPU_CONF_DI1_EN)
+ val &= ~IPU_DI1_COUNTER_RELEASE;
+
+ ipu_cm_write(ipu, val, IPU_DISP_GEN);
+
+ spin_unlock_irqrestore(&ipu->lock, lock_flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_module_disable);
+
+void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ 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(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
+ else
+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
+
+int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
+ val |= idma_mask(channel->num);
+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
+
+int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ u32 val;
+ unsigned long flags;
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(50);
+ while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
+ idma_mask(channel->num)) {
+ if (time_after(jiffies, timeout)) {
+ dev_warn(ipu->dev, "disabling busy idmac channel %d\n",
+ channel->num);
+ break;
+ }
+ cpu_relax();
+ }
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ /* Disable DMA channel(s) */
+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
+ val &= ~idma_mask(channel->num);
+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
+
+ /* Set channel buffers NOT to be ready */
+ ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
+
+ if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
+ idma_mask(channel->num)) {
+ ipu_cm_write(ipu, idma_mask(channel->num),
+ IPU_CHA_BUF0_RDY(channel->num));
+ }
+
+ if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
+ idma_mask(channel->num)) {
+ ipu_cm_write(ipu, idma_mask(channel->num),
+ IPU_CHA_BUF1_RDY(channel->num));
+ }
+
+ ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
+
+ /* Reset the double buffer */
+ val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
+ val &= ~idma_mask(channel->num);
+ ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
+
+static int ipu_reset(struct ipu_soc *ipu)
+{
+ unsigned long timeout;
+
+ ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
+
+ timeout = jiffies + msecs_to_jiffies(1000);
+ while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
+ if (time_after(jiffies, timeout))
+ return -ETIME;
+ cpu_relax();
+ }
+
+ mdelay(300);
+
+ return 0;
+}
+
+struct ipu_devtype {
+ const char *name;
+ unsigned long cm_ofs;
+ unsigned long cpmem_ofs;
+ unsigned long srm_ofs;
+ unsigned long tpm_ofs;
+ unsigned long disp0_ofs;
+ unsigned long disp1_ofs;
+ unsigned long dc_tmpl_ofs;
+ unsigned long vdi_ofs;
+ enum ipuv3_type type;
+};
+
+static struct ipu_devtype ipu_type_imx51 = {
+ .name = "IPUv3EX",
+ .cm_ofs = 0x1e000000,
+ .cpmem_ofs = 0x1f000000,
+ .srm_ofs = 0x1f040000,
+ .tpm_ofs = 0x1f060000,
+ .disp0_ofs = 0x1e040000,
+ .disp1_ofs = 0x1e048000,
+ .dc_tmpl_ofs = 0x1f080000,
+ .vdi_ofs = 0x1e068000,
+ .type = IPUV3EX,
+};
+
+static struct ipu_devtype ipu_type_imx53 = {
+ .name = "IPUv3M",
+ .cm_ofs = 0x06000000,
+ .cpmem_ofs = 0x07000000,
+ .srm_ofs = 0x07040000,
+ .tpm_ofs = 0x07060000,
+ .disp0_ofs = 0x06040000,
+ .disp1_ofs = 0x06048000,
+ .dc_tmpl_ofs = 0x07080000,
+ .vdi_ofs = 0x06068000,
+ .type = IPUV3M,
+};
+
+static struct ipu_devtype ipu_type_imx6q = {
+ .name = "IPUv3H",
+ .cm_ofs = 0x00200000,
+ .cpmem_ofs = 0x00300000,
+ .srm_ofs = 0x00340000,
+ .tpm_ofs = 0x00360000,
+ .disp0_ofs = 0x00240000,
+ .disp1_ofs = 0x00248000,
+ .dc_tmpl_ofs = 0x00380000,
+ .vdi_ofs = 0x00268000,
+ .type = IPUV3H,
+};
+
+static const struct of_device_id imx_ipu_dt_ids[] = {
+ { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
+ { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
+ { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
+
+static int ipu_submodules_init(struct ipu_soc *ipu,
+ struct platform_device *pdev, unsigned long ipu_base,
+ struct clk *ipu_clk)
+{
+ char *unit;
+ int ret;
+ struct device *dev = &pdev->dev;
+ const struct ipu_devtype *devtype = ipu->devtype;
+
+ ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
+ IPU_CONF_DI0_EN, ipu_clk);
+ if (ret) {
+ unit = "di0";
+ goto err_di_0;
+ }
+
+ ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
+ IPU_CONF_DI1_EN, ipu_clk);
+ if (ret) {
+ unit = "di1";
+ goto err_di_1;
+ }
+
+ ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
+ IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
+ if (ret) {
+ unit = "dc_template";
+ goto err_dc;
+ }
+
+ ret = ipu_dmfc_init(ipu, dev, ipu_base +
+ devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
+ if (ret) {
+ unit = "dmfc";
+ goto err_dmfc;
+ }
+
+ ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
+ if (ret) {
+ unit = "dp";
+ goto err_dp;
+ }
+
+ return 0;
+
+err_dp:
+ ipu_dmfc_exit(ipu);
+err_dmfc:
+ ipu_dc_exit(ipu);
+err_dc:
+ ipu_di_exit(ipu, 1);
+err_di_1:
+ ipu_di_exit(ipu, 0);
+err_di_0:
+ dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
+ return ret;
+}
+
+static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs)
+{
+ unsigned long status;
+ int i, bit, irq_base;
+
+ for (i = 0; i < num_regs; i++) {
+
+ status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i]));
+ status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i]));
+
+ irq_base = ipu->irq_start + regs[i] * 32;
+ for_each_set_bit(bit, &status, 32)
+ generic_handle_irq(irq_base + bit);
+ }
+}
+
+static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
+ const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14};
+ struct irq_chip *chip = irq_get_chip(irq);
+
+ chained_irq_enter(chip, desc);
+
+ ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
+
+ chained_irq_exit(chip, desc);
+}
+
+static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
+ const int int_reg[] = { 4, 5, 8, 9};
+ struct irq_chip *chip = irq_get_chip(irq);
+
+ chained_irq_enter(chip, desc);
+
+ ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
+
+ chained_irq_exit(chip, desc);
+}
+
+static void ipu_ack_irq(struct irq_data *d)
+{
+ struct ipu_soc *ipu = irq_data_get_irq_chip_data(d);
+ unsigned int irq = d->irq - ipu->irq_start;
+
+ ipu_cm_write(ipu, 1 << (irq % 32), IPU_INT_STAT(irq / 32));
+}
+
+static void ipu_unmask_irq(struct irq_data *d)
+{
+ struct ipu_soc *ipu = irq_data_get_irq_chip_data(d);
+ unsigned int irq = d->irq - ipu->irq_start;
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ reg = ipu_cm_read(ipu, IPU_INT_CTRL(irq / 32));
+ reg |= 1 << (irq % 32);
+ ipu_cm_write(ipu, reg, IPU_INT_CTRL(irq / 32));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+
+static void ipu_mask_irq(struct irq_data *d)
+{
+ struct ipu_soc *ipu = irq_data_get_irq_chip_data(d);
+ unsigned int irq = d->irq - ipu->irq_start;
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ reg = ipu_cm_read(ipu, IPU_INT_CTRL(irq / 32));
+ reg &= ~(1 << (irq % 32));
+ ipu_cm_write(ipu, reg, IPU_INT_CTRL(irq / 32));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+
+static struct irq_chip ipu_irq_chip = {
+ .name = "IPU",
+ .irq_ack = ipu_ack_irq,
+ .irq_mask = ipu_mask_irq,
+ .irq_unmask = ipu_unmask_irq,
+};
+
+int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
+ enum ipu_channel_irq irq_type)
+{
+ return ipu->irq_start + irq_type + channel->num;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
+
+static void ipu_submodules_exit(struct ipu_soc *ipu)
+{
+ ipu_dp_exit(ipu);
+ ipu_dmfc_exit(ipu);
+ ipu_dc_exit(ipu);
+ ipu_di_exit(ipu, 1);
+ ipu_di_exit(ipu, 0);
+}
+
+static int platform_remove_devices_fn(struct device *dev, void *unused)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+
+ return 0;
+}
+
+static void platform_device_unregister_children(struct platform_device *pdev)
+{
+ device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn);
+}
+
+struct ipu_platform_reg {
+ struct ipu_client_platformdata pdata;
+ const char *name;
+};
+
+static const struct ipu_platform_reg client_reg[] = {
+ {
+ .pdata = {
+ .di = 0,
+ .dc = 5,
+ .dp = IPU_DP_FLOW_SYNC_BG,
+ .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
+ .dma[1] = -EINVAL,
+ },
+ .name = "imx-ipuv3-crtc",
+ }, {
+ .pdata = {
+ .di = 1,
+ .dc = 1,
+ .dp = -EINVAL,
+ .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
+ .dma[1] = -EINVAL,
+ },
+ .name = "imx-ipuv3-crtc",
+ },
+};
+
+static int ipu_client_id;
+
+static int ipu_add_subdevice_pdata(struct device *dev,
+ const struct ipu_platform_reg *reg)
+{
+ struct platform_device *pdev;
+
+ pdev = platform_device_register_data(dev, reg->name, ipu_client_id++,
+ &reg->pdata, sizeof(struct ipu_platform_reg));
+
+ return pdev ? 0 : -EINVAL;
+}
+
+static int ipu_add_client_devices(struct ipu_soc *ipu)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
+ const struct ipu_platform_reg *reg = &client_reg[i];
+ ret = ipu_add_subdevice_pdata(ipu->dev, reg);
+ if (ret)
+ goto err_register;
+ }
+
+ return 0;
+
+err_register:
+ platform_device_unregister_children(to_platform_device(ipu->dev));
+
+ return ret;
+}
+
+static int ipu_irq_init(struct ipu_soc *ipu)
+{
+ int i;
+
+ ipu->irq_start = irq_alloc_descs(-1, 0, IPU_NUM_IRQS, 0);
+ if (ipu->irq_start < 0)
+ return ipu->irq_start;
+
+ for (i = ipu->irq_start; i < ipu->irq_start + IPU_NUM_IRQS; i++) {
+ irq_set_chip_and_handler(i, &ipu_irq_chip, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID);
+ irq_set_chip_data(i, ipu);
+ }
+
+ irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler);
+ irq_set_handler_data(ipu->irq_sync, ipu);
+ irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler);
+ irq_set_handler_data(ipu->irq_err, ipu);
+
+ return 0;
+}
+
+static void ipu_irq_exit(struct ipu_soc *ipu)
+{
+ int i;
+
+ irq_set_chained_handler(ipu->irq_err, NULL);
+ irq_set_handler_data(ipu->irq_err, NULL);
+ irq_set_chained_handler(ipu->irq_sync, NULL);
+ irq_set_handler_data(ipu->irq_sync, NULL);
+
+ for (i = ipu->irq_start; i < ipu->irq_start + IPU_NUM_IRQS; i++) {
+ set_irq_flags(i, 0);
+ irq_set_chip(i, NULL);
+ irq_set_chip_data(i, NULL);
+ }
+
+ irq_free_descs(ipu->irq_start, IPU_NUM_IRQS);
+}
+
+static int __devinit ipu_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(imx_ipu_dt_ids, &pdev->dev);
+ struct ipu_soc *ipu;
+ struct resource *res;
+ unsigned long ipu_base;
+ int i, ret, irq_sync, irq_err;
+ const struct ipu_devtype *devtype;
+
+ devtype = of_id->data;
+
+ dev_info(&pdev->dev, "Initializing %s\n", devtype->name);
+
+ irq_sync = platform_get_irq(pdev, 0);
+ irq_err = platform_get_irq(pdev, 1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ dev_info(&pdev->dev, "irq_sync: %d irq_err: %d\n",
+ irq_sync, irq_err);
+
+ if (!res || irq_sync < 0 || irq_err < 0)
+ return -ENODEV;
+
+ ipu_base = res->start;
+
+ ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL);
+ if (!ipu)
+ return -ENODEV;
+
+ for (i = 0; i < 64; i++)
+ ipu->channel[i].ipu = ipu;
+ ipu->devtype = devtype;
+ ipu->ipu_type = devtype->type;
+
+ spin_lock_init(&ipu->lock);
+ mutex_init(&ipu->channel_lock);
+
+ dev_info(&pdev->dev, "cm_reg: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs);
+ dev_info(&pdev->dev, "idmac: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
+ dev_info(&pdev->dev, "cpmem: 0x%08lx\n",
+ ipu_base + devtype->cpmem_ofs);
+ dev_info(&pdev->dev, "disp0: 0x%08lx\n",
+ ipu_base + devtype->disp0_ofs);
+ dev_info(&pdev->dev, "disp1: 0x%08lx\n",
+ ipu_base + devtype->disp1_ofs);
+ dev_info(&pdev->dev, "srm: 0x%08lx\n",
+ ipu_base + devtype->srm_ofs);
+ dev_info(&pdev->dev, "tpm: 0x%08lx\n",
+ ipu_base + devtype->tpm_ofs);
+ dev_info(&pdev->dev, "dc: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
+ dev_info(&pdev->dev, "ic: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
+ dev_info(&pdev->dev, "dmfc: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
+ dev_info(&pdev->dev, "vdi: 0x%08lx\n",
+ ipu_base + devtype->vdi_ofs);
+
+ ipu->cm_reg = devm_ioremap(&pdev->dev,
+ ipu_base + devtype->cm_ofs, PAGE_SIZE);
+ ipu->idmac_reg = devm_ioremap(&pdev->dev,
+ ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS,
+ PAGE_SIZE);
+ ipu->cpmem_base = devm_ioremap(&pdev->dev,
+ ipu_base + devtype->cpmem_ofs, PAGE_SIZE);
+
+ if (!ipu->cm_reg || !ipu->idmac_reg || !ipu->cpmem_base) {
+ ret = -ENOMEM;
+ goto failed_ioremap;
+ }
+
+ ipu->clk = devm_clk_get(&pdev->dev, "bus");
+ if (IS_ERR(ipu->clk)) {
+ ret = PTR_ERR(ipu->clk);
+ dev_err(&pdev->dev, "clk_get failed with %d", ret);
+ goto failed_clk_get;
+ }
+
+ platform_set_drvdata(pdev, ipu);
+
+ clk_prepare_enable(ipu->clk);
+
+ ipu->dev = &pdev->dev;
+ ipu->irq_sync = irq_sync;
+ ipu->irq_err = irq_err;
+
+ ret = ipu_irq_init(ipu);
+ if (ret)
+ goto out_failed_irq;
+
+ ipu_reset(ipu);
+
+ /* Set MCU_T to divide MCU access window into 2 */
+ ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
+ IPU_DISP_GEN);
+
+ ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk);
+ if (ret)
+ goto failed_submodules_init;
+
+ ret = ipu_add_client_devices(ipu);
+ if (ret) {
+ dev_err(&pdev->dev, "adding client devices failed with %d\n",
+ ret);
+ goto failed_add_clients;
+ }
+
+ return 0;
+
+failed_add_clients:
+ ipu_submodules_exit(ipu);
+failed_submodules_init:
+ ipu_irq_exit(ipu);
+out_failed_irq:
+ clk_disable_unprepare(ipu->clk);
+failed_clk_get:
+failed_ioremap:
+ return ret;
+}
+
+static int __devexit ipu_remove(struct platform_device *pdev)
+{
+ struct ipu_soc *ipu = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ platform_device_unregister_children(pdev);
+ ipu_submodules_exit(ipu);
+ ipu_irq_exit(ipu);
+
+ clk_disable_unprepare(ipu->clk);
+
+ return 0;
+}
+
+static struct platform_driver imx_ipu_driver = {
+ .driver = {
+ .name = "imx-ipuv3",
+ .of_match_table = imx_ipu_dt_ids,
+ },
+ .probe = ipu_probe,
+ .remove = __devexit_p(ipu_remove),
+};
+
+module_platform_driver(imx_ipu_driver);
+
+MODULE_DESCRIPTION("i.MX IPU v3 driver");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c
new file mode 100644
index 000000000000..93c7579417be
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c
@@ -0,0 +1,372 @@
+/*
+ * 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/export.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+#define DC_MAP_CONF_PTR(n) (0x108 + ((n) & ~0x1) * 2)
+#define DC_MAP_CONF_VAL(n) (0x144 + ((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 0x0
+#define DC_WR_CH_ADDR 0x4
+#define DC_RL_CH(evt) (8 + ((evt) & ~0x1) * 2)
+
+#define DC_GEN 0xd4
+#define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4)
+#define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4)
+#define DC_STAT 0x1c8
+
+#define WROD(lf) (0x18 | ((lf) << 1))
+#define WRG 0x01
+
+#define SYNC_WAVE 0
+
+#define DC_GEN_SYNC_1_6_SYNC (2 << 1)
+#define DC_GEN_SYNC_PRIORITY_1 (1 << 7)
+
+#define DC_WR_CH_CONF_WORD_SIZE_8 (0 << 0)
+#define DC_WR_CH_CONF_WORD_SIZE_16 (1 << 0)
+#define DC_WR_CH_CONF_WORD_SIZE_24 (2 << 0)
+#define DC_WR_CH_CONF_WORD_SIZE_32 (3 << 0)
+#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i) (((i) & 0x1) << 3)
+#define DC_WR_CH_CONF_DISP_ID_SERIAL (2 << 3)
+#define DC_WR_CH_CONF_DISP_ID_ASYNC (3 << 4)
+#define DC_WR_CH_CONF_FIELD_MODE (1 << 9)
+#define DC_WR_CH_CONF_PROG_TYPE_NORMAL (4 << 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(i) (((i) & 0x1) << 3)
+
+#define IPU_DC_NUM_CHANNELS 10
+
+struct ipu_dc_priv;
+
+enum ipu_dc_map {
+ IPU_DC_MAP_RGB24,
+ IPU_DC_MAP_RGB565,
+};
+
+struct ipu_dc {
+ /* The display interface number assigned to this dc channel */
+ unsigned int di;
+ void __iomem *base;
+ struct ipu_dc_priv *priv;
+ int chno;
+ bool in_use;
+};
+
+struct ipu_dc_priv {
+ void __iomem *dc_reg;
+ void __iomem *dc_tmpl_reg;
+ struct ipu_soc *ipu;
+ struct device *dev;
+ struct ipu_dc channels[IPU_DC_NUM_CHANNELS];
+ struct mutex mutex;
+};
+
+static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority)
+{
+ u32 reg;
+
+ reg = readl(dc->base + DC_RL_CH(event));
+ reg &= ~(0xffff << (16 * (event & 0x1)));
+ reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
+ writel(reg, dc->base + DC_RL_CH(event));
+}
+
+static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand,
+ int map, int wave, int glue, int sync)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+ u32 reg;
+ int stop = 1;
+
+ reg = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000);
+ writel(reg, priv->dc_tmpl_reg + word * 8);
+ reg = operand >> 12 | opcode << 4 | stop << 9;
+ writel(reg, priv->dc_tmpl_reg + word * 8 + 4);
+}
+
+static int ipu_pixfmt_to_map(u32 fmt)
+{
+ switch (fmt) {
+ case V4L2_PIX_FMT_RGB24:
+ return IPU_DC_MAP_RGB24;
+ case V4L2_PIX_FMT_RGB565:
+ return IPU_DC_MAP_RGB565;
+ default:
+ return -EINVAL;
+ }
+}
+
+int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
+ u32 pixel_fmt, u32 width)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+ u32 reg = 0, map;
+
+ dc->di = ipu_di_get_num(di);
+
+ map = ipu_pixfmt_to_map(pixel_fmt);
+ if (map < 0) {
+ dev_dbg(priv->dev, "IPU_DISP: No MAP\n");
+ return -EINVAL;
+ }
+
+ if (interlaced) {
+ dc_link_event(dc, DC_EVT_NL, 0, 3);
+ dc_link_event(dc, DC_EVT_EOL, 0, 2);
+ dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
+
+ /* Init template microcode */
+ dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8);
+ } else {
+ if (dc->di) {
+ dc_link_event(dc, DC_EVT_NL, 2, 3);
+ dc_link_event(dc, DC_EVT_EOL, 3, 2);
+ dc_link_event(dc, DC_EVT_NEW_DATA, 4, 1);
+ /* Init template microcode */
+ dc_write_tmpl(dc, 2, WROD(0), 0, map, SYNC_WAVE, 8, 5);
+ dc_write_tmpl(dc, 3, WROD(0), 0, map, SYNC_WAVE, 4, 5);
+ dc_write_tmpl(dc, 4, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+ } else {
+ dc_link_event(dc, DC_EVT_NL, 5, 3);
+ dc_link_event(dc, DC_EVT_EOL, 6, 2);
+ dc_link_event(dc, DC_EVT_NEW_DATA, 7, 1);
+ /* Init template microcode */
+ dc_write_tmpl(dc, 5, WROD(0), 0, map, SYNC_WAVE, 8, 5);
+ dc_write_tmpl(dc, 6, WROD(0), 0, map, SYNC_WAVE, 4, 5);
+ dc_write_tmpl(dc, 7, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+ }
+ }
+ dc_link_event(dc, DC_EVT_NF, 0, 0);
+ dc_link_event(dc, DC_EVT_NFIELD, 0, 0);
+ dc_link_event(dc, DC_EVT_EOF, 0, 0);
+ dc_link_event(dc, DC_EVT_EOFIELD, 0, 0);
+ dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0);
+ dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0);
+
+ reg = readl(dc->base + DC_WR_CH_CONF);
+ if (interlaced)
+ reg |= DC_WR_CH_CONF_FIELD_MODE;
+ else
+ reg &= ~DC_WR_CH_CONF_FIELD_MODE;
+ writel(reg, dc->base + DC_WR_CH_CONF);
+
+ writel(0x0, dc->base + DC_WR_CH_ADDR);
+ writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di));
+
+ ipu_module_enable(priv->ipu, IPU_CONF_DC_EN);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dc_init_sync);
+
+void ipu_dc_enable_channel(struct ipu_dc *dc)
+{
+ int di;
+ u32 reg;
+
+ di = dc->di;
+
+ reg = readl(dc->base + DC_WR_CH_CONF);
+ reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL;
+ writel(reg, dc->base + DC_WR_CH_CONF);
+}
+EXPORT_SYMBOL_GPL(ipu_dc_enable_channel);
+
+void ipu_dc_disable_channel(struct ipu_dc *dc)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+ u32 val;
+ int irq = 0, timeout = 50;
+
+ if (dc->chno == 1)
+ irq = IPU_IRQ_DC_FC_1;
+ else if (dc->chno == 5)
+ irq = IPU_IRQ_DP_SF_END;
+ else
+ return;
+
+ /* should wait for the interrupt here */
+ mdelay(50);
+
+ if (dc->di == 0)
+ val = 0x00000002;
+ else
+ val = 0x00000020;
+
+ /* Wait for DC triple buffer to empty */
+ while ((readl(priv->dc_reg + DC_STAT) & val) != val) {
+ msleep(2);
+ timeout -= 2;
+ if (timeout <= 0)
+ break;
+ }
+
+ val = readl(dc->base + DC_WR_CH_CONF);
+ val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+ writel(val, dc->base + DC_WR_CH_CONF);
+}
+EXPORT_SYMBOL_GPL(ipu_dc_disable_channel);
+
+static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map,
+ int byte_num, int offset, int mask)
+{
+ int ptr = map * 3 + byte_num;
+ u32 reg;
+
+ reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr));
+ reg &= ~(0xffff << (16 * (ptr & 0x1)));
+ reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
+ writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr));
+
+ reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map));
+ reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num)));
+ reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
+ writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map));
+}
+
+static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map)
+{
+ u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map));
+
+ writel(reg & ~(0xffff << (16 * (map & 0x1))),
+ priv->dc_reg + DC_MAP_CONF_PTR(map));
+}
+
+struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel)
+{
+ struct ipu_dc_priv *priv = ipu->dc_priv;
+ struct ipu_dc *dc;
+
+ if (channel >= IPU_DC_NUM_CHANNELS)
+ return ERR_PTR(-ENODEV);
+
+ dc = &priv->channels[channel];
+
+ mutex_lock(&priv->mutex);
+
+ if (dc->in_use) {
+ mutex_unlock(&priv->mutex);
+ return ERR_PTR(-EBUSY);
+ }
+
+ dc->in_use = 1;
+
+ mutex_unlock(&priv->mutex);
+
+ return dc;
+}
+EXPORT_SYMBOL_GPL(ipu_dc_get);
+
+void ipu_dc_put(struct ipu_dc *dc)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+
+ mutex_lock(&priv->mutex);
+ dc->in_use = 0;
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(ipu_dc_put);
+
+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev,
+ unsigned long base, unsigned long template_base)
+{
+ struct ipu_dc_priv *priv;
+ static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c,
+ 0x78, 0, 0x94, 0xb4};
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->mutex);
+
+ priv->dev = dev;
+ priv->ipu = ipu;
+ priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE);
+ priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE);
+ if (!priv->dc_reg || !priv->dc_tmpl_reg)
+ return -ENOMEM;
+
+ for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) {
+ priv->channels[i].chno = i;
+ priv->channels[i].priv = priv;
+ priv->channels[i].base = priv->dc_reg + channel_offsets[i];
+ }
+
+ writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) |
+ DC_WR_CH_CONF_PROG_DI_ID,
+ priv->channels[1].base + DC_WR_CH_CONF);
+ writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0),
+ priv->channels[5].base + DC_WR_CH_CONF);
+
+ writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, priv->dc_reg + DC_GEN);
+
+ ipu->dc_priv = priv;
+
+ dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n",
+ base, template_base);
+
+ /* rgb24 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24);
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */
+
+ /* rgb565 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565);
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */
+
+ return 0;
+}
+
+void ipu_dc_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-di.c b/drivers/staging/imx-drm/ipu-v3/ipu-di.c
new file mode 100644
index 000000000000..67d974f7be36
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-di.c
@@ -0,0 +1,700 @@
+/*
+ * 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/export.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+struct ipu_di {
+ void __iomem *base;
+ int id;
+ u32 module;
+ struct clk *clk_di; /* display input clock */
+ struct clk *clk_ipu; /* IPU bus clock */
+ struct clk *clk_di_pixel; /* resulting pixel clock */
+ struct clk_hw clk_hw_out;
+ char *clk_name;
+ bool inuse;
+ unsigned long clkflags;
+ struct ipu_soc *ipu;
+};
+
+static DEFINE_MUTEX(di_mutex);
+
+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 SYNC_WAVE 0
+
+#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_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_GEN_POLARITY_DISP_CLK (1 << 17)
+#define DI_GEN_DI_CLK_EXT (1 << 20)
+#define DI_GEN_DI_VSYNC_EXT (1 << 21)
+
+#define DI_POL_DRDY_DATA_POLARITY (1 << 7)
+#define DI_POL_DRDY_POLARITY_15 (1 << 4)
+
+#define DI_VSYNC_SEL_OFFSET 13
+
+static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset)
+{
+ return readl(di->base + offset);
+}
+
+static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset)
+{
+ writel(value, di->base + offset);
+}
+
+static int ipu_di_clk_calc_div(unsigned long inrate, unsigned long outrate)
+{
+ u64 tmp = inrate;
+ int div;
+
+ tmp *= 16;
+
+ do_div(tmp, outrate);
+
+ div = tmp;
+
+ if (div < 0x10)
+ div = 0x10;
+
+#ifdef WTF_IS_THIS
+ /*
+ * Freescale has this in their Kernel. It is neither clear what
+ * it does nor why it does it
+ */
+ if (div & 0x10)
+ div &= ~0x7;
+ else {
+ /* Round up divider if it gets us closer to desired pix clk */
+ if ((div & 0xC) == 0xC) {
+ div += 0x10;
+ div &= ~0xF;
+ }
+ }
+#endif
+ return div;
+}
+
+static unsigned long clk_di_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
+ unsigned long outrate;
+ u32 div = ipu_di_read(di, DI_BS_CLKGEN0);
+
+ if (div < 0x10)
+ div = 0x10;
+
+ outrate = (parent_rate / div) * 16;
+
+ return outrate;
+}
+
+static long clk_di_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
+ unsigned long outrate;
+ int div;
+ u32 val;
+
+ div = ipu_di_clk_calc_div(*prate, rate);
+
+ outrate = (*prate / div) * 16;
+
+ val = ipu_di_read(di, DI_GENERAL);
+
+ if (!(val & DI_GEN_DI_CLK_EXT) && outrate > *prate / 2)
+ outrate = *prate / 2;
+
+ dev_dbg(di->ipu->dev,
+ "%s: inrate: %ld div: 0x%08x outrate: %ld wanted: %ld\n",
+ __func__, *prate, div, outrate, rate);
+
+ return outrate;
+}
+
+static int clk_di_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
+ int div;
+ u32 clkgen0;
+
+ clkgen0 = ipu_di_read(di, DI_BS_CLKGEN0) & ~0xfff;
+
+ div = ipu_di_clk_calc_div(parent_rate, rate);
+
+ ipu_di_write(di, clkgen0 | div, DI_BS_CLKGEN0);
+
+ dev_dbg(di->ipu->dev, "%s: inrate: %ld desired: %ld div: 0x%08x\n",
+ __func__, parent_rate, rate, div);
+ return 0;
+}
+
+static u8 clk_di_get_parent(struct clk_hw *hw)
+{
+ struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
+ u32 val;
+
+ val = ipu_di_read(di, DI_GENERAL);
+
+ return val & DI_GEN_DI_CLK_EXT ? 1 : 0;
+}
+
+static int clk_di_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
+ u32 val;
+
+ val = ipu_di_read(di, DI_GENERAL);
+
+ if (index)
+ val |= DI_GEN_DI_CLK_EXT;
+ else
+ val &= ~DI_GEN_DI_CLK_EXT;
+
+ ipu_di_write(di, val, DI_GENERAL);
+
+ return 0;
+}
+
+static struct clk_ops clk_di_ops = {
+ .round_rate = clk_di_round_rate,
+ .set_rate = clk_di_set_rate,
+ .recalc_rate = clk_di_recalc_rate,
+ .set_parent = clk_di_set_parent,
+ .get_parent = clk_di_get_parent,
+};
+
+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 start, int count)
+{
+ u32 reg;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ struct di_sync_config *c = &config[i];
+ int wave_gen = start + i + 1;
+
+ if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) ||
+ (c->repeat_count >= 0x1000) ||
+ (c->cnt_up >= 0x400) ||
+ (c->cnt_down >= 0x400)) {
+ dev_err(di->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);
+
+ /* Enable auto reload */
+ if (c->repeat_count == 0)
+ 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, 0, 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;
+ 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, 0, ARRAY_SIZE(cfg));
+}
+
+int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
+{
+ u32 reg;
+ u32 di_gen, vsync_cnt;
+ u32 div;
+ u32 h_total, v_total;
+ int ret;
+ unsigned long round;
+ struct clk *parent;
+
+ dev_dbg(di->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;
+
+ if (sig->clkflags & IPU_DI_CLKMODE_EXT)
+ parent = di->clk_di;
+ else
+ parent = di->clk_ipu;
+
+ ret = clk_set_parent(di->clk_di_pixel, parent);
+ if (ret) {
+ dev_err(di->ipu->dev,
+ "setting pixel clock to parent %s failed with %d\n",
+ __clk_get_name(parent), ret);
+ return ret;
+ }
+
+ if (sig->clkflags & IPU_DI_CLKMODE_SYNC)
+ round = clk_get_rate(parent);
+ else
+ round = clk_round_rate(di->clk_di_pixel, sig->pixelclock);
+
+ ret = clk_set_rate(di->clk_di_pixel, round);
+
+ 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);
+
+ div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff;
+ div = div / 16; /* Now divider is integer portion */
+
+ /* Setup pixel clock timing */
+ /* Down time is half of period */
+ ipu_di_write(di, (div << 16), DI_BS_CLKGEN1);
+
+ ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1);
+ ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2);
+
+ di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT;
+ di_gen |= DI_GEN_DI_VSYNC_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;
+ }
+
+ if (!sig->clk_pol)
+ di_gen |= DI_GEN_POLARITY_DISP_CLK;
+
+ ipu_di_write(di, di_gen, DI_GENERAL);
+
+ ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002,
+ DI_SYNC_AS_GEN);
+
+ reg = ipu_di_read(di, DI_POL);
+ reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15);
+
+ if (sig->enable_pol)
+ reg |= DI_POL_DRDY_POLARITY_15;
+ if (sig->data_pol)
+ reg |= DI_POL_DRDY_DATA_POLARITY;
+
+ ipu_di_write(di, reg, DI_POL);
+
+ mutex_unlock(&di_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel);
+
+int ipu_di_enable(struct ipu_di *di)
+{
+ clk_prepare_enable(di->clk_di_pixel);
+
+ ipu_module_enable(di->ipu, di->module);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_di_enable);
+
+int ipu_di_disable(struct ipu_di *di)
+{
+ ipu_module_disable(di->ipu, di->module);
+
+ clk_disable_unprepare(di->clk_di_pixel);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_di_disable);
+
+int ipu_di_get_num(struct ipu_di *di)
+{
+ return di->id;
+}
+EXPORT_SYMBOL_GPL(ipu_di_get_num);
+
+static DEFINE_MUTEX(ipu_di_lock);
+
+struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp)
+{
+ struct ipu_di *di;
+
+ if (disp > 1)
+ return ERR_PTR(-EINVAL);
+
+ di = ipu->di_priv[disp];
+
+ mutex_lock(&ipu_di_lock);
+
+ if (di->inuse) {
+ di = ERR_PTR(-EBUSY);
+ goto out;
+ }
+
+ di->inuse = true;
+out:
+ mutex_unlock(&ipu_di_lock);
+
+ return di;
+}
+EXPORT_SYMBOL_GPL(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_GPL(ipu_di_put);
+
+int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
+ unsigned long base,
+ u32 module, struct clk *clk_ipu)
+{
+ struct ipu_di *di;
+ int ret;
+ const char *di_parent[2];
+ struct clk_init_data init = {
+ .ops = &clk_di_ops,
+ .num_parents = 2,
+ .flags = 0,
+ };
+
+ if (id > 1)
+ return -ENODEV;
+
+ di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ ipu->di_priv[id] = di;
+
+ di->clk_di = devm_clk_get(dev, id ? "di1" : "di0");
+ if (IS_ERR(di->clk_di))
+ return PTR_ERR(di->clk_di);
+
+ di->module = module;
+ di->id = id;
+ di->clk_ipu = clk_ipu;
+ di->base = devm_ioremap(dev, base, PAGE_SIZE);
+ if (!di->base)
+ return -ENOMEM;
+
+ di_parent[0] = __clk_get_name(di->clk_ipu);
+ di_parent[1] = __clk_get_name(di->clk_di);
+
+ ipu_di_write(di, 0x10, DI_BS_CLKGEN0);
+
+ init.parent_names = (const char **)&di_parent;
+ di->clk_name = kasprintf(GFP_KERNEL, "%s_di%d_pixel",
+ dev_name(dev), id);
+ if (!di->clk_name)
+ return -ENOMEM;
+
+ init.name = di->clk_name;
+
+ di->clk_hw_out.init = &init;
+ di->clk_di_pixel = clk_register(dev, &di->clk_hw_out);
+
+ if (IS_ERR(di->clk_di_pixel)) {
+ ret = PTR_ERR(di->clk_di_pixel);
+ goto failed_clk_register;
+ }
+
+ dev_info(dev, "DI%d base: 0x%08lx remapped to %p\n",
+ id, base, di->base);
+ di->inuse = false;
+ di->ipu = ipu;
+
+ return 0;
+
+failed_clk_register:
+
+ kfree(di->clk_name);
+
+ return ret;
+}
+
+void ipu_di_exit(struct ipu_soc *ipu, int id)
+{
+ struct ipu_di *di = ipu->di_priv[id];
+
+ clk_unregister(di->clk_di_pixel);
+ kfree(di->clk_name);
+}
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c
new file mode 100644
index 000000000000..91821bc30f41
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c
@@ -0,0 +1,408 @@
+/*
+ * 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/export.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+
+#include "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_128 (0 << 6)
+#define DMFC_BURSTSIZE_64 (1 << 6)
+#define DMFC_BURSTSIZE_32 (2 << 6)
+#define DMFC_BURSTSIZE_16 (3 << 6)
+
+struct dmfc_channel_data {
+ int ipu_channel;
+ unsigned long channel_reg;
+ unsigned long shift;
+ unsigned eot_shift;
+ unsigned max_fifo_lines;
+};
+
+static const struct dmfc_channel_data dmfcdata[] = {
+ {
+ .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(dmfcdata)
+
+struct ipu_dmfc_priv;
+
+struct dmfc_channel {
+ unsigned slots;
+ unsigned slotmask;
+ unsigned segment;
+ int burstsize;
+ struct ipu_soc *ipu;
+ struct ipu_dmfc_priv *priv;
+ const struct dmfc_channel_data *data;
+};
+
+struct ipu_dmfc_priv {
+ struct ipu_soc *ipu;
+ struct device *dev;
+ struct dmfc_channel channels[DMFC_NUM_CHANNELS];
+ struct mutex mutex;
+ unsigned long bandwidth_per_slot;
+ void __iomem *base;
+ int use_count;
+};
+
+int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ mutex_lock(&priv->mutex);
+
+ if (!priv->use_count)
+ ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN);
+
+ priv->use_count++;
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel);
+
+void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+
+ mutex_lock(&priv->mutex);
+
+ priv->use_count--;
+
+ if (!priv->use_count)
+ ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN);
+
+ if (priv->use_count < 0)
+ priv->use_count = 0;
+
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel);
+
+static int ipu_dmfc_setup_channel(struct dmfc_channel *dmfc, int slots,
+ int segment, int burstsize)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ u32 val, field;
+
+ dev_dbg(priv->dev,
+ "dmfc: using %d slots starting from segment %d for IPU channel %d\n",
+ slots, segment, dmfc->data->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;
+ }
+
+ switch (burstsize) {
+ case 16:
+ field |= DMFC_BURSTSIZE_16;
+ break;
+ case 32:
+ field |= DMFC_BURSTSIZE_32;
+ break;
+ case 64:
+ field |= DMFC_BURSTSIZE_64;
+ break;
+ case 128:
+ field |= DMFC_BURSTSIZE_128;
+ break;
+ }
+
+ field |= DMFC_SEGMENT(segment);
+
+ val = readl(priv->base + dmfc->data->channel_reg);
+
+ val &= ~(0xff << dmfc->data->shift);
+ val |= field << dmfc->data->shift;
+
+ writel(val, priv->base + dmfc->data->channel_reg);
+
+ dmfc->slots = slots;
+ dmfc->segment = segment;
+ dmfc->burstsize = burstsize;
+ dmfc->slotmask = ((1 << slots) - 1) << segment;
+
+ return 0;
+}
+
+static int dmfc_bandwidth_to_slots(struct ipu_dmfc_priv *priv,
+ unsigned long bandwidth)
+{
+ int slots = 1;
+
+ while (slots * priv->bandwidth_per_slot < bandwidth)
+ slots *= 2;
+
+ return slots;
+}
+
+static int dmfc_find_slots(struct ipu_dmfc_priv *priv, 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 |= priv->channels[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)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ int i;
+
+ dev_dbg(priv->dev, "dmfc: freeing %d slots starting from segment %d\n",
+ dmfc->slots, dmfc->segment);
+
+ mutex_lock(&priv->mutex);
+
+ if (!dmfc->slots)
+ goto out;
+
+ dmfc->slotmask = 0;
+ dmfc->slots = 0;
+ dmfc->segment = 0;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ priv->channels[i].slotmask = 0;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
+ if (priv->channels[i].slots > 0) {
+ priv->channels[i].segment =
+ dmfc_find_slots(priv, priv->channels[i].slots);
+ priv->channels[i].slotmask =
+ ((1 << priv->channels[i].slots) - 1) <<
+ priv->channels[i].segment;
+ }
+ }
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
+ if (priv->channels[i].slots > 0)
+ ipu_dmfc_setup_channel(&priv->channels[i],
+ priv->channels[i].slots,
+ priv->channels[i].segment,
+ priv->channels[i].burstsize);
+ }
+out:
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_free_bandwidth);
+
+int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
+ unsigned long bandwidth_pixel_per_second, int burstsize)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ int slots = dmfc_bandwidth_to_slots(priv, bandwidth_pixel_per_second);
+ int segment = 0, ret = 0;
+
+ dev_dbg(priv->dev, "dmfc: trying to allocate %ldMpixel/s for IPU channel %d\n",
+ bandwidth_pixel_per_second / 1000000,
+ dmfc->data->ipu_channel);
+
+ ipu_dmfc_free_bandwidth(dmfc);
+
+ mutex_lock(&priv->mutex);
+
+ if (slots > 8) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ segment = dmfc_find_slots(priv, slots);
+ if (segment < 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ipu_dmfc_setup_channel(dmfc, slots, segment, burstsize);
+
+out:
+ mutex_unlock(&priv->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_alloc_bandwidth);
+
+int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ u32 dmfc_gen1;
+
+ dmfc_gen1 = readl(priv->base + DMFC_GENERAL1);
+
+ if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines)
+ dmfc_gen1 |= 1 << dmfc->data->eot_shift;
+ else
+ dmfc_gen1 &= ~(1 << dmfc->data->eot_shift);
+
+ writel(dmfc_gen1, priv->base + DMFC_GENERAL1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_init_channel);
+
+struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel)
+{
+ struct ipu_dmfc_priv *priv = ipu->dmfc_priv;
+ int i;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ if (dmfcdata[i].ipu_channel == ipu_channel)
+ return &priv->channels[i];
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_get);
+
+void ipu_dmfc_put(struct dmfc_channel *dmfc)
+{
+ ipu_dmfc_free_bandwidth(dmfc);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_put);
+
+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base,
+ struct clk *ipu_clk)
+{
+ struct ipu_dmfc_priv *priv;
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_ioremap(dev, base, PAGE_SIZE);
+ if (!priv->base)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->ipu = ipu;
+ mutex_init(&priv->mutex);
+
+ ipu->dmfc_priv = priv;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
+ priv->channels[i].priv = priv;
+ priv->channels[i].ipu = ipu;
+ priv->channels[i].data = &dmfcdata[i];
+ }
+
+ writel(0x0, priv->base + DMFC_WR_CHAN);
+ writel(0x0, priv->base + DMFC_DP_CHAN);
+
+ /*
+ * We have a total bandwidth of clkrate * 4pixel divided
+ * into 8 slots.
+ */
+ priv->bandwidth_per_slot = clk_get_rate(ipu_clk) / 8;
+
+ dev_dbg(dev, "dmfc: 8 slots with %ldMpixel/s bandwidth each\n",
+ priv->bandwidth_per_slot / 1000000);
+
+ writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF);
+ writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF);
+ writel(0x00000003, priv->base + DMFC_GENERAL1);
+
+ return 0;
+}
+
+void ipu_dmfc_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dp.c b/drivers/staging/imx-drm/ipu-v3/ipu-dp.c
new file mode 100644
index 000000000000..26aecaf9677f
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-dp.c
@@ -0,0 +1,336 @@
+/*
+ * 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/export.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+#define DP_SYNC 0
+#define DP_ASYNC0 0x60
+#define DP_ASYNC1 0xBC
+
+#define DP_COM_CONF 0x0
+#define DP_GRAPH_WIND_CTRL 0x0004
+#define DP_FG_POS 0x0008
+#define DP_CSC_A_0 0x0044
+#define DP_CSC_A_1 0x0048
+#define DP_CSC_A_2 0x004C
+#define DP_CSC_A_3 0x0050
+#define DP_CSC_0 0x0054
+#define DP_CSC_1 0x0058
+
+#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_priv;
+
+struct ipu_dp {
+ u32 flow;
+ bool in_use;
+ bool foreground;
+ enum ipu_color_space in_cs;
+};
+
+struct ipu_flow {
+ struct ipu_dp foreground;
+ struct ipu_dp background;
+ enum ipu_color_space out_cs;
+ void __iomem *base;
+ struct ipu_dp_priv *priv;
+};
+
+struct ipu_dp_priv {
+ struct ipu_soc *ipu;
+ struct device *dev;
+ void __iomem *base;
+ struct ipu_flow flow[3];
+ struct mutex mutex;
+ int use_count;
+};
+
+static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
+
+static inline struct ipu_flow *to_flow(struct ipu_dp *dp)
+{
+ if (dp->foreground)
+ return container_of(dp, struct ipu_flow, foreground);
+ else
+ return container_of(dp, struct ipu_flow, background);
+}
+
+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable,
+ u8 alpha, bool bg_chan)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+ u32 reg;
+
+ mutex_lock(&priv->mutex);
+
+ reg = readl(flow->base + DP_COM_CONF);
+ if (bg_chan)
+ reg &= ~DP_COM_CONF_GWSEL;
+ else
+ reg |= DP_COM_CONF_GWSEL;
+ writel(reg, flow->base + DP_COM_CONF);
+
+ if (enable) {
+ reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL;
+ writel(reg | ((u32) alpha << 24),
+ flow->base + DP_GRAPH_WIND_CTRL);
+
+ reg = readl(flow->base + DP_COM_CONF);
+ writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
+ } else {
+ reg = readl(flow->base + DP_COM_CONF);
+ writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
+ }
+
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha);
+
+int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS);
+
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos);
+
+static void ipu_dp_csc_init(struct ipu_flow *flow,
+ enum ipu_color_space in,
+ enum ipu_color_space out,
+ u32 place)
+{
+ u32 reg;
+
+ reg = readl(flow->base + DP_COM_CONF);
+ reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+
+ if (in == out) {
+ writel(reg, flow->base + DP_COM_CONF);
+ return;
+ }
+
+ if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) {
+ writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0);
+ writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1);
+ writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2);
+ writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3);
+ writel(0x3d6 | (0x0000 << 16) | (2 << 30),
+ flow->base + DP_CSC_0);
+ writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30),
+ flow->base + DP_CSC_1);
+ } else {
+ writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
+ writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1);
+ writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2);
+ writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3);
+ writel(0x000 | (0x3e42 << 16) | (1 << 30),
+ flow->base + DP_CSC_0);
+ writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30),
+ flow->base + DP_CSC_1);
+ }
+
+ reg |= place;
+
+ writel(reg, flow->base + DP_COM_CONF);
+}
+
+int ipu_dp_setup_channel(struct ipu_dp *dp,
+ enum ipu_color_space in,
+ enum ipu_color_space out)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ mutex_lock(&priv->mutex);
+
+ dp->in_cs = in;
+
+ if (!dp->foreground)
+ flow->out_cs = out;
+
+ if (flow->foreground.in_cs == flow->background.in_cs) {
+ /*
+ * foreground and background are of same colorspace, put
+ * colorspace converter after combining unit.
+ */
+ ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs,
+ DP_COM_CONF_CSC_DEF_BOTH);
+ } else {
+ if (flow->foreground.in_cs == flow->out_cs)
+ /*
+ * foreground identical to output, apply color
+ * conversion on background
+ */
+ ipu_dp_csc_init(flow, flow->background.in_cs,
+ flow->out_cs, DP_COM_CONF_CSC_DEF_BG);
+ else
+ ipu_dp_csc_init(flow, flow->foreground.in_cs,
+ flow->out_cs, DP_COM_CONF_CSC_DEF_FG);
+ }
+
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_setup_channel);
+
+int ipu_dp_enable_channel(struct ipu_dp *dp)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ mutex_lock(&priv->mutex);
+
+ if (!priv->use_count)
+ ipu_module_enable(priv->ipu, IPU_CONF_DP_EN);
+
+ priv->use_count++;
+
+ if (dp->foreground) {
+ u32 reg;
+
+ reg = readl(flow->base + DP_COM_CONF);
+ reg |= DP_COM_CONF_FG_EN;
+ writel(reg, flow->base + DP_COM_CONF);
+
+ ipu_srm_dp_sync_update(priv->ipu);
+ }
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_enable_channel);
+
+void ipu_dp_disable_channel(struct ipu_dp *dp)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ mutex_lock(&priv->mutex);
+
+ priv->use_count--;
+
+ if (dp->foreground) {
+ u32 reg, csc;
+
+ reg = readl(flow->base + DP_COM_CONF);
+ 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;
+ writel(reg, flow->base + DP_COM_CONF);
+
+ writel(0, flow->base + DP_FG_POS);
+ ipu_srm_dp_sync_update(priv->ipu);
+ }
+
+ if (!priv->use_count)
+ ipu_module_disable(priv->ipu, IPU_CONF_DP_EN);
+
+ if (priv->use_count < 0)
+ priv->use_count = 0;
+
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
+
+struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow)
+{
+ struct ipu_dp_priv *priv = ipu->dp_priv;
+ struct ipu_dp *dp;
+
+ if (flow > 5)
+ return ERR_PTR(-EINVAL);
+
+ if (flow & 1)
+ dp = &priv->flow[flow >> 1].foreground;
+ else
+ dp = &priv->flow[flow >> 1].background;
+
+ if (dp->in_use)
+ return ERR_PTR(-EBUSY);
+
+ dp->in_use = true;
+
+ return dp;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_get);
+
+void ipu_dp_put(struct ipu_dp *dp)
+{
+ dp->in_use = false;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_put);
+
+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base)
+{
+ struct ipu_dp_priv *priv;
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ priv->dev = dev;
+ priv->ipu = ipu;
+
+ ipu->dp_priv = priv;
+
+ priv->base = devm_ioremap(dev, base, PAGE_SIZE);
+ if (!priv->base) {
+ kfree(priv);
+ return -ENOMEM;
+ }
+
+ mutex_init(&priv->mutex);
+
+ for (i = 0; i < 3; i++) {
+ priv->flow[i].foreground.foreground = 1;
+ priv->flow[i].base = priv->base + ipu_dp_flow_base[i];
+ priv->flow[i].priv = priv;
+ }
+
+ return 0;
+}
+
+void ipu_dp_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-prv.h b/drivers/staging/imx-drm/ipu-v3/ipu-prv.h
new file mode 100644
index 000000000000..551802863fd5
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-prv.h
@@ -0,0 +1,206 @@
+/*
+ * 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__
+
+struct ipu_soc;
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include "imx-ipu-v3.h"
+
+#define IPUV3_CHANNEL_CSI0 0
+#define IPUV3_CHANNEL_CSI1 1
+#define IPUV3_CHANNEL_CSI2 2
+#define IPUV3_CHANNEL_CSI3 3
+#define IPUV3_CHANNEL_MEM_BG_SYNC 23
+#define IPUV3_CHANNEL_MEM_FG_SYNC 27
+#define IPUV3_CHANNEL_MEM_DC_SYNC 28
+#define IPUV3_CHANNEL_MEM_FG_SYNC_ALPHA 31
+#define IPUV3_CHANNEL_MEM_DC_ASYNC 41
+#define IPUV3_CHANNEL_ROT_ENC_MEM 45
+#define IPUV3_CHANNEL_ROT_VF_MEM 46
+#define IPUV3_CHANNEL_ROT_PP_MEM 47
+#define IPUV3_CHANNEL_ROT_ENC_MEM_OUT 48
+#define IPUV3_CHANNEL_ROT_VF_MEM_OUT 49
+#define IPUV3_CHANNEL_ROT_PP_MEM_OUT 50
+#define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA 51
+
+#define IPU_MCU_T_DEFAULT 8
+#define IPU_CM_IDMAC_REG_OFS 0x00008000
+#define IPU_CM_IC_REG_OFS 0x00020000
+#define IPU_CM_IRT_REG_OFS 0x00028000
+#define IPU_CM_CSI0_REG_OFS 0x00030000
+#define IPU_CM_CSI1_REG_OFS 0x00038000
+#define IPU_CM_SMFC_REG_OFS 0x00050000
+#define IPU_CM_DC_REG_OFS 0x00058000
+#define IPU_CM_DMFC_REG_OFS 0x00060000
+
+/* 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))
+#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n))
+
+#define IPU_DI0_COUNTER_RELEASE (1 << 24)
+#define IPU_DI1_COUNTER_RELEASE (1 << 25)
+
+#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))
+
+#define IPU_NUM_IRQS (32 * 5)
+
+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 ipuv3_channel {
+ unsigned int num;
+
+ bool enabled;
+ bool busy;
+
+ struct ipu_soc *ipu;
+};
+
+struct ipu_dc_priv;
+struct ipu_dmfc_priv;
+struct ipu_di;
+struct ipu_devtype;
+
+struct ipu_soc {
+ struct device *dev;
+ const struct ipu_devtype *devtype;
+ enum ipuv3_type ipu_type;
+ spinlock_t lock;
+ struct mutex channel_lock;
+
+ void __iomem *cm_reg;
+ void __iomem *idmac_reg;
+ struct ipu_ch_param __iomem *cpmem_base;
+
+ int usecount;
+
+ struct clk *clk;
+
+ struct ipuv3_channel channel[64];
+
+ int irq_start;
+ int irq_sync;
+ int irq_err;
+
+ struct ipu_dc_priv *dc_priv;
+ struct ipu_dp_priv *dp_priv;
+ struct ipu_dmfc_priv *dmfc_priv;
+ struct ipu_di *di_priv[2];
+};
+
+void ipu_srm_dp_sync_update(struct ipu_soc *ipu);
+
+int ipu_module_enable(struct ipu_soc *ipu, u32 mask);
+int ipu_module_disable(struct ipu_soc *ipu, u32 mask);
+
+int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
+ unsigned long base, u32 module, struct clk *ipu_clk);
+void ipu_di_exit(struct ipu_soc *ipu, int id);
+
+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base,
+ struct clk *ipu_clk);
+void ipu_dmfc_exit(struct ipu_soc *ipu);
+
+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base);
+void ipu_dp_exit(struct ipu_soc *ipu);
+
+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base,
+ unsigned long template_base);
+void ipu_dc_exit(struct ipu_soc *ipu);
+
+int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base);
+void ipu_cpmem_exit(struct ipu_soc *ipu);
+
+#endif /* __IPU_PRV_H__ */
diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c
new file mode 100644
index 000000000000..78d3edac75c1
--- /dev/null
+++ b/drivers/staging/imx-drm/ipuv3-crtc.c
@@ -0,0 +1,579 @@
+/*
+ * i.MX IPUv3 Graphics driver
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "ipu-v3/imx-ipu-v3.h"
+#include "imx-drm.h"
+
+#define DRIVER_DESC "i.MX IPUv3 Graphics"
+
+struct ipu_framebuffer {
+ struct drm_framebuffer base;
+ void *virt;
+ dma_addr_t phys;
+ size_t len;
+};
+
+struct ipu_crtc {
+ struct drm_fb_helper fb_helper;
+ struct ipu_framebuffer ifb;
+ int num_crtcs;
+ struct device *dev;
+ struct drm_crtc base;
+ struct imx_drm_crtc *imx_crtc;
+ struct ipuv3_channel *ipu_ch;
+ struct ipu_dc *dc;
+ struct ipu_dp *dp;
+ struct dmfc_channel *dmfc;
+ struct ipu_di *di;
+ int enabled;
+ struct ipu_priv *ipu_priv;
+ struct drm_pending_vblank_event *page_flip_event;
+ struct drm_framebuffer *newfb;
+ int irq;
+ u32 interface_pix_fmt;
+ unsigned long di_clkflags;
+};
+
+#define to_ipu_crtc(x) container_of(x, struct ipu_crtc, base)
+
+static int calc_vref(struct drm_display_mode *mode)
+{
+ unsigned long htotal, vtotal;
+
+ htotal = mode->htotal;
+ vtotal = mode->vtotal;
+
+ if (!htotal || !vtotal)
+ return 60;
+
+ return mode->clock * 1000 / vtotal / htotal;
+}
+
+static int calc_bandwidth(struct drm_display_mode *mode, unsigned int vref)
+{
+ return mode->hdisplay * mode->vdisplay * vref;
+}
+
+static void ipu_fb_enable(struct ipu_crtc *ipu_crtc)
+{
+ if (ipu_crtc->enabled)
+ return;
+
+ ipu_di_enable(ipu_crtc->di);
+ ipu_dmfc_enable_channel(ipu_crtc->dmfc);
+ ipu_idmac_enable_channel(ipu_crtc->ipu_ch);
+ ipu_dc_enable_channel(ipu_crtc->dc);
+ if (ipu_crtc->dp)
+ ipu_dp_enable_channel(ipu_crtc->dp);
+
+ ipu_crtc->enabled = 1;
+}
+
+static void ipu_fb_disable(struct ipu_crtc *ipu_crtc)
+{
+ if (!ipu_crtc->enabled)
+ return;
+
+ if (ipu_crtc->dp)
+ ipu_dp_disable_channel(ipu_crtc->dp);
+ ipu_dc_disable_channel(ipu_crtc->dc);
+ ipu_idmac_disable_channel(ipu_crtc->ipu_ch);
+ ipu_dmfc_disable_channel(ipu_crtc->dmfc);
+ ipu_di_disable(ipu_crtc->di);
+
+ ipu_crtc->enabled = 0;
+}
+
+static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+ dev_info(ipu_crtc->dev, "%s mode: %d\n", __func__, mode);
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ ipu_fb_enable(ipu_crtc);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ ipu_fb_disable(ipu_crtc);
+ break;
+ }
+}
+
+static int ipu_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+ int ret;
+
+ if (ipu_crtc->newfb)
+ return -EBUSY;
+
+ ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc);
+ if (ret) {
+ dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n");
+ list_del(&event->base.link);
+
+ return ret;
+ }
+
+ ipu_crtc->newfb = fb;
+ ipu_crtc->page_flip_event = event;
+
+ return 0;
+}
+
+static const struct drm_crtc_funcs ipu_crtc_funcs = {
+ .set_config = drm_crtc_helper_set_config,
+ .destroy = drm_crtc_cleanup,
+ .page_flip = ipu_page_flip,
+};
+
+static int ipu_drm_set_base(struct drm_crtc *crtc, int x, int y)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+ struct drm_gem_cma_object *cma_obj;
+ struct drm_framebuffer *fb = crtc->fb;
+ unsigned long phys;
+
+ cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
+ if (!cma_obj) {
+ DRM_LOG_KMS("entry is null.\n");
+ return -EFAULT;
+ }
+
+ phys = cma_obj->paddr;
+ phys += x * (fb->bits_per_pixel >> 3);
+ phys += y * fb->pitches[0];
+
+ dev_dbg(ipu_crtc->dev, "%s: phys: 0x%lx\n", __func__, phys);
+ dev_dbg(ipu_crtc->dev, "%s: xy: %dx%d\n", __func__, x, y);
+
+ ipu_cpmem_set_stride(ipu_get_cpmem(ipu_crtc->ipu_ch), fb->pitches[0]);
+ ipu_cpmem_set_buffer(ipu_get_cpmem(ipu_crtc->ipu_ch),
+ 0, phys);
+
+ return 0;
+}
+
+static int ipu_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *orig_mode,
+ struct drm_display_mode *mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+ struct drm_framebuffer *fb = ipu_crtc->base.fb;
+ int ret;
+ struct ipu_di_signal_cfg sig_cfg = {};
+ u32 out_pixel_fmt;
+ struct ipu_ch_param __iomem *cpmem = ipu_get_cpmem(ipu_crtc->ipu_ch);
+ int bpp;
+ u32 v4l2_fmt;
+
+ dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__,
+ mode->hdisplay);
+ dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__,
+ mode->vdisplay);
+
+ ipu_ch_param_zero(cpmem);
+
+ switch (fb->pixel_format) {
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ARGB8888:
+ v4l2_fmt = V4L2_PIX_FMT_RGB32;
+ bpp = 32;
+ break;
+ case DRM_FORMAT_RGB565:
+ v4l2_fmt = V4L2_PIX_FMT_RGB565;
+ bpp = 16;
+ break;
+ case DRM_FORMAT_RGB888:
+ v4l2_fmt = V4L2_PIX_FMT_RGB24;
+ bpp = 24;
+ break;
+ default:
+ dev_err(ipu_crtc->dev, "unsupported pixel format 0x%08x\n",
+ fb->pixel_format);
+ return -EINVAL;
+ }
+
+ out_pixel_fmt = ipu_crtc->interface_pix_fmt;
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ sig_cfg.interlaced = 1;
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ sig_cfg.Hsync_pol = 1;
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ sig_cfg.Vsync_pol = 1;
+
+ sig_cfg.enable_pol = 1;
+ sig_cfg.clk_pol = 0;
+ sig_cfg.width = mode->hdisplay;
+ sig_cfg.height = mode->vdisplay;
+ sig_cfg.pixel_fmt = out_pixel_fmt;
+ sig_cfg.h_start_width = mode->htotal - mode->hsync_end;
+ sig_cfg.h_sync_width = mode->hsync_end - mode->hsync_start;
+ sig_cfg.h_end_width = mode->hsync_start - mode->hdisplay;
+
+ sig_cfg.v_start_width = mode->vtotal - mode->vsync_end;
+ sig_cfg.v_sync_width = mode->vsync_end - mode->vsync_start;
+ sig_cfg.v_end_width = mode->vsync_start - mode->vdisplay;
+ sig_cfg.pixelclock = mode->clock * 1000;
+ sig_cfg.clkflags = ipu_crtc->di_clkflags;
+
+ sig_cfg.v_to_h_sync = 0;
+
+ if (ipu_crtc->dp) {
+ ret = ipu_dp_setup_channel(ipu_crtc->dp, IPUV3_COLORSPACE_RGB,
+ IPUV3_COLORSPACE_RGB);
+ if (ret) {
+ dev_err(ipu_crtc->dev,
+ "initializing display processor failed with %d\n",
+ ret);
+ return ret;
+ }
+ ipu_dp_set_global_alpha(ipu_crtc->dp, 1, 0, 1);
+ }
+
+ ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, sig_cfg.interlaced,
+ out_pixel_fmt, mode->hdisplay);
+ if (ret) {
+ dev_err(ipu_crtc->dev,
+ "initializing display controller failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg);
+ if (ret) {
+ dev_err(ipu_crtc->dev,
+ "initializing panel failed with %d\n", ret);
+ return ret;
+ }
+
+ ipu_cpmem_set_resolution(cpmem, mode->hdisplay, mode->vdisplay);
+ ipu_cpmem_set_fmt(cpmem, v4l2_fmt);
+ ipu_cpmem_set_high_priority(ipu_crtc->ipu_ch);
+
+ ret = ipu_dmfc_init_channel(ipu_crtc->dmfc, mode->hdisplay);
+ if (ret) {
+ dev_err(ipu_crtc->dev,
+ "initializing dmfc channel failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ipu_dmfc_alloc_bandwidth(ipu_crtc->dmfc,
+ calc_bandwidth(mode, calc_vref(mode)), 64);
+ if (ret) {
+ dev_err(ipu_crtc->dev,
+ "allocating dmfc bandwidth failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ipu_drm_set_base(crtc, x, y);
+
+ return 0;
+}
+
+static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc)
+{
+ struct drm_pending_vblank_event *e;
+ struct timeval now;
+ unsigned long flags;
+ struct drm_device *drm = ipu_crtc->base.dev;
+
+ spin_lock_irqsave(&drm->event_lock, flags);
+
+ e = ipu_crtc->page_flip_event;
+ if (!e) {
+ spin_unlock_irqrestore(&drm->event_lock, flags);
+ return;
+ }
+
+ do_gettimeofday(&now);
+ e->event.sequence = 0;
+ e->event.tv_sec = now.tv_sec;
+ e->event.tv_usec = now.tv_usec;
+ ipu_crtc->page_flip_event = NULL;
+
+ imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc);
+
+ list_add_tail(&e->base.link, &e->base.file_priv->event_list);
+
+ wake_up_interruptible(&e->base.file_priv->event_wait);
+
+ spin_unlock_irqrestore(&drm->event_lock, flags);
+}
+
+static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
+{
+ struct ipu_crtc *ipu_crtc = dev_id;
+
+ imx_drm_handle_vblank(ipu_crtc->imx_crtc);
+
+ if (ipu_crtc->newfb) {
+ ipu_crtc->base.fb = ipu_crtc->newfb;
+ ipu_crtc->newfb = NULL;
+ ipu_drm_set_base(&ipu_crtc->base, 0, 0);
+ ipu_crtc_handle_pageflip(ipu_crtc);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void ipu_crtc_prepare(struct drm_crtc *crtc)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+ ipu_fb_disable(ipu_crtc);
+}
+
+static void ipu_crtc_commit(struct drm_crtc *crtc)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+ ipu_fb_enable(ipu_crtc);
+}
+
+static void ipu_crtc_load_lut(struct drm_crtc *crtc)
+{
+}
+
+static struct drm_crtc_helper_funcs ipu_helper_funcs = {
+ .dpms = ipu_crtc_dpms,
+ .mode_fixup = ipu_crtc_mode_fixup,
+ .mode_set = ipu_crtc_mode_set,
+ .prepare = ipu_crtc_prepare,
+ .commit = ipu_crtc_commit,
+ .load_lut = ipu_crtc_load_lut,
+};
+
+static int ipu_enable_vblank(struct drm_crtc *crtc)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+ enable_irq(ipu_crtc->irq);
+
+ return 0;
+}
+
+static void ipu_disable_vblank(struct drm_crtc *crtc)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+ disable_irq(ipu_crtc->irq);
+}
+
+static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type,
+ u32 pixfmt)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+ ipu_crtc->interface_pix_fmt = pixfmt;
+
+ switch (encoder_type) {
+ case DRM_MODE_ENCODER_LVDS:
+ ipu_crtc->di_clkflags = IPU_DI_CLKMODE_SYNC |
+ IPU_DI_CLKMODE_EXT;
+ break;
+ case DRM_MODE_ENCODER_NONE:
+ ipu_crtc->di_clkflags = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct imx_drm_crtc_helper_funcs ipu_crtc_helper_funcs = {
+ .enable_vblank = ipu_enable_vblank,
+ .disable_vblank = ipu_disable_vblank,
+ .set_interface_pix_fmt = ipu_set_interface_pix_fmt,
+ .crtc_funcs = &ipu_crtc_funcs,
+ .crtc_helper_funcs = &ipu_helper_funcs,
+};
+
+static void ipu_put_resources(struct ipu_crtc *ipu_crtc)
+{
+ if (!IS_ERR_OR_NULL(ipu_crtc->ipu_ch))
+ ipu_idmac_put(ipu_crtc->ipu_ch);
+ if (!IS_ERR_OR_NULL(ipu_crtc->dmfc))
+ ipu_dmfc_put(ipu_crtc->dmfc);
+ if (!IS_ERR_OR_NULL(ipu_crtc->dp))
+ ipu_dp_put(ipu_crtc->dp);
+ if (!IS_ERR_OR_NULL(ipu_crtc->di))
+ ipu_di_put(ipu_crtc->di);
+}
+
+static int ipu_get_resources(struct ipu_crtc *ipu_crtc,
+ struct ipu_client_platformdata *pdata)
+{
+ struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
+ int ret;
+
+ ipu_crtc->ipu_ch = ipu_idmac_get(ipu, pdata->dma[0]);
+ if (IS_ERR_OR_NULL(ipu_crtc->ipu_ch)) {
+ ret = PTR_ERR(ipu_crtc->ipu_ch);
+ goto err_out;
+ }
+
+ ipu_crtc->dc = ipu_dc_get(ipu, pdata->dc);
+ if (IS_ERR(ipu_crtc->dc)) {
+ ret = PTR_ERR(ipu_crtc->dc);
+ goto err_out;
+ }
+
+ ipu_crtc->dmfc = ipu_dmfc_get(ipu, pdata->dma[0]);
+ if (IS_ERR(ipu_crtc->dmfc)) {
+ ret = PTR_ERR(ipu_crtc->dmfc);
+ goto err_out;
+ }
+
+ if (pdata->dp >= 0) {
+ ipu_crtc->dp = ipu_dp_get(ipu, pdata->dp);
+ if (IS_ERR(ipu_crtc->dp)) {
+ ret = PTR_ERR(ipu_crtc->ipu_ch);
+ goto err_out;
+ }
+ }
+
+ ipu_crtc->di = ipu_di_get(ipu, pdata->di);
+ if (IS_ERR(ipu_crtc->di)) {
+ ret = PTR_ERR(ipu_crtc->di);
+ goto err_out;
+ }
+
+ ipu_crtc->irq = ipu_idmac_channel_irq(ipu, ipu_crtc->ipu_ch,
+ IPU_IRQ_EOF);
+ ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0,
+ "imx_drm", ipu_crtc);
+ if (ret < 0) {
+ dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret);
+ goto err_out;
+ }
+
+ disable_irq(ipu_crtc->irq);
+
+ return 0;
+err_out:
+ ipu_put_resources(ipu_crtc);
+
+ return ret;
+}
+
+static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
+ struct ipu_client_platformdata *pdata)
+{
+ int ret;
+
+ ret = ipu_get_resources(ipu_crtc, pdata);
+ if (ret) {
+ dev_err(ipu_crtc->dev, "getting resources failed with %d.\n",
+ ret);
+ return ret;
+ }
+
+ ret = imx_drm_add_crtc(&ipu_crtc->base,
+ &ipu_crtc->imx_crtc,
+ &ipu_crtc_helper_funcs, THIS_MODULE,
+ ipu_crtc->dev->parent->of_node, pdata->di);
+ if (ret) {
+ dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret);
+ goto err_put_resources;
+ }
+
+ return 0;
+
+err_put_resources:
+ ipu_put_resources(ipu_crtc);
+
+ return ret;
+}
+
+static int __devinit ipu_drm_probe(struct platform_device *pdev)
+{
+ struct ipu_client_platformdata *pdata = pdev->dev.platform_data;
+ struct ipu_crtc *ipu_crtc;
+ int ret;
+
+ if (!pdata)
+ return -EINVAL;
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ipu_crtc = devm_kzalloc(&pdev->dev, sizeof(*ipu_crtc), GFP_KERNEL);
+ if (!ipu_crtc)
+ return -ENOMEM;
+
+ ipu_crtc->dev = &pdev->dev;
+
+ ret = ipu_crtc_init(ipu_crtc, pdata);
+
+ platform_set_drvdata(pdev, ipu_crtc);
+
+ return 0;
+}
+
+static int __devexit ipu_drm_remove(struct platform_device *pdev)
+{
+ struct ipu_crtc *ipu_crtc = platform_get_drvdata(pdev);
+
+ imx_drm_remove_crtc(ipu_crtc->imx_crtc);
+
+ ipu_put_resources(ipu_crtc);
+
+ return 0;
+}
+
+static struct platform_driver ipu_drm_driver = {
+ .driver = {
+ .name = "imx-ipuv3-crtc",
+ },
+ .probe = ipu_drm_probe,
+ .remove = __devexit_p(ipu_drm_remove),
+};
+module_platform_driver(ipu_drm_driver);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/imx-drm/parallel-display.c b/drivers/staging/imx-drm/parallel-display.c
new file mode 100644
index 000000000000..9b51d732eefa
--- /dev/null
+++ b/drivers/staging/imx-drm/parallel-display.c
@@ -0,0 +1,261 @@
+/*
+ * i.MX drm driver - parallel display implementation
+ *
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/videodev2.h>
+
+#include "imx-drm.h"
+
+#define con_to_imxpd(x) container_of(x, struct imx_parallel_display, connector)
+#define enc_to_imxpd(x) container_of(x, struct imx_parallel_display, encoder)
+
+struct imx_parallel_display {
+ struct drm_connector connector;
+ struct imx_drm_connector *imx_drm_connector;
+ struct drm_encoder encoder;
+ struct imx_drm_encoder *imx_drm_encoder;
+ struct device *dev;
+ void *edid;
+ int edid_len;
+ u32 interface_pix_fmt;
+ int mode_valid;
+ struct drm_display_mode mode;
+};
+
+static enum drm_connector_status imx_pd_connector_detect(
+ struct drm_connector *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static void imx_pd_connector_destroy(struct drm_connector *connector)
+{
+ /* do not free here */
+}
+
+static int imx_pd_connector_get_modes(struct drm_connector *connector)
+{
+ struct imx_parallel_display *imxpd = con_to_imxpd(connector);
+ int num_modes = 0;
+
+ if (imxpd->edid) {
+ drm_mode_connector_update_edid_property(connector, imxpd->edid);
+ num_modes = drm_add_edid_modes(connector, imxpd->edid);
+ }
+
+ if (imxpd->mode_valid) {
+ struct drm_display_mode *mode = drm_mode_create(connector->dev);
+ drm_mode_copy(mode, &imxpd->mode);
+ mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ drm_mode_probed_add(connector, mode);
+ num_modes++;
+ }
+
+ return num_modes;
+}
+
+static int imx_pd_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return 0;
+}
+
+static struct drm_encoder *imx_pd_connector_best_encoder(
+ struct drm_connector *connector)
+{
+ struct imx_parallel_display *imxpd = con_to_imxpd(connector);
+
+ return &imxpd->encoder;
+}
+
+static void imx_pd_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool imx_pd_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void imx_pd_encoder_prepare(struct drm_encoder *encoder)
+{
+ struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
+
+ imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_NONE,
+ imxpd->interface_pix_fmt);
+}
+
+static void imx_pd_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static void imx_pd_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void imx_pd_encoder_disable(struct drm_encoder *encoder)
+{
+}
+
+static void imx_pd_encoder_destroy(struct drm_encoder *encoder)
+{
+ /* do not free here */
+}
+
+static struct drm_connector_funcs imx_pd_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = imx_pd_connector_detect,
+ .destroy = imx_pd_connector_destroy,
+};
+
+static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = {
+ .get_modes = imx_pd_connector_get_modes,
+ .best_encoder = imx_pd_connector_best_encoder,
+ .mode_valid = imx_pd_connector_mode_valid,
+};
+
+static struct drm_encoder_funcs imx_pd_encoder_funcs = {
+ .destroy = imx_pd_encoder_destroy,
+};
+
+static struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = {
+ .dpms = imx_pd_encoder_dpms,
+ .mode_fixup = imx_pd_encoder_mode_fixup,
+ .prepare = imx_pd_encoder_prepare,
+ .commit = imx_pd_encoder_commit,
+ .mode_set = imx_pd_encoder_mode_set,
+ .disable = imx_pd_encoder_disable,
+};
+
+static int imx_pd_register(struct imx_parallel_display *imxpd)
+{
+ int ret;
+
+ drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder);
+
+ imxpd->connector.funcs = &imx_pd_connector_funcs;
+ imxpd->encoder.funcs = &imx_pd_encoder_funcs;
+
+ imxpd->encoder.encoder_type = DRM_MODE_ENCODER_NONE;
+ imxpd->connector.connector_type = DRM_MODE_CONNECTOR_VGA;
+
+ drm_encoder_helper_add(&imxpd->encoder, &imx_pd_encoder_helper_funcs);
+ ret = imx_drm_add_encoder(&imxpd->encoder, &imxpd->imx_drm_encoder,
+ THIS_MODULE);
+ if (ret) {
+ dev_err(imxpd->dev, "adding encoder failed with %d\n", ret);
+ return ret;
+ }
+
+ drm_connector_helper_add(&imxpd->connector,
+ &imx_pd_connector_helper_funcs);
+
+ ret = imx_drm_add_connector(&imxpd->connector,
+ &imxpd->imx_drm_connector, THIS_MODULE);
+ if (ret) {
+ imx_drm_remove_encoder(imxpd->imx_drm_encoder);
+ dev_err(imxpd->dev, "adding connector failed with %d\n", ret);
+ return ret;
+ }
+
+ imxpd->connector.encoder = &imxpd->encoder;
+
+ return 0;
+}
+
+static int __devinit imx_pd_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const u8 *edidp;
+ struct imx_parallel_display *imxpd;
+ int ret;
+ const char *fmt;
+
+ imxpd = devm_kzalloc(&pdev->dev, sizeof(*imxpd), GFP_KERNEL);
+ if (!imxpd)
+ return -ENOMEM;
+
+ edidp = of_get_property(np, "edid", &imxpd->edid_len);
+ if (edidp)
+ imxpd->edid = kmemdup(edidp, imxpd->edid_len, GFP_KERNEL);
+
+ ret = of_property_read_string(np, "interface-pix-fmt", &fmt);
+ if (!ret) {
+ if (!strcmp(fmt, "rgb24"))
+ imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB24;
+ else if (!strcmp(fmt, "rgb565"))
+ imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB565;
+ }
+
+ imxpd->dev = &pdev->dev;
+
+ ret = imx_pd_register(imxpd);
+ if (ret)
+ return ret;
+
+ ret = imx_drm_encoder_add_possible_crtcs(imxpd->imx_drm_encoder, np);
+
+ platform_set_drvdata(pdev, imxpd);
+
+ return 0;
+}
+
+static int __devexit imx_pd_remove(struct platform_device *pdev)
+{
+ struct imx_parallel_display *imxpd = platform_get_drvdata(pdev);
+ struct drm_connector *connector = &imxpd->connector;
+ struct drm_encoder *encoder = &imxpd->encoder;
+
+ drm_mode_connector_detach_encoder(connector, encoder);
+
+ imx_drm_remove_connector(imxpd->imx_drm_connector);
+ imx_drm_remove_encoder(imxpd->imx_drm_encoder);
+
+ return 0;
+}
+
+static const struct of_device_id imx_pd_dt_ids[] = {
+ { .compatible = "fsl,imx-parallel-display", },
+ { /* sentinel */ }
+};
+
+static struct platform_driver imx_pd_driver = {
+ .probe = imx_pd_probe,
+ .remove = __devexit_p(imx_pd_remove),
+ .driver = {
+ .of_match_table = imx_pd_dt_ids,
+ .name = "imx-parallel-display",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(imx_pd_driver);
+
+MODULE_DESCRIPTION("i.MX parallel display driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");