All of lore.kernel.org
 help / color / mirror / Atom feed
* No subject
@ 2012-05-18 12:27 ` Sascha Hauer
  0 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-18 12:27 UTC (permalink / raw)
  To: linux-arm-kernel

Hi All,

The following adds a drm/kms driver for the Freescale i.MX LCDC
controller. Most notable change to the last SDRM based version is that
the SDRM layer has been removed and the driver now is purely i.MX
specific. I hope that this is more acceptable now.

Another change is that the probe is now devicetree based. For now I
took the easy way out and only put an edid blob into the devicetree.
I haven't documented the binding yet, I would add that when the rest
is considered ok.

Comments very welcome.

Thanks
 Sascha

----------------------------------------------------------------
Sascha Hauer (2):
      DRM: add Freescale i.MX LCDC driver
      pcm038 lcdc support

 arch/arm/boot/dts/imx27-phytec-phycore.dts |   39 ++
 arch/arm/boot/dts/imx27.dtsi               |    7 +
 arch/arm/mach-imx/clock-imx27.c            |    1 +
 drivers/gpu/drm/Kconfig                    |    2 +
 drivers/gpu/drm/Makefile                   |    1 +
 drivers/gpu/drm/imx/Kconfig                |   18 +
 drivers/gpu/drm/imx/Makefile               |    8 +
 drivers/gpu/drm/imx/imx-drm-core.c         |  745 ++++++++++++++++++++++++++++
 drivers/gpu/drm/imx/imx-fb.c               |  179 +++++++
 drivers/gpu/drm/imx/imx-fbdev.c            |  275 ++++++++++
 drivers/gpu/drm/imx/imx-gem.c              |  343 +++++++++++++
 drivers/gpu/drm/imx/imx-lcdc-crtc.c        |  517 +++++++++++++++++++
 drivers/gpu/drm/imx/imx-parallel-display.c |  228 +++++++++
 13 files changed, 2363 insertions(+)
 create mode 100644 drivers/gpu/drm/imx/Kconfig
 create mode 100644 drivers/gpu/drm/imx/Makefile
 create mode 100644 drivers/gpu/drm/imx/imx-drm-core.c
 create mode 100644 drivers/gpu/drm/imx/imx-fb.c
 create mode 100644 drivers/gpu/drm/imx/imx-fbdev.c
 create mode 100644 drivers/gpu/drm/imx/imx-gem.c
 create mode 100644 drivers/gpu/drm/imx/imx-lcdc-crtc.c
 create mode 100644 drivers/gpu/drm/imx/imx-parallel-display.c

^ permalink raw reply	[flat|nested] 26+ messages in thread

* (unknown), 
@ 2012-05-18 12:27 ` Sascha Hauer
  0 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-18 12:27 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-arm-kernel

Hi All,

The following adds a drm/kms driver for the Freescale i.MX LCDC
controller. Most notable change to the last SDRM based version is that
the SDRM layer has been removed and the driver now is purely i.MX
specific. I hope that this is more acceptable now.

Another change is that the probe is now devicetree based. For now I
took the easy way out and only put an edid blob into the devicetree.
I haven't documented the binding yet, I would add that when the rest
is considered ok.

Comments very welcome.

Thanks
 Sascha

----------------------------------------------------------------
Sascha Hauer (2):
      DRM: add Freescale i.MX LCDC driver
      pcm038 lcdc support

 arch/arm/boot/dts/imx27-phytec-phycore.dts |   39 ++
 arch/arm/boot/dts/imx27.dtsi               |    7 +
 arch/arm/mach-imx/clock-imx27.c            |    1 +
 drivers/gpu/drm/Kconfig                    |    2 +
 drivers/gpu/drm/Makefile                   |    1 +
 drivers/gpu/drm/imx/Kconfig                |   18 +
 drivers/gpu/drm/imx/Makefile               |    8 +
 drivers/gpu/drm/imx/imx-drm-core.c         |  745 ++++++++++++++++++++++++++++
 drivers/gpu/drm/imx/imx-fb.c               |  179 +++++++
 drivers/gpu/drm/imx/imx-fbdev.c            |  275 ++++++++++
 drivers/gpu/drm/imx/imx-gem.c              |  343 +++++++++++++
 drivers/gpu/drm/imx/imx-lcdc-crtc.c        |  517 +++++++++++++++++++
 drivers/gpu/drm/imx/imx-parallel-display.c |  228 +++++++++
 13 files changed, 2363 insertions(+)
 create mode 100644 drivers/gpu/drm/imx/Kconfig
 create mode 100644 drivers/gpu/drm/imx/Makefile
 create mode 100644 drivers/gpu/drm/imx/imx-drm-core.c
 create mode 100644 drivers/gpu/drm/imx/imx-fb.c
 create mode 100644 drivers/gpu/drm/imx/imx-fbdev.c
 create mode 100644 drivers/gpu/drm/imx/imx-gem.c
 create mode 100644 drivers/gpu/drm/imx/imx-lcdc-crtc.c
 create mode 100644 drivers/gpu/drm/imx/imx-parallel-display.c

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 1/2] DRM: add Freescale i.MX LCDC driver
  2012-05-18 12:27 ` (unknown), Sascha Hauer
@ 2012-05-18 12:27   ` Sascha Hauer
  -1 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-18 12:27 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/Kconfig                    |    2 +
 drivers/gpu/drm/Makefile                   |    1 +
 drivers/gpu/drm/imx/Kconfig                |   18 +
 drivers/gpu/drm/imx/Makefile               |    8 +
 drivers/gpu/drm/imx/imx-drm-core.c         |  745 ++++++++++++++++++++++++++++
 drivers/gpu/drm/imx/imx-fb.c               |  179 +++++++
 drivers/gpu/drm/imx/imx-fbdev.c            |  275 ++++++++++
 drivers/gpu/drm/imx/imx-gem.c              |  343 +++++++++++++
 drivers/gpu/drm/imx/imx-lcdc-crtc.c        |  517 +++++++++++++++++++
 drivers/gpu/drm/imx/imx-parallel-display.c |  228 +++++++++
 10 files changed, 2316 insertions(+)
 create mode 100644 drivers/gpu/drm/imx/Kconfig
 create mode 100644 drivers/gpu/drm/imx/Makefile
 create mode 100644 drivers/gpu/drm/imx/imx-drm-core.c
 create mode 100644 drivers/gpu/drm/imx/imx-fb.c
 create mode 100644 drivers/gpu/drm/imx/imx-fbdev.c
 create mode 100644 drivers/gpu/drm/imx/imx-gem.c
 create mode 100644 drivers/gpu/drm/imx/imx-lcdc-crtc.c
 create mode 100644 drivers/gpu/drm/imx/imx-parallel-display.c

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index e354bc0..759502c 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -186,3 +186,5 @@ source "drivers/gpu/drm/vmwgfx/Kconfig"
 source "drivers/gpu/drm/gma500/Kconfig"
 
 source "drivers/gpu/drm/udl/Kconfig"
+
+source "drivers/gpu/drm/imx/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index c20da5b..6569d8d 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -42,4 +42,5 @@ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
 obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
+obj-$(CONFIG_DRM_IMX) += imx/
 obj-y			+= i2c/
diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig
new file mode 100644
index 0000000..5fc3a44
--- /dev/null
+++ b/drivers/gpu/drm/imx/Kconfig
@@ -0,0 +1,18 @@
+config DRM_IMX
+	tristate "DRM Support for Freescale i.MX"
+	select DRM_KMS_HELPER
+	depends on DRM && ARCH_MXC
+
+config DRM_IMX_FB_HELPER
+	tristate "provide legacy framebuffer /dev/fb0"
+	depends on DRM_IMX
+
+config DRM_IMX_LCDC
+	tristate "DRM Support for Freescale i.MX1 and i.MX2"
+	depends on DRM_IMX
+	help
+	  Choose this if you have a i.MX1, i.MX21, i.MX25 or i.MX27 processor.
+
+config DRM_IMX_PARALLEL_DISPLAY
+	tristate "Support for parallel displays"
+	depends on DRM_IMX
diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
new file mode 100644
index 0000000..0f7c038
--- /dev/null
+++ b/drivers/gpu/drm/imx/Makefile
@@ -0,0 +1,8 @@
+
+imxdrm-objs := imx-drm-core.o imx-fb.o imx-gem.o
+
+obj-$(CONFIG_DRM_IMX) += imxdrm.o
+
+obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += imx-parallel-display.o
+obj-$(CONFIG_DRM_IMX_LCDC) += imx-lcdc-crtc.o
+obj-$(CONFIG_DRM_IMX_FB_HELPER) += imx-fbdev.o
diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
new file mode 100644
index 0000000..29f5f10
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-drm-core.c
@@ -0,0 +1,745 @@
+/*
+ * simple 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 <asm/fb.h>
+#include <linux/module.h>
+
+#include "imx-drm.h"
+
+#define MAX_CRTC	4
+
+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;
+};
+
+struct imx_drm_crtc {
+	struct drm_crtc				*crtc;
+	struct list_head			list;
+	struct imx_drm_device			*imxdrm;
+	int					pipe;
+	struct drm_crtc_helper_funcs		crtc_helper_funcs;
+	struct drm_crtc_funcs			crtc_funcs;
+	struct imx_drm_crtc_helper_funcs	imx_drm_helper_funcs;
+	struct module				*owner;
+};
+
+struct imx_drm_encoder {
+	struct drm_encoder			*encoder;
+	struct list_head			list;
+	struct module				*owner;
+};
+
+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)
+{
+	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_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 struct vm_operations_struct imx_drm_gem_vm_ops = {
+	.fault = imx_drm_gem_fault,
+	.open = drm_gem_vm_open,
+	.close = drm_gem_vm_close,
+};
+
+static const struct file_operations imx_drm_driver_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.release = drm_release,
+	.unlocked_ioctl = drm_ioctl,
+	.mmap = imx_drm_gem_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 imx_drm_device->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();
+
+	drm_encoder_init(imxdrm->drm, imx_drm_encoder->encoder,
+			imx_drm_encoder->encoder->funcs,
+			DRM_MODE_ENCODER_TMDS);
+
+	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();
+	int ret;
+
+	drm_connector_init(imxdrm->drm, imx_drm_connector->connector,
+			imx_drm_connector->connector->funcs,
+			DRM_MODE_CONNECTOR_VGA);
+	drm_mode_group_reinit(imxdrm->drm);
+	ret = drm_sysfs_connector_add(imx_drm_connector->connector);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+
+	return ret;
+}
+
+/*
+ * 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->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->crtc_helper_funcs);
+
+	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;
+}
+
+/*
+ * 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 drm_crtc_funcs *crtc_funcs,
+		const struct drm_crtc_helper_funcs *crtc_helper_funcs,
+		const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs,
+		struct module *owner)
+{
+	struct imx_drm_device *imxdrm = __imx_drm_device();
+	struct imx_drm_crtc *imx_drm_crtc;
+	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->crtc_funcs = *crtc_funcs;
+	imx_drm_crtc->crtc_helper_funcs = *crtc_helper_funcs;
+	imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs;
+
+	WARN_ON(crtc_funcs->set_config);
+	WARN_ON(crtc_funcs->destroy);
+
+	imx_drm_crtc->crtc_funcs.set_config = drm_crtc_helper_set_config;
+	imx_drm_crtc->crtc_funcs.destroy = drm_crtc_cleanup;
+
+	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;
+
+	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);
+
+	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(struct 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);
+
+/*
+ * 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();
+
+	mutex_lock(&imxdrm->mutex);
+
+	imx_drm_encoder_unregister(imx_drm_encoder);
+
+	list_del(&imx_drm_encoder->list);
+
+	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(struct 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);
+
+/*
+ * 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	= imx_drm_gem_free_object,
+	.gem_vm_ops		= &imx_drm_gem_vm_ops,
+	.dumb_create		= imx_drm_gem_dumb_create,
+	.dumb_map_offset	= imx_drm_gem_dumb_map_offset,
+	.dumb_destroy		= imx_drm_gem_dumb_destroy,
+
+	.get_vblank_counter	= drm_vblank_count,
+	.enable_vblank		= imx_drm_enable_vblank,
+	.disable_vblank		= imx_drm_disable_vblank,
+	.reclaim_buffers	= drm_core_reclaim_buffers,
+	.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_pdev:
+	kfree(imx_drm_device);
+err_pdrv:
+	platform_device_unregister(imx_drm_pdev);
+
+	return ret;
+}
+
+static void __exit imx_drm_exit(void)
+{
+	DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+	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/gpu/drm/imx/imx-fb.c b/drivers/gpu/drm/imx/imx-fb.c
new file mode 100644
index 0000000..5a08c86
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-fb.c
@@ -0,0 +1,179 @@
+/*
+ * 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 "imx-drm.h"
+
+#define to_imx_drm_fb(x)	container_of(x, struct imx_drm_fb, fb)
+
+/*
+ * imx specific framebuffer structure.
+ *
+ * @fb: drm framebuffer obejct.
+ * @imx_drm_gem_obj: drm ec specific gem object containing a gem object.
+ * @entry: pointer to ec drm buffer entry object.
+ *	- containing only the information to physically continuous memory
+ *	region allocated at default framebuffer creation.
+ */
+struct imx_drm_fb {
+	struct drm_framebuffer		fb;
+	struct imx_drm_gem_obj	*imx_drm_gem_obj;
+	struct imx_drm_buf_entry	*entry;
+};
+
+static void imx_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct imx_drm_fb *imx_drm_fb = to_imx_drm_fb(fb);
+
+	drm_framebuffer_cleanup(fb);
+
+	/*
+	 * default framebuffer has no gem object so
+	 * a buffer of the default framebuffer should be released at here.
+	 */
+	if (!imx_drm_fb->imx_drm_gem_obj && imx_drm_fb->entry)
+		imx_drm_buf_destroy(fb->dev, imx_drm_fb->entry);
+
+	kfree(imx_drm_fb);
+}
+
+static int imx_drm_fb_create_handle(struct drm_framebuffer *fb,
+		struct drm_file *file_priv, unsigned int *handle)
+{
+	struct imx_drm_fb *imx_drm_fb = to_imx_drm_fb(fb);
+
+	return drm_gem_handle_create(file_priv,
+			&imx_drm_fb->imx_drm_gem_obj->base, handle);
+}
+
+static struct drm_framebuffer_funcs imx_drm_fb_funcs = {
+	.destroy	= imx_drm_fb_destroy,
+	.create_handle	= imx_drm_fb_create_handle,
+};
+
+static struct drm_framebuffer *imx_drm_fb_create(struct drm_device *dev,
+		struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	struct imx_drm_fb *imx_drm_fb;
+	struct drm_framebuffer *fb;
+	struct drm_gem_object *obj;
+	unsigned int size;
+	int ret;
+	u32 bpp, depth;
+
+	drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp);
+
+	mode_cmd->pitches[0] = max(mode_cmd->pitches[0],
+			mode_cmd->width * (bpp >> 3));
+
+	dev_dbg(dev->dev, "drm fb create(%dx%d)\n",
+			mode_cmd->width, mode_cmd->height);
+
+	imx_drm_fb = kzalloc(sizeof(*imx_drm_fb), GFP_KERNEL);
+	if (!imx_drm_fb) {
+		dev_err(dev->dev, "failed to allocate drm framebuffer.\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	fb = &imx_drm_fb->fb;
+	ret = drm_framebuffer_init(dev, fb, &imx_drm_fb_funcs);
+	if (ret) {
+		dev_err(dev->dev, "failed to initialize framebuffer.\n");
+		goto err_init;
+	}
+
+	dev_dbg(dev->dev, "create: fb id: %d\n", fb->base.id);
+
+	size = mode_cmd->pitches[0] * mode_cmd->height;
+
+	/*
+	 * without file_priv we are called from imx_drm_fbdev_create in which
+	 * case we only create a framebuffer without a handle.
+	 */
+	if (!file_priv) {
+		struct imx_drm_buf_entry *entry;
+
+		entry = imx_drm_buf_create(dev, size);
+		if (IS_ERR(entry)) {
+			ret = PTR_ERR(entry);
+			goto err_buffer;
+		}
+
+		imx_drm_fb->entry = entry;
+	} else {
+		obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
+		if (!obj) {
+			ret = -EINVAL;
+			goto err_buffer;
+		}
+
+		imx_drm_fb->imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
+
+		drm_gem_object_unreference_unlocked(obj);
+
+		imx_drm_fb->entry = imx_drm_fb->imx_drm_gem_obj->entry;
+	}
+
+	drm_helper_mode_fill_fb_struct(fb, mode_cmd);
+
+	return fb;
+
+err_buffer:
+	drm_framebuffer_cleanup(fb);
+
+err_init:
+	kfree(imx_drm_fb);
+
+	return ERR_PTR(ret);
+}
+
+struct imx_drm_buf_entry *imx_drm_fb_get_buf(struct drm_framebuffer *fb)
+{
+	struct imx_drm_fb *imx_drm_fb = to_imx_drm_fb(fb);
+	struct imx_drm_buf_entry *entry;
+
+	entry = imx_drm_fb->entry;
+
+	return entry;
+}
+EXPORT_SYMBOL_GPL(imx_drm_fb_get_buf);
+
+static struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
+	.fb_create = imx_drm_fb_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/gpu/drm/imx/imx-fbdev.c b/drivers/gpu/drm/imx/imx-fbdev.c
new file mode 100644
index 0000000..f038797
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-fbdev.c
@@ -0,0 +1,275 @@
+/*
+ * 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_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "imx-drm.h"
+
+#define MAX_CONNECTOR		4
+#define PREFERRED_BPP		16
+
+static struct fb_ops imx_drm_fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+	.fb_check_var	= drm_fb_helper_check_var,
+	.fb_set_par	= drm_fb_helper_set_par,
+	.fb_blank	= drm_fb_helper_blank,
+	.fb_pan_display	= drm_fb_helper_pan_display,
+	.fb_setcmap	= drm_fb_helper_setcmap,
+};
+
+static int imx_drm_fbdev_update(struct drm_fb_helper *helper,
+				     struct drm_framebuffer *fb,
+				     unsigned int fb_width,
+				     unsigned int fb_height)
+{
+	struct fb_info *fbi = helper->fbdev;
+	struct drm_device *drm = helper->dev;
+	struct imx_drm_buf_entry *entry;
+	unsigned int size = fb_width * fb_height * (fb->bits_per_pixel >> 3);
+	unsigned long offset;
+
+	drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
+	drm_fb_helper_fill_var(fbi, helper, fb_width, fb_height);
+
+	entry = imx_drm_fb_get_buf(fb);
+	if (!entry) {
+		dev_dbg(drm->dev, "entry is null.\n");
+		return -EFAULT;
+	}
+
+	offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
+	offset += fbi->var.yoffset * fb->pitches[0];
+
+	drm->mode_config.fb_base = entry->paddr;
+	fbi->screen_base = entry->vaddr + offset;
+	fbi->fix.smem_start = entry->paddr + offset;
+	fbi->screen_size = size;
+	fbi->fix.smem_len = size;
+	fbi->flags |= FBINFO_CAN_FORCE_OUTPUT;
+
+	return 0;
+}
+
+static int imx_drm_fbdev_create(struct drm_fb_helper *helper,
+				    struct drm_fb_helper_surface_size *sizes)
+{
+	struct drm_device *drm = helper->dev;
+	struct fb_info *fbi;
+	struct drm_framebuffer *fb;
+	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+	struct platform_device *pdev = drm->platformdev;
+	int ret;
+
+	dev_dbg(drm->dev, "surface width(%d), height(%d) and bpp(%d\n",
+			sizes->surface_width, sizes->surface_height,
+			sizes->surface_bpp);
+
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.height = sizes->surface_height;
+	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+			sizes->surface_depth);
+
+	mutex_lock(&drm->struct_mutex);
+
+	fbi = framebuffer_alloc(0, &pdev->dev);
+	if (!fbi) {
+		ret = -ENOMEM;
+		goto err_fb_alloc;
+	}
+
+	fb = drm->mode_config.funcs->fb_create(drm, NULL, &mode_cmd);
+	if (IS_ERR(fb)) {
+		dev_err(drm->dev, "failed to create drm framebuffer.\n");
+		ret = PTR_ERR(fb);
+		goto err_fb_create;
+	}
+
+	helper->fb = fb;
+	helper->fbdev = fbi;
+
+	fbi->par = helper;
+	fbi->flags = FBINFO_FLAG_DEFAULT;
+	fbi->fbops = &imx_drm_fb_ops;
+
+	ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+	if (ret)
+		goto err_alloc_cmap;
+
+	ret = imx_drm_fbdev_update(helper, helper->fb, sizes->fb_width,
+			sizes->fb_height);
+	if (ret)
+		goto err_fbdev_update;
+
+	mutex_unlock(&drm->struct_mutex);
+
+	return 0;
+
+err_fbdev_update:
+	fb_dealloc_cmap(&fbi->cmap);
+
+err_alloc_cmap:
+	fb->funcs->destroy(fb);
+
+err_fb_create:
+	framebuffer_release(fbi);
+
+err_fb_alloc:
+	mutex_unlock(&drm->struct_mutex);
+
+	return ret;
+}
+
+static int imx_drm_fbdev_probe(struct drm_fb_helper *helper,
+				   struct drm_fb_helper_surface_size *sizes)
+{
+	int ret;
+
+	BUG_ON(helper->fb);
+
+	ret = imx_drm_fbdev_create(helper, sizes);
+	if (ret) {
+		dev_err(helper->dev->dev, "creating fbdev failed with %d\n", ret);
+		return ret;
+	}
+
+	/*
+	 * fb_helper expects a value more than 1 if succeed
+	 * because register_framebuffer() should be called.
+	 */
+	return 1;
+}
+
+static struct drm_fb_helper_funcs imx_drm_fb_helper_funcs = {
+	.fb_probe = imx_drm_fbdev_probe,
+};
+
+static struct drm_fb_helper *imx_drm_fbdev_init(struct drm_device *drm,
+		int preferred_bpp)
+{
+	struct drm_fb_helper *helper;
+	unsigned int num_crtc;
+	int ret;
+
+	helper = kzalloc(sizeof(*helper), GFP_KERNEL);
+	if (!helper)
+		return NULL;
+
+	helper->funcs = &imx_drm_fb_helper_funcs;
+
+	num_crtc = drm->mode_config.num_crtc;
+
+	ret = drm_fb_helper_init(drm, helper, num_crtc, MAX_CONNECTOR);
+	if (ret) {
+		dev_err(drm->dev, "initializing drm fb helper failed with %d\n",
+				ret);
+		goto err_init;
+	}
+
+	ret = drm_fb_helper_single_add_all_connectors(helper);
+	if (ret) {
+		dev_err(drm->dev, "registering drm_fb_helper_connector failed with %d\n",
+				ret);
+		goto err_setup;
+
+	}
+
+	ret = drm_fb_helper_initial_config(helper, preferred_bpp);
+	if (ret) {
+		dev_err(drm->dev, "initial config failed with %d\n", ret);
+		goto err_setup;
+	}
+
+	return helper;
+
+err_setup:
+	drm_fb_helper_fini(helper);
+
+err_init:
+	kfree(helper);
+
+	return NULL;
+}
+
+static void imx_drm_fbdev_fini(struct drm_fb_helper *helper)
+{
+	struct imx_drm_buf_entry *entry;
+
+	if (helper->fbdev) {
+		struct fb_info *info;
+		int ret;
+
+		info = helper->fbdev;
+		ret = unregister_framebuffer(info);
+		if (ret)
+			dev_err(helper->dev->dev, "unregister_framebuffer failed with %d\n",
+					ret);
+
+		if (info->cmap.len)
+			fb_dealloc_cmap(&info->cmap);
+
+		entry = imx_drm_fb_get_buf(helper->fb);
+
+		imx_drm_buf_destroy(helper->dev, entry);
+
+		framebuffer_release(info);
+	}
+
+	drm_fb_helper_fini(helper);
+
+	kfree(helper);
+}
+
+static struct drm_fb_helper *imx_fb_helper;
+
+static int __init imx_fb_helper_init(void)
+{
+	struct drm_device *drm = imx_drm_device_get();
+	int ret;
+
+	if (!drm)
+		return -EINVAL;
+
+	imx_fb_helper = imx_drm_fbdev_init(drm, PREFERRED_BPP);
+	if (!imx_fb_helper) {
+		imx_drm_device_put();
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void __exit imx_fb_helper_exit(void)
+{
+	imx_drm_fbdev_fini(imx_fb_helper);
+	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/gpu/drm/imx/imx-gem.c b/drivers/gpu/drm/imx/imx-gem.c
new file mode 100644
index 0000000..b0866fb
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-gem.c
@@ -0,0 +1,343 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "imx-drm.h"
+
+static int lowlevel_buffer_allocate(struct drm_device *drm,
+		struct imx_drm_buf_entry *entry)
+{
+	entry->vaddr = dma_alloc_writecombine(drm->dev, entry->size,
+			(dma_addr_t *)&entry->paddr, GFP_KERNEL);
+	if (!entry->vaddr) {
+		dev_err(drm->dev, "failed to allocate buffer.\n");
+		return -ENOMEM;
+	}
+
+	dev_dbg(drm->dev, "allocated : vaddr(0x%x), paddr(0x%x), size(0x%x)\n",
+			(unsigned int)entry->vaddr, entry->paddr, entry->size);
+
+	return 0;
+}
+
+static void lowlevel_buffer_free(struct drm_device *drm,
+		struct imx_drm_buf_entry *entry)
+{
+	dma_free_writecombine(drm->dev, entry->size, entry->vaddr,
+			entry->paddr);
+}
+
+struct imx_drm_buf_entry *imx_drm_buf_create(struct drm_device *drm,
+		unsigned int size)
+{
+	struct imx_drm_buf_entry *entry;
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return ERR_PTR(-ENOMEM);
+
+	entry->size = size;
+
+	/*
+	 * allocate memory region with size and set the memory information
+	 * to vaddr and paddr of a entry object.
+	 */
+	if (lowlevel_buffer_allocate(drm, entry) < 0) {
+		kfree(entry);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	return entry;
+}
+
+void imx_drm_buf_destroy(struct drm_device *drm,
+		struct imx_drm_buf_entry *entry)
+{
+	lowlevel_buffer_free(drm, entry);
+
+	kfree(entry);
+	entry = NULL;
+}
+EXPORT_SYMBOL_GPL(imx_drm_buf_destroy);
+
+static unsigned int convert_to_vm_err_msg(int msg)
+{
+	unsigned int out_msg;
+
+	switch (msg) {
+	case 0:
+	case -ERESTARTSYS:
+	case -EINTR:
+		out_msg = VM_FAULT_NOPAGE;
+		break;
+
+	case -ENOMEM:
+		out_msg = VM_FAULT_OOM;
+		break;
+
+	default:
+		out_msg = VM_FAULT_SIGBUS;
+		break;
+	}
+
+	return out_msg;
+}
+
+static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
+{
+	return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
+}
+
+static struct imx_drm_gem_obj *imx_drm_gem_create(struct drm_device *drm,
+		unsigned int size)
+{
+	struct imx_drm_gem_obj *imx_drm_gem_obj;
+	struct imx_drm_buf_entry *entry;
+	struct drm_gem_object *obj;
+	int ret;
+
+	size = roundup(size, PAGE_SIZE);
+
+	imx_drm_gem_obj = kzalloc(sizeof(*imx_drm_gem_obj), GFP_KERNEL);
+	if (!imx_drm_gem_obj)
+		return ERR_PTR(-ENOMEM);
+
+	/* allocate the new buffer object and memory region. */
+	entry = imx_drm_buf_create(drm, size);
+	if (!entry) {
+		ret = -ENOMEM;
+		goto err_alloc;
+	}
+
+	imx_drm_gem_obj->entry = entry;
+
+	obj = &imx_drm_gem_obj->base;
+
+	ret = drm_gem_object_init(drm, obj, size);
+	if (ret) {
+		dev_err(drm->dev, "initializing GEM object failed with %d\n", ret);
+		goto err_obj_init;
+	}
+
+	ret = drm_gem_create_mmap_offset(obj);
+	if (ret) {
+		dev_err(drm->dev, "creating mmap offset failed with %d\n", ret);
+		goto err_create_mmap_offset;
+	}
+
+	return imx_drm_gem_obj;
+
+err_create_mmap_offset:
+	drm_gem_object_release(obj);
+
+err_obj_init:
+	imx_drm_buf_destroy(drm, imx_drm_gem_obj->entry);
+
+err_alloc:
+	kfree(imx_drm_gem_obj);
+
+	return ERR_PTR(ret);
+}
+
+static struct imx_drm_gem_obj *imx_drm_gem_create_with_handle(struct drm_file *file_priv,
+		struct drm_device *drm, unsigned int size,
+		unsigned int *handle)
+{
+	struct imx_drm_gem_obj *imx_drm_gem_obj;
+	struct drm_gem_object *obj;
+	int ret;
+
+	imx_drm_gem_obj = imx_drm_gem_create(drm, size);
+	if (IS_ERR(imx_drm_gem_obj))
+		return imx_drm_gem_obj;
+
+	obj = &imx_drm_gem_obj->base;
+
+	/*
+	 * allocate a id of idr table where the obj is registered
+	 * and handle has the id what user can see.
+	 */
+	ret = drm_gem_handle_create(file_priv, obj, handle);
+	if (ret)
+		goto err_handle_create;
+
+	/* drop reference from allocate - handle holds it now. */
+	drm_gem_object_unreference_unlocked(obj);
+
+	return imx_drm_gem_obj;
+
+err_handle_create:
+	imx_drm_gem_free_object(obj);
+
+	return ERR_PTR(ret);
+}
+
+static int imx_drm_gem_mmap_buffer(struct file *filp,
+		struct vm_area_struct *vma)
+{
+	struct drm_gem_object *obj = filp->private_data;
+	struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
+	struct imx_drm_buf_entry *entry;
+	unsigned long pfn, vm_size;
+
+	vma->vm_flags |= VM_IO | VM_RESERVED;
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	vma->vm_file = filp;
+
+	vm_size = vma->vm_end - vma->vm_start;
+	/*
+	 * an entry contains information to physically continuous memory
+	 * allocated by user request or at framebuffer creation.
+	 */
+	entry = imx_drm_gem_obj->entry;
+
+	/* check if user-requested size is valid. */
+	if (vm_size > entry->size)
+		return -EINVAL;
+
+	/*
+	 * get page frame number to physical memory to be mapped
+	 * to user space.
+	 */
+	pfn = imx_drm_gem_obj->entry->paddr >> PAGE_SHIFT;
+
+	if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
+				vma->vm_page_prot)) {
+		dev_err(obj->dev->dev, "failed to remap pfn range.\n");
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+
+static const struct file_operations imx_drm_gem_fops = {
+	.mmap = imx_drm_gem_mmap_buffer,
+};
+
+int imx_drm_gem_init_object(struct drm_gem_object *obj)
+{
+	return 0;
+}
+
+void imx_drm_gem_free_object(struct drm_gem_object *gem_obj)
+{
+	struct imx_drm_gem_obj *imx_drm_gem_obj;
+
+	if (gem_obj->map_list.map)
+		drm_gem_free_mmap_offset(gem_obj);
+
+	drm_gem_object_release(gem_obj);
+
+	imx_drm_gem_obj = to_imx_drm_gem_obj(gem_obj);
+
+	imx_drm_buf_destroy(gem_obj->dev, imx_drm_gem_obj->entry);
+
+	kfree(imx_drm_gem_obj);
+}
+
+int imx_drm_gem_dumb_create(struct drm_file *file_priv,
+		struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+	struct imx_drm_gem_obj *imx_drm_gem_obj;
+
+	/* FIXME: This should be configured by the crtc driver */
+	args->pitch = args->width * args->bpp >> 3;
+	args->size = args->pitch * args->height;
+
+	imx_drm_gem_obj = imx_drm_gem_create_with_handle(file_priv, dev, args->size,
+			&args->handle);
+	if (IS_ERR(imx_drm_gem_obj))
+		return PTR_ERR(imx_drm_gem_obj);
+
+	return 0;
+}
+
+int imx_drm_gem_dumb_map_offset(struct drm_file *file_priv,
+		struct drm_device *drm, uint32_t handle, uint64_t *offset)
+{
+	struct imx_drm_gem_obj *imx_drm_gem_obj;
+	struct drm_gem_object *obj;
+
+	mutex_lock(&drm->struct_mutex);
+
+	obj = drm_gem_object_lookup(drm, file_priv, handle);
+	if (!obj) {
+		dev_err(drm->dev, "failed to lookup gem object\n");
+		mutex_unlock(&drm->struct_mutex);
+		return -EINVAL;
+	}
+
+	imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
+
+	*offset = get_gem_mmap_offset(&imx_drm_gem_obj->base);
+
+	drm_gem_object_unreference(obj);
+
+	mutex_unlock(&drm->struct_mutex);
+
+	return 0;
+}
+
+int imx_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	struct drm_gem_object *obj = vma->vm_private_data;
+	struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
+	struct drm_device *dev = obj->dev;
+	unsigned long pfn;
+	pgoff_t page_offset;
+	int ret;
+
+	page_offset = ((unsigned long)vmf->virtual_address -
+			vma->vm_start) >> PAGE_SHIFT;
+
+	mutex_lock(&dev->struct_mutex);
+
+	pfn = (imx_drm_gem_obj->entry->paddr >> PAGE_SHIFT) + page_offset;
+
+	ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
+
+	mutex_unlock(&dev->struct_mutex);
+
+	return convert_to_vm_err_msg(ret);
+}
+
+int imx_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	int ret;
+
+	ret = drm_gem_mmap(filp, vma);
+	if (ret)
+		return ret;
+
+	vma->vm_flags &= ~VM_PFNMAP;
+	vma->vm_flags |= VM_MIXEDMAP;
+
+	return ret;
+}
+
+
+int imx_drm_gem_dumb_destroy(struct drm_file *file_priv,
+		struct drm_device *dev, unsigned int handle)
+{
+	return drm_gem_handle_delete(file_priv, handle);
+}
diff --git a/drivers/gpu/drm/imx/imx-lcdc-crtc.c b/drivers/gpu/drm/imx/imx-lcdc-crtc.c
new file mode 100644
index 0000000..e77c015
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-lcdc-crtc.c
@@ -0,0 +1,517 @@
+/*
+ * i.MX LCDC crtc driver
+ *
+ * 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.
+ *
+ */
+#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 <asm/fb.h>
+#include <linux/module.h>
+#include <mach/hardware.h>
+#include <mach/imxfb.h>
+#include <generated/mach-types.h>
+
+#include "imx-drm.h"
+
+#define LCDC_SSA		0x00
+#define LCDC_SIZE		0x04
+#define LCDC_VPW		0x08
+#define LCDC_CPOS		0x0C
+#define LCDC_LCWHB		0x10
+#define LCDC_LCHCC		0x14
+#define LCDC_PCR		0x18
+#define LCDC_HCR		0x1C
+#define LCDC_VCR		0x20
+#define LCDC_POS		0x24
+#define LCDC_LSCR1		0x28
+#define LCDC_PWMR		0x2C
+#define LCDC_DMACR		0x30
+#define LCDC_RMCR		0x34
+#define LCDC_LCDICR		0x38
+#define LCDC_LIER		0x3c
+#define LCDC_LISR		0x40
+
+#define SIZE_XMAX(x)		((((x) >> 4) & 0x3f) << 20)
+
+#define YMAX_MASK		(cpu_is_mx1() ? 0x1ff : 0x3ff)
+#define SIZE_YMAX(y)		((y) & YMAX_MASK)
+
+#define VPW_VPW(x)		((x) & 0x3ff)
+
+#define HCR_H_WIDTH(x)		(((x) & 0x3f) << 26)
+#define HCR_H_WAIT_1(x)		(((x) & 0xff) << 8)
+#define HCR_H_WAIT_2(x)		((x) & 0xff)
+
+#define VCR_V_WIDTH(x)		(((x) & 0x3f) << 26)
+#define VCR_V_WAIT_1(x)		(((x) & 0xff) << 8)
+#define VCR_V_WAIT_2(x)		((x) & 0xff)
+
+#define RMCR_LCDC_EN_MX1	(1 << 1)
+
+#define RMCR_SELF_REF		(1 << 0)
+
+#define LIER_EOF		(1 << 1)
+
+struct imx_crtc {
+	struct drm_crtc		base;
+	struct imx_drm_crtc	*imx_drm_crtc;
+	int			di_no;
+	int			enabled;
+	void __iomem		*regs;
+	u32			pwmr;
+	u32			lscr1;
+	u32			dmacr;
+	u32			pcr;
+	struct clk		*clk;
+	struct device		*dev;
+	int			vblank_enable;
+
+	struct drm_pending_vblank_event *page_flip_event;
+	struct drm_framebuffer	*newfb;
+};
+
+#define to_imx_crtc(x) container_of(x, struct imx_crtc, base)
+
+static void imx_crtc_load_lut(struct drm_crtc *crtc)
+{
+}
+
+#define PCR_BPIX_8		(3 << 25)
+#define PCR_BPIX_12		(4 << 25)
+#define PCR_BPIX_16		(5 << 25)
+#define PCR_BPIX_18		(6 << 25)
+#define PCR_END_SEL		(1 << 18)
+#define PCR_END_BYTE_SWAP	(1 << 17)
+
+const char *fourcc_to_str(u32 fourcc)
+{
+	static char buf[5];
+
+	*(u32 *)buf = fourcc;
+	buf[4] = 0;
+
+	return buf;
+}
+
+static int imx_drm_crtc_set(struct drm_crtc *crtc,
+		struct drm_display_mode *mode)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+	struct drm_framebuffer *fb = crtc->fb;
+	int lower_margin = mode->vsync_start - mode->vdisplay;
+	int upper_margin = mode->vtotal - mode->vsync_end;
+	int vsync_len = mode->vsync_end - mode->vsync_start;
+	int hsync_len = mode->hsync_end - mode->hsync_start;
+	int right_margin = mode->hsync_start - mode->hdisplay;
+	int left_margin = mode->htotal - mode->hsync_end;
+	unsigned long lcd_clk;
+	u32 pcr;
+
+	lcd_clk = clk_get_rate(imx_crtc->clk) / 1000;
+
+	if (!mode->clock)
+		return -EINVAL;
+
+	pcr = DIV_ROUND_CLOSEST(lcd_clk, mode->clock);
+	if (--pcr > 0x3f)
+		pcr = 0x3f;
+
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+		pcr |= PCR_BPIX_18;
+		pcr |= PCR_END_SEL | PCR_END_BYTE_SWAP;
+		break;
+	case DRM_FORMAT_RGB565:
+		if (cpu_is_mx1())
+			pcr |= PCR_BPIX_12;
+		else
+			pcr |= PCR_BPIX_16;
+		break;
+	case DRM_FORMAT_RGB332:
+		pcr |= PCR_BPIX_8;
+		break;
+	default:
+		dev_err(imx_crtc->dev, "unsupported pixel format %s\n",
+				fourcc_to_str(fb->pixel_format));
+		return -EINVAL;
+	}
+
+	/* add sync polarities */
+	pcr |= imx_crtc->pcr & ~(0x3f | (7 << 25));
+
+	dev_dbg(imx_crtc->dev,
+			"xres=%d hsync_len=%d left_margin=%d right_margin=%d\n",
+			mode->hdisplay, hsync_len,
+			left_margin, right_margin);
+	dev_dbg(imx_crtc->dev,
+			"yres=%d vsync_len=%d upper_margin=%d lower_margin=%d\n",
+			mode->vdisplay, vsync_len,
+			upper_margin, lower_margin);
+
+	writel(VPW_VPW(mode->hdisplay * fb->bits_per_pixel / 8 / 4),
+		imx_crtc->regs + LCDC_VPW);
+
+	writel(HCR_H_WIDTH(hsync_len - 1) |
+		HCR_H_WAIT_1(right_margin - 1) |
+		HCR_H_WAIT_2(left_margin - 3),
+		imx_crtc->regs + LCDC_HCR);
+
+	writel(VCR_V_WIDTH(vsync_len) |
+		VCR_V_WAIT_1(lower_margin) |
+		VCR_V_WAIT_2(upper_margin),
+		imx_crtc->regs + LCDC_VCR);
+
+	writel(SIZE_XMAX(mode->hdisplay) | SIZE_YMAX(mode->vdisplay),
+			imx_crtc->regs + LCDC_SIZE);
+
+	writel(pcr, imx_crtc->regs + LCDC_PCR);
+	writel(imx_crtc->pwmr, imx_crtc->regs + LCDC_PWMR);
+	writel(imx_crtc->lscr1, imx_crtc->regs + LCDC_LSCR1);
+	/* reset default */
+	writel(0x00040060, imx_crtc->regs + LCDC_DMACR);
+
+	return 0;
+}
+
+static int imx_drm_set_base(struct drm_crtc *crtc, int x, int y)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+	struct imx_drm_buf_entry *entry;
+	struct drm_framebuffer *fb = crtc->fb;
+	unsigned long phys;
+
+	entry = imx_drm_fb_get_buf(fb);
+	if (!entry)
+		return -EFAULT;
+
+	phys = entry->paddr;
+	phys += x * (fb->bits_per_pixel >> 3);
+	phys += y * fb->pitches[0];
+
+	dev_dbg(imx_crtc->dev, "%s: phys: 0x%lx\n", __func__, phys);
+	dev_dbg(imx_crtc->dev, "%s: xy: %dx%d\n", __func__, x, y);
+
+	writel(phys, imx_crtc->regs + LCDC_SSA);
+
+	return 0;
+}
+
+static int imx_crtc_mode_set(struct drm_crtc *crtc,
+			       struct drm_display_mode *mode,
+			       struct drm_display_mode *adjusted_mode,
+			       int x, int y,
+			       struct drm_framebuffer *old_fb)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	imx_drm_set_base(crtc, x, y);
+
+	dev_dbg(imx_crtc->dev, "mode->hdisplay: %d\n", mode->hdisplay);
+	dev_dbg(imx_crtc->dev, "mode->vdisplay: %d\n", mode->vdisplay);
+
+	return imx_drm_crtc_set(crtc, mode);
+}
+
+static void imx_crtc_enable(struct imx_crtc *imx_crtc)
+{
+	if (!imx_crtc->enabled)
+		clk_enable(imx_crtc->clk);
+	imx_crtc->enabled = 1;
+}
+
+static void imx_crtc_disable(struct imx_crtc *imx_crtc)
+{
+	if (imx_crtc->enabled)
+		clk_disable(imx_crtc->clk);
+	imx_crtc->enabled = 0;
+}
+
+static void imx_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	dev_dbg(imx_crtc->dev, "%s mode: %d\n", __func__, mode);
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		imx_crtc_enable(imx_crtc);
+		break;
+	default:
+		imx_crtc_disable(imx_crtc);
+		break;
+	}
+}
+
+static bool imx_crtc_mode_fixup(struct drm_crtc *crtc,
+				  struct drm_display_mode *mode,
+				  struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void imx_crtc_prepare(struct drm_crtc *crtc)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	imx_crtc_disable(imx_crtc);
+}
+
+static void imx_crtc_commit(struct drm_crtc *crtc)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	imx_crtc_enable(imx_crtc);
+}
+
+static struct drm_crtc_helper_funcs imx_helper_funcs = {
+	.dpms = imx_crtc_dpms,
+	.mode_fixup = imx_crtc_mode_fixup,
+	.mode_set = imx_crtc_mode_set,
+	.prepare = imx_crtc_prepare,
+	.commit = imx_crtc_commit,
+	.load_lut = imx_crtc_load_lut,
+};
+
+static void imx_drm_handle_pageflip(struct imx_crtc *imx_crtc)
+{
+	struct drm_pending_vblank_event *e;
+	struct timeval now;
+	unsigned long flags;
+	struct drm_device *drm = imx_crtc->base.dev;
+
+	spin_lock_irqsave(&drm->event_lock, flags);
+
+	e = imx_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;
+	imx_crtc->page_flip_event = NULL;
+
+	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 int imx_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	writel(LIER_EOF, imx_crtc->regs + LCDC_LIER);
+
+	return 0;
+}
+
+static void imx_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	writel(0, imx_crtc->regs + LCDC_LIER);
+}
+
+static irqreturn_t imx_irq_handler(int irq, void *dev_id)
+{
+	struct imx_crtc *imx_crtc = dev_id;
+	struct drm_device *drm = imx_crtc->base.dev;
+
+	/* Acknowledge interrupt */
+	readl(imx_crtc->regs + LCDC_LISR);
+
+	drm_handle_vblank(drm, 0);
+
+	if (imx_crtc->newfb) {
+		imx_crtc->base.fb = imx_crtc->newfb;
+		imx_crtc->newfb = NULL;
+		imx_drm_set_base(&imx_crtc->base, 0, 0);
+		imx_drm_handle_pageflip(imx_crtc);
+		imx_drm_crtc_vblank_put(imx_crtc->imx_drm_crtc);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int imx_page_flip(struct drm_crtc *crtc,
+                         struct drm_framebuffer *fb,
+                         struct drm_pending_vblank_event *event)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	if (imx_crtc->newfb)
+		return -EBUSY;
+
+	imx_crtc->newfb = fb;
+	imx_crtc->page_flip_event = event;
+	imx_drm_crtc_vblank_get(imx_crtc->imx_drm_crtc);
+
+	return 0;
+}
+
+static const struct drm_crtc_funcs imx_crtc_funcs = {
+	.page_flip = imx_page_flip,
+};
+
+static const struct imx_drm_crtc_helper_funcs imx_imx_drm_helper = {
+	.enable_vblank = imx_crtc_enable_vblank,
+	.disable_vblank = imx_crtc_disable_vblank,
+};
+
+#define DRIVER_NAME "imx-lcdc-crtc"
+
+/*
+ * the pcr bits to be allowed to set in platform data
+ */
+#define PDATA_PCR	(PCR_PIXPOL | PCR_FLMPOL | PCR_LPPOL | \
+			PCR_CLKPOL | PCR_OEPOL | PCR_TFT | PCR_COLOR | \
+			PCR_PBSIZ_8 | PCR_ACD(0x7f) | PCR_ACD_SEL | \
+			PCR_SCLK_SEL | PCR_SHARP)
+
+static int __devinit imx_crtc_probe(struct platform_device *pdev)
+{
+	struct imx_crtc *imx_crtc;
+	struct resource *res;
+	int ret, irq;
+	u32 pcr_value = 0xf00080c0;
+	u32 lscr1_value = 0x00120300;
+	u32 pwmr_value = 0x00a903ff;
+
+	imx_crtc = devm_kzalloc(&pdev->dev, sizeof(*imx_crtc), GFP_KERNEL);
+	if (!imx_crtc)
+		return -ENOMEM;
+
+	imx_crtc->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	res = devm_request_mem_region(&pdev->dev, res->start,
+			resource_size(res), DRIVER_NAME);
+	if (!res)
+		return -EBUSY;
+
+	imx_crtc->regs = devm_ioremap(&pdev->dev, res->start,
+			resource_size(res));
+	if (!imx_crtc->regs) {
+		dev_err(&pdev->dev, "Cannot map frame buffer registers\n");
+		return -EBUSY;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_irq(&pdev->dev, irq, imx_irq_handler, 0, "imx_drm",
+			imx_crtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "irq request failed with %d\n", ret);
+		return ret;
+	}
+
+	imx_crtc->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(imx_crtc->clk)) {
+		ret = PTR_ERR(imx_crtc->clk);
+		dev_err(&pdev->dev, "unable to get clock: %d\n", ret);
+		return ret;
+	}
+
+	clk_prepare_enable(imx_crtc->clk);
+	imx_crtc->enabled = 1;
+
+	platform_set_drvdata(pdev, imx_crtc);
+
+	imx_crtc->pcr = pcr_value & PDATA_PCR;
+
+	if (imx_crtc->pcr != pcr_value)
+		dev_err(&pdev->dev, "invalid bits set in pcr: 0x%08x\n",
+				pcr_value & ~PDATA_PCR);
+
+	imx_crtc->lscr1 = lscr1_value;
+	imx_crtc->pwmr = pwmr_value;
+
+	ret = imx_drm_add_crtc(&imx_crtc->base,
+			&imx_crtc->imx_drm_crtc,
+			&imx_crtc_funcs, &imx_helper_funcs,
+			&imx_imx_drm_helper, THIS_MODULE);
+	if (ret)
+		goto err_init;
+
+	dev_info(&pdev->dev, "probed\n");
+
+	return 0;
+
+err_init:
+	clk_disable_unprepare(imx_crtc->clk);
+	clk_put(imx_crtc->clk);
+
+	return ret;
+}
+
+static int __devexit imx_crtc_remove(struct platform_device *pdev)
+{
+	struct imx_crtc *imx_crtc = platform_get_drvdata(pdev);
+
+	imx_drm_remove_crtc(imx_crtc->imx_drm_crtc);
+
+	writel(0, imx_crtc->regs + LCDC_LIER);
+
+	clk_disable_unprepare(imx_crtc->clk);
+	clk_put(imx_crtc->clk);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id imx_lcdc_dt_ids[] = {
+	{ .compatible = "fsl,imx1-lcdc", .data = NULL, },
+	{ .compatible = "fsl,imx21-lcdc", .data = NULL, },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver imx_crtc_driver = {
+	.remove		= __devexit_p(imx_crtc_remove),
+	.probe		= imx_crtc_probe,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = imx_lcdc_dt_ids,
+	},
+};
+
+static int __init imx_lcdc_init(void)
+{
+	return platform_driver_register(&imx_crtc_driver);
+}
+
+static void __exit imx_lcdc_exit(void)
+{
+	platform_driver_unregister(&imx_crtc_driver);
+}
+
+module_init(imx_lcdc_init);
+module_exit(imx_lcdc_exit)
+
+MODULE_DESCRIPTION("Freescale i.MX framebuffer driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/imx/imx-parallel-display.c b/drivers/gpu/drm/imx/imx-parallel-display.c
new file mode 100644
index 0000000..8c96113
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-parallel-display.c
@@ -0,0 +1,228 @@
+/*
+ * 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 "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;
+};
+
+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 ret;
+
+	drm_mode_connector_update_edid_property(connector, imxpd->edid);
+	ret = drm_add_edid_modes(connector, imxpd->edid);
+	connector->display_info.raw_edid = NULL;
+
+	return ret;
+}
+
+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,
+			   struct drm_display_mode *mode,
+			   struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void imx_pd_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+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 */
+}
+
+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,
+};
+
+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;
+
+	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;
+
+	imxpd = devm_kzalloc(&pdev->dev, sizeof(*imxpd), GFP_KERNEL);
+	if (!imxpd)
+		return -ENOMEM;
+
+	edidp = of_get_property(np, "edid", &imxpd->edid_len);
+
+	imxpd->edid = kmemdup(edidp, imxpd->edid_len, GFP_KERNEL);
+	imxpd->encoder.possible_crtcs = 0x1;
+	imxpd->encoder.possible_clones = 0x1;
+	imxpd->dev = &pdev->dev;
+
+	ret = imx_pd_register(imxpd);
+	if (ret)
+		return ret;
+
+	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", .data = NULL, },
+	{ /* 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");
-- 
1.7.10

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 1/2] DRM: add Freescale i.MX LCDC driver
@ 2012-05-18 12:27   ` Sascha Hauer
  0 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-18 12:27 UTC (permalink / raw)
  To: dri-devel; +Cc: Sascha Hauer, linux-arm-kernel

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/Kconfig                    |    2 +
 drivers/gpu/drm/Makefile                   |    1 +
 drivers/gpu/drm/imx/Kconfig                |   18 +
 drivers/gpu/drm/imx/Makefile               |    8 +
 drivers/gpu/drm/imx/imx-drm-core.c         |  745 ++++++++++++++++++++++++++++
 drivers/gpu/drm/imx/imx-fb.c               |  179 +++++++
 drivers/gpu/drm/imx/imx-fbdev.c            |  275 ++++++++++
 drivers/gpu/drm/imx/imx-gem.c              |  343 +++++++++++++
 drivers/gpu/drm/imx/imx-lcdc-crtc.c        |  517 +++++++++++++++++++
 drivers/gpu/drm/imx/imx-parallel-display.c |  228 +++++++++
 10 files changed, 2316 insertions(+)
 create mode 100644 drivers/gpu/drm/imx/Kconfig
 create mode 100644 drivers/gpu/drm/imx/Makefile
 create mode 100644 drivers/gpu/drm/imx/imx-drm-core.c
 create mode 100644 drivers/gpu/drm/imx/imx-fb.c
 create mode 100644 drivers/gpu/drm/imx/imx-fbdev.c
 create mode 100644 drivers/gpu/drm/imx/imx-gem.c
 create mode 100644 drivers/gpu/drm/imx/imx-lcdc-crtc.c
 create mode 100644 drivers/gpu/drm/imx/imx-parallel-display.c

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index e354bc0..759502c 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -186,3 +186,5 @@ source "drivers/gpu/drm/vmwgfx/Kconfig"
 source "drivers/gpu/drm/gma500/Kconfig"
 
 source "drivers/gpu/drm/udl/Kconfig"
+
+source "drivers/gpu/drm/imx/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index c20da5b..6569d8d 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -42,4 +42,5 @@ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
 obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
+obj-$(CONFIG_DRM_IMX) += imx/
 obj-y			+= i2c/
diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig
new file mode 100644
index 0000000..5fc3a44
--- /dev/null
+++ b/drivers/gpu/drm/imx/Kconfig
@@ -0,0 +1,18 @@
+config DRM_IMX
+	tristate "DRM Support for Freescale i.MX"
+	select DRM_KMS_HELPER
+	depends on DRM && ARCH_MXC
+
+config DRM_IMX_FB_HELPER
+	tristate "provide legacy framebuffer /dev/fb0"
+	depends on DRM_IMX
+
+config DRM_IMX_LCDC
+	tristate "DRM Support for Freescale i.MX1 and i.MX2"
+	depends on DRM_IMX
+	help
+	  Choose this if you have a i.MX1, i.MX21, i.MX25 or i.MX27 processor.
+
+config DRM_IMX_PARALLEL_DISPLAY
+	tristate "Support for parallel displays"
+	depends on DRM_IMX
diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
new file mode 100644
index 0000000..0f7c038
--- /dev/null
+++ b/drivers/gpu/drm/imx/Makefile
@@ -0,0 +1,8 @@
+
+imxdrm-objs := imx-drm-core.o imx-fb.o imx-gem.o
+
+obj-$(CONFIG_DRM_IMX) += imxdrm.o
+
+obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += imx-parallel-display.o
+obj-$(CONFIG_DRM_IMX_LCDC) += imx-lcdc-crtc.o
+obj-$(CONFIG_DRM_IMX_FB_HELPER) += imx-fbdev.o
diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
new file mode 100644
index 0000000..29f5f10
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-drm-core.c
@@ -0,0 +1,745 @@
+/*
+ * simple 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 <asm/fb.h>
+#include <linux/module.h>
+
+#include "imx-drm.h"
+
+#define MAX_CRTC	4
+
+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;
+};
+
+struct imx_drm_crtc {
+	struct drm_crtc				*crtc;
+	struct list_head			list;
+	struct imx_drm_device			*imxdrm;
+	int					pipe;
+	struct drm_crtc_helper_funcs		crtc_helper_funcs;
+	struct drm_crtc_funcs			crtc_funcs;
+	struct imx_drm_crtc_helper_funcs	imx_drm_helper_funcs;
+	struct module				*owner;
+};
+
+struct imx_drm_encoder {
+	struct drm_encoder			*encoder;
+	struct list_head			list;
+	struct module				*owner;
+};
+
+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)
+{
+	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_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 struct vm_operations_struct imx_drm_gem_vm_ops = {
+	.fault = imx_drm_gem_fault,
+	.open = drm_gem_vm_open,
+	.close = drm_gem_vm_close,
+};
+
+static const struct file_operations imx_drm_driver_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.release = drm_release,
+	.unlocked_ioctl = drm_ioctl,
+	.mmap = imx_drm_gem_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 imx_drm_device->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();
+
+	drm_encoder_init(imxdrm->drm, imx_drm_encoder->encoder,
+			imx_drm_encoder->encoder->funcs,
+			DRM_MODE_ENCODER_TMDS);
+
+	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();
+	int ret;
+
+	drm_connector_init(imxdrm->drm, imx_drm_connector->connector,
+			imx_drm_connector->connector->funcs,
+			DRM_MODE_CONNECTOR_VGA);
+	drm_mode_group_reinit(imxdrm->drm);
+	ret = drm_sysfs_connector_add(imx_drm_connector->connector);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+
+	return ret;
+}
+
+/*
+ * 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->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->crtc_helper_funcs);
+
+	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;
+}
+
+/*
+ * 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 drm_crtc_funcs *crtc_funcs,
+		const struct drm_crtc_helper_funcs *crtc_helper_funcs,
+		const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs,
+		struct module *owner)
+{
+	struct imx_drm_device *imxdrm = __imx_drm_device();
+	struct imx_drm_crtc *imx_drm_crtc;
+	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->crtc_funcs = *crtc_funcs;
+	imx_drm_crtc->crtc_helper_funcs = *crtc_helper_funcs;
+	imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs;
+
+	WARN_ON(crtc_funcs->set_config);
+	WARN_ON(crtc_funcs->destroy);
+
+	imx_drm_crtc->crtc_funcs.set_config = drm_crtc_helper_set_config;
+	imx_drm_crtc->crtc_funcs.destroy = drm_crtc_cleanup;
+
+	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;
+
+	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);
+
+	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(struct 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);
+
+/*
+ * 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();
+
+	mutex_lock(&imxdrm->mutex);
+
+	imx_drm_encoder_unregister(imx_drm_encoder);
+
+	list_del(&imx_drm_encoder->list);
+
+	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(struct 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);
+
+/*
+ * 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	= imx_drm_gem_free_object,
+	.gem_vm_ops		= &imx_drm_gem_vm_ops,
+	.dumb_create		= imx_drm_gem_dumb_create,
+	.dumb_map_offset	= imx_drm_gem_dumb_map_offset,
+	.dumb_destroy		= imx_drm_gem_dumb_destroy,
+
+	.get_vblank_counter	= drm_vblank_count,
+	.enable_vblank		= imx_drm_enable_vblank,
+	.disable_vblank		= imx_drm_disable_vblank,
+	.reclaim_buffers	= drm_core_reclaim_buffers,
+	.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_pdev:
+	kfree(imx_drm_device);
+err_pdrv:
+	platform_device_unregister(imx_drm_pdev);
+
+	return ret;
+}
+
+static void __exit imx_drm_exit(void)
+{
+	DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+	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/gpu/drm/imx/imx-fb.c b/drivers/gpu/drm/imx/imx-fb.c
new file mode 100644
index 0000000..5a08c86
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-fb.c
@@ -0,0 +1,179 @@
+/*
+ * 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 "imx-drm.h"
+
+#define to_imx_drm_fb(x)	container_of(x, struct imx_drm_fb, fb)
+
+/*
+ * imx specific framebuffer structure.
+ *
+ * @fb: drm framebuffer obejct.
+ * @imx_drm_gem_obj: drm ec specific gem object containing a gem object.
+ * @entry: pointer to ec drm buffer entry object.
+ *	- containing only the information to physically continuous memory
+ *	region allocated at default framebuffer creation.
+ */
+struct imx_drm_fb {
+	struct drm_framebuffer		fb;
+	struct imx_drm_gem_obj	*imx_drm_gem_obj;
+	struct imx_drm_buf_entry	*entry;
+};
+
+static void imx_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct imx_drm_fb *imx_drm_fb = to_imx_drm_fb(fb);
+
+	drm_framebuffer_cleanup(fb);
+
+	/*
+	 * default framebuffer has no gem object so
+	 * a buffer of the default framebuffer should be released at here.
+	 */
+	if (!imx_drm_fb->imx_drm_gem_obj && imx_drm_fb->entry)
+		imx_drm_buf_destroy(fb->dev, imx_drm_fb->entry);
+
+	kfree(imx_drm_fb);
+}
+
+static int imx_drm_fb_create_handle(struct drm_framebuffer *fb,
+		struct drm_file *file_priv, unsigned int *handle)
+{
+	struct imx_drm_fb *imx_drm_fb = to_imx_drm_fb(fb);
+
+	return drm_gem_handle_create(file_priv,
+			&imx_drm_fb->imx_drm_gem_obj->base, handle);
+}
+
+static struct drm_framebuffer_funcs imx_drm_fb_funcs = {
+	.destroy	= imx_drm_fb_destroy,
+	.create_handle	= imx_drm_fb_create_handle,
+};
+
+static struct drm_framebuffer *imx_drm_fb_create(struct drm_device *dev,
+		struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	struct imx_drm_fb *imx_drm_fb;
+	struct drm_framebuffer *fb;
+	struct drm_gem_object *obj;
+	unsigned int size;
+	int ret;
+	u32 bpp, depth;
+
+	drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp);
+
+	mode_cmd->pitches[0] = max(mode_cmd->pitches[0],
+			mode_cmd->width * (bpp >> 3));
+
+	dev_dbg(dev->dev, "drm fb create(%dx%d)\n",
+			mode_cmd->width, mode_cmd->height);
+
+	imx_drm_fb = kzalloc(sizeof(*imx_drm_fb), GFP_KERNEL);
+	if (!imx_drm_fb) {
+		dev_err(dev->dev, "failed to allocate drm framebuffer.\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	fb = &imx_drm_fb->fb;
+	ret = drm_framebuffer_init(dev, fb, &imx_drm_fb_funcs);
+	if (ret) {
+		dev_err(dev->dev, "failed to initialize framebuffer.\n");
+		goto err_init;
+	}
+
+	dev_dbg(dev->dev, "create: fb id: %d\n", fb->base.id);
+
+	size = mode_cmd->pitches[0] * mode_cmd->height;
+
+	/*
+	 * without file_priv we are called from imx_drm_fbdev_create in which
+	 * case we only create a framebuffer without a handle.
+	 */
+	if (!file_priv) {
+		struct imx_drm_buf_entry *entry;
+
+		entry = imx_drm_buf_create(dev, size);
+		if (IS_ERR(entry)) {
+			ret = PTR_ERR(entry);
+			goto err_buffer;
+		}
+
+		imx_drm_fb->entry = entry;
+	} else {
+		obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
+		if (!obj) {
+			ret = -EINVAL;
+			goto err_buffer;
+		}
+
+		imx_drm_fb->imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
+
+		drm_gem_object_unreference_unlocked(obj);
+
+		imx_drm_fb->entry = imx_drm_fb->imx_drm_gem_obj->entry;
+	}
+
+	drm_helper_mode_fill_fb_struct(fb, mode_cmd);
+
+	return fb;
+
+err_buffer:
+	drm_framebuffer_cleanup(fb);
+
+err_init:
+	kfree(imx_drm_fb);
+
+	return ERR_PTR(ret);
+}
+
+struct imx_drm_buf_entry *imx_drm_fb_get_buf(struct drm_framebuffer *fb)
+{
+	struct imx_drm_fb *imx_drm_fb = to_imx_drm_fb(fb);
+	struct imx_drm_buf_entry *entry;
+
+	entry = imx_drm_fb->entry;
+
+	return entry;
+}
+EXPORT_SYMBOL_GPL(imx_drm_fb_get_buf);
+
+static struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
+	.fb_create = imx_drm_fb_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/gpu/drm/imx/imx-fbdev.c b/drivers/gpu/drm/imx/imx-fbdev.c
new file mode 100644
index 0000000..f038797
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-fbdev.c
@@ -0,0 +1,275 @@
+/*
+ * 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_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "imx-drm.h"
+
+#define MAX_CONNECTOR		4
+#define PREFERRED_BPP		16
+
+static struct fb_ops imx_drm_fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+	.fb_check_var	= drm_fb_helper_check_var,
+	.fb_set_par	= drm_fb_helper_set_par,
+	.fb_blank	= drm_fb_helper_blank,
+	.fb_pan_display	= drm_fb_helper_pan_display,
+	.fb_setcmap	= drm_fb_helper_setcmap,
+};
+
+static int imx_drm_fbdev_update(struct drm_fb_helper *helper,
+				     struct drm_framebuffer *fb,
+				     unsigned int fb_width,
+				     unsigned int fb_height)
+{
+	struct fb_info *fbi = helper->fbdev;
+	struct drm_device *drm = helper->dev;
+	struct imx_drm_buf_entry *entry;
+	unsigned int size = fb_width * fb_height * (fb->bits_per_pixel >> 3);
+	unsigned long offset;
+
+	drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
+	drm_fb_helper_fill_var(fbi, helper, fb_width, fb_height);
+
+	entry = imx_drm_fb_get_buf(fb);
+	if (!entry) {
+		dev_dbg(drm->dev, "entry is null.\n");
+		return -EFAULT;
+	}
+
+	offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
+	offset += fbi->var.yoffset * fb->pitches[0];
+
+	drm->mode_config.fb_base = entry->paddr;
+	fbi->screen_base = entry->vaddr + offset;
+	fbi->fix.smem_start = entry->paddr + offset;
+	fbi->screen_size = size;
+	fbi->fix.smem_len = size;
+	fbi->flags |= FBINFO_CAN_FORCE_OUTPUT;
+
+	return 0;
+}
+
+static int imx_drm_fbdev_create(struct drm_fb_helper *helper,
+				    struct drm_fb_helper_surface_size *sizes)
+{
+	struct drm_device *drm = helper->dev;
+	struct fb_info *fbi;
+	struct drm_framebuffer *fb;
+	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+	struct platform_device *pdev = drm->platformdev;
+	int ret;
+
+	dev_dbg(drm->dev, "surface width(%d), height(%d) and bpp(%d\n",
+			sizes->surface_width, sizes->surface_height,
+			sizes->surface_bpp);
+
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.height = sizes->surface_height;
+	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+			sizes->surface_depth);
+
+	mutex_lock(&drm->struct_mutex);
+
+	fbi = framebuffer_alloc(0, &pdev->dev);
+	if (!fbi) {
+		ret = -ENOMEM;
+		goto err_fb_alloc;
+	}
+
+	fb = drm->mode_config.funcs->fb_create(drm, NULL, &mode_cmd);
+	if (IS_ERR(fb)) {
+		dev_err(drm->dev, "failed to create drm framebuffer.\n");
+		ret = PTR_ERR(fb);
+		goto err_fb_create;
+	}
+
+	helper->fb = fb;
+	helper->fbdev = fbi;
+
+	fbi->par = helper;
+	fbi->flags = FBINFO_FLAG_DEFAULT;
+	fbi->fbops = &imx_drm_fb_ops;
+
+	ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+	if (ret)
+		goto err_alloc_cmap;
+
+	ret = imx_drm_fbdev_update(helper, helper->fb, sizes->fb_width,
+			sizes->fb_height);
+	if (ret)
+		goto err_fbdev_update;
+
+	mutex_unlock(&drm->struct_mutex);
+
+	return 0;
+
+err_fbdev_update:
+	fb_dealloc_cmap(&fbi->cmap);
+
+err_alloc_cmap:
+	fb->funcs->destroy(fb);
+
+err_fb_create:
+	framebuffer_release(fbi);
+
+err_fb_alloc:
+	mutex_unlock(&drm->struct_mutex);
+
+	return ret;
+}
+
+static int imx_drm_fbdev_probe(struct drm_fb_helper *helper,
+				   struct drm_fb_helper_surface_size *sizes)
+{
+	int ret;
+
+	BUG_ON(helper->fb);
+
+	ret = imx_drm_fbdev_create(helper, sizes);
+	if (ret) {
+		dev_err(helper->dev->dev, "creating fbdev failed with %d\n", ret);
+		return ret;
+	}
+
+	/*
+	 * fb_helper expects a value more than 1 if succeed
+	 * because register_framebuffer() should be called.
+	 */
+	return 1;
+}
+
+static struct drm_fb_helper_funcs imx_drm_fb_helper_funcs = {
+	.fb_probe = imx_drm_fbdev_probe,
+};
+
+static struct drm_fb_helper *imx_drm_fbdev_init(struct drm_device *drm,
+		int preferred_bpp)
+{
+	struct drm_fb_helper *helper;
+	unsigned int num_crtc;
+	int ret;
+
+	helper = kzalloc(sizeof(*helper), GFP_KERNEL);
+	if (!helper)
+		return NULL;
+
+	helper->funcs = &imx_drm_fb_helper_funcs;
+
+	num_crtc = drm->mode_config.num_crtc;
+
+	ret = drm_fb_helper_init(drm, helper, num_crtc, MAX_CONNECTOR);
+	if (ret) {
+		dev_err(drm->dev, "initializing drm fb helper failed with %d\n",
+				ret);
+		goto err_init;
+	}
+
+	ret = drm_fb_helper_single_add_all_connectors(helper);
+	if (ret) {
+		dev_err(drm->dev, "registering drm_fb_helper_connector failed with %d\n",
+				ret);
+		goto err_setup;
+
+	}
+
+	ret = drm_fb_helper_initial_config(helper, preferred_bpp);
+	if (ret) {
+		dev_err(drm->dev, "initial config failed with %d\n", ret);
+		goto err_setup;
+	}
+
+	return helper;
+
+err_setup:
+	drm_fb_helper_fini(helper);
+
+err_init:
+	kfree(helper);
+
+	return NULL;
+}
+
+static void imx_drm_fbdev_fini(struct drm_fb_helper *helper)
+{
+	struct imx_drm_buf_entry *entry;
+
+	if (helper->fbdev) {
+		struct fb_info *info;
+		int ret;
+
+		info = helper->fbdev;
+		ret = unregister_framebuffer(info);
+		if (ret)
+			dev_err(helper->dev->dev, "unregister_framebuffer failed with %d\n",
+					ret);
+
+		if (info->cmap.len)
+			fb_dealloc_cmap(&info->cmap);
+
+		entry = imx_drm_fb_get_buf(helper->fb);
+
+		imx_drm_buf_destroy(helper->dev, entry);
+
+		framebuffer_release(info);
+	}
+
+	drm_fb_helper_fini(helper);
+
+	kfree(helper);
+}
+
+static struct drm_fb_helper *imx_fb_helper;
+
+static int __init imx_fb_helper_init(void)
+{
+	struct drm_device *drm = imx_drm_device_get();
+	int ret;
+
+	if (!drm)
+		return -EINVAL;
+
+	imx_fb_helper = imx_drm_fbdev_init(drm, PREFERRED_BPP);
+	if (!imx_fb_helper) {
+		imx_drm_device_put();
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void __exit imx_fb_helper_exit(void)
+{
+	imx_drm_fbdev_fini(imx_fb_helper);
+	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/gpu/drm/imx/imx-gem.c b/drivers/gpu/drm/imx/imx-gem.c
new file mode 100644
index 0000000..b0866fb
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-gem.c
@@ -0,0 +1,343 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "imx-drm.h"
+
+static int lowlevel_buffer_allocate(struct drm_device *drm,
+		struct imx_drm_buf_entry *entry)
+{
+	entry->vaddr = dma_alloc_writecombine(drm->dev, entry->size,
+			(dma_addr_t *)&entry->paddr, GFP_KERNEL);
+	if (!entry->vaddr) {
+		dev_err(drm->dev, "failed to allocate buffer.\n");
+		return -ENOMEM;
+	}
+
+	dev_dbg(drm->dev, "allocated : vaddr(0x%x), paddr(0x%x), size(0x%x)\n",
+			(unsigned int)entry->vaddr, entry->paddr, entry->size);
+
+	return 0;
+}
+
+static void lowlevel_buffer_free(struct drm_device *drm,
+		struct imx_drm_buf_entry *entry)
+{
+	dma_free_writecombine(drm->dev, entry->size, entry->vaddr,
+			entry->paddr);
+}
+
+struct imx_drm_buf_entry *imx_drm_buf_create(struct drm_device *drm,
+		unsigned int size)
+{
+	struct imx_drm_buf_entry *entry;
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return ERR_PTR(-ENOMEM);
+
+	entry->size = size;
+
+	/*
+	 * allocate memory region with size and set the memory information
+	 * to vaddr and paddr of a entry object.
+	 */
+	if (lowlevel_buffer_allocate(drm, entry) < 0) {
+		kfree(entry);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	return entry;
+}
+
+void imx_drm_buf_destroy(struct drm_device *drm,
+		struct imx_drm_buf_entry *entry)
+{
+	lowlevel_buffer_free(drm, entry);
+
+	kfree(entry);
+	entry = NULL;
+}
+EXPORT_SYMBOL_GPL(imx_drm_buf_destroy);
+
+static unsigned int convert_to_vm_err_msg(int msg)
+{
+	unsigned int out_msg;
+
+	switch (msg) {
+	case 0:
+	case -ERESTARTSYS:
+	case -EINTR:
+		out_msg = VM_FAULT_NOPAGE;
+		break;
+
+	case -ENOMEM:
+		out_msg = VM_FAULT_OOM;
+		break;
+
+	default:
+		out_msg = VM_FAULT_SIGBUS;
+		break;
+	}
+
+	return out_msg;
+}
+
+static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
+{
+	return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
+}
+
+static struct imx_drm_gem_obj *imx_drm_gem_create(struct drm_device *drm,
+		unsigned int size)
+{
+	struct imx_drm_gem_obj *imx_drm_gem_obj;
+	struct imx_drm_buf_entry *entry;
+	struct drm_gem_object *obj;
+	int ret;
+
+	size = roundup(size, PAGE_SIZE);
+
+	imx_drm_gem_obj = kzalloc(sizeof(*imx_drm_gem_obj), GFP_KERNEL);
+	if (!imx_drm_gem_obj)
+		return ERR_PTR(-ENOMEM);
+
+	/* allocate the new buffer object and memory region. */
+	entry = imx_drm_buf_create(drm, size);
+	if (!entry) {
+		ret = -ENOMEM;
+		goto err_alloc;
+	}
+
+	imx_drm_gem_obj->entry = entry;
+
+	obj = &imx_drm_gem_obj->base;
+
+	ret = drm_gem_object_init(drm, obj, size);
+	if (ret) {
+		dev_err(drm->dev, "initializing GEM object failed with %d\n", ret);
+		goto err_obj_init;
+	}
+
+	ret = drm_gem_create_mmap_offset(obj);
+	if (ret) {
+		dev_err(drm->dev, "creating mmap offset failed with %d\n", ret);
+		goto err_create_mmap_offset;
+	}
+
+	return imx_drm_gem_obj;
+
+err_create_mmap_offset:
+	drm_gem_object_release(obj);
+
+err_obj_init:
+	imx_drm_buf_destroy(drm, imx_drm_gem_obj->entry);
+
+err_alloc:
+	kfree(imx_drm_gem_obj);
+
+	return ERR_PTR(ret);
+}
+
+static struct imx_drm_gem_obj *imx_drm_gem_create_with_handle(struct drm_file *file_priv,
+		struct drm_device *drm, unsigned int size,
+		unsigned int *handle)
+{
+	struct imx_drm_gem_obj *imx_drm_gem_obj;
+	struct drm_gem_object *obj;
+	int ret;
+
+	imx_drm_gem_obj = imx_drm_gem_create(drm, size);
+	if (IS_ERR(imx_drm_gem_obj))
+		return imx_drm_gem_obj;
+
+	obj = &imx_drm_gem_obj->base;
+
+	/*
+	 * allocate a id of idr table where the obj is registered
+	 * and handle has the id what user can see.
+	 */
+	ret = drm_gem_handle_create(file_priv, obj, handle);
+	if (ret)
+		goto err_handle_create;
+
+	/* drop reference from allocate - handle holds it now. */
+	drm_gem_object_unreference_unlocked(obj);
+
+	return imx_drm_gem_obj;
+
+err_handle_create:
+	imx_drm_gem_free_object(obj);
+
+	return ERR_PTR(ret);
+}
+
+static int imx_drm_gem_mmap_buffer(struct file *filp,
+		struct vm_area_struct *vma)
+{
+	struct drm_gem_object *obj = filp->private_data;
+	struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
+	struct imx_drm_buf_entry *entry;
+	unsigned long pfn, vm_size;
+
+	vma->vm_flags |= VM_IO | VM_RESERVED;
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	vma->vm_file = filp;
+
+	vm_size = vma->vm_end - vma->vm_start;
+	/*
+	 * an entry contains information to physically continuous memory
+	 * allocated by user request or at framebuffer creation.
+	 */
+	entry = imx_drm_gem_obj->entry;
+
+	/* check if user-requested size is valid. */
+	if (vm_size > entry->size)
+		return -EINVAL;
+
+	/*
+	 * get page frame number to physical memory to be mapped
+	 * to user space.
+	 */
+	pfn = imx_drm_gem_obj->entry->paddr >> PAGE_SHIFT;
+
+	if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
+				vma->vm_page_prot)) {
+		dev_err(obj->dev->dev, "failed to remap pfn range.\n");
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+
+static const struct file_operations imx_drm_gem_fops = {
+	.mmap = imx_drm_gem_mmap_buffer,
+};
+
+int imx_drm_gem_init_object(struct drm_gem_object *obj)
+{
+	return 0;
+}
+
+void imx_drm_gem_free_object(struct drm_gem_object *gem_obj)
+{
+	struct imx_drm_gem_obj *imx_drm_gem_obj;
+
+	if (gem_obj->map_list.map)
+		drm_gem_free_mmap_offset(gem_obj);
+
+	drm_gem_object_release(gem_obj);
+
+	imx_drm_gem_obj = to_imx_drm_gem_obj(gem_obj);
+
+	imx_drm_buf_destroy(gem_obj->dev, imx_drm_gem_obj->entry);
+
+	kfree(imx_drm_gem_obj);
+}
+
+int imx_drm_gem_dumb_create(struct drm_file *file_priv,
+		struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+	struct imx_drm_gem_obj *imx_drm_gem_obj;
+
+	/* FIXME: This should be configured by the crtc driver */
+	args->pitch = args->width * args->bpp >> 3;
+	args->size = args->pitch * args->height;
+
+	imx_drm_gem_obj = imx_drm_gem_create_with_handle(file_priv, dev, args->size,
+			&args->handle);
+	if (IS_ERR(imx_drm_gem_obj))
+		return PTR_ERR(imx_drm_gem_obj);
+
+	return 0;
+}
+
+int imx_drm_gem_dumb_map_offset(struct drm_file *file_priv,
+		struct drm_device *drm, uint32_t handle, uint64_t *offset)
+{
+	struct imx_drm_gem_obj *imx_drm_gem_obj;
+	struct drm_gem_object *obj;
+
+	mutex_lock(&drm->struct_mutex);
+
+	obj = drm_gem_object_lookup(drm, file_priv, handle);
+	if (!obj) {
+		dev_err(drm->dev, "failed to lookup gem object\n");
+		mutex_unlock(&drm->struct_mutex);
+		return -EINVAL;
+	}
+
+	imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
+
+	*offset = get_gem_mmap_offset(&imx_drm_gem_obj->base);
+
+	drm_gem_object_unreference(obj);
+
+	mutex_unlock(&drm->struct_mutex);
+
+	return 0;
+}
+
+int imx_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	struct drm_gem_object *obj = vma->vm_private_data;
+	struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
+	struct drm_device *dev = obj->dev;
+	unsigned long pfn;
+	pgoff_t page_offset;
+	int ret;
+
+	page_offset = ((unsigned long)vmf->virtual_address -
+			vma->vm_start) >> PAGE_SHIFT;
+
+	mutex_lock(&dev->struct_mutex);
+
+	pfn = (imx_drm_gem_obj->entry->paddr >> PAGE_SHIFT) + page_offset;
+
+	ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
+
+	mutex_unlock(&dev->struct_mutex);
+
+	return convert_to_vm_err_msg(ret);
+}
+
+int imx_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	int ret;
+
+	ret = drm_gem_mmap(filp, vma);
+	if (ret)
+		return ret;
+
+	vma->vm_flags &= ~VM_PFNMAP;
+	vma->vm_flags |= VM_MIXEDMAP;
+
+	return ret;
+}
+
+
+int imx_drm_gem_dumb_destroy(struct drm_file *file_priv,
+		struct drm_device *dev, unsigned int handle)
+{
+	return drm_gem_handle_delete(file_priv, handle);
+}
diff --git a/drivers/gpu/drm/imx/imx-lcdc-crtc.c b/drivers/gpu/drm/imx/imx-lcdc-crtc.c
new file mode 100644
index 0000000..e77c015
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-lcdc-crtc.c
@@ -0,0 +1,517 @@
+/*
+ * i.MX LCDC crtc driver
+ *
+ * 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.
+ *
+ */
+#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 <asm/fb.h>
+#include <linux/module.h>
+#include <mach/hardware.h>
+#include <mach/imxfb.h>
+#include <generated/mach-types.h>
+
+#include "imx-drm.h"
+
+#define LCDC_SSA		0x00
+#define LCDC_SIZE		0x04
+#define LCDC_VPW		0x08
+#define LCDC_CPOS		0x0C
+#define LCDC_LCWHB		0x10
+#define LCDC_LCHCC		0x14
+#define LCDC_PCR		0x18
+#define LCDC_HCR		0x1C
+#define LCDC_VCR		0x20
+#define LCDC_POS		0x24
+#define LCDC_LSCR1		0x28
+#define LCDC_PWMR		0x2C
+#define LCDC_DMACR		0x30
+#define LCDC_RMCR		0x34
+#define LCDC_LCDICR		0x38
+#define LCDC_LIER		0x3c
+#define LCDC_LISR		0x40
+
+#define SIZE_XMAX(x)		((((x) >> 4) & 0x3f) << 20)
+
+#define YMAX_MASK		(cpu_is_mx1() ? 0x1ff : 0x3ff)
+#define SIZE_YMAX(y)		((y) & YMAX_MASK)
+
+#define VPW_VPW(x)		((x) & 0x3ff)
+
+#define HCR_H_WIDTH(x)		(((x) & 0x3f) << 26)
+#define HCR_H_WAIT_1(x)		(((x) & 0xff) << 8)
+#define HCR_H_WAIT_2(x)		((x) & 0xff)
+
+#define VCR_V_WIDTH(x)		(((x) & 0x3f) << 26)
+#define VCR_V_WAIT_1(x)		(((x) & 0xff) << 8)
+#define VCR_V_WAIT_2(x)		((x) & 0xff)
+
+#define RMCR_LCDC_EN_MX1	(1 << 1)
+
+#define RMCR_SELF_REF		(1 << 0)
+
+#define LIER_EOF		(1 << 1)
+
+struct imx_crtc {
+	struct drm_crtc		base;
+	struct imx_drm_crtc	*imx_drm_crtc;
+	int			di_no;
+	int			enabled;
+	void __iomem		*regs;
+	u32			pwmr;
+	u32			lscr1;
+	u32			dmacr;
+	u32			pcr;
+	struct clk		*clk;
+	struct device		*dev;
+	int			vblank_enable;
+
+	struct drm_pending_vblank_event *page_flip_event;
+	struct drm_framebuffer	*newfb;
+};
+
+#define to_imx_crtc(x) container_of(x, struct imx_crtc, base)
+
+static void imx_crtc_load_lut(struct drm_crtc *crtc)
+{
+}
+
+#define PCR_BPIX_8		(3 << 25)
+#define PCR_BPIX_12		(4 << 25)
+#define PCR_BPIX_16		(5 << 25)
+#define PCR_BPIX_18		(6 << 25)
+#define PCR_END_SEL		(1 << 18)
+#define PCR_END_BYTE_SWAP	(1 << 17)
+
+const char *fourcc_to_str(u32 fourcc)
+{
+	static char buf[5];
+
+	*(u32 *)buf = fourcc;
+	buf[4] = 0;
+
+	return buf;
+}
+
+static int imx_drm_crtc_set(struct drm_crtc *crtc,
+		struct drm_display_mode *mode)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+	struct drm_framebuffer *fb = crtc->fb;
+	int lower_margin = mode->vsync_start - mode->vdisplay;
+	int upper_margin = mode->vtotal - mode->vsync_end;
+	int vsync_len = mode->vsync_end - mode->vsync_start;
+	int hsync_len = mode->hsync_end - mode->hsync_start;
+	int right_margin = mode->hsync_start - mode->hdisplay;
+	int left_margin = mode->htotal - mode->hsync_end;
+	unsigned long lcd_clk;
+	u32 pcr;
+
+	lcd_clk = clk_get_rate(imx_crtc->clk) / 1000;
+
+	if (!mode->clock)
+		return -EINVAL;
+
+	pcr = DIV_ROUND_CLOSEST(lcd_clk, mode->clock);
+	if (--pcr > 0x3f)
+		pcr = 0x3f;
+
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+		pcr |= PCR_BPIX_18;
+		pcr |= PCR_END_SEL | PCR_END_BYTE_SWAP;
+		break;
+	case DRM_FORMAT_RGB565:
+		if (cpu_is_mx1())
+			pcr |= PCR_BPIX_12;
+		else
+			pcr |= PCR_BPIX_16;
+		break;
+	case DRM_FORMAT_RGB332:
+		pcr |= PCR_BPIX_8;
+		break;
+	default:
+		dev_err(imx_crtc->dev, "unsupported pixel format %s\n",
+				fourcc_to_str(fb->pixel_format));
+		return -EINVAL;
+	}
+
+	/* add sync polarities */
+	pcr |= imx_crtc->pcr & ~(0x3f | (7 << 25));
+
+	dev_dbg(imx_crtc->dev,
+			"xres=%d hsync_len=%d left_margin=%d right_margin=%d\n",
+			mode->hdisplay, hsync_len,
+			left_margin, right_margin);
+	dev_dbg(imx_crtc->dev,
+			"yres=%d vsync_len=%d upper_margin=%d lower_margin=%d\n",
+			mode->vdisplay, vsync_len,
+			upper_margin, lower_margin);
+
+	writel(VPW_VPW(mode->hdisplay * fb->bits_per_pixel / 8 / 4),
+		imx_crtc->regs + LCDC_VPW);
+
+	writel(HCR_H_WIDTH(hsync_len - 1) |
+		HCR_H_WAIT_1(right_margin - 1) |
+		HCR_H_WAIT_2(left_margin - 3),
+		imx_crtc->regs + LCDC_HCR);
+
+	writel(VCR_V_WIDTH(vsync_len) |
+		VCR_V_WAIT_1(lower_margin) |
+		VCR_V_WAIT_2(upper_margin),
+		imx_crtc->regs + LCDC_VCR);
+
+	writel(SIZE_XMAX(mode->hdisplay) | SIZE_YMAX(mode->vdisplay),
+			imx_crtc->regs + LCDC_SIZE);
+
+	writel(pcr, imx_crtc->regs + LCDC_PCR);
+	writel(imx_crtc->pwmr, imx_crtc->regs + LCDC_PWMR);
+	writel(imx_crtc->lscr1, imx_crtc->regs + LCDC_LSCR1);
+	/* reset default */
+	writel(0x00040060, imx_crtc->regs + LCDC_DMACR);
+
+	return 0;
+}
+
+static int imx_drm_set_base(struct drm_crtc *crtc, int x, int y)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+	struct imx_drm_buf_entry *entry;
+	struct drm_framebuffer *fb = crtc->fb;
+	unsigned long phys;
+
+	entry = imx_drm_fb_get_buf(fb);
+	if (!entry)
+		return -EFAULT;
+
+	phys = entry->paddr;
+	phys += x * (fb->bits_per_pixel >> 3);
+	phys += y * fb->pitches[0];
+
+	dev_dbg(imx_crtc->dev, "%s: phys: 0x%lx\n", __func__, phys);
+	dev_dbg(imx_crtc->dev, "%s: xy: %dx%d\n", __func__, x, y);
+
+	writel(phys, imx_crtc->regs + LCDC_SSA);
+
+	return 0;
+}
+
+static int imx_crtc_mode_set(struct drm_crtc *crtc,
+			       struct drm_display_mode *mode,
+			       struct drm_display_mode *adjusted_mode,
+			       int x, int y,
+			       struct drm_framebuffer *old_fb)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	imx_drm_set_base(crtc, x, y);
+
+	dev_dbg(imx_crtc->dev, "mode->hdisplay: %d\n", mode->hdisplay);
+	dev_dbg(imx_crtc->dev, "mode->vdisplay: %d\n", mode->vdisplay);
+
+	return imx_drm_crtc_set(crtc, mode);
+}
+
+static void imx_crtc_enable(struct imx_crtc *imx_crtc)
+{
+	if (!imx_crtc->enabled)
+		clk_enable(imx_crtc->clk);
+	imx_crtc->enabled = 1;
+}
+
+static void imx_crtc_disable(struct imx_crtc *imx_crtc)
+{
+	if (imx_crtc->enabled)
+		clk_disable(imx_crtc->clk);
+	imx_crtc->enabled = 0;
+}
+
+static void imx_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	dev_dbg(imx_crtc->dev, "%s mode: %d\n", __func__, mode);
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		imx_crtc_enable(imx_crtc);
+		break;
+	default:
+		imx_crtc_disable(imx_crtc);
+		break;
+	}
+}
+
+static bool imx_crtc_mode_fixup(struct drm_crtc *crtc,
+				  struct drm_display_mode *mode,
+				  struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void imx_crtc_prepare(struct drm_crtc *crtc)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	imx_crtc_disable(imx_crtc);
+}
+
+static void imx_crtc_commit(struct drm_crtc *crtc)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	imx_crtc_enable(imx_crtc);
+}
+
+static struct drm_crtc_helper_funcs imx_helper_funcs = {
+	.dpms = imx_crtc_dpms,
+	.mode_fixup = imx_crtc_mode_fixup,
+	.mode_set = imx_crtc_mode_set,
+	.prepare = imx_crtc_prepare,
+	.commit = imx_crtc_commit,
+	.load_lut = imx_crtc_load_lut,
+};
+
+static void imx_drm_handle_pageflip(struct imx_crtc *imx_crtc)
+{
+	struct drm_pending_vblank_event *e;
+	struct timeval now;
+	unsigned long flags;
+	struct drm_device *drm = imx_crtc->base.dev;
+
+	spin_lock_irqsave(&drm->event_lock, flags);
+
+	e = imx_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;
+	imx_crtc->page_flip_event = NULL;
+
+	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 int imx_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	writel(LIER_EOF, imx_crtc->regs + LCDC_LIER);
+
+	return 0;
+}
+
+static void imx_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	writel(0, imx_crtc->regs + LCDC_LIER);
+}
+
+static irqreturn_t imx_irq_handler(int irq, void *dev_id)
+{
+	struct imx_crtc *imx_crtc = dev_id;
+	struct drm_device *drm = imx_crtc->base.dev;
+
+	/* Acknowledge interrupt */
+	readl(imx_crtc->regs + LCDC_LISR);
+
+	drm_handle_vblank(drm, 0);
+
+	if (imx_crtc->newfb) {
+		imx_crtc->base.fb = imx_crtc->newfb;
+		imx_crtc->newfb = NULL;
+		imx_drm_set_base(&imx_crtc->base, 0, 0);
+		imx_drm_handle_pageflip(imx_crtc);
+		imx_drm_crtc_vblank_put(imx_crtc->imx_drm_crtc);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int imx_page_flip(struct drm_crtc *crtc,
+                         struct drm_framebuffer *fb,
+                         struct drm_pending_vblank_event *event)
+{
+	struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
+
+	if (imx_crtc->newfb)
+		return -EBUSY;
+
+	imx_crtc->newfb = fb;
+	imx_crtc->page_flip_event = event;
+	imx_drm_crtc_vblank_get(imx_crtc->imx_drm_crtc);
+
+	return 0;
+}
+
+static const struct drm_crtc_funcs imx_crtc_funcs = {
+	.page_flip = imx_page_flip,
+};
+
+static const struct imx_drm_crtc_helper_funcs imx_imx_drm_helper = {
+	.enable_vblank = imx_crtc_enable_vblank,
+	.disable_vblank = imx_crtc_disable_vblank,
+};
+
+#define DRIVER_NAME "imx-lcdc-crtc"
+
+/*
+ * the pcr bits to be allowed to set in platform data
+ */
+#define PDATA_PCR	(PCR_PIXPOL | PCR_FLMPOL | PCR_LPPOL | \
+			PCR_CLKPOL | PCR_OEPOL | PCR_TFT | PCR_COLOR | \
+			PCR_PBSIZ_8 | PCR_ACD(0x7f) | PCR_ACD_SEL | \
+			PCR_SCLK_SEL | PCR_SHARP)
+
+static int __devinit imx_crtc_probe(struct platform_device *pdev)
+{
+	struct imx_crtc *imx_crtc;
+	struct resource *res;
+	int ret, irq;
+	u32 pcr_value = 0xf00080c0;
+	u32 lscr1_value = 0x00120300;
+	u32 pwmr_value = 0x00a903ff;
+
+	imx_crtc = devm_kzalloc(&pdev->dev, sizeof(*imx_crtc), GFP_KERNEL);
+	if (!imx_crtc)
+		return -ENOMEM;
+
+	imx_crtc->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	res = devm_request_mem_region(&pdev->dev, res->start,
+			resource_size(res), DRIVER_NAME);
+	if (!res)
+		return -EBUSY;
+
+	imx_crtc->regs = devm_ioremap(&pdev->dev, res->start,
+			resource_size(res));
+	if (!imx_crtc->regs) {
+		dev_err(&pdev->dev, "Cannot map frame buffer registers\n");
+		return -EBUSY;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_irq(&pdev->dev, irq, imx_irq_handler, 0, "imx_drm",
+			imx_crtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "irq request failed with %d\n", ret);
+		return ret;
+	}
+
+	imx_crtc->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(imx_crtc->clk)) {
+		ret = PTR_ERR(imx_crtc->clk);
+		dev_err(&pdev->dev, "unable to get clock: %d\n", ret);
+		return ret;
+	}
+
+	clk_prepare_enable(imx_crtc->clk);
+	imx_crtc->enabled = 1;
+
+	platform_set_drvdata(pdev, imx_crtc);
+
+	imx_crtc->pcr = pcr_value & PDATA_PCR;
+
+	if (imx_crtc->pcr != pcr_value)
+		dev_err(&pdev->dev, "invalid bits set in pcr: 0x%08x\n",
+				pcr_value & ~PDATA_PCR);
+
+	imx_crtc->lscr1 = lscr1_value;
+	imx_crtc->pwmr = pwmr_value;
+
+	ret = imx_drm_add_crtc(&imx_crtc->base,
+			&imx_crtc->imx_drm_crtc,
+			&imx_crtc_funcs, &imx_helper_funcs,
+			&imx_imx_drm_helper, THIS_MODULE);
+	if (ret)
+		goto err_init;
+
+	dev_info(&pdev->dev, "probed\n");
+
+	return 0;
+
+err_init:
+	clk_disable_unprepare(imx_crtc->clk);
+	clk_put(imx_crtc->clk);
+
+	return ret;
+}
+
+static int __devexit imx_crtc_remove(struct platform_device *pdev)
+{
+	struct imx_crtc *imx_crtc = platform_get_drvdata(pdev);
+
+	imx_drm_remove_crtc(imx_crtc->imx_drm_crtc);
+
+	writel(0, imx_crtc->regs + LCDC_LIER);
+
+	clk_disable_unprepare(imx_crtc->clk);
+	clk_put(imx_crtc->clk);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id imx_lcdc_dt_ids[] = {
+	{ .compatible = "fsl,imx1-lcdc", .data = NULL, },
+	{ .compatible = "fsl,imx21-lcdc", .data = NULL, },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver imx_crtc_driver = {
+	.remove		= __devexit_p(imx_crtc_remove),
+	.probe		= imx_crtc_probe,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = imx_lcdc_dt_ids,
+	},
+};
+
+static int __init imx_lcdc_init(void)
+{
+	return platform_driver_register(&imx_crtc_driver);
+}
+
+static void __exit imx_lcdc_exit(void)
+{
+	platform_driver_unregister(&imx_crtc_driver);
+}
+
+module_init(imx_lcdc_init);
+module_exit(imx_lcdc_exit)
+
+MODULE_DESCRIPTION("Freescale i.MX framebuffer driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/imx/imx-parallel-display.c b/drivers/gpu/drm/imx/imx-parallel-display.c
new file mode 100644
index 0000000..8c96113
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-parallel-display.c
@@ -0,0 +1,228 @@
+/*
+ * 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 "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;
+};
+
+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 ret;
+
+	drm_mode_connector_update_edid_property(connector, imxpd->edid);
+	ret = drm_add_edid_modes(connector, imxpd->edid);
+	connector->display_info.raw_edid = NULL;
+
+	return ret;
+}
+
+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,
+			   struct drm_display_mode *mode,
+			   struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void imx_pd_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+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 */
+}
+
+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,
+};
+
+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;
+
+	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;
+
+	imxpd = devm_kzalloc(&pdev->dev, sizeof(*imxpd), GFP_KERNEL);
+	if (!imxpd)
+		return -ENOMEM;
+
+	edidp = of_get_property(np, "edid", &imxpd->edid_len);
+
+	imxpd->edid = kmemdup(edidp, imxpd->edid_len, GFP_KERNEL);
+	imxpd->encoder.possible_crtcs = 0x1;
+	imxpd->encoder.possible_clones = 0x1;
+	imxpd->dev = &pdev->dev;
+
+	ret = imx_pd_register(imxpd);
+	if (ret)
+		return ret;
+
+	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", .data = NULL, },
+	{ /* 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");
-- 
1.7.10

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 2/2] pcm038 lcdc support
  2012-05-18 12:27 ` (unknown), Sascha Hauer
@ 2012-05-18 12:27   ` Sascha Hauer
  -1 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-18 12:27 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm/boot/dts/imx27-phytec-phycore.dts |   39 ++++++++++++++++++++++++++++
 arch/arm/boot/dts/imx27.dtsi               |    7 +++++
 arch/arm/mach-imx/clock-imx27.c            |    1 +
 3 files changed, 47 insertions(+)

diff --git a/arch/arm/boot/dts/imx27-phytec-phycore.dts b/arch/arm/boot/dts/imx27-phytec-phycore.dts
index a51a08f..bdb7547 100644
--- a/arch/arm/boot/dts/imx27-phytec-phycore.dts
+++ b/arch/arm/boot/dts/imx27-phytec-phycore.dts
@@ -20,6 +20,41 @@
 		reg = <0x0 0x0>;
 	};
 
+	baseboard {
+		compatible = "simple-bus";
+		#address-cells = <2>;
+                #size-cells = <1>;
+
+		display {
+			compatible = "fsl,imx-parallel-display";
+			edid = [00 ff ff ff ff ff ff 00 4c 2d 6c 03 36 32 49 4b
+				0f 13 01 03 80 37 22 a0 2a fe 21 a8 53 37 ae 24
+				11 50 54
+
+				/* est timings */
+				00 00 00
+
+				/* std timings */
+				00 00
+				00 00
+				00 00
+				00 00
+				00 00
+				00 00
+				00 00
+				00 00
+
+				/* detailed timings */
+				05 0D 20 A0 30 58 1C 20 28 20 14 00 26 57 21 00 00 1E
+				00 00 00 fd 00 32 4b 1b	51 11 00 0a 20 20 20 20 20 20
+				00 00 00 fc 00 53 79 6e 63 4d 61 73 74 65 72 0a 20 20
+				00 00 00 ff 00 48 39 58 53 34 30 30 34 34 32 0a 20 20
+				00 20];
+
+			crtc = <&lcdc 0>;
+		};
+	};
+
 	soc {
 		aipi at 10000000 { /* aipi */
 
@@ -46,6 +81,10 @@
 				status = "okay";
 			};
 
+			lcdc at 10021000 {
+				status = "okay";
+			};
+
 			i2c at 1001d000 {
 				clock-frequency = <400000>;
 				status = "okay";
diff --git a/arch/arm/boot/dts/imx27.dtsi b/arch/arm/boot/dts/imx27.dtsi
index bc5e7d5..eab9095 100644
--- a/arch/arm/boot/dts/imx27.dtsi
+++ b/arch/arm/boot/dts/imx27.dtsi
@@ -206,6 +206,13 @@
 				status = "disabled";
 			};
 
+			lcdc: lcdc at 10021000 {
+				compatible = "fsl,imx27-lcdc", "fsl,imx21-lcdc";
+				reg = <0x10021000 0x4000>;
+				interrupts = <61>;
+				status = "enabled";
+			};
+
 			fec: fec at 1002b000 {
 				compatible = "fsl,imx27-fec";
 				reg = <0x1002b000 0x4000>;
diff --git a/arch/arm/mach-imx/clock-imx27.c b/arch/arm/mach-imx/clock-imx27.c
index 98e04f5..a393483 100644
--- a/arch/arm/mach-imx/clock-imx27.c
+++ b/arch/arm/mach-imx/clock-imx27.c
@@ -646,6 +646,7 @@ static struct clk_lookup lookups[] = {
 	_REGISTER_CLOCK("imx27-cspi.1", NULL, cspi2_clk)
 	_REGISTER_CLOCK("imx27-cspi.2", NULL, cspi3_clk)
 	_REGISTER_CLOCK("imx-fb.0", NULL, lcdc_clk)
+	_REGISTER_CLOCK("10021000.lcdc", NULL, lcdc_clk)
 	_REGISTER_CLOCK("mx2-camera.0", NULL, csi_clk)
 	_REGISTER_CLOCK("fsl-usb2-udc", "usb", usb_clk)
 	_REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", usb_clk1)
-- 
1.7.10

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 2/2] pcm038 lcdc support
@ 2012-05-18 12:27   ` Sascha Hauer
  0 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-18 12:27 UTC (permalink / raw)
  To: dri-devel; +Cc: Sascha Hauer, linux-arm-kernel

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm/boot/dts/imx27-phytec-phycore.dts |   39 ++++++++++++++++++++++++++++
 arch/arm/boot/dts/imx27.dtsi               |    7 +++++
 arch/arm/mach-imx/clock-imx27.c            |    1 +
 3 files changed, 47 insertions(+)

diff --git a/arch/arm/boot/dts/imx27-phytec-phycore.dts b/arch/arm/boot/dts/imx27-phytec-phycore.dts
index a51a08f..bdb7547 100644
--- a/arch/arm/boot/dts/imx27-phytec-phycore.dts
+++ b/arch/arm/boot/dts/imx27-phytec-phycore.dts
@@ -20,6 +20,41 @@
 		reg = <0x0 0x0>;
 	};
 
+	baseboard {
+		compatible = "simple-bus";
+		#address-cells = <2>;
+                #size-cells = <1>;
+
+		display {
+			compatible = "fsl,imx-parallel-display";
+			edid = [00 ff ff ff ff ff ff 00 4c 2d 6c 03 36 32 49 4b
+				0f 13 01 03 80 37 22 a0 2a fe 21 a8 53 37 ae 24
+				11 50 54
+
+				/* est timings */
+				00 00 00
+
+				/* std timings */
+				00 00
+				00 00
+				00 00
+				00 00
+				00 00
+				00 00
+				00 00
+				00 00
+
+				/* detailed timings */
+				05 0D 20 A0 30 58 1C 20 28 20 14 00 26 57 21 00 00 1E
+				00 00 00 fd 00 32 4b 1b	51 11 00 0a 20 20 20 20 20 20
+				00 00 00 fc 00 53 79 6e 63 4d 61 73 74 65 72 0a 20 20
+				00 00 00 ff 00 48 39 58 53 34 30 30 34 34 32 0a 20 20
+				00 20];
+
+			crtc = <&lcdc 0>;
+		};
+	};
+
 	soc {
 		aipi@10000000 { /* aipi */
 
@@ -46,6 +81,10 @@
 				status = "okay";
 			};
 
+			lcdc@10021000 {
+				status = "okay";
+			};
+
 			i2c@1001d000 {
 				clock-frequency = <400000>;
 				status = "okay";
diff --git a/arch/arm/boot/dts/imx27.dtsi b/arch/arm/boot/dts/imx27.dtsi
index bc5e7d5..eab9095 100644
--- a/arch/arm/boot/dts/imx27.dtsi
+++ b/arch/arm/boot/dts/imx27.dtsi
@@ -206,6 +206,13 @@
 				status = "disabled";
 			};
 
+			lcdc: lcdc@10021000 {
+				compatible = "fsl,imx27-lcdc", "fsl,imx21-lcdc";
+				reg = <0x10021000 0x4000>;
+				interrupts = <61>;
+				status = "enabled";
+			};
+
 			fec: fec@1002b000 {
 				compatible = "fsl,imx27-fec";
 				reg = <0x1002b000 0x4000>;
diff --git a/arch/arm/mach-imx/clock-imx27.c b/arch/arm/mach-imx/clock-imx27.c
index 98e04f5..a393483 100644
--- a/arch/arm/mach-imx/clock-imx27.c
+++ b/arch/arm/mach-imx/clock-imx27.c
@@ -646,6 +646,7 @@ static struct clk_lookup lookups[] = {
 	_REGISTER_CLOCK("imx27-cspi.1", NULL, cspi2_clk)
 	_REGISTER_CLOCK("imx27-cspi.2", NULL, cspi3_clk)
 	_REGISTER_CLOCK("imx-fb.0", NULL, lcdc_clk)
+	_REGISTER_CLOCK("10021000.lcdc", NULL, lcdc_clk)
 	_REGISTER_CLOCK("mx2-camera.0", NULL, csi_clk)
 	_REGISTER_CLOCK("fsl-usb2-udc", "usb", usb_clk)
 	_REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", usb_clk1)
-- 
1.7.10

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 2/2] pcm038 lcdc support
  2012-05-18 12:27   ` Sascha Hauer
@ 2012-05-18 14:03     ` Adam Jackson
  -1 siblings, 0 replies; 26+ messages in thread
From: Adam Jackson @ 2012-05-18 14:03 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 2012-05-18 at 14:27 +0200, Sascha Hauer wrote:

> +			edid = [00 ff ff ff ff ff ff 00 4c 2d 6c 03 36 32 49 4b
> +				0f 13 01 03 80 37 22 a0 2a fe 21 a8 53 37 ae 24
> +				11 50 54
> +
> +				/* est timings */
> +				00 00 00
> +
> +				/* std timings */
> +				00 00
> +				00 00
> +				00 00
> +				00 00
> +				00 00
> +				00 00
> +				00 00
> +				00 00
> +
> +				/* detailed timings */
> +				05 0D 20 A0 30 58 1C 20 28 20 14 00 26 57 21 00 00 1E
> +				00 00 00 fd 00 32 4b 1b	51 11 00 0a 20 20 20 20 20 20
> +				00 00 00 fc 00 53 79 6e 63 4d 61 73 74 65 72 0a 20 20
> +				00 00 00 ff 00 48 39 58 53 34 30 30 34 34 32 0a 20 20
> +				00 20];

This EDID block claims to be a Samsung SyncMaster, which isn't really
the right thing to do.  The question is what to call it instead.  Red
Hat has a PNP ID we can use for virtual EDID blocks like this if we
want, I'd want to set up a little database to keep track of them but
that's pretty trivial.

Also, empty standard timing fields are 01 01, not 00 00.

- ajax
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: This is a digitally signed message part
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120518/70b0e903/attachment.sig>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/2] pcm038 lcdc support
@ 2012-05-18 14:03     ` Adam Jackson
  0 siblings, 0 replies; 26+ messages in thread
From: Adam Jackson @ 2012-05-18 14:03 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-arm-kernel, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 1062 bytes --]

On Fri, 2012-05-18 at 14:27 +0200, Sascha Hauer wrote:

> +			edid = [00 ff ff ff ff ff ff 00 4c 2d 6c 03 36 32 49 4b
> +				0f 13 01 03 80 37 22 a0 2a fe 21 a8 53 37 ae 24
> +				11 50 54
> +
> +				/* est timings */
> +				00 00 00
> +
> +				/* std timings */
> +				00 00
> +				00 00
> +				00 00
> +				00 00
> +				00 00
> +				00 00
> +				00 00
> +				00 00
> +
> +				/* detailed timings */
> +				05 0D 20 A0 30 58 1C 20 28 20 14 00 26 57 21 00 00 1E
> +				00 00 00 fd 00 32 4b 1b	51 11 00 0a 20 20 20 20 20 20
> +				00 00 00 fc 00 53 79 6e 63 4d 61 73 74 65 72 0a 20 20
> +				00 00 00 ff 00 48 39 58 53 34 30 30 34 34 32 0a 20 20
> +				00 20];

This EDID block claims to be a Samsung SyncMaster, which isn't really
the right thing to do.  The question is what to call it instead.  Red
Hat has a PNP ID we can use for virtual EDID blocks like this if we
want, I'd want to set up a little database to keep track of them but
that's pretty trivial.

Also, empty standard timing fields are 01 01, not 00 00.

- ajax

[-- Attachment #1.2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 2/2] pcm038 lcdc support
  2012-05-18 14:03     ` Adam Jackson
@ 2012-05-18 15:13       ` Sascha Hauer
  -1 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-18 15:13 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, May 18, 2012 at 10:03:54AM -0400, Adam Jackson wrote:
> On Fri, 2012-05-18 at 14:27 +0200, Sascha Hauer wrote:
> 
> > +			edid = [00 ff ff ff ff ff ff 00 4c 2d 6c 03 36 32 49 4b
> > +				0f 13 01 03 80 37 22 a0 2a fe 21 a8 53 37 ae 24
> > +				11 50 54
> > +
> > +				/* est timings */
> > +				00 00 00
> > +
> > +				/* std timings */
> > +				00 00
> > +				00 00
> > +				00 00
> > +				00 00
> > +				00 00
> > +				00 00
> > +				00 00
> > +				00 00
> > +
> > +				/* detailed timings */
> > +				05 0D 20 A0 30 58 1C 20 28 20 14 00 26 57 21 00 00 1E
> > +				00 00 00 fd 00 32 4b 1b	51 11 00 0a 20 20 20 20 20 20
> > +				00 00 00 fc 00 53 79 6e 63 4d 61 73 74 65 72 0a 20 20
> > +				00 00 00 ff 00 48 39 58 53 34 30 30 34 34 32 0a 20 20
> > +				00 20];
> 
> This EDID block claims to be a Samsung SyncMaster, which isn't really
> the right thing to do.  The question is what to call it instead.  Red
> Hat has a PNP ID we can use for virtual EDID blocks like this if we
> want, I'd want to set up a little database to keep track of them but
> that's pretty trivial.

Sorry, should have mentioned this in the commit log. This in fact is a
hacked version of my office monitor. This patch is more meant as a usage
example and not for upstream. I don't know yet if it's even acceptable
to put edid data into the devicetree. I saw some discussion about it,
but also about some generic display description, which I would prefer.

BTW is there a more convenient tool than a hex editor around to generate
edid data? I only found some windows tools

> 
> Also, empty standard timing fields are 01 01, not 00 00.

Good to know

Thanks
 Sascha

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

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/2] pcm038 lcdc support
@ 2012-05-18 15:13       ` Sascha Hauer
  0 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-18 15:13 UTC (permalink / raw)
  To: Adam Jackson; +Cc: linux-arm-kernel, dri-devel

On Fri, May 18, 2012 at 10:03:54AM -0400, Adam Jackson wrote:
> On Fri, 2012-05-18 at 14:27 +0200, Sascha Hauer wrote:
> 
> > +			edid = [00 ff ff ff ff ff ff 00 4c 2d 6c 03 36 32 49 4b
> > +				0f 13 01 03 80 37 22 a0 2a fe 21 a8 53 37 ae 24
> > +				11 50 54
> > +
> > +				/* est timings */
> > +				00 00 00
> > +
> > +				/* std timings */
> > +				00 00
> > +				00 00
> > +				00 00
> > +				00 00
> > +				00 00
> > +				00 00
> > +				00 00
> > +				00 00
> > +
> > +				/* detailed timings */
> > +				05 0D 20 A0 30 58 1C 20 28 20 14 00 26 57 21 00 00 1E
> > +				00 00 00 fd 00 32 4b 1b	51 11 00 0a 20 20 20 20 20 20
> > +				00 00 00 fc 00 53 79 6e 63 4d 61 73 74 65 72 0a 20 20
> > +				00 00 00 ff 00 48 39 58 53 34 30 30 34 34 32 0a 20 20
> > +				00 20];
> 
> This EDID block claims to be a Samsung SyncMaster, which isn't really
> the right thing to do.  The question is what to call it instead.  Red
> Hat has a PNP ID we can use for virtual EDID blocks like this if we
> want, I'd want to set up a little database to keep track of them but
> that's pretty trivial.

Sorry, should have mentioned this in the commit log. This in fact is a
hacked version of my office monitor. This patch is more meant as a usage
example and not for upstream. I don't know yet if it's even acceptable
to put edid data into the devicetree. I saw some discussion about it,
but also about some generic display description, which I would prefer.

BTW is there a more convenient tool than a hex editor around to generate
edid data? I only found some windows tools

> 
> Also, empty standard timing fields are 01 01, not 00 00.

Good to know

Thanks
 Sascha

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

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 2/2] pcm038 lcdc support
  2012-05-18 15:13       ` Sascha Hauer
@ 2012-05-22 10:02         ` Dave Airlie
  -1 siblings, 0 replies; 26+ messages in thread
From: Dave Airlie @ 2012-05-22 10:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, May 18, 2012 at 4:13 PM, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> On Fri, May 18, 2012 at 10:03:54AM -0400, Adam Jackson wrote:
>> On Fri, 2012-05-18 at 14:27 +0200, Sascha Hauer wrote:
>>
>> > + ? ? ? ? ? ? ? ? ? edid = [00 ff ff ff ff ff ff 00 4c 2d 6c 03 36 32 49 4b
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 0f 13 01 03 80 37 22 a0 2a fe 21 a8 53 37 ae 24
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 11 50 54
>> > +
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? /* est timings */
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00 00
>> > +
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? /* std timings */
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00
>> > +
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? /* detailed timings */
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 05 0D 20 A0 30 58 1C 20 28 20 14 00 26 57 21 00 00 1E
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00 00 fd 00 32 4b 1b 51 11 00 0a 20 20 20 20 20 20
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00 00 fc 00 53 79 6e 63 4d 61 73 74 65 72 0a 20 20
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00 00 ff 00 48 39 58 53 34 30 30 34 34 32 0a 20 20
>> > + ? ? ? ? ? ? ? ? ? ? ? ? ? 00 20];
>>
>> This EDID block claims to be a Samsung SyncMaster, which isn't really
>> the right thing to do. ?The question is what to call it instead. ?Red
>> Hat has a PNP ID we can use for virtual EDID blocks like this if we
>> want, I'd want to set up a little database to keep track of them but
>> that's pretty trivial.
>
> Sorry, should have mentioned this in the commit log. This in fact is a
> hacked version of my office monitor. This patch is more meant as a usage
> example and not for upstream. I don't know yet if it's even acceptable
> to put edid data into the devicetree. I saw some discussion about it,
> but also about some generic display description, which I would prefer.
>
> BTW is there a more convenient tool than a hex editor around to generate
> edid data? I only found some windows tools

Some basic stuff in Documentation/EDID/ but yeah don't know of
anything graphical for Linux.

Dave.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/2] pcm038 lcdc support
@ 2012-05-22 10:02         ` Dave Airlie
  0 siblings, 0 replies; 26+ messages in thread
From: Dave Airlie @ 2012-05-22 10:02 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: dri-devel, linux-arm-kernel

On Fri, May 18, 2012 at 4:13 PM, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> On Fri, May 18, 2012 at 10:03:54AM -0400, Adam Jackson wrote:
>> On Fri, 2012-05-18 at 14:27 +0200, Sascha Hauer wrote:
>>
>> > +                   edid = [00 ff ff ff ff ff ff 00 4c 2d 6c 03 36 32 49 4b
>> > +                           0f 13 01 03 80 37 22 a0 2a fe 21 a8 53 37 ae 24
>> > +                           11 50 54
>> > +
>> > +                           /* est timings */
>> > +                           00 00 00
>> > +
>> > +                           /* std timings */
>> > +                           00 00
>> > +                           00 00
>> > +                           00 00
>> > +                           00 00
>> > +                           00 00
>> > +                           00 00
>> > +                           00 00
>> > +                           00 00
>> > +
>> > +                           /* detailed timings */
>> > +                           05 0D 20 A0 30 58 1C 20 28 20 14 00 26 57 21 00 00 1E
>> > +                           00 00 00 fd 00 32 4b 1b 51 11 00 0a 20 20 20 20 20 20
>> > +                           00 00 00 fc 00 53 79 6e 63 4d 61 73 74 65 72 0a 20 20
>> > +                           00 00 00 ff 00 48 39 58 53 34 30 30 34 34 32 0a 20 20
>> > +                           00 20];
>>
>> This EDID block claims to be a Samsung SyncMaster, which isn't really
>> the right thing to do.  The question is what to call it instead.  Red
>> Hat has a PNP ID we can use for virtual EDID blocks like this if we
>> want, I'd want to set up a little database to keep track of them but
>> that's pretty trivial.
>
> Sorry, should have mentioned this in the commit log. This in fact is a
> hacked version of my office monitor. This patch is more meant as a usage
> example and not for upstream. I don't know yet if it's even acceptable
> to put edid data into the devicetree. I saw some discussion about it,
> but also about some generic display description, which I would prefer.
>
> BTW is there a more convenient tool than a hex editor around to generate
> edid data? I only found some windows tools

Some basic stuff in Documentation/EDID/ but yeah don't know of
anything graphical for Linux.

Dave.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* (no subject)
  2012-05-18 12:27 ` (unknown), Sascha Hauer
@ 2012-05-22 14:06   ` Lars-Peter Clausen
  -1 siblings, 0 replies; 26+ messages in thread
From: Lars-Peter Clausen @ 2012-05-22 14:06 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/18/2012 02:27 PM, Sascha Hauer wrote:
> Hi All,
> 
> The following adds a drm/kms driver for the Freescale i.MX LCDC
> controller. Most notable change to the last SDRM based version is that
> the SDRM layer has been removed and the driver now is purely i.MX
> specific. I hope that this is more acceptable now.
> 
> Another change is that the probe is now devicetree based. For now I
> took the easy way out and only put an edid blob into the devicetree.
> I haven't documented the binding yet, I would add that when the rest
> is considered ok.
> 
> Comments very welcome.
> 

Hi,

I really liked the sdrm layer. At least some bits of it. I've been working
on a "simple" DRM driver as well. The hardware has no fancy acceleration
features, just a simple buffer and some scanout logic. I'm basically using
the same gem buffer structure and the buffer is also allocated using
dma_alloc_writecombine, which means we can probably share all of the GEM
handling code and probably also most of the fbdev code. I also started with
the Exynos GEM code as a template, but reworked it later to be more like the
UDL code, which made it a bit more compact. I think it would be a good idea
to put at least the GEM handling in some common code as I expect that we'll
see more similar "simple" DRM drivers pop up.

The code in question can be found at
https://github.com/lclausen-adi/linux-2.6/commit/87a8fd6b98eeee317c7a486846cc8405d0bd68d8

Btw. the imx-drm.h is missing in your patch.

- Lars

> Thanks
>  Sascha
> 
> ----------------------------------------------------------------
> Sascha Hauer (2):
>       DRM: add Freescale i.MX LCDC driver
>       pcm038 lcdc support
> 
>  arch/arm/boot/dts/imx27-phytec-phycore.dts |   39 ++
>  arch/arm/boot/dts/imx27.dtsi               |    7 +
>  arch/arm/mach-imx/clock-imx27.c            |    1 +
>  drivers/gpu/drm/Kconfig                    |    2 +
>  drivers/gpu/drm/Makefile                   |    1 +
>  drivers/gpu/drm/imx/Kconfig                |   18 +
>  drivers/gpu/drm/imx/Makefile               |    8 +
>  drivers/gpu/drm/imx/imx-drm-core.c         |  745 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/imx/imx-fb.c               |  179 +++++++
>  drivers/gpu/drm/imx/imx-fbdev.c            |  275 ++++++++++
>  drivers/gpu/drm/imx/imx-gem.c              |  343 +++++++++++++
>  drivers/gpu/drm/imx/imx-lcdc-crtc.c        |  517 +++++++++++++++++++
>  drivers/gpu/drm/imx/imx-parallel-display.c |  228 +++++++++
>  13 files changed, 2363 insertions(+)
>  create mode 100644 drivers/gpu/drm/imx/Kconfig
>  create mode 100644 drivers/gpu/drm/imx/Makefile
>  create mode 100644 drivers/gpu/drm/imx/imx-drm-core.c
>  create mode 100644 drivers/gpu/drm/imx/imx-fb.c
>  create mode 100644 drivers/gpu/drm/imx/imx-fbdev.c
>  create mode 100644 drivers/gpu/drm/imx/imx-gem.c
>  create mode 100644 drivers/gpu/drm/imx/imx-lcdc-crtc.c
>  create mode 100644 drivers/gpu/drm/imx/imx-parallel-display.c
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re:
@ 2012-05-22 14:06   ` Lars-Peter Clausen
  0 siblings, 0 replies; 26+ messages in thread
From: Lars-Peter Clausen @ 2012-05-22 14:06 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-arm-kernel, dri-devel

On 05/18/2012 02:27 PM, Sascha Hauer wrote:
> Hi All,
> 
> The following adds a drm/kms driver for the Freescale i.MX LCDC
> controller. Most notable change to the last SDRM based version is that
> the SDRM layer has been removed and the driver now is purely i.MX
> specific. I hope that this is more acceptable now.
> 
> Another change is that the probe is now devicetree based. For now I
> took the easy way out and only put an edid blob into the devicetree.
> I haven't documented the binding yet, I would add that when the rest
> is considered ok.
> 
> Comments very welcome.
> 

Hi,

I really liked the sdrm layer. At least some bits of it. I've been working
on a "simple" DRM driver as well. The hardware has no fancy acceleration
features, just a simple buffer and some scanout logic. I'm basically using
the same gem buffer structure and the buffer is also allocated using
dma_alloc_writecombine, which means we can probably share all of the GEM
handling code and probably also most of the fbdev code. I also started with
the Exynos GEM code as a template, but reworked it later to be more like the
UDL code, which made it a bit more compact. I think it would be a good idea
to put at least the GEM handling in some common code as I expect that we'll
see more similar "simple" DRM drivers pop up.

The code in question can be found at
https://github.com/lclausen-adi/linux-2.6/commit/87a8fd6b98eeee317c7a486846cc8405d0bd68d8

Btw. the imx-drm.h is missing in your patch.

- Lars

> Thanks
>  Sascha
> 
> ----------------------------------------------------------------
> Sascha Hauer (2):
>       DRM: add Freescale i.MX LCDC driver
>       pcm038 lcdc support
> 
>  arch/arm/boot/dts/imx27-phytec-phycore.dts |   39 ++
>  arch/arm/boot/dts/imx27.dtsi               |    7 +
>  arch/arm/mach-imx/clock-imx27.c            |    1 +
>  drivers/gpu/drm/Kconfig                    |    2 +
>  drivers/gpu/drm/Makefile                   |    1 +
>  drivers/gpu/drm/imx/Kconfig                |   18 +
>  drivers/gpu/drm/imx/Makefile               |    8 +
>  drivers/gpu/drm/imx/imx-drm-core.c         |  745 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/imx/imx-fb.c               |  179 +++++++
>  drivers/gpu/drm/imx/imx-fbdev.c            |  275 ++++++++++
>  drivers/gpu/drm/imx/imx-gem.c              |  343 +++++++++++++
>  drivers/gpu/drm/imx/imx-lcdc-crtc.c        |  517 +++++++++++++++++++
>  drivers/gpu/drm/imx/imx-parallel-display.c |  228 +++++++++
>  13 files changed, 2363 insertions(+)
>  create mode 100644 drivers/gpu/drm/imx/Kconfig
>  create mode 100644 drivers/gpu/drm/imx/Makefile
>  create mode 100644 drivers/gpu/drm/imx/imx-drm-core.c
>  create mode 100644 drivers/gpu/drm/imx/imx-fb.c
>  create mode 100644 drivers/gpu/drm/imx/imx-fbdev.c
>  create mode 100644 drivers/gpu/drm/imx/imx-gem.c
>  create mode 100644 drivers/gpu/drm/imx/imx-lcdc-crtc.c
>  create mode 100644 drivers/gpu/drm/imx/imx-parallel-display.c
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 1/2] DRM: add Freescale i.MX LCDC driver
  2012-05-18 12:27   ` Sascha Hauer
@ 2012-05-22 21:28     ` Rob Clark
  -1 siblings, 0 replies; 26+ messages in thread
From: Rob Clark @ 2012-05-22 21:28 UTC (permalink / raw)
  To: linux-arm-kernel

just a few comments from a cursory review..  I need a bit more time
for a more in-depth review but that won't be tonight so I thought I'd
send what I have so far..


On Fri, May 18, 2012 at 6:27 AM, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
> ?drivers/gpu/drm/Kconfig ? ? ? ? ? ? ? ? ? ?| ? ?2 +
> ?drivers/gpu/drm/Makefile ? ? ? ? ? ? ? ? ? | ? ?1 +
> ?drivers/gpu/drm/imx/Kconfig ? ? ? ? ? ? ? ?| ? 18 +
> ?drivers/gpu/drm/imx/Makefile ? ? ? ? ? ? ? | ? ?8 +
> ?drivers/gpu/drm/imx/imx-drm-core.c ? ? ? ? | ?745 ++++++++++++++++++++++++++++
> ?drivers/gpu/drm/imx/imx-fb.c ? ? ? ? ? ? ? | ?179 +++++++
> ?drivers/gpu/drm/imx/imx-fbdev.c ? ? ? ? ? ?| ?275 ++++++++++
> ?drivers/gpu/drm/imx/imx-gem.c ? ? ? ? ? ? ?| ?343 +++++++++++++
> ?drivers/gpu/drm/imx/imx-lcdc-crtc.c ? ? ? ?| ?517 +++++++++++++++++++
> ?drivers/gpu/drm/imx/imx-parallel-display.c | ?228 +++++++++
> ?10 files changed, 2316 insertions(+)
> ?create mode 100644 drivers/gpu/drm/imx/Kconfig
> ?create mode 100644 drivers/gpu/drm/imx/Makefile
> ?create mode 100644 drivers/gpu/drm/imx/imx-drm-core.c
> ?create mode 100644 drivers/gpu/drm/imx/imx-fb.c
> ?create mode 100644 drivers/gpu/drm/imx/imx-fbdev.c
> ?create mode 100644 drivers/gpu/drm/imx/imx-gem.c
> ?create mode 100644 drivers/gpu/drm/imx/imx-lcdc-crtc.c
> ?create mode 100644 drivers/gpu/drm/imx/imx-parallel-display.c
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index e354bc0..759502c 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -186,3 +186,5 @@ source "drivers/gpu/drm/vmwgfx/Kconfig"
> ?source "drivers/gpu/drm/gma500/Kconfig"
>
> ?source "drivers/gpu/drm/udl/Kconfig"
> +
> +source "drivers/gpu/drm/imx/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index c20da5b..6569d8d 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -42,4 +42,5 @@ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
> ?obj-$(CONFIG_DRM_EXYNOS) +=exynos/
> ?obj-$(CONFIG_DRM_GMA500) += gma500/
> ?obj-$(CONFIG_DRM_UDL) += udl/
> +obj-$(CONFIG_DRM_IMX) += imx/
> ?obj-y ? ? ? ? ? ? ? ? ?+= i2c/
> diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig
> new file mode 100644
> index 0000000..5fc3a44
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/Kconfig
> @@ -0,0 +1,18 @@
> +config DRM_IMX
> + ? ? ? tristate "DRM Support for Freescale i.MX"
> + ? ? ? select DRM_KMS_HELPER
> + ? ? ? depends on DRM && ARCH_MXC
> +
> +config DRM_IMX_FB_HELPER
> + ? ? ? tristate "provide legacy framebuffer /dev/fb0"
> + ? ? ? depends on DRM_IMX
> +
> +config DRM_IMX_LCDC
> + ? ? ? tristate "DRM Support for Freescale i.MX1 and i.MX2"
> + ? ? ? depends on DRM_IMX
> + ? ? ? help
> + ? ? ? ? Choose this if you have a i.MX1, i.MX21, i.MX25 or i.MX27 processor.

do you have something like cpu_is_imx2() type macros?  It would be
preferable not to have a compile time config option for building for
certain hw versions.  Ie. on OMAP we could do something like 'if
(cpu_is_omap3xxx()) { ... }' if there was something that needed to be
done different on omap3 family of devices.  This way you could choose
to build a kernel supporting either a single omap variants, or all
omap variants.

> +
> +config DRM_IMX_PARALLEL_DISPLAY
> + ? ? ? tristate "Support for parallel displays"
> + ? ? ? depends on DRM_IMX
> diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
> new file mode 100644
> index 0000000..0f7c038
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/Makefile
> @@ -0,0 +1,8 @@
> +
> +imxdrm-objs := imx-drm-core.o imx-fb.o imx-gem.o
> +
> +obj-$(CONFIG_DRM_IMX) += imxdrm.o
> +
> +obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += imx-parallel-display.o
> +obj-$(CONFIG_DRM_IMX_LCDC) += imx-lcdc-crtc.o
> +obj-$(CONFIG_DRM_IMX_FB_HELPER) += imx-fbdev.o
> diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
> new file mode 100644
> index 0000000..29f5f10
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/imx-drm-core.c
> @@ -0,0 +1,745 @@
> +/*
> + * simple 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 <asm/fb.h>
> +#include <linux/module.h>
> +
> +#include "imx-drm.h"
> +
> +#define MAX_CRTC ? ? ? 4
> +
> +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;
> +};
> +
> +struct imx_drm_crtc {
> + ? ? ? struct drm_crtc ? ? ? ? ? ? ? ? ? ? ? ? *crtc;
> + ? ? ? struct list_head ? ? ? ? ? ? ? ? ? ? ? ?list;
> + ? ? ? struct imx_drm_device ? ? ? ? ? ? ? ? ? *imxdrm;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pipe;
> + ? ? ? struct drm_crtc_helper_funcs ? ? ? ? ? ?crtc_helper_funcs;
> + ? ? ? struct drm_crtc_funcs ? ? ? ? ? ? ? ? ? crtc_funcs;
> + ? ? ? struct imx_drm_crtc_helper_funcs ? ? ? ?imx_drm_helper_funcs;
> + ? ? ? struct module ? ? ? ? ? ? ? ? ? ? ? ? ? *owner;
> +};
> +
> +struct imx_drm_encoder {
> + ? ? ? struct drm_encoder ? ? ? ? ? ? ? ? ? ? ?*encoder;
> + ? ? ? struct list_head ? ? ? ? ? ? ? ? ? ? ? ?list;
> + ? ? ? struct module ? ? ? ? ? ? ? ? ? ? ? ? ? *owner;
> +};
> +
> +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)
> +{
> + ? ? ? 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_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 struct vm_operations_struct imx_drm_gem_vm_ops = {
> + ? ? ? .fault = imx_drm_gem_fault,
> + ? ? ? .open = drm_gem_vm_open,
> + ? ? ? .close = drm_gem_vm_close,
> +};
> +
> +static const struct file_operations imx_drm_driver_fops = {
> + ? ? ? .owner = THIS_MODULE,
> + ? ? ? .open = drm_open,
> + ? ? ? .release = drm_release,
> + ? ? ? .unlocked_ioctl = drm_ioctl,
> + ? ? ? .mmap = imx_drm_gem_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 imx_drm_device->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();
> +
> + ? ? ? drm_encoder_init(imxdrm->drm, imx_drm_encoder->encoder,
> + ? ? ? ? ? ? ? ? ? ? ? imx_drm_encoder->encoder->funcs,
> + ? ? ? ? ? ? ? ? ? ? ? DRM_MODE_ENCODER_TMDS);
> +
> + ? ? ? 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();
> + ? ? ? int ret;
> +
> + ? ? ? drm_connector_init(imxdrm->drm, imx_drm_connector->connector,
> + ? ? ? ? ? ? ? ? ? ? ? imx_drm_connector->connector->funcs,
> + ? ? ? ? ? ? ? ? ? ? ? DRM_MODE_CONNECTOR_VGA);
> + ? ? ? drm_mode_group_reinit(imxdrm->drm);
> + ? ? ? ret = drm_sysfs_connector_add(imx_drm_connector->connector);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? goto err;
> +
> + ? ? ? return 0;
> +err:
> +
> + ? ? ? return ret;
> +}
> +
> +/*
> + * 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->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->crtc_helper_funcs);
> +
> + ? ? ? 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;
> +}
> +
> +/*
> + * 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 drm_crtc_funcs *crtc_funcs,
> + ? ? ? ? ? ? ? const struct drm_crtc_helper_funcs *crtc_helper_funcs,
> + ? ? ? ? ? ? ? const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs,
> + ? ? ? ? ? ? ? struct module *owner)
> +{
> + ? ? ? struct imx_drm_device *imxdrm = __imx_drm_device();
> + ? ? ? struct imx_drm_crtc *imx_drm_crtc;
> + ? ? ? 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->crtc_funcs = *crtc_funcs;
> + ? ? ? imx_drm_crtc->crtc_helper_funcs = *crtc_helper_funcs;
> + ? ? ? imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs;
> +
> + ? ? ? WARN_ON(crtc_funcs->set_config);
> + ? ? ? WARN_ON(crtc_funcs->destroy);
> +
> + ? ? ? imx_drm_crtc->crtc_funcs.set_config = drm_crtc_helper_set_config;
> + ? ? ? imx_drm_crtc->crtc_funcs.destroy = drm_crtc_cleanup;
> +
> + ? ? ? 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;
> +
> + ? ? ? 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);
> +
> + ? ? ? 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(struct 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);
> +
> +/*
> + * 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();
> +
> + ? ? ? mutex_lock(&imxdrm->mutex);
> +
> + ? ? ? imx_drm_encoder_unregister(imx_drm_encoder);
> +
> + ? ? ? list_del(&imx_drm_encoder->list);
> +
> + ? ? ? 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(struct 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);
> +
> +/*
> + * 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 ? ? ? ?= imx_drm_gem_free_object,
> + ? ? ? .gem_vm_ops ? ? ? ? ? ? = &imx_drm_gem_vm_ops,
> + ? ? ? .dumb_create ? ? ? ? ? ?= imx_drm_gem_dumb_create,
> + ? ? ? .dumb_map_offset ? ? ? ?= imx_drm_gem_dumb_map_offset,
> + ? ? ? .dumb_destroy ? ? ? ? ? = imx_drm_gem_dumb_destroy,
> +
> + ? ? ? .get_vblank_counter ? ? = drm_vblank_count,
> + ? ? ? .enable_vblank ? ? ? ? ?= imx_drm_enable_vblank,
> + ? ? ? .disable_vblank ? ? ? ? = imx_drm_disable_vblank,
> + ? ? ? .reclaim_buffers ? ? ? ?= drm_core_reclaim_buffers,
> + ? ? ? .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_pdev:
> + ? ? ? kfree(imx_drm_device);
> +err_pdrv:
> + ? ? ? platform_device_unregister(imx_drm_pdev);
> +
> + ? ? ? return ret;
> +}
> +
> +static void __exit imx_drm_exit(void)
> +{
> + ? ? ? DRM_DEBUG_DRIVER("%s\n", __FILE__);
> +
> + ? ? ? 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/gpu/drm/imx/imx-fb.c b/drivers/gpu/drm/imx/imx-fb.c
> new file mode 100644
> index 0000000..5a08c86
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/imx-fb.c
> @@ -0,0 +1,179 @@
> +/*
> + * 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 "imx-drm.h"
> +
> +#define to_imx_drm_fb(x) ? ? ? container_of(x, struct imx_drm_fb, fb)
> +
> +/*
> + * imx specific framebuffer structure.
> + *
> + * @fb: drm framebuffer obejct.
> + * @imx_drm_gem_obj: drm ec specific gem object containing a gem object.
> + * @entry: pointer to ec drm buffer entry object.
> + * ? ? - containing only the information to physically continuous memory
> + * ? ? region allocated at default framebuffer creation.
> + */
> +struct imx_drm_fb {
> + ? ? ? struct drm_framebuffer ? ? ? ? ?fb;
> + ? ? ? struct imx_drm_gem_obj ?*imx_drm_gem_obj;
> + ? ? ? struct imx_drm_buf_entry ? ? ? ?*entry;
> +};
> +
> +static void imx_drm_fb_destroy(struct drm_framebuffer *fb)
> +{
> + ? ? ? struct imx_drm_fb *imx_drm_fb = to_imx_drm_fb(fb);
> +
> + ? ? ? drm_framebuffer_cleanup(fb);
> +
> + ? ? ? /*
> + ? ? ? ?* default framebuffer has no gem object so
> + ? ? ? ?* a buffer of the default framebuffer should be released at here.
> + ? ? ? ?*/
> + ? ? ? if (!imx_drm_fb->imx_drm_gem_obj && imx_drm_fb->entry)
> + ? ? ? ? ? ? ? imx_drm_buf_destroy(fb->dev, imx_drm_fb->entry);
> +
> + ? ? ? kfree(imx_drm_fb);
> +}
> +
> +static int imx_drm_fb_create_handle(struct drm_framebuffer *fb,
> + ? ? ? ? ? ? ? struct drm_file *file_priv, unsigned int *handle)
> +{
> + ? ? ? struct imx_drm_fb *imx_drm_fb = to_imx_drm_fb(fb);
> +
> + ? ? ? return drm_gem_handle_create(file_priv,
> + ? ? ? ? ? ? ? ? ? ? ? &imx_drm_fb->imx_drm_gem_obj->base, handle);
> +}
> +
> +static struct drm_framebuffer_funcs imx_drm_fb_funcs = {
> + ? ? ? .destroy ? ? ? ?= imx_drm_fb_destroy,
> + ? ? ? .create_handle ?= imx_drm_fb_create_handle,
> +};
> +
> +static struct drm_framebuffer *imx_drm_fb_create(struct drm_device *dev,
> + ? ? ? ? ? ? ? struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
> +{
> + ? ? ? struct imx_drm_fb *imx_drm_fb;
> + ? ? ? struct drm_framebuffer *fb;
> + ? ? ? struct drm_gem_object *obj;
> + ? ? ? unsigned int size;
> + ? ? ? int ret;
> + ? ? ? u32 bpp, depth;
> +
> + ? ? ? drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp);
> +
> + ? ? ? mode_cmd->pitches[0] = max(mode_cmd->pitches[0],
> + ? ? ? ? ? ? ? ? ? ? ? mode_cmd->width * (bpp >> 3));
> +
> + ? ? ? dev_dbg(dev->dev, "drm fb create(%dx%d)\n",
> + ? ? ? ? ? ? ? ? ? ? ? mode_cmd->width, mode_cmd->height);
> +
> + ? ? ? imx_drm_fb = kzalloc(sizeof(*imx_drm_fb), GFP_KERNEL);
> + ? ? ? if (!imx_drm_fb) {
> + ? ? ? ? ? ? ? dev_err(dev->dev, "failed to allocate drm framebuffer.\n");
> + ? ? ? ? ? ? ? return ERR_PTR(-ENOMEM);
> + ? ? ? }
> +
> + ? ? ? fb = &imx_drm_fb->fb;
> + ? ? ? ret = drm_framebuffer_init(dev, fb, &imx_drm_fb_funcs);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(dev->dev, "failed to initialize framebuffer.\n");
> + ? ? ? ? ? ? ? goto err_init;
> + ? ? ? }
> +
> + ? ? ? dev_dbg(dev->dev, "create: fb id: %d\n", fb->base.id);
> +
> + ? ? ? size = mode_cmd->pitches[0] * mode_cmd->height;
> +
> + ? ? ? /*
> + ? ? ? ?* without file_priv we are called from imx_drm_fbdev_create in which
> + ? ? ? ?* case we only create a framebuffer without a handle.
> + ? ? ? ?*/
> + ? ? ? if (!file_priv) {
> + ? ? ? ? ? ? ? struct imx_drm_buf_entry *entry;
> +
> + ? ? ? ? ? ? ? entry = imx_drm_buf_create(dev, size);
> + ? ? ? ? ? ? ? if (IS_ERR(entry)) {
> + ? ? ? ? ? ? ? ? ? ? ? ret = PTR_ERR(entry);
> + ? ? ? ? ? ? ? ? ? ? ? goto err_buffer;
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? imx_drm_fb->entry = entry;
> + ? ? ? } else {
> + ? ? ? ? ? ? ? obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
> + ? ? ? ? ? ? ? if (!obj) {
> + ? ? ? ? ? ? ? ? ? ? ? ret = -EINVAL;
> + ? ? ? ? ? ? ? ? ? ? ? goto err_buffer;
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? imx_drm_fb->imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> +
> + ? ? ? ? ? ? ? drm_gem_object_unreference_unlocked(obj);


the fb probably should hold the reference to the GEM obj until it is
deleted..  so you don't end up freeing the buffer you are scanning out
until you are done scanning it out.  Possibly this gets a bit more
straightforward if you don't have to special case the default/fbcon
framebuffer?

IIRC, Laurent had sent a patch recently to fix the same issue in the
exynos driver.

> + ? ? ? ? ? ? ? imx_drm_fb->entry = imx_drm_fb->imx_drm_gem_obj->entry;
> + ? ? ? }
> +
> + ? ? ? drm_helper_mode_fill_fb_struct(fb, mode_cmd);
> +
> + ? ? ? return fb;
> +
> +err_buffer:
> + ? ? ? drm_framebuffer_cleanup(fb);
> +
> +err_init:
> + ? ? ? kfree(imx_drm_fb);
> +
> + ? ? ? return ERR_PTR(ret);
> +}
> +
> +struct imx_drm_buf_entry *imx_drm_fb_get_buf(struct drm_framebuffer *fb)
> +{
> + ? ? ? struct imx_drm_fb *imx_drm_fb = to_imx_drm_fb(fb);
> + ? ? ? struct imx_drm_buf_entry *entry;
> +
> + ? ? ? entry = imx_drm_fb->entry;
> +
> + ? ? ? return entry;
> +}
> +EXPORT_SYMBOL_GPL(imx_drm_fb_get_buf);
> +
> +static struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
> + ? ? ? .fb_create = imx_drm_fb_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/gpu/drm/imx/imx-fbdev.c b/drivers/gpu/drm/imx/imx-fbdev.c
> new file mode 100644
> index 0000000..f038797
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/imx-fbdev.c
> @@ -0,0 +1,275 @@
> +/*
> + * 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_fb_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "imx-drm.h"
> +
> +#define MAX_CONNECTOR ? ? ? ? ?4
> +#define PREFERRED_BPP ? ? ? ? ?16
> +
> +static struct fb_ops imx_drm_fb_ops = {
> + ? ? ? .owner ? ? ? ? ?= THIS_MODULE,
> + ? ? ? .fb_fillrect ? ?= cfb_fillrect,
> + ? ? ? .fb_copyarea ? ?= cfb_copyarea,
> + ? ? ? .fb_imageblit ? = cfb_imageblit,
> + ? ? ? .fb_check_var ? = drm_fb_helper_check_var,
> + ? ? ? .fb_set_par ? ? = drm_fb_helper_set_par,
> + ? ? ? .fb_blank ? ? ? = drm_fb_helper_blank,
> + ? ? ? .fb_pan_display = drm_fb_helper_pan_display,
> + ? ? ? .fb_setcmap ? ? = drm_fb_helper_setcmap,
> +};
> +
> +static int imx_drm_fbdev_update(struct drm_fb_helper *helper,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct drm_framebuffer *fb,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?unsigned int fb_width,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?unsigned int fb_height)
> +{
> + ? ? ? struct fb_info *fbi = helper->fbdev;
> + ? ? ? struct drm_device *drm = helper->dev;
> + ? ? ? struct imx_drm_buf_entry *entry;
> + ? ? ? unsigned int size = fb_width * fb_height * (fb->bits_per_pixel >> 3);
> + ? ? ? unsigned long offset;
> +
> + ? ? ? drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
> + ? ? ? drm_fb_helper_fill_var(fbi, helper, fb_width, fb_height);
> +
> + ? ? ? entry = imx_drm_fb_get_buf(fb);
> + ? ? ? if (!entry) {
> + ? ? ? ? ? ? ? dev_dbg(drm->dev, "entry is null.\n");
> + ? ? ? ? ? ? ? return -EFAULT;
> + ? ? ? }
> +
> + ? ? ? offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
> + ? ? ? offset += fbi->var.yoffset * fb->pitches[0];
> +
> + ? ? ? drm->mode_config.fb_base = entry->paddr;
> + ? ? ? fbi->screen_base = entry->vaddr + offset;
> + ? ? ? fbi->fix.smem_start = entry->paddr + offset;
> + ? ? ? fbi->screen_size = size;
> + ? ? ? fbi->fix.smem_len = size;
> + ? ? ? fbi->flags |= FBINFO_CAN_FORCE_OUTPUT;
> +
> + ? ? ? return 0;
> +}
> +
> +static int imx_drm_fbdev_create(struct drm_fb_helper *helper,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct drm_fb_helper_surface_size *sizes)
> +{
> + ? ? ? struct drm_device *drm = helper->dev;
> + ? ? ? struct fb_info *fbi;
> + ? ? ? struct drm_framebuffer *fb;
> + ? ? ? struct drm_mode_fb_cmd2 mode_cmd = { 0 };
> + ? ? ? struct platform_device *pdev = drm->platformdev;
> + ? ? ? int ret;
> +
> + ? ? ? dev_dbg(drm->dev, "surface width(%d), height(%d) and bpp(%d\n",
> + ? ? ? ? ? ? ? ? ? ? ? sizes->surface_width, sizes->surface_height,
> + ? ? ? ? ? ? ? ? ? ? ? sizes->surface_bpp);
> +
> + ? ? ? mode_cmd.width = sizes->surface_width;
> + ? ? ? mode_cmd.height = sizes->surface_height;
> + ? ? ? mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
> + ? ? ? ? ? ? ? ? ? ? ? sizes->surface_depth);
> +
> + ? ? ? mutex_lock(&drm->struct_mutex);
> +
> + ? ? ? fbi = framebuffer_alloc(0, &pdev->dev);
> + ? ? ? if (!fbi) {
> + ? ? ? ? ? ? ? ret = -ENOMEM;
> + ? ? ? ? ? ? ? goto err_fb_alloc;
> + ? ? ? }
> +
> + ? ? ? fb = drm->mode_config.funcs->fb_create(drm, NULL, &mode_cmd);
> + ? ? ? if (IS_ERR(fb)) {
> + ? ? ? ? ? ? ? dev_err(drm->dev, "failed to create drm framebuffer.\n");
> + ? ? ? ? ? ? ? ret = PTR_ERR(fb);
> + ? ? ? ? ? ? ? goto err_fb_create;
> + ? ? ? }
> +
> + ? ? ? helper->fb = fb;
> + ? ? ? helper->fbdev = fbi;
> +
> + ? ? ? fbi->par = helper;
> + ? ? ? fbi->flags = FBINFO_FLAG_DEFAULT;
> + ? ? ? fbi->fbops = &imx_drm_fb_ops;
> +
> + ? ? ? ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? goto err_alloc_cmap;
> +
> + ? ? ? ret = imx_drm_fbdev_update(helper, helper->fb, sizes->fb_width,
> + ? ? ? ? ? ? ? ? ? ? ? sizes->fb_height);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? goto err_fbdev_update;
> +
> + ? ? ? mutex_unlock(&drm->struct_mutex);
> +
> + ? ? ? return 0;
> +
> +err_fbdev_update:
> + ? ? ? fb_dealloc_cmap(&fbi->cmap);
> +
> +err_alloc_cmap:
> + ? ? ? fb->funcs->destroy(fb);
> +
> +err_fb_create:
> + ? ? ? framebuffer_release(fbi);
> +
> +err_fb_alloc:
> + ? ? ? mutex_unlock(&drm->struct_mutex);
> +
> + ? ? ? return ret;
> +}
> +
> +static int imx_drm_fbdev_probe(struct drm_fb_helper *helper,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct drm_fb_helper_surface_size *sizes)
> +{
> + ? ? ? int ret;
> +
> + ? ? ? BUG_ON(helper->fb);
> +
> + ? ? ? ret = imx_drm_fbdev_create(helper, sizes);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(helper->dev->dev, "creating fbdev failed with %d\n", ret);
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> +
> + ? ? ? /*
> + ? ? ? ?* fb_helper expects a value more than 1 if succeed
> + ? ? ? ?* because register_framebuffer() should be called.
> + ? ? ? ?*/
> + ? ? ? return 1;
> +}
> +
> +static struct drm_fb_helper_funcs imx_drm_fb_helper_funcs = {
> + ? ? ? .fb_probe = imx_drm_fbdev_probe,
> +};
> +
> +static struct drm_fb_helper *imx_drm_fbdev_init(struct drm_device *drm,
> + ? ? ? ? ? ? ? int preferred_bpp)
> +{
> + ? ? ? struct drm_fb_helper *helper;
> + ? ? ? unsigned int num_crtc;
> + ? ? ? int ret;
> +
> + ? ? ? helper = kzalloc(sizeof(*helper), GFP_KERNEL);
> + ? ? ? if (!helper)
> + ? ? ? ? ? ? ? return NULL;
> +
> + ? ? ? helper->funcs = &imx_drm_fb_helper_funcs;
> +
> + ? ? ? num_crtc = drm->mode_config.num_crtc;
> +
> + ? ? ? ret = drm_fb_helper_init(drm, helper, num_crtc, MAX_CONNECTOR);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(drm->dev, "initializing drm fb helper failed with %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret);
> + ? ? ? ? ? ? ? goto err_init;
> + ? ? ? }
> +
> + ? ? ? ret = drm_fb_helper_single_add_all_connectors(helper);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(drm->dev, "registering drm_fb_helper_connector failed with %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret);
> + ? ? ? ? ? ? ? goto err_setup;
> +
> + ? ? ? }
> +
> + ? ? ? ret = drm_fb_helper_initial_config(helper, preferred_bpp);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(drm->dev, "initial config failed with %d\n", ret);
> + ? ? ? ? ? ? ? goto err_setup;
> + ? ? ? }
> +
> + ? ? ? return helper;
> +
> +err_setup:
> + ? ? ? drm_fb_helper_fini(helper);
> +
> +err_init:
> + ? ? ? kfree(helper);
> +
> + ? ? ? return NULL;
> +}
> +
> +static void imx_drm_fbdev_fini(struct drm_fb_helper *helper)
> +{
> + ? ? ? struct imx_drm_buf_entry *entry;
> +
> + ? ? ? if (helper->fbdev) {
> + ? ? ? ? ? ? ? struct fb_info *info;
> + ? ? ? ? ? ? ? int ret;
> +
> + ? ? ? ? ? ? ? info = helper->fbdev;
> + ? ? ? ? ? ? ? ret = unregister_framebuffer(info);
> + ? ? ? ? ? ? ? if (ret)
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(helper->dev->dev, "unregister_framebuffer failed with %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret);
> +
> + ? ? ? ? ? ? ? if (info->cmap.len)
> + ? ? ? ? ? ? ? ? ? ? ? fb_dealloc_cmap(&info->cmap);
> +
> + ? ? ? ? ? ? ? entry = imx_drm_fb_get_buf(helper->fb);
> +
> + ? ? ? ? ? ? ? imx_drm_buf_destroy(helper->dev, entry);
> +
> + ? ? ? ? ? ? ? framebuffer_release(info);
> + ? ? ? }
> +
> + ? ? ? drm_fb_helper_fini(helper);
> +
> + ? ? ? kfree(helper);
> +}
> +
> +static struct drm_fb_helper *imx_fb_helper;
> +
> +static int __init imx_fb_helper_init(void)
> +{
> + ? ? ? struct drm_device *drm = imx_drm_device_get();
> + ? ? ? int ret;
> +
> + ? ? ? if (!drm)
> + ? ? ? ? ? ? ? return -EINVAL;
> +
> + ? ? ? imx_fb_helper = imx_drm_fbdev_init(drm, PREFERRED_BPP);
> + ? ? ? if (!imx_fb_helper) {
> + ? ? ? ? ? ? ? imx_drm_device_put();
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> +
> + ? ? ? return 0;
> +}
> +
> +static void __exit imx_fb_helper_exit(void)
> +{
> + ? ? ? imx_drm_fbdev_fini(imx_fb_helper);
> + ? ? ? 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/gpu/drm/imx/imx-gem.c b/drivers/gpu/drm/imx/imx-gem.c
> new file mode 100644
> index 0000000..b0866fb
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/imx-gem.c
> @@ -0,0 +1,343 @@
> +/*
> + * 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 <drm/drmP.h>
> +#include <drm/drm.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "imx-drm.h"
> +
> +static int lowlevel_buffer_allocate(struct drm_device *drm,
> + ? ? ? ? ? ? ? struct imx_drm_buf_entry *entry)
> +{
> + ? ? ? entry->vaddr = dma_alloc_writecombine(drm->dev, entry->size,
> + ? ? ? ? ? ? ? ? ? ? ? (dma_addr_t *)&entry->paddr, GFP_KERNEL);
> + ? ? ? if (!entry->vaddr) {
> + ? ? ? ? ? ? ? dev_err(drm->dev, "failed to allocate buffer.\n");
> + ? ? ? ? ? ? ? return -ENOMEM;
> + ? ? ? }
> +
> + ? ? ? dev_dbg(drm->dev, "allocated : vaddr(0x%x), paddr(0x%x), size(0x%x)\n",
> + ? ? ? ? ? ? ? ? ? ? ? (unsigned int)entry->vaddr, entry->paddr, entry->size);
> +
> + ? ? ? return 0;
> +}
> +
> +static void lowlevel_buffer_free(struct drm_device *drm,
> + ? ? ? ? ? ? ? struct imx_drm_buf_entry *entry)
> +{
> + ? ? ? dma_free_writecombine(drm->dev, entry->size, entry->vaddr,
> + ? ? ? ? ? ? ? ? ? ? ? entry->paddr);
> +}
> +
> +struct imx_drm_buf_entry *imx_drm_buf_create(struct drm_device *drm,
> + ? ? ? ? ? ? ? unsigned int size)
> +{
> + ? ? ? struct imx_drm_buf_entry *entry;
> +
> + ? ? ? entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> + ? ? ? if (!entry)
> + ? ? ? ? ? ? ? return ERR_PTR(-ENOMEM);
> +
> + ? ? ? entry->size = size;
> +
> + ? ? ? /*
> + ? ? ? ?* allocate memory region with size and set the memory information
> + ? ? ? ?* to vaddr and paddr of a entry object.
> + ? ? ? ?*/
> + ? ? ? if (lowlevel_buffer_allocate(drm, entry) < 0) {
> + ? ? ? ? ? ? ? kfree(entry);
> + ? ? ? ? ? ? ? return ERR_PTR(-ENOMEM);
> + ? ? ? }
> +
> + ? ? ? return entry;
> +}
> +
> +void imx_drm_buf_destroy(struct drm_device *drm,
> + ? ? ? ? ? ? ? struct imx_drm_buf_entry *entry)
> +{
> + ? ? ? lowlevel_buffer_free(drm, entry);
> +
> + ? ? ? kfree(entry);
> + ? ? ? entry = NULL;
> +}
> +EXPORT_SYMBOL_GPL(imx_drm_buf_destroy);
> +
> +static unsigned int convert_to_vm_err_msg(int msg)
> +{
> + ? ? ? unsigned int out_msg;
> +
> + ? ? ? switch (msg) {
> + ? ? ? case 0:
> + ? ? ? case -ERESTARTSYS:
> + ? ? ? case -EINTR:
> + ? ? ? ? ? ? ? out_msg = VM_FAULT_NOPAGE;
> + ? ? ? ? ? ? ? break;
> +
> + ? ? ? case -ENOMEM:
> + ? ? ? ? ? ? ? out_msg = VM_FAULT_OOM;
> + ? ? ? ? ? ? ? break;
> +
> + ? ? ? default:
> + ? ? ? ? ? ? ? out_msg = VM_FAULT_SIGBUS;
> + ? ? ? ? ? ? ? break;
> + ? ? ? }
> +
> + ? ? ? return out_msg;
> +}
> +
> +static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
> +{
> + ? ? ? return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
> +}
> +
> +static struct imx_drm_gem_obj *imx_drm_gem_create(struct drm_device *drm,
> + ? ? ? ? ? ? ? unsigned int size)
> +{
> + ? ? ? struct imx_drm_gem_obj *imx_drm_gem_obj;
> + ? ? ? struct imx_drm_buf_entry *entry;
> + ? ? ? struct drm_gem_object *obj;
> + ? ? ? int ret;
> +
> + ? ? ? size = roundup(size, PAGE_SIZE);
> +
> + ? ? ? imx_drm_gem_obj = kzalloc(sizeof(*imx_drm_gem_obj), GFP_KERNEL);
> + ? ? ? if (!imx_drm_gem_obj)
> + ? ? ? ? ? ? ? return ERR_PTR(-ENOMEM);
> +
> + ? ? ? /* allocate the new buffer object and memory region. */
> + ? ? ? entry = imx_drm_buf_create(drm, size);
> + ? ? ? if (!entry) {
> + ? ? ? ? ? ? ? ret = -ENOMEM;
> + ? ? ? ? ? ? ? goto err_alloc;
> + ? ? ? }
> +
> + ? ? ? imx_drm_gem_obj->entry = entry;
> +
> + ? ? ? obj = &imx_drm_gem_obj->base;
> +
> + ? ? ? ret = drm_gem_object_init(drm, obj, size);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(drm->dev, "initializing GEM object failed with %d\n", ret);
> + ? ? ? ? ? ? ? goto err_obj_init;
> + ? ? ? }
> +
> + ? ? ? ret = drm_gem_create_mmap_offset(obj);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(drm->dev, "creating mmap offset failed with %d\n", ret);
> + ? ? ? ? ? ? ? goto err_create_mmap_offset;
> + ? ? ? }
> +
> + ? ? ? return imx_drm_gem_obj;
> +
> +err_create_mmap_offset:
> + ? ? ? drm_gem_object_release(obj);
> +
> +err_obj_init:
> + ? ? ? imx_drm_buf_destroy(drm, imx_drm_gem_obj->entry);
> +
> +err_alloc:
> + ? ? ? kfree(imx_drm_gem_obj);
> +
> + ? ? ? return ERR_PTR(ret);
> +}
> +
> +static struct imx_drm_gem_obj *imx_drm_gem_create_with_handle(struct drm_file *file_priv,
> + ? ? ? ? ? ? ? struct drm_device *drm, unsigned int size,
> + ? ? ? ? ? ? ? unsigned int *handle)
> +{
> + ? ? ? struct imx_drm_gem_obj *imx_drm_gem_obj;
> + ? ? ? struct drm_gem_object *obj;
> + ? ? ? int ret;
> +
> + ? ? ? imx_drm_gem_obj = imx_drm_gem_create(drm, size);
> + ? ? ? if (IS_ERR(imx_drm_gem_obj))
> + ? ? ? ? ? ? ? return imx_drm_gem_obj;
> +
> + ? ? ? obj = &imx_drm_gem_obj->base;
> +
> + ? ? ? /*
> + ? ? ? ?* allocate a id of idr table where the obj is registered
> + ? ? ? ?* and handle has the id what user can see.
> + ? ? ? ?*/
> + ? ? ? ret = drm_gem_handle_create(file_priv, obj, handle);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? goto err_handle_create;
> +
> + ? ? ? /* drop reference from allocate - handle holds it now. */
> + ? ? ? drm_gem_object_unreference_unlocked(obj);
> +
> + ? ? ? return imx_drm_gem_obj;
> +
> +err_handle_create:
> + ? ? ? imx_drm_gem_free_object(obj);
> +
> + ? ? ? return ERR_PTR(ret);
> +}
> +
> +static int imx_drm_gem_mmap_buffer(struct file *filp,
> + ? ? ? ? ? ? ? struct vm_area_struct *vma)
> +{
> + ? ? ? struct drm_gem_object *obj = filp->private_data;
> + ? ? ? struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> + ? ? ? struct imx_drm_buf_entry *entry;
> + ? ? ? unsigned long pfn, vm_size;
> +
> + ? ? ? vma->vm_flags |= VM_IO | VM_RESERVED;
> +
> + ? ? ? vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

pgprot_writecombine()?

> + ? ? ? vma->vm_file = filp;
> +
> + ? ? ? vm_size = vma->vm_end - vma->vm_start;
> + ? ? ? /*
> + ? ? ? ?* an entry contains information to physically continuous memory
> + ? ? ? ?* allocated by user request or at framebuffer creation.
> + ? ? ? ?*/
> + ? ? ? entry = imx_drm_gem_obj->entry;
> +
> + ? ? ? /* check if user-requested size is valid. */
> + ? ? ? if (vm_size > entry->size)
> + ? ? ? ? ? ? ? return -EINVAL;
> +
> + ? ? ? /*
> + ? ? ? ?* get page frame number to physical memory to be mapped
> + ? ? ? ?* to user space.
> + ? ? ? ?*/
> + ? ? ? pfn = imx_drm_gem_obj->entry->paddr >> PAGE_SHIFT;
> +
> + ? ? ? if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? vma->vm_page_prot)) {
> + ? ? ? ? ? ? ? dev_err(obj->dev->dev, "failed to remap pfn range.\n");
> + ? ? ? ? ? ? ? return -EAGAIN;
> + ? ? ? }
> +
> + ? ? ? return 0;
> +}
> +
> +static const struct file_operations imx_drm_gem_fops = {
> + ? ? ? .mmap = imx_drm_gem_mmap_buffer,
> +};
> +
> +int imx_drm_gem_init_object(struct drm_gem_object *obj)
> +{
> + ? ? ? return 0;
> +}
> +
> +void imx_drm_gem_free_object(struct drm_gem_object *gem_obj)
> +{
> + ? ? ? struct imx_drm_gem_obj *imx_drm_gem_obj;
> +
> + ? ? ? if (gem_obj->map_list.map)
> + ? ? ? ? ? ? ? drm_gem_free_mmap_offset(gem_obj);
> +
> + ? ? ? drm_gem_object_release(gem_obj);
> +
> + ? ? ? imx_drm_gem_obj = to_imx_drm_gem_obj(gem_obj);
> +
> + ? ? ? imx_drm_buf_destroy(gem_obj->dev, imx_drm_gem_obj->entry);
> +
> + ? ? ? kfree(imx_drm_gem_obj);
> +}
> +
> +int imx_drm_gem_dumb_create(struct drm_file *file_priv,
> + ? ? ? ? ? ? ? struct drm_device *dev, struct drm_mode_create_dumb *args)
> +{
> + ? ? ? struct imx_drm_gem_obj *imx_drm_gem_obj;
> +
> + ? ? ? /* FIXME: This should be configured by the crtc driver */
> + ? ? ? args->pitch = args->width * args->bpp >> 3;
> + ? ? ? args->size = args->pitch * args->height;
> +
> + ? ? ? imx_drm_gem_obj = imx_drm_gem_create_with_handle(file_priv, dev, args->size,
> + ? ? ? ? ? ? ? ? ? ? ? &args->handle);
> + ? ? ? if (IS_ERR(imx_drm_gem_obj))
> + ? ? ? ? ? ? ? return PTR_ERR(imx_drm_gem_obj);
> +
> + ? ? ? return 0;
> +}
> +
> +int imx_drm_gem_dumb_map_offset(struct drm_file *file_priv,
> + ? ? ? ? ? ? ? struct drm_device *drm, uint32_t handle, uint64_t *offset)
> +{
> + ? ? ? struct imx_drm_gem_obj *imx_drm_gem_obj;
> + ? ? ? struct drm_gem_object *obj;
> +
> + ? ? ? mutex_lock(&drm->struct_mutex);
> +
> + ? ? ? obj = drm_gem_object_lookup(drm, file_priv, handle);
> + ? ? ? if (!obj) {
> + ? ? ? ? ? ? ? dev_err(drm->dev, "failed to lookup gem object\n");
> + ? ? ? ? ? ? ? mutex_unlock(&drm->struct_mutex);
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> +
> + ? ? ? imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> +
> + ? ? ? *offset = get_gem_mmap_offset(&imx_drm_gem_obj->base);
> +
> + ? ? ? drm_gem_object_unreference(obj);
> +
> + ? ? ? mutex_unlock(&drm->struct_mutex);
> +
> + ? ? ? return 0;
> +}
> +
> +int imx_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> +{
> + ? ? ? struct drm_gem_object *obj = vma->vm_private_data;
> + ? ? ? struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> + ? ? ? struct drm_device *dev = obj->dev;
> + ? ? ? unsigned long pfn;
> + ? ? ? pgoff_t page_offset;
> + ? ? ? int ret;
> +
> + ? ? ? page_offset = ((unsigned long)vmf->virtual_address -
> + ? ? ? ? ? ? ? ? ? ? ? vma->vm_start) >> PAGE_SHIFT;
> +
> + ? ? ? mutex_lock(&dev->struct_mutex);
> +
> + ? ? ? pfn = (imx_drm_gem_obj->entry->paddr >> PAGE_SHIFT) + page_offset;
> +
> + ? ? ? ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
> +
> + ? ? ? mutex_unlock(&dev->struct_mutex);
> +
> + ? ? ? return convert_to_vm_err_msg(ret);
> +}
> +
> +int imx_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> + ? ? ? int ret;
> +
> + ? ? ? ret = drm_gem_mmap(filp, vma);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? return ret;
> +
> + ? ? ? vma->vm_flags &= ~VM_PFNMAP;
> + ? ? ? vma->vm_flags |= VM_MIXEDMAP;
> +
> + ? ? ? return ret;
> +}
> +
> +
> +int imx_drm_gem_dumb_destroy(struct drm_file *file_priv,
> + ? ? ? ? ? ? ? struct drm_device *dev, unsigned int handle)
> +{
> + ? ? ? return drm_gem_handle_delete(file_priv, handle);
> +}
> diff --git a/drivers/gpu/drm/imx/imx-lcdc-crtc.c b/drivers/gpu/drm/imx/imx-lcdc-crtc.c
> new file mode 100644
> index 0000000..e77c015
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/imx-lcdc-crtc.c
> @@ -0,0 +1,517 @@
> +/*
> + * i.MX LCDC crtc driver
> + *
> + * 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.
> + *
> + */
> +#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 <asm/fb.h>
> +#include <linux/module.h>
> +#include <mach/hardware.h>
> +#include <mach/imxfb.h>
> +#include <generated/mach-types.h>
> +
> +#include "imx-drm.h"
> +
> +#define LCDC_SSA ? ? ? ? ? ? ? 0x00
> +#define LCDC_SIZE ? ? ? ? ? ? ?0x04
> +#define LCDC_VPW ? ? ? ? ? ? ? 0x08
> +#define LCDC_CPOS ? ? ? ? ? ? ?0x0C
> +#define LCDC_LCWHB ? ? ? ? ? ? 0x10
> +#define LCDC_LCHCC ? ? ? ? ? ? 0x14
> +#define LCDC_PCR ? ? ? ? ? ? ? 0x18
> +#define LCDC_HCR ? ? ? ? ? ? ? 0x1C
> +#define LCDC_VCR ? ? ? ? ? ? ? 0x20
> +#define LCDC_POS ? ? ? ? ? ? ? 0x24
> +#define LCDC_LSCR1 ? ? ? ? ? ? 0x28
> +#define LCDC_PWMR ? ? ? ? ? ? ?0x2C
> +#define LCDC_DMACR ? ? ? ? ? ? 0x30
> +#define LCDC_RMCR ? ? ? ? ? ? ?0x34
> +#define LCDC_LCDICR ? ? ? ? ? ?0x38
> +#define LCDC_LIER ? ? ? ? ? ? ?0x3c
> +#define LCDC_LISR ? ? ? ? ? ? ?0x40
> +
> +#define SIZE_XMAX(x) ? ? ? ? ? ((((x) >> 4) & 0x3f) << 20)
> +
> +#define YMAX_MASK ? ? ? ? ? ? ?(cpu_is_mx1() ? 0x1ff : 0x3ff)
> +#define SIZE_YMAX(y) ? ? ? ? ? ((y) & YMAX_MASK)
> +
> +#define VPW_VPW(x) ? ? ? ? ? ? ((x) & 0x3ff)
> +
> +#define HCR_H_WIDTH(x) ? ? ? ? (((x) & 0x3f) << 26)
> +#define HCR_H_WAIT_1(x) ? ? ? ? ? ? ? ?(((x) & 0xff) << 8)
> +#define HCR_H_WAIT_2(x) ? ? ? ? ? ? ? ?((x) & 0xff)
> +
> +#define VCR_V_WIDTH(x) ? ? ? ? (((x) & 0x3f) << 26)
> +#define VCR_V_WAIT_1(x) ? ? ? ? ? ? ? ?(((x) & 0xff) << 8)
> +#define VCR_V_WAIT_2(x) ? ? ? ? ? ? ? ?((x) & 0xff)
> +
> +#define RMCR_LCDC_EN_MX1 ? ? ? (1 << 1)
> +
> +#define RMCR_SELF_REF ? ? ? ? ?(1 << 0)
> +
> +#define LIER_EOF ? ? ? ? ? ? ? (1 << 1)
> +
> +struct imx_crtc {
> + ? ? ? struct drm_crtc ? ? ? ? base;
> + ? ? ? struct imx_drm_crtc ? ? *imx_drm_crtc;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? di_no;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? enabled;
> + ? ? ? void __iomem ? ? ? ? ? ?*regs;
> + ? ? ? u32 ? ? ? ? ? ? ? ? ? ? pwmr;
> + ? ? ? u32 ? ? ? ? ? ? ? ? ? ? lscr1;
> + ? ? ? u32 ? ? ? ? ? ? ? ? ? ? dmacr;
> + ? ? ? u32 ? ? ? ? ? ? ? ? ? ? pcr;
> + ? ? ? struct clk ? ? ? ? ? ? ?*clk;
> + ? ? ? struct device ? ? ? ? ? *dev;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? vblank_enable;
> +
> + ? ? ? struct drm_pending_vblank_event *page_flip_event;
> + ? ? ? struct drm_framebuffer ?*newfb;
> +};
> +
> +#define to_imx_crtc(x) container_of(x, struct imx_crtc, base)
> +
> +static void imx_crtc_load_lut(struct drm_crtc *crtc)
> +{
> +}
> +
> +#define PCR_BPIX_8 ? ? ? ? ? ? (3 << 25)
> +#define PCR_BPIX_12 ? ? ? ? ? ?(4 << 25)
> +#define PCR_BPIX_16 ? ? ? ? ? ?(5 << 25)
> +#define PCR_BPIX_18 ? ? ? ? ? ?(6 << 25)
> +#define PCR_END_SEL ? ? ? ? ? ?(1 << 18)
> +#define PCR_END_BYTE_SWAP ? ? ?(1 << 17)
> +
> +const char *fourcc_to_str(u32 fourcc)
> +{
> + ? ? ? static char buf[5];
> +
> + ? ? ? *(u32 *)buf = fourcc;
> + ? ? ? buf[4] = 0;
> +
> + ? ? ? return buf;
> +}
> +
> +static int imx_drm_crtc_set(struct drm_crtc *crtc,
> + ? ? ? ? ? ? ? struct drm_display_mode *mode)
> +{
> + ? ? ? struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> + ? ? ? struct drm_framebuffer *fb = crtc->fb;
> + ? ? ? int lower_margin = mode->vsync_start - mode->vdisplay;
> + ? ? ? int upper_margin = mode->vtotal - mode->vsync_end;
> + ? ? ? int vsync_len = mode->vsync_end - mode->vsync_start;
> + ? ? ? int hsync_len = mode->hsync_end - mode->hsync_start;
> + ? ? ? int right_margin = mode->hsync_start - mode->hdisplay;
> + ? ? ? int left_margin = mode->htotal - mode->hsync_end;
> + ? ? ? unsigned long lcd_clk;
> + ? ? ? u32 pcr;
> +
> + ? ? ? lcd_clk = clk_get_rate(imx_crtc->clk) / 1000;
> +
> + ? ? ? if (!mode->clock)
> + ? ? ? ? ? ? ? return -EINVAL;
> +
> + ? ? ? pcr = DIV_ROUND_CLOSEST(lcd_clk, mode->clock);
> + ? ? ? if (--pcr > 0x3f)
> + ? ? ? ? ? ? ? pcr = 0x3f;
> +
> + ? ? ? switch (fb->pixel_format) {
> + ? ? ? case DRM_FORMAT_XRGB8888:
> + ? ? ? case DRM_FORMAT_ARGB8888:
> + ? ? ? ? ? ? ? pcr |= PCR_BPIX_18;
> + ? ? ? ? ? ? ? pcr |= PCR_END_SEL | PCR_END_BYTE_SWAP;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case DRM_FORMAT_RGB565:
> + ? ? ? ? ? ? ? if (cpu_is_mx1())
> + ? ? ? ? ? ? ? ? ? ? ? pcr |= PCR_BPIX_12;
> + ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? pcr |= PCR_BPIX_16;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case DRM_FORMAT_RGB332:
> + ? ? ? ? ? ? ? pcr |= PCR_BPIX_8;
> + ? ? ? ? ? ? ? break;
> + ? ? ? default:
> + ? ? ? ? ? ? ? dev_err(imx_crtc->dev, "unsupported pixel format %s\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fourcc_to_str(fb->pixel_format));
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> +
> + ? ? ? /* add sync polarities */
> + ? ? ? pcr |= imx_crtc->pcr & ~(0x3f | (7 << 25));
> +
> + ? ? ? dev_dbg(imx_crtc->dev,
> + ? ? ? ? ? ? ? ? ? ? ? "xres=%d hsync_len=%d left_margin=%d right_margin=%d\n",
> + ? ? ? ? ? ? ? ? ? ? ? mode->hdisplay, hsync_len,
> + ? ? ? ? ? ? ? ? ? ? ? left_margin, right_margin);
> + ? ? ? dev_dbg(imx_crtc->dev,
> + ? ? ? ? ? ? ? ? ? ? ? "yres=%d vsync_len=%d upper_margin=%d lower_margin=%d\n",
> + ? ? ? ? ? ? ? ? ? ? ? mode->vdisplay, vsync_len,
> + ? ? ? ? ? ? ? ? ? ? ? upper_margin, lower_margin);
> +
> + ? ? ? writel(VPW_VPW(mode->hdisplay * fb->bits_per_pixel / 8 / 4),
> + ? ? ? ? ? ? ? imx_crtc->regs + LCDC_VPW);
> +
> + ? ? ? writel(HCR_H_WIDTH(hsync_len - 1) |
> + ? ? ? ? ? ? ? HCR_H_WAIT_1(right_margin - 1) |
> + ? ? ? ? ? ? ? HCR_H_WAIT_2(left_margin - 3),
> + ? ? ? ? ? ? ? imx_crtc->regs + LCDC_HCR);
> +
> + ? ? ? writel(VCR_V_WIDTH(vsync_len) |
> + ? ? ? ? ? ? ? VCR_V_WAIT_1(lower_margin) |
> + ? ? ? ? ? ? ? VCR_V_WAIT_2(upper_margin),
> + ? ? ? ? ? ? ? imx_crtc->regs + LCDC_VCR);
> +
> + ? ? ? writel(SIZE_XMAX(mode->hdisplay) | SIZE_YMAX(mode->vdisplay),
> + ? ? ? ? ? ? ? ? ? ? ? imx_crtc->regs + LCDC_SIZE);
> +
> + ? ? ? writel(pcr, imx_crtc->regs + LCDC_PCR);
> + ? ? ? writel(imx_crtc->pwmr, imx_crtc->regs + LCDC_PWMR);
> + ? ? ? writel(imx_crtc->lscr1, imx_crtc->regs + LCDC_LSCR1);
> + ? ? ? /* reset default */
> + ? ? ? writel(0x00040060, imx_crtc->regs + LCDC_DMACR);
> +
> + ? ? ? return 0;
> +}
> +
> +static int imx_drm_set_base(struct drm_crtc *crtc, int x, int y)
> +{
> + ? ? ? struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> + ? ? ? struct imx_drm_buf_entry *entry;
> + ? ? ? struct drm_framebuffer *fb = crtc->fb;
> + ? ? ? unsigned long phys;
> +
> + ? ? ? entry = imx_drm_fb_get_buf(fb);
> + ? ? ? if (!entry)
> + ? ? ? ? ? ? ? return -EFAULT;
> +
> + ? ? ? phys = entry->paddr;
> + ? ? ? phys += x * (fb->bits_per_pixel >> 3);
> + ? ? ? phys += y * fb->pitches[0];
> +
> + ? ? ? dev_dbg(imx_crtc->dev, "%s: phys: 0x%lx\n", __func__, phys);
> + ? ? ? dev_dbg(imx_crtc->dev, "%s: xy: %dx%d\n", __func__, x, y);
> +
> + ? ? ? writel(phys, imx_crtc->regs + LCDC_SSA);
> +
> + ? ? ? return 0;
> +}
> +
> +static int imx_crtc_mode_set(struct drm_crtc *crtc,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct drm_display_mode *mode,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct drm_display_mode *adjusted_mode,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int x, int y,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct drm_framebuffer *old_fb)
> +{
> + ? ? ? struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> + ? ? ? imx_drm_set_base(crtc, x, y);
> +
> + ? ? ? dev_dbg(imx_crtc->dev, "mode->hdisplay: %d\n", mode->hdisplay);
> + ? ? ? dev_dbg(imx_crtc->dev, "mode->vdisplay: %d\n", mode->vdisplay);
> +
> + ? ? ? return imx_drm_crtc_set(crtc, mode);
> +}
> +
> +static void imx_crtc_enable(struct imx_crtc *imx_crtc)
> +{
> + ? ? ? if (!imx_crtc->enabled)
> + ? ? ? ? ? ? ? clk_enable(imx_crtc->clk);
> + ? ? ? imx_crtc->enabled = 1;
> +}
> +
> +static void imx_crtc_disable(struct imx_crtc *imx_crtc)
> +{
> + ? ? ? if (imx_crtc->enabled)
> + ? ? ? ? ? ? ? clk_disable(imx_crtc->clk);
> + ? ? ? imx_crtc->enabled = 0;
> +}
> +
> +static void imx_crtc_dpms(struct drm_crtc *crtc, int mode)
> +{
> + ? ? ? struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> + ? ? ? dev_dbg(imx_crtc->dev, "%s mode: %d\n", __func__, mode);
> +
> + ? ? ? switch (mode) {
> + ? ? ? case DRM_MODE_DPMS_ON:
> + ? ? ? ? ? ? ? imx_crtc_enable(imx_crtc);
> + ? ? ? ? ? ? ? break;
> + ? ? ? default:
> + ? ? ? ? ? ? ? imx_crtc_disable(imx_crtc);
> + ? ? ? ? ? ? ? break;
> + ? ? ? }
> +}
> +
> +static bool imx_crtc_mode_fixup(struct drm_crtc *crtc,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct drm_display_mode *mode,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct drm_display_mode *adjusted_mode)
> +{
> + ? ? ? return true;
> +}
> +
> +static void imx_crtc_prepare(struct drm_crtc *crtc)
> +{
> + ? ? ? struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> + ? ? ? imx_crtc_disable(imx_crtc);
> +}
> +
> +static void imx_crtc_commit(struct drm_crtc *crtc)
> +{
> + ? ? ? struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> + ? ? ? imx_crtc_enable(imx_crtc);
> +}
> +
> +static struct drm_crtc_helper_funcs imx_helper_funcs = {
> + ? ? ? .dpms = imx_crtc_dpms,
> + ? ? ? .mode_fixup = imx_crtc_mode_fixup,
> + ? ? ? .mode_set = imx_crtc_mode_set,
> + ? ? ? .prepare = imx_crtc_prepare,
> + ? ? ? .commit = imx_crtc_commit,
> + ? ? ? .load_lut = imx_crtc_load_lut,
> +};
> +
> +static void imx_drm_handle_pageflip(struct imx_crtc *imx_crtc)
> +{
> + ? ? ? struct drm_pending_vblank_event *e;
> + ? ? ? struct timeval now;
> + ? ? ? unsigned long flags;
> + ? ? ? struct drm_device *drm = imx_crtc->base.dev;
> +
> + ? ? ? spin_lock_irqsave(&drm->event_lock, flags);
> +
> + ? ? ? e = imx_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;
> + ? ? ? imx_crtc->page_flip_event = NULL;
> +
> + ? ? ? 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 int imx_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> + ? ? ? struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> + ? ? ? writel(LIER_EOF, imx_crtc->regs + LCDC_LIER);
> +
> + ? ? ? return 0;
> +}
> +
> +static void imx_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> + ? ? ? struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> + ? ? ? writel(0, imx_crtc->regs + LCDC_LIER);
> +}
> +
> +static irqreturn_t imx_irq_handler(int irq, void *dev_id)
> +{
> + ? ? ? struct imx_crtc *imx_crtc = dev_id;
> + ? ? ? struct drm_device *drm = imx_crtc->base.dev;
> +
> + ? ? ? /* Acknowledge interrupt */
> + ? ? ? readl(imx_crtc->regs + LCDC_LISR);
> +
> + ? ? ? drm_handle_vblank(drm, 0);
> +
> + ? ? ? if (imx_crtc->newfb) {
> + ? ? ? ? ? ? ? imx_crtc->base.fb = imx_crtc->newfb;
> + ? ? ? ? ? ? ? imx_crtc->newfb = NULL;
> + ? ? ? ? ? ? ? imx_drm_set_base(&imx_crtc->base, 0, 0);
> + ? ? ? ? ? ? ? imx_drm_handle_pageflip(imx_crtc);
> + ? ? ? ? ? ? ? imx_drm_crtc_vblank_put(imx_crtc->imx_drm_crtc);
> + ? ? ? }
> +
> + ? ? ? return IRQ_HANDLED;
> +}
> +
> +static int imx_page_flip(struct drm_crtc *crtc,
> + ? ? ? ? ? ? ? ? ? ? ? ? struct drm_framebuffer *fb,
> + ? ? ? ? ? ? ? ? ? ? ? ? struct drm_pending_vblank_event *event)
> +{
> + ? ? ? struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> + ? ? ? if (imx_crtc->newfb)
> + ? ? ? ? ? ? ? return -EBUSY;
> +
> + ? ? ? imx_crtc->newfb = fb;
> + ? ? ? imx_crtc->page_flip_event = event;
> + ? ? ? imx_drm_crtc_vblank_get(imx_crtc->imx_drm_crtc);
> +
> + ? ? ? return 0;
> +}
> +
> +static const struct drm_crtc_funcs imx_crtc_funcs = {
> + ? ? ? .page_flip = imx_page_flip,
> +};
> +
> +static const struct imx_drm_crtc_helper_funcs imx_imx_drm_helper = {
> + ? ? ? .enable_vblank = imx_crtc_enable_vblank,
> + ? ? ? .disable_vblank = imx_crtc_disable_vblank,
> +};
> +
> +#define DRIVER_NAME "imx-lcdc-crtc"
> +
> +/*
> + * the pcr bits to be allowed to set in platform data
> + */
> +#define PDATA_PCR ? ? ?(PCR_PIXPOL | PCR_FLMPOL | PCR_LPPOL | \
> + ? ? ? ? ? ? ? ? ? ? ? PCR_CLKPOL | PCR_OEPOL | PCR_TFT | PCR_COLOR | \
> + ? ? ? ? ? ? ? ? ? ? ? PCR_PBSIZ_8 | PCR_ACD(0x7f) | PCR_ACD_SEL | \
> + ? ? ? ? ? ? ? ? ? ? ? PCR_SCLK_SEL | PCR_SHARP)
> +
> +static int __devinit imx_crtc_probe(struct platform_device *pdev)
> +{
> + ? ? ? struct imx_crtc *imx_crtc;
> + ? ? ? struct resource *res;
> + ? ? ? int ret, irq;
> + ? ? ? u32 pcr_value = 0xf00080c0;
> + ? ? ? u32 lscr1_value = 0x00120300;
> + ? ? ? u32 pwmr_value = 0x00a903ff;
> +
> + ? ? ? imx_crtc = devm_kzalloc(&pdev->dev, sizeof(*imx_crtc), GFP_KERNEL);
> + ? ? ? if (!imx_crtc)
> + ? ? ? ? ? ? ? return -ENOMEM;
> +
> + ? ? ? imx_crtc->dev = &pdev->dev;
> +
> + ? ? ? res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + ? ? ? if (!res)
> + ? ? ? ? ? ? ? return -ENODEV;
> +
> + ? ? ? res = devm_request_mem_region(&pdev->dev, res->start,
> + ? ? ? ? ? ? ? ? ? ? ? resource_size(res), DRIVER_NAME);
> + ? ? ? if (!res)
> + ? ? ? ? ? ? ? return -EBUSY;
> +
> + ? ? ? imx_crtc->regs = devm_ioremap(&pdev->dev, res->start,
> + ? ? ? ? ? ? ? ? ? ? ? resource_size(res));
> + ? ? ? if (!imx_crtc->regs) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Cannot map frame buffer registers\n");
> + ? ? ? ? ? ? ? return -EBUSY;
> + ? ? ? }
> +
> + ? ? ? irq = platform_get_irq(pdev, 0);
> + ? ? ? ret = devm_request_irq(&pdev->dev, irq, imx_irq_handler, 0, "imx_drm",
> + ? ? ? ? ? ? ? ? ? ? ? imx_crtc);
> + ? ? ? if (ret < 0) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "irq request failed with %d\n", ret);
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> +
> + ? ? ? imx_crtc->clk = clk_get(&pdev->dev, NULL);
> + ? ? ? if (IS_ERR(imx_crtc->clk)) {
> + ? ? ? ? ? ? ? ret = PTR_ERR(imx_crtc->clk);
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "unable to get clock: %d\n", ret);
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> +
> + ? ? ? clk_prepare_enable(imx_crtc->clk);
> + ? ? ? imx_crtc->enabled = 1;
> +
> + ? ? ? platform_set_drvdata(pdev, imx_crtc);
> +
> + ? ? ? imx_crtc->pcr = pcr_value & PDATA_PCR;
> +
> + ? ? ? if (imx_crtc->pcr != pcr_value)
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "invalid bits set in pcr: 0x%08x\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pcr_value & ~PDATA_PCR);
> +
> + ? ? ? imx_crtc->lscr1 = lscr1_value;
> + ? ? ? imx_crtc->pwmr = pwmr_value;
> +
> + ? ? ? ret = imx_drm_add_crtc(&imx_crtc->base,
> + ? ? ? ? ? ? ? ? ? ? ? &imx_crtc->imx_drm_crtc,
> + ? ? ? ? ? ? ? ? ? ? ? &imx_crtc_funcs, &imx_helper_funcs,
> + ? ? ? ? ? ? ? ? ? ? ? &imx_imx_drm_helper, THIS_MODULE);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? goto err_init;
> +
> + ? ? ? dev_info(&pdev->dev, "probed\n");
> +
> + ? ? ? return 0;
> +
> +err_init:
> + ? ? ? clk_disable_unprepare(imx_crtc->clk);
> + ? ? ? clk_put(imx_crtc->clk);
> +
> + ? ? ? return ret;
> +}
> +
> +static int __devexit imx_crtc_remove(struct platform_device *pdev)
> +{
> + ? ? ? struct imx_crtc *imx_crtc = platform_get_drvdata(pdev);
> +
> + ? ? ? imx_drm_remove_crtc(imx_crtc->imx_drm_crtc);
> +
> + ? ? ? writel(0, imx_crtc->regs + LCDC_LIER);
> +
> + ? ? ? clk_disable_unprepare(imx_crtc->clk);
> + ? ? ? clk_put(imx_crtc->clk);
> +
> + ? ? ? platform_set_drvdata(pdev, NULL);
> +
> + ? ? ? return 0;
> +}
> +
> +static const struct of_device_id imx_lcdc_dt_ids[] = {
> + ? ? ? { .compatible = "fsl,imx1-lcdc", .data = NULL, },
> + ? ? ? { .compatible = "fsl,imx21-lcdc", .data = NULL, },
> + ? ? ? { /* sentinel */ }
> +};
> +
> +static struct platform_driver imx_crtc_driver = {
> + ? ? ? .remove ? ? ? ? = __devexit_p(imx_crtc_remove),
> + ? ? ? .probe ? ? ? ? ?= imx_crtc_probe,
> + ? ? ? .driver ? ? ? ? = {
> + ? ? ? ? ? ? ? .name ? = DRIVER_NAME,
> + ? ? ? ? ? ? ? .owner ?= THIS_MODULE,
> + ? ? ? ? ? ? ? .of_match_table = imx_lcdc_dt_ids,
> + ? ? ? },
> +};
> +
> +static int __init imx_lcdc_init(void)
> +{
> + ? ? ? return platform_driver_register(&imx_crtc_driver);
> +}
> +
> +static void __exit imx_lcdc_exit(void)
> +{
> + ? ? ? platform_driver_unregister(&imx_crtc_driver);
> +}
> +
> +module_init(imx_lcdc_init);
> +module_exit(imx_lcdc_exit)
> +
> +MODULE_DESCRIPTION("Freescale i.MX framebuffer driver");
> +MODULE_AUTHOR("Sascha Hauer, Pengutronix");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/imx/imx-parallel-display.c b/drivers/gpu/drm/imx/imx-parallel-display.c
> new file mode 100644
> index 0000000..8c96113
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/imx-parallel-display.c
> @@ -0,0 +1,228 @@
> +/*
> + * 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 "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;
> +};
> +
> +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 ret;
> +
> + ? ? ? drm_mode_connector_update_edid_property(connector, imxpd->edid);
> + ? ? ? ret = drm_add_edid_modes(connector, imxpd->edid);
> + ? ? ? connector->display_info.raw_edid = NULL;
> +
> + ? ? ? return ret;
> +}
> +
> +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,
> + ? ? ? ? ? ? ? ? ? ? ? ? ?struct drm_display_mode *mode,
> + ? ? ? ? ? ? ? ? ? ? ? ? ?struct drm_display_mode *adjusted_mode)
> +{
> + ? ? ? return true;
> +}
> +
> +static void imx_pd_encoder_prepare(struct drm_encoder *encoder)
> +{
> +}
> +
> +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 */
> +}
> +
> +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,
> +};
> +
> +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;
> +
> + ? ? ? 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;
> +
> + ? ? ? imxpd = devm_kzalloc(&pdev->dev, sizeof(*imxpd), GFP_KERNEL);
> + ? ? ? if (!imxpd)
> + ? ? ? ? ? ? ? return -ENOMEM;
> +
> + ? ? ? edidp = of_get_property(np, "edid", &imxpd->edid_len);
> +
> + ? ? ? imxpd->edid = kmemdup(edidp, imxpd->edid_len, GFP_KERNEL);
> + ? ? ? imxpd->encoder.possible_crtcs = 0x1;
> + ? ? ? imxpd->encoder.possible_clones = 0x1;
> + ? ? ? imxpd->dev = &pdev->dev;
> +
> + ? ? ? ret = imx_pd_register(imxpd);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? return ret;
> +
> + ? ? ? 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", .data = NULL, },
> + ? ? ? { /* 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");
> --
> 1.7.10
>
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 1/2] DRM: add Freescale i.MX LCDC driver
@ 2012-05-22 21:28     ` Rob Clark
  0 siblings, 0 replies; 26+ messages in thread
From: Rob Clark @ 2012-05-22 21:28 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-arm-kernel, dri-devel

just a few comments from a cursory review..  I need a bit more time
for a more in-depth review but that won't be tonight so I thought I'd
send what I have so far..


On Fri, May 18, 2012 at 6:27 AM, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/gpu/drm/Kconfig                    |    2 +
>  drivers/gpu/drm/Makefile                   |    1 +
>  drivers/gpu/drm/imx/Kconfig                |   18 +
>  drivers/gpu/drm/imx/Makefile               |    8 +
>  drivers/gpu/drm/imx/imx-drm-core.c         |  745 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/imx/imx-fb.c               |  179 +++++++
>  drivers/gpu/drm/imx/imx-fbdev.c            |  275 ++++++++++
>  drivers/gpu/drm/imx/imx-gem.c              |  343 +++++++++++++
>  drivers/gpu/drm/imx/imx-lcdc-crtc.c        |  517 +++++++++++++++++++
>  drivers/gpu/drm/imx/imx-parallel-display.c |  228 +++++++++
>  10 files changed, 2316 insertions(+)
>  create mode 100644 drivers/gpu/drm/imx/Kconfig
>  create mode 100644 drivers/gpu/drm/imx/Makefile
>  create mode 100644 drivers/gpu/drm/imx/imx-drm-core.c
>  create mode 100644 drivers/gpu/drm/imx/imx-fb.c
>  create mode 100644 drivers/gpu/drm/imx/imx-fbdev.c
>  create mode 100644 drivers/gpu/drm/imx/imx-gem.c
>  create mode 100644 drivers/gpu/drm/imx/imx-lcdc-crtc.c
>  create mode 100644 drivers/gpu/drm/imx/imx-parallel-display.c
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index e354bc0..759502c 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -186,3 +186,5 @@ source "drivers/gpu/drm/vmwgfx/Kconfig"
>  source "drivers/gpu/drm/gma500/Kconfig"
>
>  source "drivers/gpu/drm/udl/Kconfig"
> +
> +source "drivers/gpu/drm/imx/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index c20da5b..6569d8d 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -42,4 +42,5 @@ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
>  obj-$(CONFIG_DRM_EXYNOS) +=exynos/
>  obj-$(CONFIG_DRM_GMA500) += gma500/
>  obj-$(CONFIG_DRM_UDL) += udl/
> +obj-$(CONFIG_DRM_IMX) += imx/
>  obj-y                  += i2c/
> diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig
> new file mode 100644
> index 0000000..5fc3a44
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/Kconfig
> @@ -0,0 +1,18 @@
> +config DRM_IMX
> +       tristate "DRM Support for Freescale i.MX"
> +       select DRM_KMS_HELPER
> +       depends on DRM && ARCH_MXC
> +
> +config DRM_IMX_FB_HELPER
> +       tristate "provide legacy framebuffer /dev/fb0"
> +       depends on DRM_IMX
> +
> +config DRM_IMX_LCDC
> +       tristate "DRM Support for Freescale i.MX1 and i.MX2"
> +       depends on DRM_IMX
> +       help
> +         Choose this if you have a i.MX1, i.MX21, i.MX25 or i.MX27 processor.

do you have something like cpu_is_imx2() type macros?  It would be
preferable not to have a compile time config option for building for
certain hw versions.  Ie. on OMAP we could do something like 'if
(cpu_is_omap3xxx()) { ... }' if there was something that needed to be
done different on omap3 family of devices.  This way you could choose
to build a kernel supporting either a single omap variants, or all
omap variants.

> +
> +config DRM_IMX_PARALLEL_DISPLAY
> +       tristate "Support for parallel displays"
> +       depends on DRM_IMX
> diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
> new file mode 100644
> index 0000000..0f7c038
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/Makefile
> @@ -0,0 +1,8 @@
> +
> +imxdrm-objs := imx-drm-core.o imx-fb.o imx-gem.o
> +
> +obj-$(CONFIG_DRM_IMX) += imxdrm.o
> +
> +obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += imx-parallel-display.o
> +obj-$(CONFIG_DRM_IMX_LCDC) += imx-lcdc-crtc.o
> +obj-$(CONFIG_DRM_IMX_FB_HELPER) += imx-fbdev.o
> diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
> new file mode 100644
> index 0000000..29f5f10
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/imx-drm-core.c
> @@ -0,0 +1,745 @@
> +/*
> + * simple 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 <asm/fb.h>
> +#include <linux/module.h>
> +
> +#include "imx-drm.h"
> +
> +#define MAX_CRTC       4
> +
> +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;
> +};
> +
> +struct imx_drm_crtc {
> +       struct drm_crtc                         *crtc;
> +       struct list_head                        list;
> +       struct imx_drm_device                   *imxdrm;
> +       int                                     pipe;
> +       struct drm_crtc_helper_funcs            crtc_helper_funcs;
> +       struct drm_crtc_funcs                   crtc_funcs;
> +       struct imx_drm_crtc_helper_funcs        imx_drm_helper_funcs;
> +       struct module                           *owner;
> +};
> +
> +struct imx_drm_encoder {
> +       struct drm_encoder                      *encoder;
> +       struct list_head                        list;
> +       struct module                           *owner;
> +};
> +
> +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)
> +{
> +       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_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 struct vm_operations_struct imx_drm_gem_vm_ops = {
> +       .fault = imx_drm_gem_fault,
> +       .open = drm_gem_vm_open,
> +       .close = drm_gem_vm_close,
> +};
> +
> +static const struct file_operations imx_drm_driver_fops = {
> +       .owner = THIS_MODULE,
> +       .open = drm_open,
> +       .release = drm_release,
> +       .unlocked_ioctl = drm_ioctl,
> +       .mmap = imx_drm_gem_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 imx_drm_device->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();
> +
> +       drm_encoder_init(imxdrm->drm, imx_drm_encoder->encoder,
> +                       imx_drm_encoder->encoder->funcs,
> +                       DRM_MODE_ENCODER_TMDS);
> +
> +       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();
> +       int ret;
> +
> +       drm_connector_init(imxdrm->drm, imx_drm_connector->connector,
> +                       imx_drm_connector->connector->funcs,
> +                       DRM_MODE_CONNECTOR_VGA);
> +       drm_mode_group_reinit(imxdrm->drm);
> +       ret = drm_sysfs_connector_add(imx_drm_connector->connector);
> +       if (ret)
> +               goto err;
> +
> +       return 0;
> +err:
> +
> +       return ret;
> +}
> +
> +/*
> + * 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->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->crtc_helper_funcs);
> +
> +       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;
> +}
> +
> +/*
> + * 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 drm_crtc_funcs *crtc_funcs,
> +               const struct drm_crtc_helper_funcs *crtc_helper_funcs,
> +               const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs,
> +               struct module *owner)
> +{
> +       struct imx_drm_device *imxdrm = __imx_drm_device();
> +       struct imx_drm_crtc *imx_drm_crtc;
> +       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->crtc_funcs = *crtc_funcs;
> +       imx_drm_crtc->crtc_helper_funcs = *crtc_helper_funcs;
> +       imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs;
> +
> +       WARN_ON(crtc_funcs->set_config);
> +       WARN_ON(crtc_funcs->destroy);
> +
> +       imx_drm_crtc->crtc_funcs.set_config = drm_crtc_helper_set_config;
> +       imx_drm_crtc->crtc_funcs.destroy = drm_crtc_cleanup;
> +
> +       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;
> +
> +       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);
> +
> +       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(struct 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);
> +
> +/*
> + * 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();
> +
> +       mutex_lock(&imxdrm->mutex);
> +
> +       imx_drm_encoder_unregister(imx_drm_encoder);
> +
> +       list_del(&imx_drm_encoder->list);
> +
> +       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(struct 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);
> +
> +/*
> + * 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        = imx_drm_gem_free_object,
> +       .gem_vm_ops             = &imx_drm_gem_vm_ops,
> +       .dumb_create            = imx_drm_gem_dumb_create,
> +       .dumb_map_offset        = imx_drm_gem_dumb_map_offset,
> +       .dumb_destroy           = imx_drm_gem_dumb_destroy,
> +
> +       .get_vblank_counter     = drm_vblank_count,
> +       .enable_vblank          = imx_drm_enable_vblank,
> +       .disable_vblank         = imx_drm_disable_vblank,
> +       .reclaim_buffers        = drm_core_reclaim_buffers,
> +       .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_pdev:
> +       kfree(imx_drm_device);
> +err_pdrv:
> +       platform_device_unregister(imx_drm_pdev);
> +
> +       return ret;
> +}
> +
> +static void __exit imx_drm_exit(void)
> +{
> +       DRM_DEBUG_DRIVER("%s\n", __FILE__);
> +
> +       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/gpu/drm/imx/imx-fb.c b/drivers/gpu/drm/imx/imx-fb.c
> new file mode 100644
> index 0000000..5a08c86
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/imx-fb.c
> @@ -0,0 +1,179 @@
> +/*
> + * 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 "imx-drm.h"
> +
> +#define to_imx_drm_fb(x)       container_of(x, struct imx_drm_fb, fb)
> +
> +/*
> + * imx specific framebuffer structure.
> + *
> + * @fb: drm framebuffer obejct.
> + * @imx_drm_gem_obj: drm ec specific gem object containing a gem object.
> + * @entry: pointer to ec drm buffer entry object.
> + *     - containing only the information to physically continuous memory
> + *     region allocated at default framebuffer creation.
> + */
> +struct imx_drm_fb {
> +       struct drm_framebuffer          fb;
> +       struct imx_drm_gem_obj  *imx_drm_gem_obj;
> +       struct imx_drm_buf_entry        *entry;
> +};
> +
> +static void imx_drm_fb_destroy(struct drm_framebuffer *fb)
> +{
> +       struct imx_drm_fb *imx_drm_fb = to_imx_drm_fb(fb);
> +
> +       drm_framebuffer_cleanup(fb);
> +
> +       /*
> +        * default framebuffer has no gem object so
> +        * a buffer of the default framebuffer should be released at here.
> +        */
> +       if (!imx_drm_fb->imx_drm_gem_obj && imx_drm_fb->entry)
> +               imx_drm_buf_destroy(fb->dev, imx_drm_fb->entry);
> +
> +       kfree(imx_drm_fb);
> +}
> +
> +static int imx_drm_fb_create_handle(struct drm_framebuffer *fb,
> +               struct drm_file *file_priv, unsigned int *handle)
> +{
> +       struct imx_drm_fb *imx_drm_fb = to_imx_drm_fb(fb);
> +
> +       return drm_gem_handle_create(file_priv,
> +                       &imx_drm_fb->imx_drm_gem_obj->base, handle);
> +}
> +
> +static struct drm_framebuffer_funcs imx_drm_fb_funcs = {
> +       .destroy        = imx_drm_fb_destroy,
> +       .create_handle  = imx_drm_fb_create_handle,
> +};
> +
> +static struct drm_framebuffer *imx_drm_fb_create(struct drm_device *dev,
> +               struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
> +{
> +       struct imx_drm_fb *imx_drm_fb;
> +       struct drm_framebuffer *fb;
> +       struct drm_gem_object *obj;
> +       unsigned int size;
> +       int ret;
> +       u32 bpp, depth;
> +
> +       drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp);
> +
> +       mode_cmd->pitches[0] = max(mode_cmd->pitches[0],
> +                       mode_cmd->width * (bpp >> 3));
> +
> +       dev_dbg(dev->dev, "drm fb create(%dx%d)\n",
> +                       mode_cmd->width, mode_cmd->height);
> +
> +       imx_drm_fb = kzalloc(sizeof(*imx_drm_fb), GFP_KERNEL);
> +       if (!imx_drm_fb) {
> +               dev_err(dev->dev, "failed to allocate drm framebuffer.\n");
> +               return ERR_PTR(-ENOMEM);
> +       }
> +
> +       fb = &imx_drm_fb->fb;
> +       ret = drm_framebuffer_init(dev, fb, &imx_drm_fb_funcs);
> +       if (ret) {
> +               dev_err(dev->dev, "failed to initialize framebuffer.\n");
> +               goto err_init;
> +       }
> +
> +       dev_dbg(dev->dev, "create: fb id: %d\n", fb->base.id);
> +
> +       size = mode_cmd->pitches[0] * mode_cmd->height;
> +
> +       /*
> +        * without file_priv we are called from imx_drm_fbdev_create in which
> +        * case we only create a framebuffer without a handle.
> +        */
> +       if (!file_priv) {
> +               struct imx_drm_buf_entry *entry;
> +
> +               entry = imx_drm_buf_create(dev, size);
> +               if (IS_ERR(entry)) {
> +                       ret = PTR_ERR(entry);
> +                       goto err_buffer;
> +               }
> +
> +               imx_drm_fb->entry = entry;
> +       } else {
> +               obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
> +               if (!obj) {
> +                       ret = -EINVAL;
> +                       goto err_buffer;
> +               }
> +
> +               imx_drm_fb->imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> +
> +               drm_gem_object_unreference_unlocked(obj);


the fb probably should hold the reference to the GEM obj until it is
deleted..  so you don't end up freeing the buffer you are scanning out
until you are done scanning it out.  Possibly this gets a bit more
straightforward if you don't have to special case the default/fbcon
framebuffer?

IIRC, Laurent had sent a patch recently to fix the same issue in the
exynos driver.

> +               imx_drm_fb->entry = imx_drm_fb->imx_drm_gem_obj->entry;
> +       }
> +
> +       drm_helper_mode_fill_fb_struct(fb, mode_cmd);
> +
> +       return fb;
> +
> +err_buffer:
> +       drm_framebuffer_cleanup(fb);
> +
> +err_init:
> +       kfree(imx_drm_fb);
> +
> +       return ERR_PTR(ret);
> +}
> +
> +struct imx_drm_buf_entry *imx_drm_fb_get_buf(struct drm_framebuffer *fb)
> +{
> +       struct imx_drm_fb *imx_drm_fb = to_imx_drm_fb(fb);
> +       struct imx_drm_buf_entry *entry;
> +
> +       entry = imx_drm_fb->entry;
> +
> +       return entry;
> +}
> +EXPORT_SYMBOL_GPL(imx_drm_fb_get_buf);
> +
> +static struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
> +       .fb_create = imx_drm_fb_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/gpu/drm/imx/imx-fbdev.c b/drivers/gpu/drm/imx/imx-fbdev.c
> new file mode 100644
> index 0000000..f038797
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/imx-fbdev.c
> @@ -0,0 +1,275 @@
> +/*
> + * 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_fb_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "imx-drm.h"
> +
> +#define MAX_CONNECTOR          4
> +#define PREFERRED_BPP          16
> +
> +static struct fb_ops imx_drm_fb_ops = {
> +       .owner          = THIS_MODULE,
> +       .fb_fillrect    = cfb_fillrect,
> +       .fb_copyarea    = cfb_copyarea,
> +       .fb_imageblit   = cfb_imageblit,
> +       .fb_check_var   = drm_fb_helper_check_var,
> +       .fb_set_par     = drm_fb_helper_set_par,
> +       .fb_blank       = drm_fb_helper_blank,
> +       .fb_pan_display = drm_fb_helper_pan_display,
> +       .fb_setcmap     = drm_fb_helper_setcmap,
> +};
> +
> +static int imx_drm_fbdev_update(struct drm_fb_helper *helper,
> +                                    struct drm_framebuffer *fb,
> +                                    unsigned int fb_width,
> +                                    unsigned int fb_height)
> +{
> +       struct fb_info *fbi = helper->fbdev;
> +       struct drm_device *drm = helper->dev;
> +       struct imx_drm_buf_entry *entry;
> +       unsigned int size = fb_width * fb_height * (fb->bits_per_pixel >> 3);
> +       unsigned long offset;
> +
> +       drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
> +       drm_fb_helper_fill_var(fbi, helper, fb_width, fb_height);
> +
> +       entry = imx_drm_fb_get_buf(fb);
> +       if (!entry) {
> +               dev_dbg(drm->dev, "entry is null.\n");
> +               return -EFAULT;
> +       }
> +
> +       offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
> +       offset += fbi->var.yoffset * fb->pitches[0];
> +
> +       drm->mode_config.fb_base = entry->paddr;
> +       fbi->screen_base = entry->vaddr + offset;
> +       fbi->fix.smem_start = entry->paddr + offset;
> +       fbi->screen_size = size;
> +       fbi->fix.smem_len = size;
> +       fbi->flags |= FBINFO_CAN_FORCE_OUTPUT;
> +
> +       return 0;
> +}
> +
> +static int imx_drm_fbdev_create(struct drm_fb_helper *helper,
> +                                   struct drm_fb_helper_surface_size *sizes)
> +{
> +       struct drm_device *drm = helper->dev;
> +       struct fb_info *fbi;
> +       struct drm_framebuffer *fb;
> +       struct drm_mode_fb_cmd2 mode_cmd = { 0 };
> +       struct platform_device *pdev = drm->platformdev;
> +       int ret;
> +
> +       dev_dbg(drm->dev, "surface width(%d), height(%d) and bpp(%d\n",
> +                       sizes->surface_width, sizes->surface_height,
> +                       sizes->surface_bpp);
> +
> +       mode_cmd.width = sizes->surface_width;
> +       mode_cmd.height = sizes->surface_height;
> +       mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
> +                       sizes->surface_depth);
> +
> +       mutex_lock(&drm->struct_mutex);
> +
> +       fbi = framebuffer_alloc(0, &pdev->dev);
> +       if (!fbi) {
> +               ret = -ENOMEM;
> +               goto err_fb_alloc;
> +       }
> +
> +       fb = drm->mode_config.funcs->fb_create(drm, NULL, &mode_cmd);
> +       if (IS_ERR(fb)) {
> +               dev_err(drm->dev, "failed to create drm framebuffer.\n");
> +               ret = PTR_ERR(fb);
> +               goto err_fb_create;
> +       }
> +
> +       helper->fb = fb;
> +       helper->fbdev = fbi;
> +
> +       fbi->par = helper;
> +       fbi->flags = FBINFO_FLAG_DEFAULT;
> +       fbi->fbops = &imx_drm_fb_ops;
> +
> +       ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
> +       if (ret)
> +               goto err_alloc_cmap;
> +
> +       ret = imx_drm_fbdev_update(helper, helper->fb, sizes->fb_width,
> +                       sizes->fb_height);
> +       if (ret)
> +               goto err_fbdev_update;
> +
> +       mutex_unlock(&drm->struct_mutex);
> +
> +       return 0;
> +
> +err_fbdev_update:
> +       fb_dealloc_cmap(&fbi->cmap);
> +
> +err_alloc_cmap:
> +       fb->funcs->destroy(fb);
> +
> +err_fb_create:
> +       framebuffer_release(fbi);
> +
> +err_fb_alloc:
> +       mutex_unlock(&drm->struct_mutex);
> +
> +       return ret;
> +}
> +
> +static int imx_drm_fbdev_probe(struct drm_fb_helper *helper,
> +                                  struct drm_fb_helper_surface_size *sizes)
> +{
> +       int ret;
> +
> +       BUG_ON(helper->fb);
> +
> +       ret = imx_drm_fbdev_create(helper, sizes);
> +       if (ret) {
> +               dev_err(helper->dev->dev, "creating fbdev failed with %d\n", ret);
> +               return ret;
> +       }
> +
> +       /*
> +        * fb_helper expects a value more than 1 if succeed
> +        * because register_framebuffer() should be called.
> +        */
> +       return 1;
> +}
> +
> +static struct drm_fb_helper_funcs imx_drm_fb_helper_funcs = {
> +       .fb_probe = imx_drm_fbdev_probe,
> +};
> +
> +static struct drm_fb_helper *imx_drm_fbdev_init(struct drm_device *drm,
> +               int preferred_bpp)
> +{
> +       struct drm_fb_helper *helper;
> +       unsigned int num_crtc;
> +       int ret;
> +
> +       helper = kzalloc(sizeof(*helper), GFP_KERNEL);
> +       if (!helper)
> +               return NULL;
> +
> +       helper->funcs = &imx_drm_fb_helper_funcs;
> +
> +       num_crtc = drm->mode_config.num_crtc;
> +
> +       ret = drm_fb_helper_init(drm, helper, num_crtc, MAX_CONNECTOR);
> +       if (ret) {
> +               dev_err(drm->dev, "initializing drm fb helper failed with %d\n",
> +                               ret);
> +               goto err_init;
> +       }
> +
> +       ret = drm_fb_helper_single_add_all_connectors(helper);
> +       if (ret) {
> +               dev_err(drm->dev, "registering drm_fb_helper_connector failed with %d\n",
> +                               ret);
> +               goto err_setup;
> +
> +       }
> +
> +       ret = drm_fb_helper_initial_config(helper, preferred_bpp);
> +       if (ret) {
> +               dev_err(drm->dev, "initial config failed with %d\n", ret);
> +               goto err_setup;
> +       }
> +
> +       return helper;
> +
> +err_setup:
> +       drm_fb_helper_fini(helper);
> +
> +err_init:
> +       kfree(helper);
> +
> +       return NULL;
> +}
> +
> +static void imx_drm_fbdev_fini(struct drm_fb_helper *helper)
> +{
> +       struct imx_drm_buf_entry *entry;
> +
> +       if (helper->fbdev) {
> +               struct fb_info *info;
> +               int ret;
> +
> +               info = helper->fbdev;
> +               ret = unregister_framebuffer(info);
> +               if (ret)
> +                       dev_err(helper->dev->dev, "unregister_framebuffer failed with %d\n",
> +                                       ret);
> +
> +               if (info->cmap.len)
> +                       fb_dealloc_cmap(&info->cmap);
> +
> +               entry = imx_drm_fb_get_buf(helper->fb);
> +
> +               imx_drm_buf_destroy(helper->dev, entry);
> +
> +               framebuffer_release(info);
> +       }
> +
> +       drm_fb_helper_fini(helper);
> +
> +       kfree(helper);
> +}
> +
> +static struct drm_fb_helper *imx_fb_helper;
> +
> +static int __init imx_fb_helper_init(void)
> +{
> +       struct drm_device *drm = imx_drm_device_get();
> +       int ret;
> +
> +       if (!drm)
> +               return -EINVAL;
> +
> +       imx_fb_helper = imx_drm_fbdev_init(drm, PREFERRED_BPP);
> +       if (!imx_fb_helper) {
> +               imx_drm_device_put();
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static void __exit imx_fb_helper_exit(void)
> +{
> +       imx_drm_fbdev_fini(imx_fb_helper);
> +       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/gpu/drm/imx/imx-gem.c b/drivers/gpu/drm/imx/imx-gem.c
> new file mode 100644
> index 0000000..b0866fb
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/imx-gem.c
> @@ -0,0 +1,343 @@
> +/*
> + * 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 <drm/drmP.h>
> +#include <drm/drm.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "imx-drm.h"
> +
> +static int lowlevel_buffer_allocate(struct drm_device *drm,
> +               struct imx_drm_buf_entry *entry)
> +{
> +       entry->vaddr = dma_alloc_writecombine(drm->dev, entry->size,
> +                       (dma_addr_t *)&entry->paddr, GFP_KERNEL);
> +       if (!entry->vaddr) {
> +               dev_err(drm->dev, "failed to allocate buffer.\n");
> +               return -ENOMEM;
> +       }
> +
> +       dev_dbg(drm->dev, "allocated : vaddr(0x%x), paddr(0x%x), size(0x%x)\n",
> +                       (unsigned int)entry->vaddr, entry->paddr, entry->size);
> +
> +       return 0;
> +}
> +
> +static void lowlevel_buffer_free(struct drm_device *drm,
> +               struct imx_drm_buf_entry *entry)
> +{
> +       dma_free_writecombine(drm->dev, entry->size, entry->vaddr,
> +                       entry->paddr);
> +}
> +
> +struct imx_drm_buf_entry *imx_drm_buf_create(struct drm_device *drm,
> +               unsigned int size)
> +{
> +       struct imx_drm_buf_entry *entry;
> +
> +       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> +       if (!entry)
> +               return ERR_PTR(-ENOMEM);
> +
> +       entry->size = size;
> +
> +       /*
> +        * allocate memory region with size and set the memory information
> +        * to vaddr and paddr of a entry object.
> +        */
> +       if (lowlevel_buffer_allocate(drm, entry) < 0) {
> +               kfree(entry);
> +               return ERR_PTR(-ENOMEM);
> +       }
> +
> +       return entry;
> +}
> +
> +void imx_drm_buf_destroy(struct drm_device *drm,
> +               struct imx_drm_buf_entry *entry)
> +{
> +       lowlevel_buffer_free(drm, entry);
> +
> +       kfree(entry);
> +       entry = NULL;
> +}
> +EXPORT_SYMBOL_GPL(imx_drm_buf_destroy);
> +
> +static unsigned int convert_to_vm_err_msg(int msg)
> +{
> +       unsigned int out_msg;
> +
> +       switch (msg) {
> +       case 0:
> +       case -ERESTARTSYS:
> +       case -EINTR:
> +               out_msg = VM_FAULT_NOPAGE;
> +               break;
> +
> +       case -ENOMEM:
> +               out_msg = VM_FAULT_OOM;
> +               break;
> +
> +       default:
> +               out_msg = VM_FAULT_SIGBUS;
> +               break;
> +       }
> +
> +       return out_msg;
> +}
> +
> +static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
> +{
> +       return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
> +}
> +
> +static struct imx_drm_gem_obj *imx_drm_gem_create(struct drm_device *drm,
> +               unsigned int size)
> +{
> +       struct imx_drm_gem_obj *imx_drm_gem_obj;
> +       struct imx_drm_buf_entry *entry;
> +       struct drm_gem_object *obj;
> +       int ret;
> +
> +       size = roundup(size, PAGE_SIZE);
> +
> +       imx_drm_gem_obj = kzalloc(sizeof(*imx_drm_gem_obj), GFP_KERNEL);
> +       if (!imx_drm_gem_obj)
> +               return ERR_PTR(-ENOMEM);
> +
> +       /* allocate the new buffer object and memory region. */
> +       entry = imx_drm_buf_create(drm, size);
> +       if (!entry) {
> +               ret = -ENOMEM;
> +               goto err_alloc;
> +       }
> +
> +       imx_drm_gem_obj->entry = entry;
> +
> +       obj = &imx_drm_gem_obj->base;
> +
> +       ret = drm_gem_object_init(drm, obj, size);
> +       if (ret) {
> +               dev_err(drm->dev, "initializing GEM object failed with %d\n", ret);
> +               goto err_obj_init;
> +       }
> +
> +       ret = drm_gem_create_mmap_offset(obj);
> +       if (ret) {
> +               dev_err(drm->dev, "creating mmap offset failed with %d\n", ret);
> +               goto err_create_mmap_offset;
> +       }
> +
> +       return imx_drm_gem_obj;
> +
> +err_create_mmap_offset:
> +       drm_gem_object_release(obj);
> +
> +err_obj_init:
> +       imx_drm_buf_destroy(drm, imx_drm_gem_obj->entry);
> +
> +err_alloc:
> +       kfree(imx_drm_gem_obj);
> +
> +       return ERR_PTR(ret);
> +}
> +
> +static struct imx_drm_gem_obj *imx_drm_gem_create_with_handle(struct drm_file *file_priv,
> +               struct drm_device *drm, unsigned int size,
> +               unsigned int *handle)
> +{
> +       struct imx_drm_gem_obj *imx_drm_gem_obj;
> +       struct drm_gem_object *obj;
> +       int ret;
> +
> +       imx_drm_gem_obj = imx_drm_gem_create(drm, size);
> +       if (IS_ERR(imx_drm_gem_obj))
> +               return imx_drm_gem_obj;
> +
> +       obj = &imx_drm_gem_obj->base;
> +
> +       /*
> +        * allocate a id of idr table where the obj is registered
> +        * and handle has the id what user can see.
> +        */
> +       ret = drm_gem_handle_create(file_priv, obj, handle);
> +       if (ret)
> +               goto err_handle_create;
> +
> +       /* drop reference from allocate - handle holds it now. */
> +       drm_gem_object_unreference_unlocked(obj);
> +
> +       return imx_drm_gem_obj;
> +
> +err_handle_create:
> +       imx_drm_gem_free_object(obj);
> +
> +       return ERR_PTR(ret);
> +}
> +
> +static int imx_drm_gem_mmap_buffer(struct file *filp,
> +               struct vm_area_struct *vma)
> +{
> +       struct drm_gem_object *obj = filp->private_data;
> +       struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> +       struct imx_drm_buf_entry *entry;
> +       unsigned long pfn, vm_size;
> +
> +       vma->vm_flags |= VM_IO | VM_RESERVED;
> +
> +       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

pgprot_writecombine()?

> +       vma->vm_file = filp;
> +
> +       vm_size = vma->vm_end - vma->vm_start;
> +       /*
> +        * an entry contains information to physically continuous memory
> +        * allocated by user request or at framebuffer creation.
> +        */
> +       entry = imx_drm_gem_obj->entry;
> +
> +       /* check if user-requested size is valid. */
> +       if (vm_size > entry->size)
> +               return -EINVAL;
> +
> +       /*
> +        * get page frame number to physical memory to be mapped
> +        * to user space.
> +        */
> +       pfn = imx_drm_gem_obj->entry->paddr >> PAGE_SHIFT;
> +
> +       if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
> +                               vma->vm_page_prot)) {
> +               dev_err(obj->dev->dev, "failed to remap pfn range.\n");
> +               return -EAGAIN;
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct file_operations imx_drm_gem_fops = {
> +       .mmap = imx_drm_gem_mmap_buffer,
> +};
> +
> +int imx_drm_gem_init_object(struct drm_gem_object *obj)
> +{
> +       return 0;
> +}
> +
> +void imx_drm_gem_free_object(struct drm_gem_object *gem_obj)
> +{
> +       struct imx_drm_gem_obj *imx_drm_gem_obj;
> +
> +       if (gem_obj->map_list.map)
> +               drm_gem_free_mmap_offset(gem_obj);
> +
> +       drm_gem_object_release(gem_obj);
> +
> +       imx_drm_gem_obj = to_imx_drm_gem_obj(gem_obj);
> +
> +       imx_drm_buf_destroy(gem_obj->dev, imx_drm_gem_obj->entry);
> +
> +       kfree(imx_drm_gem_obj);
> +}
> +
> +int imx_drm_gem_dumb_create(struct drm_file *file_priv,
> +               struct drm_device *dev, struct drm_mode_create_dumb *args)
> +{
> +       struct imx_drm_gem_obj *imx_drm_gem_obj;
> +
> +       /* FIXME: This should be configured by the crtc driver */
> +       args->pitch = args->width * args->bpp >> 3;
> +       args->size = args->pitch * args->height;
> +
> +       imx_drm_gem_obj = imx_drm_gem_create_with_handle(file_priv, dev, args->size,
> +                       &args->handle);
> +       if (IS_ERR(imx_drm_gem_obj))
> +               return PTR_ERR(imx_drm_gem_obj);
> +
> +       return 0;
> +}
> +
> +int imx_drm_gem_dumb_map_offset(struct drm_file *file_priv,
> +               struct drm_device *drm, uint32_t handle, uint64_t *offset)
> +{
> +       struct imx_drm_gem_obj *imx_drm_gem_obj;
> +       struct drm_gem_object *obj;
> +
> +       mutex_lock(&drm->struct_mutex);
> +
> +       obj = drm_gem_object_lookup(drm, file_priv, handle);
> +       if (!obj) {
> +               dev_err(drm->dev, "failed to lookup gem object\n");
> +               mutex_unlock(&drm->struct_mutex);
> +               return -EINVAL;
> +       }
> +
> +       imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> +
> +       *offset = get_gem_mmap_offset(&imx_drm_gem_obj->base);
> +
> +       drm_gem_object_unreference(obj);
> +
> +       mutex_unlock(&drm->struct_mutex);
> +
> +       return 0;
> +}
> +
> +int imx_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> +{
> +       struct drm_gem_object *obj = vma->vm_private_data;
> +       struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> +       struct drm_device *dev = obj->dev;
> +       unsigned long pfn;
> +       pgoff_t page_offset;
> +       int ret;
> +
> +       page_offset = ((unsigned long)vmf->virtual_address -
> +                       vma->vm_start) >> PAGE_SHIFT;
> +
> +       mutex_lock(&dev->struct_mutex);
> +
> +       pfn = (imx_drm_gem_obj->entry->paddr >> PAGE_SHIFT) + page_offset;
> +
> +       ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
> +
> +       mutex_unlock(&dev->struct_mutex);
> +
> +       return convert_to_vm_err_msg(ret);
> +}
> +
> +int imx_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> +       int ret;
> +
> +       ret = drm_gem_mmap(filp, vma);
> +       if (ret)
> +               return ret;
> +
> +       vma->vm_flags &= ~VM_PFNMAP;
> +       vma->vm_flags |= VM_MIXEDMAP;
> +
> +       return ret;
> +}
> +
> +
> +int imx_drm_gem_dumb_destroy(struct drm_file *file_priv,
> +               struct drm_device *dev, unsigned int handle)
> +{
> +       return drm_gem_handle_delete(file_priv, handle);
> +}
> diff --git a/drivers/gpu/drm/imx/imx-lcdc-crtc.c b/drivers/gpu/drm/imx/imx-lcdc-crtc.c
> new file mode 100644
> index 0000000..e77c015
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/imx-lcdc-crtc.c
> @@ -0,0 +1,517 @@
> +/*
> + * i.MX LCDC crtc driver
> + *
> + * 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.
> + *
> + */
> +#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 <asm/fb.h>
> +#include <linux/module.h>
> +#include <mach/hardware.h>
> +#include <mach/imxfb.h>
> +#include <generated/mach-types.h>
> +
> +#include "imx-drm.h"
> +
> +#define LCDC_SSA               0x00
> +#define LCDC_SIZE              0x04
> +#define LCDC_VPW               0x08
> +#define LCDC_CPOS              0x0C
> +#define LCDC_LCWHB             0x10
> +#define LCDC_LCHCC             0x14
> +#define LCDC_PCR               0x18
> +#define LCDC_HCR               0x1C
> +#define LCDC_VCR               0x20
> +#define LCDC_POS               0x24
> +#define LCDC_LSCR1             0x28
> +#define LCDC_PWMR              0x2C
> +#define LCDC_DMACR             0x30
> +#define LCDC_RMCR              0x34
> +#define LCDC_LCDICR            0x38
> +#define LCDC_LIER              0x3c
> +#define LCDC_LISR              0x40
> +
> +#define SIZE_XMAX(x)           ((((x) >> 4) & 0x3f) << 20)
> +
> +#define YMAX_MASK              (cpu_is_mx1() ? 0x1ff : 0x3ff)
> +#define SIZE_YMAX(y)           ((y) & YMAX_MASK)
> +
> +#define VPW_VPW(x)             ((x) & 0x3ff)
> +
> +#define HCR_H_WIDTH(x)         (((x) & 0x3f) << 26)
> +#define HCR_H_WAIT_1(x)                (((x) & 0xff) << 8)
> +#define HCR_H_WAIT_2(x)                ((x) & 0xff)
> +
> +#define VCR_V_WIDTH(x)         (((x) & 0x3f) << 26)
> +#define VCR_V_WAIT_1(x)                (((x) & 0xff) << 8)
> +#define VCR_V_WAIT_2(x)                ((x) & 0xff)
> +
> +#define RMCR_LCDC_EN_MX1       (1 << 1)
> +
> +#define RMCR_SELF_REF          (1 << 0)
> +
> +#define LIER_EOF               (1 << 1)
> +
> +struct imx_crtc {
> +       struct drm_crtc         base;
> +       struct imx_drm_crtc     *imx_drm_crtc;
> +       int                     di_no;
> +       int                     enabled;
> +       void __iomem            *regs;
> +       u32                     pwmr;
> +       u32                     lscr1;
> +       u32                     dmacr;
> +       u32                     pcr;
> +       struct clk              *clk;
> +       struct device           *dev;
> +       int                     vblank_enable;
> +
> +       struct drm_pending_vblank_event *page_flip_event;
> +       struct drm_framebuffer  *newfb;
> +};
> +
> +#define to_imx_crtc(x) container_of(x, struct imx_crtc, base)
> +
> +static void imx_crtc_load_lut(struct drm_crtc *crtc)
> +{
> +}
> +
> +#define PCR_BPIX_8             (3 << 25)
> +#define PCR_BPIX_12            (4 << 25)
> +#define PCR_BPIX_16            (5 << 25)
> +#define PCR_BPIX_18            (6 << 25)
> +#define PCR_END_SEL            (1 << 18)
> +#define PCR_END_BYTE_SWAP      (1 << 17)
> +
> +const char *fourcc_to_str(u32 fourcc)
> +{
> +       static char buf[5];
> +
> +       *(u32 *)buf = fourcc;
> +       buf[4] = 0;
> +
> +       return buf;
> +}
> +
> +static int imx_drm_crtc_set(struct drm_crtc *crtc,
> +               struct drm_display_mode *mode)
> +{
> +       struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +       struct drm_framebuffer *fb = crtc->fb;
> +       int lower_margin = mode->vsync_start - mode->vdisplay;
> +       int upper_margin = mode->vtotal - mode->vsync_end;
> +       int vsync_len = mode->vsync_end - mode->vsync_start;
> +       int hsync_len = mode->hsync_end - mode->hsync_start;
> +       int right_margin = mode->hsync_start - mode->hdisplay;
> +       int left_margin = mode->htotal - mode->hsync_end;
> +       unsigned long lcd_clk;
> +       u32 pcr;
> +
> +       lcd_clk = clk_get_rate(imx_crtc->clk) / 1000;
> +
> +       if (!mode->clock)
> +               return -EINVAL;
> +
> +       pcr = DIV_ROUND_CLOSEST(lcd_clk, mode->clock);
> +       if (--pcr > 0x3f)
> +               pcr = 0x3f;
> +
> +       switch (fb->pixel_format) {
> +       case DRM_FORMAT_XRGB8888:
> +       case DRM_FORMAT_ARGB8888:
> +               pcr |= PCR_BPIX_18;
> +               pcr |= PCR_END_SEL | PCR_END_BYTE_SWAP;
> +               break;
> +       case DRM_FORMAT_RGB565:
> +               if (cpu_is_mx1())
> +                       pcr |= PCR_BPIX_12;
> +               else
> +                       pcr |= PCR_BPIX_16;
> +               break;
> +       case DRM_FORMAT_RGB332:
> +               pcr |= PCR_BPIX_8;
> +               break;
> +       default:
> +               dev_err(imx_crtc->dev, "unsupported pixel format %s\n",
> +                               fourcc_to_str(fb->pixel_format));
> +               return -EINVAL;
> +       }
> +
> +       /* add sync polarities */
> +       pcr |= imx_crtc->pcr & ~(0x3f | (7 << 25));
> +
> +       dev_dbg(imx_crtc->dev,
> +                       "xres=%d hsync_len=%d left_margin=%d right_margin=%d\n",
> +                       mode->hdisplay, hsync_len,
> +                       left_margin, right_margin);
> +       dev_dbg(imx_crtc->dev,
> +                       "yres=%d vsync_len=%d upper_margin=%d lower_margin=%d\n",
> +                       mode->vdisplay, vsync_len,
> +                       upper_margin, lower_margin);
> +
> +       writel(VPW_VPW(mode->hdisplay * fb->bits_per_pixel / 8 / 4),
> +               imx_crtc->regs + LCDC_VPW);
> +
> +       writel(HCR_H_WIDTH(hsync_len - 1) |
> +               HCR_H_WAIT_1(right_margin - 1) |
> +               HCR_H_WAIT_2(left_margin - 3),
> +               imx_crtc->regs + LCDC_HCR);
> +
> +       writel(VCR_V_WIDTH(vsync_len) |
> +               VCR_V_WAIT_1(lower_margin) |
> +               VCR_V_WAIT_2(upper_margin),
> +               imx_crtc->regs + LCDC_VCR);
> +
> +       writel(SIZE_XMAX(mode->hdisplay) | SIZE_YMAX(mode->vdisplay),
> +                       imx_crtc->regs + LCDC_SIZE);
> +
> +       writel(pcr, imx_crtc->regs + LCDC_PCR);
> +       writel(imx_crtc->pwmr, imx_crtc->regs + LCDC_PWMR);
> +       writel(imx_crtc->lscr1, imx_crtc->regs + LCDC_LSCR1);
> +       /* reset default */
> +       writel(0x00040060, imx_crtc->regs + LCDC_DMACR);
> +
> +       return 0;
> +}
> +
> +static int imx_drm_set_base(struct drm_crtc *crtc, int x, int y)
> +{
> +       struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +       struct imx_drm_buf_entry *entry;
> +       struct drm_framebuffer *fb = crtc->fb;
> +       unsigned long phys;
> +
> +       entry = imx_drm_fb_get_buf(fb);
> +       if (!entry)
> +               return -EFAULT;
> +
> +       phys = entry->paddr;
> +       phys += x * (fb->bits_per_pixel >> 3);
> +       phys += y * fb->pitches[0];
> +
> +       dev_dbg(imx_crtc->dev, "%s: phys: 0x%lx\n", __func__, phys);
> +       dev_dbg(imx_crtc->dev, "%s: xy: %dx%d\n", __func__, x, y);
> +
> +       writel(phys, imx_crtc->regs + LCDC_SSA);
> +
> +       return 0;
> +}
> +
> +static int imx_crtc_mode_set(struct drm_crtc *crtc,
> +                              struct drm_display_mode *mode,
> +                              struct drm_display_mode *adjusted_mode,
> +                              int x, int y,
> +                              struct drm_framebuffer *old_fb)
> +{
> +       struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> +       imx_drm_set_base(crtc, x, y);
> +
> +       dev_dbg(imx_crtc->dev, "mode->hdisplay: %d\n", mode->hdisplay);
> +       dev_dbg(imx_crtc->dev, "mode->vdisplay: %d\n", mode->vdisplay);
> +
> +       return imx_drm_crtc_set(crtc, mode);
> +}
> +
> +static void imx_crtc_enable(struct imx_crtc *imx_crtc)
> +{
> +       if (!imx_crtc->enabled)
> +               clk_enable(imx_crtc->clk);
> +       imx_crtc->enabled = 1;
> +}
> +
> +static void imx_crtc_disable(struct imx_crtc *imx_crtc)
> +{
> +       if (imx_crtc->enabled)
> +               clk_disable(imx_crtc->clk);
> +       imx_crtc->enabled = 0;
> +}
> +
> +static void imx_crtc_dpms(struct drm_crtc *crtc, int mode)
> +{
> +       struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> +       dev_dbg(imx_crtc->dev, "%s mode: %d\n", __func__, mode);
> +
> +       switch (mode) {
> +       case DRM_MODE_DPMS_ON:
> +               imx_crtc_enable(imx_crtc);
> +               break;
> +       default:
> +               imx_crtc_disable(imx_crtc);
> +               break;
> +       }
> +}
> +
> +static bool imx_crtc_mode_fixup(struct drm_crtc *crtc,
> +                                 struct drm_display_mode *mode,
> +                                 struct drm_display_mode *adjusted_mode)
> +{
> +       return true;
> +}
> +
> +static void imx_crtc_prepare(struct drm_crtc *crtc)
> +{
> +       struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> +       imx_crtc_disable(imx_crtc);
> +}
> +
> +static void imx_crtc_commit(struct drm_crtc *crtc)
> +{
> +       struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> +       imx_crtc_enable(imx_crtc);
> +}
> +
> +static struct drm_crtc_helper_funcs imx_helper_funcs = {
> +       .dpms = imx_crtc_dpms,
> +       .mode_fixup = imx_crtc_mode_fixup,
> +       .mode_set = imx_crtc_mode_set,
> +       .prepare = imx_crtc_prepare,
> +       .commit = imx_crtc_commit,
> +       .load_lut = imx_crtc_load_lut,
> +};
> +
> +static void imx_drm_handle_pageflip(struct imx_crtc *imx_crtc)
> +{
> +       struct drm_pending_vblank_event *e;
> +       struct timeval now;
> +       unsigned long flags;
> +       struct drm_device *drm = imx_crtc->base.dev;
> +
> +       spin_lock_irqsave(&drm->event_lock, flags);
> +
> +       e = imx_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;
> +       imx_crtc->page_flip_event = NULL;
> +
> +       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 int imx_crtc_enable_vblank(struct drm_crtc *crtc)
> +{
> +       struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> +       writel(LIER_EOF, imx_crtc->regs + LCDC_LIER);
> +
> +       return 0;
> +}
> +
> +static void imx_crtc_disable_vblank(struct drm_crtc *crtc)
> +{
> +       struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> +       writel(0, imx_crtc->regs + LCDC_LIER);
> +}
> +
> +static irqreturn_t imx_irq_handler(int irq, void *dev_id)
> +{
> +       struct imx_crtc *imx_crtc = dev_id;
> +       struct drm_device *drm = imx_crtc->base.dev;
> +
> +       /* Acknowledge interrupt */
> +       readl(imx_crtc->regs + LCDC_LISR);
> +
> +       drm_handle_vblank(drm, 0);
> +
> +       if (imx_crtc->newfb) {
> +               imx_crtc->base.fb = imx_crtc->newfb;
> +               imx_crtc->newfb = NULL;
> +               imx_drm_set_base(&imx_crtc->base, 0, 0);
> +               imx_drm_handle_pageflip(imx_crtc);
> +               imx_drm_crtc_vblank_put(imx_crtc->imx_drm_crtc);
> +       }
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int imx_page_flip(struct drm_crtc *crtc,
> +                         struct drm_framebuffer *fb,
> +                         struct drm_pending_vblank_event *event)
> +{
> +       struct imx_crtc *imx_crtc = to_imx_crtc(crtc);
> +
> +       if (imx_crtc->newfb)
> +               return -EBUSY;
> +
> +       imx_crtc->newfb = fb;
> +       imx_crtc->page_flip_event = event;
> +       imx_drm_crtc_vblank_get(imx_crtc->imx_drm_crtc);
> +
> +       return 0;
> +}
> +
> +static const struct drm_crtc_funcs imx_crtc_funcs = {
> +       .page_flip = imx_page_flip,
> +};
> +
> +static const struct imx_drm_crtc_helper_funcs imx_imx_drm_helper = {
> +       .enable_vblank = imx_crtc_enable_vblank,
> +       .disable_vblank = imx_crtc_disable_vblank,
> +};
> +
> +#define DRIVER_NAME "imx-lcdc-crtc"
> +
> +/*
> + * the pcr bits to be allowed to set in platform data
> + */
> +#define PDATA_PCR      (PCR_PIXPOL | PCR_FLMPOL | PCR_LPPOL | \
> +                       PCR_CLKPOL | PCR_OEPOL | PCR_TFT | PCR_COLOR | \
> +                       PCR_PBSIZ_8 | PCR_ACD(0x7f) | PCR_ACD_SEL | \
> +                       PCR_SCLK_SEL | PCR_SHARP)
> +
> +static int __devinit imx_crtc_probe(struct platform_device *pdev)
> +{
> +       struct imx_crtc *imx_crtc;
> +       struct resource *res;
> +       int ret, irq;
> +       u32 pcr_value = 0xf00080c0;
> +       u32 lscr1_value = 0x00120300;
> +       u32 pwmr_value = 0x00a903ff;
> +
> +       imx_crtc = devm_kzalloc(&pdev->dev, sizeof(*imx_crtc), GFP_KERNEL);
> +       if (!imx_crtc)
> +               return -ENOMEM;
> +
> +       imx_crtc->dev = &pdev->dev;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!res)
> +               return -ENODEV;
> +
> +       res = devm_request_mem_region(&pdev->dev, res->start,
> +                       resource_size(res), DRIVER_NAME);
> +       if (!res)
> +               return -EBUSY;
> +
> +       imx_crtc->regs = devm_ioremap(&pdev->dev, res->start,
> +                       resource_size(res));
> +       if (!imx_crtc->regs) {
> +               dev_err(&pdev->dev, "Cannot map frame buffer registers\n");
> +               return -EBUSY;
> +       }
> +
> +       irq = platform_get_irq(pdev, 0);
> +       ret = devm_request_irq(&pdev->dev, irq, imx_irq_handler, 0, "imx_drm",
> +                       imx_crtc);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "irq request failed with %d\n", ret);
> +               return ret;
> +       }
> +
> +       imx_crtc->clk = clk_get(&pdev->dev, NULL);
> +       if (IS_ERR(imx_crtc->clk)) {
> +               ret = PTR_ERR(imx_crtc->clk);
> +               dev_err(&pdev->dev, "unable to get clock: %d\n", ret);
> +               return ret;
> +       }
> +
> +       clk_prepare_enable(imx_crtc->clk);
> +       imx_crtc->enabled = 1;
> +
> +       platform_set_drvdata(pdev, imx_crtc);
> +
> +       imx_crtc->pcr = pcr_value & PDATA_PCR;
> +
> +       if (imx_crtc->pcr != pcr_value)
> +               dev_err(&pdev->dev, "invalid bits set in pcr: 0x%08x\n",
> +                               pcr_value & ~PDATA_PCR);
> +
> +       imx_crtc->lscr1 = lscr1_value;
> +       imx_crtc->pwmr = pwmr_value;
> +
> +       ret = imx_drm_add_crtc(&imx_crtc->base,
> +                       &imx_crtc->imx_drm_crtc,
> +                       &imx_crtc_funcs, &imx_helper_funcs,
> +                       &imx_imx_drm_helper, THIS_MODULE);
> +       if (ret)
> +               goto err_init;
> +
> +       dev_info(&pdev->dev, "probed\n");
> +
> +       return 0;
> +
> +err_init:
> +       clk_disable_unprepare(imx_crtc->clk);
> +       clk_put(imx_crtc->clk);
> +
> +       return ret;
> +}
> +
> +static int __devexit imx_crtc_remove(struct platform_device *pdev)
> +{
> +       struct imx_crtc *imx_crtc = platform_get_drvdata(pdev);
> +
> +       imx_drm_remove_crtc(imx_crtc->imx_drm_crtc);
> +
> +       writel(0, imx_crtc->regs + LCDC_LIER);
> +
> +       clk_disable_unprepare(imx_crtc->clk);
> +       clk_put(imx_crtc->clk);
> +
> +       platform_set_drvdata(pdev, NULL);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id imx_lcdc_dt_ids[] = {
> +       { .compatible = "fsl,imx1-lcdc", .data = NULL, },
> +       { .compatible = "fsl,imx21-lcdc", .data = NULL, },
> +       { /* sentinel */ }
> +};
> +
> +static struct platform_driver imx_crtc_driver = {
> +       .remove         = __devexit_p(imx_crtc_remove),
> +       .probe          = imx_crtc_probe,
> +       .driver         = {
> +               .name   = DRIVER_NAME,
> +               .owner  = THIS_MODULE,
> +               .of_match_table = imx_lcdc_dt_ids,
> +       },
> +};
> +
> +static int __init imx_lcdc_init(void)
> +{
> +       return platform_driver_register(&imx_crtc_driver);
> +}
> +
> +static void __exit imx_lcdc_exit(void)
> +{
> +       platform_driver_unregister(&imx_crtc_driver);
> +}
> +
> +module_init(imx_lcdc_init);
> +module_exit(imx_lcdc_exit)
> +
> +MODULE_DESCRIPTION("Freescale i.MX framebuffer driver");
> +MODULE_AUTHOR("Sascha Hauer, Pengutronix");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/imx/imx-parallel-display.c b/drivers/gpu/drm/imx/imx-parallel-display.c
> new file mode 100644
> index 0000000..8c96113
> --- /dev/null
> +++ b/drivers/gpu/drm/imx/imx-parallel-display.c
> @@ -0,0 +1,228 @@
> +/*
> + * 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 "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;
> +};
> +
> +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 ret;
> +
> +       drm_mode_connector_update_edid_property(connector, imxpd->edid);
> +       ret = drm_add_edid_modes(connector, imxpd->edid);
> +       connector->display_info.raw_edid = NULL;
> +
> +       return ret;
> +}
> +
> +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,
> +                          struct drm_display_mode *mode,
> +                          struct drm_display_mode *adjusted_mode)
> +{
> +       return true;
> +}
> +
> +static void imx_pd_encoder_prepare(struct drm_encoder *encoder)
> +{
> +}
> +
> +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 */
> +}
> +
> +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,
> +};
> +
> +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;
> +
> +       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;
> +
> +       imxpd = devm_kzalloc(&pdev->dev, sizeof(*imxpd), GFP_KERNEL);
> +       if (!imxpd)
> +               return -ENOMEM;
> +
> +       edidp = of_get_property(np, "edid", &imxpd->edid_len);
> +
> +       imxpd->edid = kmemdup(edidp, imxpd->edid_len, GFP_KERNEL);
> +       imxpd->encoder.possible_crtcs = 0x1;
> +       imxpd->encoder.possible_clones = 0x1;
> +       imxpd->dev = &pdev->dev;
> +
> +       ret = imx_pd_register(imxpd);
> +       if (ret)
> +               return ret;
> +
> +       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", .data = NULL, },
> +       { /* 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");
> --
> 1.7.10
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 1/2] DRM: add Freescale i.MX LCDC driver
  2012-05-22 21:28     ` Rob Clark
@ 2012-05-23  7:47       ` Sascha Hauer
  -1 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-23  7:47 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Rob,

On Tue, May 22, 2012 at 03:28:20PM -0600, Rob Clark wrote:
> just a few comments from a cursory review..  I need a bit more time
> for a more in-depth review but that won't be tonight so I thought I'd
> send what I have so far..

Thanks for this.

> > +++ b/drivers/gpu/drm/imx/Kconfig
> > @@ -0,0 +1,18 @@
> > +config DRM_IMX
> > + ? ? ? tristate "DRM Support for Freescale i.MX"
> > + ? ? ? select DRM_KMS_HELPER
> > + ? ? ? depends on DRM && ARCH_MXC
> > +
> > +config DRM_IMX_FB_HELPER
> > + ? ? ? tristate "provide legacy framebuffer /dev/fb0"
> > + ? ? ? depends on DRM_IMX
> > +
> > +config DRM_IMX_LCDC
> > + ? ? ? tristate "DRM Support for Freescale i.MX1 and i.MX2"
> > + ? ? ? depends on DRM_IMX
> > + ? ? ? help
> > + ? ? ? ? Choose this if you have a i.MX1, i.MX21, i.MX25 or i.MX27 processor.
> 
> do you have something like cpu_is_imx2() type macros?  It would be
> preferable not to have a compile time config option for building for
> certain hw versions.  Ie. on OMAP we could do something like 'if
> (cpu_is_omap3xxx()) { ... }' if there was something that needed to be
> done different on omap3 family of devices.  This way you could choose
> to build a kernel supporting either a single omap variants, or all
> omap variants.

Yes, we have cpu_is_* macros. I don't see though why we should use them
here. The result of DRM_IMX_LCDC will be a platform driver for which a
suitable device exists only on the correct SoCs. There will be a second
DRM_IMX_IPU option next to this one for the newer i.MX SoCs which will
be a second platform driver.

> > +
> > + ? ? ? /*
> > + ? ? ? ?* without file_priv we are called from imx_drm_fbdev_create in which
> > + ? ? ? ?* case we only create a framebuffer without a handle.
> > + ? ? ? ?*/
> > + ? ? ? if (!file_priv) {
> > + ? ? ? ? ? ? ? struct imx_drm_buf_entry *entry;
> > +
> > + ? ? ? ? ? ? ? entry = imx_drm_buf_create(dev, size);
> > + ? ? ? ? ? ? ? if (IS_ERR(entry)) {
> > + ? ? ? ? ? ? ? ? ? ? ? ret = PTR_ERR(entry);
> > + ? ? ? ? ? ? ? ? ? ? ? goto err_buffer;
> > + ? ? ? ? ? ? ? }
> > +
> > + ? ? ? ? ? ? ? imx_drm_fb->entry = entry;
> > + ? ? ? } else {
> > + ? ? ? ? ? ? ? obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
> > + ? ? ? ? ? ? ? if (!obj) {
> > + ? ? ? ? ? ? ? ? ? ? ? ret = -EINVAL;
> > + ? ? ? ? ? ? ? ? ? ? ? goto err_buffer;
> > + ? ? ? ? ? ? ? }
> > +
> > + ? ? ? ? ? ? ? imx_drm_fb->imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> > +
> > + ? ? ? ? ? ? ? drm_gem_object_unreference_unlocked(obj);
> 
> 
> the fb probably should hold the reference to the GEM obj until it is
> deleted..  so you don't end up freeing the buffer you are scanning out
> until you are done scanning it out.  Possibly this gets a bit more
> straightforward if you don't have to special case the default/fbcon
> framebuffer?
> 
> IIRC, Laurent had sent a patch recently to fix the same issue in the
> exynos driver.

Yes, just for reference, here is a link:

http://permalink.gmane.org/gmane.comp.video.dri.devel/69203

Will fix this.

> > +
> > +static int imx_drm_gem_mmap_buffer(struct file *filp,
> > + ? ? ? ? ? ? ? struct vm_area_struct *vma)
> > +{
> > + ? ? ? struct drm_gem_object *obj = filp->private_data;
> > + ? ? ? struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> > + ? ? ? struct imx_drm_buf_entry *entry;
> > + ? ? ? unsigned long pfn, vm_size;
> > +
> > + ? ? ? vma->vm_flags |= VM_IO | VM_RESERVED;
> > +
> > + ? ? ? vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> 
> pgprot_writecombine()?

copied from the exynos driver. The exynos driver recently gained support
for different cache attribute flags and I could do the same. I would
prefer not to even have to think about it by using some generic code
here instead of duplicating other peoples bugs.

Do you think it's possible to share this code as suggested by Lars?
Every SoC not having a IOMMU could share the same code here, it's just
not clear to me how we can put this in a form that is acceptable
upstream.

Sascha

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

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 1/2] DRM: add Freescale i.MX LCDC driver
@ 2012-05-23  7:47       ` Sascha Hauer
  0 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-23  7:47 UTC (permalink / raw)
  To: Rob Clark; +Cc: linux-arm-kernel, dri-devel

Hi Rob,

On Tue, May 22, 2012 at 03:28:20PM -0600, Rob Clark wrote:
> just a few comments from a cursory review..  I need a bit more time
> for a more in-depth review but that won't be tonight so I thought I'd
> send what I have so far..

Thanks for this.

> > +++ b/drivers/gpu/drm/imx/Kconfig
> > @@ -0,0 +1,18 @@
> > +config DRM_IMX
> > +       tristate "DRM Support for Freescale i.MX"
> > +       select DRM_KMS_HELPER
> > +       depends on DRM && ARCH_MXC
> > +
> > +config DRM_IMX_FB_HELPER
> > +       tristate "provide legacy framebuffer /dev/fb0"
> > +       depends on DRM_IMX
> > +
> > +config DRM_IMX_LCDC
> > +       tristate "DRM Support for Freescale i.MX1 and i.MX2"
> > +       depends on DRM_IMX
> > +       help
> > +         Choose this if you have a i.MX1, i.MX21, i.MX25 or i.MX27 processor.
> 
> do you have something like cpu_is_imx2() type macros?  It would be
> preferable not to have a compile time config option for building for
> certain hw versions.  Ie. on OMAP we could do something like 'if
> (cpu_is_omap3xxx()) { ... }' if there was something that needed to be
> done different on omap3 family of devices.  This way you could choose
> to build a kernel supporting either a single omap variants, or all
> omap variants.

Yes, we have cpu_is_* macros. I don't see though why we should use them
here. The result of DRM_IMX_LCDC will be a platform driver for which a
suitable device exists only on the correct SoCs. There will be a second
DRM_IMX_IPU option next to this one for the newer i.MX SoCs which will
be a second platform driver.

> > +
> > +       /*
> > +        * without file_priv we are called from imx_drm_fbdev_create in which
> > +        * case we only create a framebuffer without a handle.
> > +        */
> > +       if (!file_priv) {
> > +               struct imx_drm_buf_entry *entry;
> > +
> > +               entry = imx_drm_buf_create(dev, size);
> > +               if (IS_ERR(entry)) {
> > +                       ret = PTR_ERR(entry);
> > +                       goto err_buffer;
> > +               }
> > +
> > +               imx_drm_fb->entry = entry;
> > +       } else {
> > +               obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
> > +               if (!obj) {
> > +                       ret = -EINVAL;
> > +                       goto err_buffer;
> > +               }
> > +
> > +               imx_drm_fb->imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> > +
> > +               drm_gem_object_unreference_unlocked(obj);
> 
> 
> the fb probably should hold the reference to the GEM obj until it is
> deleted..  so you don't end up freeing the buffer you are scanning out
> until you are done scanning it out.  Possibly this gets a bit more
> straightforward if you don't have to special case the default/fbcon
> framebuffer?
> 
> IIRC, Laurent had sent a patch recently to fix the same issue in the
> exynos driver.

Yes, just for reference, here is a link:

http://permalink.gmane.org/gmane.comp.video.dri.devel/69203

Will fix this.

> > +
> > +static int imx_drm_gem_mmap_buffer(struct file *filp,
> > +               struct vm_area_struct *vma)
> > +{
> > +       struct drm_gem_object *obj = filp->private_data;
> > +       struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> > +       struct imx_drm_buf_entry *entry;
> > +       unsigned long pfn, vm_size;
> > +
> > +       vma->vm_flags |= VM_IO | VM_RESERVED;
> > +
> > +       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> 
> pgprot_writecombine()?

copied from the exynos driver. The exynos driver recently gained support
for different cache attribute flags and I could do the same. I would
prefer not to even have to think about it by using some generic code
here instead of duplicating other peoples bugs.

Do you think it's possible to share this code as suggested by Lars?
Every SoC not having a IOMMU could share the same code here, it's just
not clear to me how we can put this in a form that is acceptable
upstream.

Sascha

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

^ permalink raw reply	[flat|nested] 26+ messages in thread

* (no subject)
  2012-05-22 14:06   ` Lars-Peter Clausen
@ 2012-05-23  8:12     ` Sascha Hauer
  -1 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-23  8:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, May 22, 2012 at 04:06:41PM +0200, Lars-Peter Clausen wrote:
> On 05/18/2012 02:27 PM, Sascha Hauer wrote:
> > Hi All,
> > 
> > The following adds a drm/kms driver for the Freescale i.MX LCDC
> > controller. Most notable change to the last SDRM based version is that
> > the SDRM layer has been removed and the driver now is purely i.MX
> > specific. I hope that this is more acceptable now.
> > 
> > Another change is that the probe is now devicetree based. For now I
> > took the easy way out and only put an edid blob into the devicetree.
> > I haven't documented the binding yet, I would add that when the rest
> > is considered ok.
> > 
> > Comments very welcome.
> > 
> 
> Hi,
> 
> I really liked the sdrm layer. At least some bits of it. I've been working
> on a "simple" DRM driver as well. The hardware has no fancy acceleration
> features, just a simple buffer and some scanout logic. I'm basically using
> the same gem buffer structure and the buffer is also allocated using
> dma_alloc_writecombine, which means we can probably share all of the GEM
> handling code and probably also most of the fbdev code. I also started with
> the Exynos GEM code as a template, but reworked it later to be more like the
> UDL code, which made it a bit more compact. I think it would be a good idea
> to put at least the GEM handling in some common code as I expect that we'll
> see more similar "simple" DRM drivers pop up.

I totally agree. Having to track other drivers for bug fixes to apply
them on the own driver is not very convenient. As answered to Rob I do
not really have a clue how to accomplish this.

> 
> The code in question can be found at
> https://github.com/lclausen-adi/linux-2.6/commit/87a8fd6b98eeee317c7a486846cc8405d0bd68d8
> 
> Btw. the imx-drm.h is missing in your patch.

Oops, here it is for reference, will include it in the next round.


#ifndef _IMX_DRM_H_
#define _IMX_DRM_H_

/**
 * User-desired buffer creation information structure.
 *
 * @size: requested size for the object.
 *	- this size value would be page-aligned internally.
 * @flags: user request for setting memory type or cache attributes.
 * @handle: returned handle for the object.
 */
struct imx_drm_gem_create {
	unsigned int size;
	unsigned int flags;
	unsigned int handle;
};

struct imx_drm_device;
struct imx_drm_crtc;

struct imx_drm_crtc_helper_funcs {
	int (*enable_vblank)(struct drm_crtc *crtc);
	void (*disable_vblank)(struct drm_crtc *crtc);
};

int imx_drm_add_crtc(struct drm_crtc *crtc,
		struct imx_drm_crtc **new_crtc,
		const struct drm_crtc_funcs *crtc_funcs,
		const struct drm_crtc_helper_funcs *crtc_helper_funcs,
		const struct imx_drm_crtc_helper_funcs *ec_helper_funcs,
		struct module *owner);
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);

/*
 * imx drm buffer entry structure.
 *
 * @paddr: physical address of allocated memory.
 * @vaddr: kernel virtual address of allocated memory.
 * @size: size of allocated memory.
 */
struct imx_drm_buf_entry {
	dma_addr_t paddr;
	void __iomem *vaddr;
	unsigned int size;
};

/* get physical memory information of a drm framebuffer. */
struct imx_drm_buf_entry *imx_drm_fb_get_buf(struct drm_framebuffer *fb);

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);

#define to_imx_drm_gem_obj(x)	container_of(x,\
			struct imx_drm_gem_obj, base)

struct imx_drm_gem_obj {
	struct drm_gem_object base;
	struct imx_drm_buf_entry *entry;
};

/* unmap a buffer from user space. */
int imx_drm_gem_munmap_ioctl(struct drm_device *drm, void *data,
		struct drm_file *file_priv);

/* initialize gem object. */
int imx_drm_gem_init_object(struct drm_gem_object *obj);

/* free gem object. */
void imx_drm_gem_free_object(struct drm_gem_object *gem_obj);

/* create memory region for drm framebuffer. */
int imx_drm_gem_dumb_create(struct drm_file *file_priv,
		struct drm_device *drm, struct drm_mode_create_dumb *args);

/* map memory region for drm framebuffer to user space. */
int imx_drm_gem_dumb_map_offset(struct drm_file *file_priv,
		struct drm_device *drm, uint32_t handle, uint64_t *offset);

/* page fault handler and mmap fault address(virtual) to physical memory. */
int imx_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);

/* set vm_flags and we can change the vm attribute to other one at here. */
int imx_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);

/*
 * destroy memory region allocated.
 *	- a gem handle and physical memory region pointed by a gem object
 *	would be released by drm_gem_handle_delete().
 */
int imx_drm_gem_dumb_destroy(struct drm_file *file_priv,
		struct drm_device *drm, unsigned int handle);

/* allocate physical memory. */
struct imx_drm_buf_entry *imx_drm_buf_create(struct drm_device *drm,
		unsigned int size);

/* remove allocated physical memory. */
void imx_drm_buf_destroy(struct drm_device *drm, struct imx_drm_buf_entry *entry);

struct drm_device *imx_drm_device_get(void);
void imx_drm_device_put(void);

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

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re:
@ 2012-05-23  8:12     ` Sascha Hauer
  0 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-23  8:12 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: linux-arm-kernel, dri-devel

On Tue, May 22, 2012 at 04:06:41PM +0200, Lars-Peter Clausen wrote:
> On 05/18/2012 02:27 PM, Sascha Hauer wrote:
> > Hi All,
> > 
> > The following adds a drm/kms driver for the Freescale i.MX LCDC
> > controller. Most notable change to the last SDRM based version is that
> > the SDRM layer has been removed and the driver now is purely i.MX
> > specific. I hope that this is more acceptable now.
> > 
> > Another change is that the probe is now devicetree based. For now I
> > took the easy way out and only put an edid blob into the devicetree.
> > I haven't documented the binding yet, I would add that when the rest
> > is considered ok.
> > 
> > Comments very welcome.
> > 
> 
> Hi,
> 
> I really liked the sdrm layer. At least some bits of it. I've been working
> on a "simple" DRM driver as well. The hardware has no fancy acceleration
> features, just a simple buffer and some scanout logic. I'm basically using
> the same gem buffer structure and the buffer is also allocated using
> dma_alloc_writecombine, which means we can probably share all of the GEM
> handling code and probably also most of the fbdev code. I also started with
> the Exynos GEM code as a template, but reworked it later to be more like the
> UDL code, which made it a bit more compact. I think it would be a good idea
> to put at least the GEM handling in some common code as I expect that we'll
> see more similar "simple" DRM drivers pop up.

I totally agree. Having to track other drivers for bug fixes to apply
them on the own driver is not very convenient. As answered to Rob I do
not really have a clue how to accomplish this.

> 
> The code in question can be found at
> https://github.com/lclausen-adi/linux-2.6/commit/87a8fd6b98eeee317c7a486846cc8405d0bd68d8
> 
> Btw. the imx-drm.h is missing in your patch.

Oops, here it is for reference, will include it in the next round.


#ifndef _IMX_DRM_H_
#define _IMX_DRM_H_

/**
 * User-desired buffer creation information structure.
 *
 * @size: requested size for the object.
 *	- this size value would be page-aligned internally.
 * @flags: user request for setting memory type or cache attributes.
 * @handle: returned handle for the object.
 */
struct imx_drm_gem_create {
	unsigned int size;
	unsigned int flags;
	unsigned int handle;
};

struct imx_drm_device;
struct imx_drm_crtc;

struct imx_drm_crtc_helper_funcs {
	int (*enable_vblank)(struct drm_crtc *crtc);
	void (*disable_vblank)(struct drm_crtc *crtc);
};

int imx_drm_add_crtc(struct drm_crtc *crtc,
		struct imx_drm_crtc **new_crtc,
		const struct drm_crtc_funcs *crtc_funcs,
		const struct drm_crtc_helper_funcs *crtc_helper_funcs,
		const struct imx_drm_crtc_helper_funcs *ec_helper_funcs,
		struct module *owner);
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);

/*
 * imx drm buffer entry structure.
 *
 * @paddr: physical address of allocated memory.
 * @vaddr: kernel virtual address of allocated memory.
 * @size: size of allocated memory.
 */
struct imx_drm_buf_entry {
	dma_addr_t paddr;
	void __iomem *vaddr;
	unsigned int size;
};

/* get physical memory information of a drm framebuffer. */
struct imx_drm_buf_entry *imx_drm_fb_get_buf(struct drm_framebuffer *fb);

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);

#define to_imx_drm_gem_obj(x)	container_of(x,\
			struct imx_drm_gem_obj, base)

struct imx_drm_gem_obj {
	struct drm_gem_object base;
	struct imx_drm_buf_entry *entry;
};

/* unmap a buffer from user space. */
int imx_drm_gem_munmap_ioctl(struct drm_device *drm, void *data,
		struct drm_file *file_priv);

/* initialize gem object. */
int imx_drm_gem_init_object(struct drm_gem_object *obj);

/* free gem object. */
void imx_drm_gem_free_object(struct drm_gem_object *gem_obj);

/* create memory region for drm framebuffer. */
int imx_drm_gem_dumb_create(struct drm_file *file_priv,
		struct drm_device *drm, struct drm_mode_create_dumb *args);

/* map memory region for drm framebuffer to user space. */
int imx_drm_gem_dumb_map_offset(struct drm_file *file_priv,
		struct drm_device *drm, uint32_t handle, uint64_t *offset);

/* page fault handler and mmap fault address(virtual) to physical memory. */
int imx_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);

/* set vm_flags and we can change the vm attribute to other one at here. */
int imx_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);

/*
 * destroy memory region allocated.
 *	- a gem handle and physical memory region pointed by a gem object
 *	would be released by drm_gem_handle_delete().
 */
int imx_drm_gem_dumb_destroy(struct drm_file *file_priv,
		struct drm_device *drm, unsigned int handle);

/* allocate physical memory. */
struct imx_drm_buf_entry *imx_drm_buf_create(struct drm_device *drm,
		unsigned int size);

/* remove allocated physical memory. */
void imx_drm_buf_destroy(struct drm_device *drm, struct imx_drm_buf_entry *entry);

struct drm_device *imx_drm_device_get(void);
void imx_drm_device_put(void);

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

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 1/2] DRM: add Freescale i.MX LCDC driver
  2012-05-23  7:47       ` Sascha Hauer
@ 2012-05-23  8:37         ` Lars-Peter Clausen
  -1 siblings, 0 replies; 26+ messages in thread
From: Lars-Peter Clausen @ 2012-05-23  8:37 UTC (permalink / raw)
  To: linux-arm-kernel

[...]
>>> +
>>> +static int imx_drm_gem_mmap_buffer(struct file *filp,
>>> +               struct vm_area_struct *vma)
>>> +{
>>> +       struct drm_gem_object *obj = filp->private_data;
>>> +       struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
>>> +       struct imx_drm_buf_entry *entry;
>>> +       unsigned long pfn, vm_size;
>>> +
>>> +       vma->vm_flags |= VM_IO | VM_RESERVED;
>>> +
>>> +       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
>>
>> pgprot_writecombine()?
> 
> copied from the exynos driver. The exynos driver recently gained support
> for different cache attribute flags and I could do the same. I would
> prefer not to even have to think about it by using some generic code
> here instead of duplicating other peoples bugs.
> 
> Do you think it's possible to share this code as suggested by Lars?
> Every SoC not having a IOMMU could share the same code here, it's just
> not clear to me how we can put this in a form that is acceptable
> upstream.

I may have missed this in the previous discussion. But why can't we put the
gem handling code in the toplevel drm folder, give it a config symbol and
let drivers which want to use the code select the config symbol? I think the
main concern was about introducing a new intermediate layer, but the
"simple" gem support would really just a set of helper functions.

Thanks,
- Lars

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 1/2] DRM: add Freescale i.MX LCDC driver
@ 2012-05-23  8:37         ` Lars-Peter Clausen
  0 siblings, 0 replies; 26+ messages in thread
From: Lars-Peter Clausen @ 2012-05-23  8:37 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: dri-devel, linux-arm-kernel, Rob Clark

[...]
>>> +
>>> +static int imx_drm_gem_mmap_buffer(struct file *filp,
>>> +               struct vm_area_struct *vma)
>>> +{
>>> +       struct drm_gem_object *obj = filp->private_data;
>>> +       struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
>>> +       struct imx_drm_buf_entry *entry;
>>> +       unsigned long pfn, vm_size;
>>> +
>>> +       vma->vm_flags |= VM_IO | VM_RESERVED;
>>> +
>>> +       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
>>
>> pgprot_writecombine()?
> 
> copied from the exynos driver. The exynos driver recently gained support
> for different cache attribute flags and I could do the same. I would
> prefer not to even have to think about it by using some generic code
> here instead of duplicating other peoples bugs.
> 
> Do you think it's possible to share this code as suggested by Lars?
> Every SoC not having a IOMMU could share the same code here, it's just
> not clear to me how we can put this in a form that is acceptable
> upstream.

I may have missed this in the previous discussion. But why can't we put the
gem handling code in the toplevel drm folder, give it a config symbol and
let drivers which want to use the code select the config symbol? I think the
main concern was about introducing a new intermediate layer, but the
"simple" gem support would really just a set of helper functions.

Thanks,
- Lars

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 1/2] DRM: add Freescale i.MX LCDC driver
  2012-05-23  8:37         ` Lars-Peter Clausen
@ 2012-05-23  9:09           ` Daniel Vetter
  -1 siblings, 0 replies; 26+ messages in thread
From: Daniel Vetter @ 2012-05-23  9:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, May 23, 2012 at 10:37:58AM +0200, Lars-Peter Clausen wrote:
> [...]
> >>> +
> >>> +static int imx_drm_gem_mmap_buffer(struct file *filp,
> >>> +               struct vm_area_struct *vma)
> >>> +{
> >>> +       struct drm_gem_object *obj = filp->private_data;
> >>> +       struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> >>> +       struct imx_drm_buf_entry *entry;
> >>> +       unsigned long pfn, vm_size;
> >>> +
> >>> +       vma->vm_flags |= VM_IO | VM_RESERVED;
> >>> +
> >>> +       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> >>
> >> pgprot_writecombine()?
> > 
> > copied from the exynos driver. The exynos driver recently gained support
> > for different cache attribute flags and I could do the same. I would
> > prefer not to even have to think about it by using some generic code
> > here instead of duplicating other peoples bugs.
> > 
> > Do you think it's possible to share this code as suggested by Lars?
> > Every SoC not having a IOMMU could share the same code here, it's just
> > not clear to me how we can put this in a form that is acceptable
> > upstream.
> 
> I may have missed this in the previous discussion. But why can't we put the
> gem handling code in the toplevel drm folder, give it a config symbol and
> let drivers which want to use the code select the config symbol? I think the
> main concern was about introducing a new intermediate layer, but the
> "simple" gem support would really just a set of helper functions.

Same for the kms stuff. Me&Dave shot at sdrm because it introduces an
intermediate layer, not because adding some common code for simple drm/kms
drivers is pointless. That is very much welcome ;-)
-Daniel
-- 
Daniel Vetter
Mail: daniel at ffwll.ch
Mobile: +41 (0)79 365 57 48

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 1/2] DRM: add Freescale i.MX LCDC driver
@ 2012-05-23  9:09           ` Daniel Vetter
  0 siblings, 0 replies; 26+ messages in thread
From: Daniel Vetter @ 2012-05-23  9:09 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: linux-arm-kernel, dri-devel, Rob Clark

On Wed, May 23, 2012 at 10:37:58AM +0200, Lars-Peter Clausen wrote:
> [...]
> >>> +
> >>> +static int imx_drm_gem_mmap_buffer(struct file *filp,
> >>> +               struct vm_area_struct *vma)
> >>> +{
> >>> +       struct drm_gem_object *obj = filp->private_data;
> >>> +       struct imx_drm_gem_obj *imx_drm_gem_obj = to_imx_drm_gem_obj(obj);
> >>> +       struct imx_drm_buf_entry *entry;
> >>> +       unsigned long pfn, vm_size;
> >>> +
> >>> +       vma->vm_flags |= VM_IO | VM_RESERVED;
> >>> +
> >>> +       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> >>
> >> pgprot_writecombine()?
> > 
> > copied from the exynos driver. The exynos driver recently gained support
> > for different cache attribute flags and I could do the same. I would
> > prefer not to even have to think about it by using some generic code
> > here instead of duplicating other peoples bugs.
> > 
> > Do you think it's possible to share this code as suggested by Lars?
> > Every SoC not having a IOMMU could share the same code here, it's just
> > not clear to me how we can put this in a form that is acceptable
> > upstream.
> 
> I may have missed this in the previous discussion. But why can't we put the
> gem handling code in the toplevel drm folder, give it a config symbol and
> let drivers which want to use the code select the config symbol? I think the
> main concern was about introducing a new intermediate layer, but the
> "simple" gem support would really just a set of helper functions.

Same for the kms stuff. Me&Dave shot at sdrm because it introduces an
intermediate layer, not because adding some common code for simple drm/kms
drivers is pointless. That is very much welcome ;-)
-Daniel
-- 
Daniel Vetter
Mail: daniel@ffwll.ch
Mobile: +41 (0)79 365 57 48

^ permalink raw reply	[flat|nested] 26+ messages in thread

* (no subject)
  2012-05-22 14:06   ` Lars-Peter Clausen
@ 2012-05-24  6:31     ` Sascha Hauer
  -1 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-24  6:31 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, May 22, 2012 at 04:06:41PM +0200, Lars-Peter Clausen wrote:
> On 05/18/2012 02:27 PM, Sascha Hauer wrote:
> > Hi All,
> > 
> > The following adds a drm/kms driver for the Freescale i.MX LCDC
> > controller. Most notable change to the last SDRM based version is that
> > the SDRM layer has been removed and the driver now is purely i.MX
> > specific. I hope that this is more acceptable now.
> > 
> > Another change is that the probe is now devicetree based. For now I
> > took the easy way out and only put an edid blob into the devicetree.
> > I haven't documented the binding yet, I would add that when the rest
> > is considered ok.
> > 
> > Comments very welcome.
> > 
> 
> Hi,
> 
> I really liked the sdrm layer. At least some bits of it. I've been working
> on a "simple" DRM driver as well. The hardware has no fancy acceleration
> features, just a simple buffer and some scanout logic. I'm basically using
> the same gem buffer structure and the buffer is also allocated using
> dma_alloc_writecombine, which means we can probably share all of the GEM
> handling code and probably also most of the fbdev code. I also started with
> the Exynos GEM code as a template, but reworked it later to be more like the
> UDL code, which made it a bit more compact. I think it would be a good idea
> to put at least the GEM handling in some common code as I expect that we'll
> see more similar "simple" DRM drivers pop up.

Ok, I'll try to put the GEM stuff into helper functions. Would you care
to review/test it? I have something else to do right now but I hope I'll
be there next week.

Sascha

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

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re:
@ 2012-05-24  6:31     ` Sascha Hauer
  0 siblings, 0 replies; 26+ messages in thread
From: Sascha Hauer @ 2012-05-24  6:31 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: linux-arm-kernel, dri-devel

On Tue, May 22, 2012 at 04:06:41PM +0200, Lars-Peter Clausen wrote:
> On 05/18/2012 02:27 PM, Sascha Hauer wrote:
> > Hi All,
> > 
> > The following adds a drm/kms driver for the Freescale i.MX LCDC
> > controller. Most notable change to the last SDRM based version is that
> > the SDRM layer has been removed and the driver now is purely i.MX
> > specific. I hope that this is more acceptable now.
> > 
> > Another change is that the probe is now devicetree based. For now I
> > took the easy way out and only put an edid blob into the devicetree.
> > I haven't documented the binding yet, I would add that when the rest
> > is considered ok.
> > 
> > Comments very welcome.
> > 
> 
> Hi,
> 
> I really liked the sdrm layer. At least some bits of it. I've been working
> on a "simple" DRM driver as well. The hardware has no fancy acceleration
> features, just a simple buffer and some scanout logic. I'm basically using
> the same gem buffer structure and the buffer is also allocated using
> dma_alloc_writecombine, which means we can probably share all of the GEM
> handling code and probably also most of the fbdev code. I also started with
> the Exynos GEM code as a template, but reworked it later to be more like the
> UDL code, which made it a bit more compact. I think it would be a good idea
> to put at least the GEM handling in some common code as I expect that we'll
> see more similar "simple" DRM drivers pop up.

Ok, I'll try to put the GEM stuff into helper functions. Would you care
to review/test it? I have something else to do right now but I hope I'll
be there next week.

Sascha

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

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2012-05-24  6:31 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-05-18 12:27 No subject Sascha Hauer
2012-05-18 12:27 ` (unknown), Sascha Hauer
2012-05-18 12:27 ` [PATCH 1/2] DRM: add Freescale i.MX LCDC driver Sascha Hauer
2012-05-18 12:27   ` Sascha Hauer
2012-05-22 21:28   ` Rob Clark
2012-05-22 21:28     ` Rob Clark
2012-05-23  7:47     ` Sascha Hauer
2012-05-23  7:47       ` Sascha Hauer
2012-05-23  8:37       ` Lars-Peter Clausen
2012-05-23  8:37         ` Lars-Peter Clausen
2012-05-23  9:09         ` Daniel Vetter
2012-05-23  9:09           ` Daniel Vetter
2012-05-18 12:27 ` [PATCH 2/2] pcm038 lcdc support Sascha Hauer
2012-05-18 12:27   ` Sascha Hauer
2012-05-18 14:03   ` Adam Jackson
2012-05-18 14:03     ` Adam Jackson
2012-05-18 15:13     ` Sascha Hauer
2012-05-18 15:13       ` Sascha Hauer
2012-05-22 10:02       ` Dave Airlie
2012-05-22 10:02         ` Dave Airlie
2012-05-22 14:06 ` Lars-Peter Clausen
2012-05-22 14:06   ` Lars-Peter Clausen
2012-05-23  8:12   ` Sascha Hauer
2012-05-23  8:12     ` Sascha Hauer
2012-05-24  6:31   ` Sascha Hauer
2012-05-24  6:31     ` Sascha Hauer

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.