All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] DRM helpers for embedded systems
@ 2012-04-11 15:33 ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: linux-arm-kernel

Hi All,

The following series adds support for a new set of drm helpers called
sdrm. It is targeted to ease the implementation of drivers for embedded
systems. The basic idea is that instead of handling a comlete drm device
in each driver we introduce helpers which take care of the drm device
and most of the interfacing with the drm core. Users of these helpers
can register crtcs, encoders and connectors as separate devices which
reflects the hardware of embedded systems better than a monolithic drm
device. The patches are mostly based on the exynos driver. While writing
a driver for my devices I realized that I had to duplicate the bulk of
this driver, mostly replacing the exynos_ prefixes with imx_ prefixes,
hence the idea of creating a common infrastructure for this.

As a testbed driver a i.MX LCDC driver and a Intel (Marvell) PXA2xx
driver is included. Both are very simple last-decade-embedded-lcd-controllers.
The drivers have been tested with the xf86 modesetting driver, some libdrm
tests and a custom kms testing tool. Currently only the base framebuffers
are supported, but KMS plane support is definitely on my todo list.  The
drivers posted here are mostly created for demonstration purpose and to
give a template for other drivers, but the motivation for creating this
layer was the i.MX5/6 IPU (Image Processing Unit) which has two crts (four
on i.MX6), several on-SoC encoders and overlay possibilities. So this layer
should not be limited to the real 'simple' cases.

The sdrm patches currently have some limitations, but they should be
enough for being useful and to present it to a wider audience. Currently
no custom ioctls are supported, basically because I didn't need them
yet.  Also the pitch for the framebuffers are hardcoded by the sdrm
layer, this should be done in the crtc drivers instead.  Another thing
is that I currently have no idea what might be needed to support IOMMUs,
but maybe Thierry can help me out with his Tegra patches ;)

For a more complete branch including board support for a PXA27x based
Phytec board see this branch:

git://git.pengutronix.de/git/imx/linux-2.6.git gpu/sdrm-full

Comments very appreciated.

Sascha



The following changes since commit 0034102808e0dbbf3a2394b82b1bb40b5778de9e:

  Linux 3.4-rc2 (2012-04-07 18:30:41 -0700)

are available in the git repository at:

  git://git.pengutronix.de/git/imx/linux-2.6.git gpu/sdrm

for you to fetch changes up to fc3d0ff4825de998f1fd902184f7df040248d0de:

  DRM: add PXA kms simple driver (2012-04-11 17:10:46 +0200)

----------------------------------------------------------------
Philipp Zabel (1):
      DRM: add PXA kms simple driver

Sascha Hauer (6):
      drm: remove legacy mode_group handling
      drm: make gamma_set optional
      DRM: add sdrm layer for general embedded system support
      DRM: Add sdrm 1:1 encoder - connector helper
      DRM: add i.MX kms simple driver
      ARM i.MX27 pcm038: Add sdrm support

 arch/arm/mach-imx/pcm970-baseboard.c      |   78 ++-
 arch/arm/mach-pxa/include/mach/regs-lcd.h |    2 +
 arch/arm/plat-mxc/include/mach/imxfb.h    |    8 +-
 drivers/gpu/drm/Kconfig                   |    6 +
 drivers/gpu/drm/Makefile                  |    3 +
 drivers/gpu/drm/drm_crtc.c                |  163 ++----
 drivers/gpu/drm/drm_pci.c                 |    8 -
 drivers/gpu/drm/drm_platform.c            |    8 -
 drivers/gpu/drm/drm_usb.c                 |    6 -
 drivers/gpu/drm/imx/Kconfig               |    8 +
 drivers/gpu/drm/imx/Makefile              |    1 +
 drivers/gpu/drm/imx/imx-lcdc-crtc.c       |  506 ++++++++++++++++
 drivers/gpu/drm/pxa/Kconfig               |   12 +
 drivers/gpu/drm/pxa/Makefile              |    1 +
 drivers/gpu/drm/pxa/pxa-lcdc-crtc.c       |  845 +++++++++++++++++++++++++++
 drivers/gpu/drm/sdrm/Kconfig              |   11 +
 drivers/gpu/drm/sdrm/Makefile             |    5 +
 drivers/gpu/drm/sdrm/sdrm.c               |  904 +++++++++++++++++++++++++++++
 drivers/gpu/drm/sdrm/sdrm.h               |   57 ++
 drivers/gpu/drm/sdrm/sdrm_encon.c         |  211 +++++++
 drivers/gpu/drm/sdrm/sdrm_encon_dummy.c   |  193 ++++++
 drivers/gpu/drm/sdrm/sdrm_fb.c            |  191 ++++++
 drivers/gpu/drm/sdrm/sdrm_fbdev.c         |  238 ++++++++
 drivers/gpu/drm/sdrm/sdrm_gem.c           |  342 +++++++++++
 include/drm/drmP.h                        |    1 -
 include/drm/drm_crtc.h                    |   24 +-
 include/drm/sdrm.h                        |  102 ++++
 include/drm/sdrm_encon.h                  |   69 +++
 28 files changed, 3831 insertions(+), 172 deletions(-)
 create mode 100644 drivers/gpu/drm/imx/Kconfig
 create mode 100644 drivers/gpu/drm/imx/Makefile
 create mode 100644 drivers/gpu/drm/imx/imx-lcdc-crtc.c
 create mode 100644 drivers/gpu/drm/pxa/Kconfig
 create mode 100644 drivers/gpu/drm/pxa/Makefile
 create mode 100644 drivers/gpu/drm/pxa/pxa-lcdc-crtc.c
 create mode 100644 drivers/gpu/drm/sdrm/Kconfig
 create mode 100644 drivers/gpu/drm/sdrm/Makefile
 create mode 100644 drivers/gpu/drm/sdrm/sdrm.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm.h
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_encon.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_encon_dummy.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_fb.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_fbdev.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_gem.c
 create mode 100644 include/drm/sdrm.h
 create mode 100644 include/drm/sdrm_encon.h

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

* [RFC] DRM helpers for embedded systems
@ 2012-04-11 15:33 ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-arm-kernel, kernel

Hi All,

The following series adds support for a new set of drm helpers called
sdrm. It is targeted to ease the implementation of drivers for embedded
systems. The basic idea is that instead of handling a comlete drm device
in each driver we introduce helpers which take care of the drm device
and most of the interfacing with the drm core. Users of these helpers
can register crtcs, encoders and connectors as separate devices which
reflects the hardware of embedded systems better than a monolithic drm
device. The patches are mostly based on the exynos driver. While writing
a driver for my devices I realized that I had to duplicate the bulk of
this driver, mostly replacing the exynos_ prefixes with imx_ prefixes,
hence the idea of creating a common infrastructure for this.

As a testbed driver a i.MX LCDC driver and a Intel (Marvell) PXA2xx
driver is included. Both are very simple last-decade-embedded-lcd-controllers.
The drivers have been tested with the xf86 modesetting driver, some libdrm
tests and a custom kms testing tool. Currently only the base framebuffers
are supported, but KMS plane support is definitely on my todo list.  The
drivers posted here are mostly created for demonstration purpose and to
give a template for other drivers, but the motivation for creating this
layer was the i.MX5/6 IPU (Image Processing Unit) which has two crts (four
on i.MX6), several on-SoC encoders and overlay possibilities. So this layer
should not be limited to the real 'simple' cases.

The sdrm patches currently have some limitations, but they should be
enough for being useful and to present it to a wider audience. Currently
no custom ioctls are supported, basically because I didn't need them
yet.  Also the pitch for the framebuffers are hardcoded by the sdrm
layer, this should be done in the crtc drivers instead.  Another thing
is that I currently have no idea what might be needed to support IOMMUs,
but maybe Thierry can help me out with his Tegra patches ;)

For a more complete branch including board support for a PXA27x based
Phytec board see this branch:

git://git.pengutronix.de/git/imx/linux-2.6.git gpu/sdrm-full

Comments very appreciated.

Sascha



The following changes since commit 0034102808e0dbbf3a2394b82b1bb40b5778de9e:

  Linux 3.4-rc2 (2012-04-07 18:30:41 -0700)

are available in the git repository at:

  git://git.pengutronix.de/git/imx/linux-2.6.git gpu/sdrm

for you to fetch changes up to fc3d0ff4825de998f1fd902184f7df040248d0de:

  DRM: add PXA kms simple driver (2012-04-11 17:10:46 +0200)

----------------------------------------------------------------
Philipp Zabel (1):
      DRM: add PXA kms simple driver

Sascha Hauer (6):
      drm: remove legacy mode_group handling
      drm: make gamma_set optional
      DRM: add sdrm layer for general embedded system support
      DRM: Add sdrm 1:1 encoder - connector helper
      DRM: add i.MX kms simple driver
      ARM i.MX27 pcm038: Add sdrm support

 arch/arm/mach-imx/pcm970-baseboard.c      |   78 ++-
 arch/arm/mach-pxa/include/mach/regs-lcd.h |    2 +
 arch/arm/plat-mxc/include/mach/imxfb.h    |    8 +-
 drivers/gpu/drm/Kconfig                   |    6 +
 drivers/gpu/drm/Makefile                  |    3 +
 drivers/gpu/drm/drm_crtc.c                |  163 ++----
 drivers/gpu/drm/drm_pci.c                 |    8 -
 drivers/gpu/drm/drm_platform.c            |    8 -
 drivers/gpu/drm/drm_usb.c                 |    6 -
 drivers/gpu/drm/imx/Kconfig               |    8 +
 drivers/gpu/drm/imx/Makefile              |    1 +
 drivers/gpu/drm/imx/imx-lcdc-crtc.c       |  506 ++++++++++++++++
 drivers/gpu/drm/pxa/Kconfig               |   12 +
 drivers/gpu/drm/pxa/Makefile              |    1 +
 drivers/gpu/drm/pxa/pxa-lcdc-crtc.c       |  845 +++++++++++++++++++++++++++
 drivers/gpu/drm/sdrm/Kconfig              |   11 +
 drivers/gpu/drm/sdrm/Makefile             |    5 +
 drivers/gpu/drm/sdrm/sdrm.c               |  904 +++++++++++++++++++++++++++++
 drivers/gpu/drm/sdrm/sdrm.h               |   57 ++
 drivers/gpu/drm/sdrm/sdrm_encon.c         |  211 +++++++
 drivers/gpu/drm/sdrm/sdrm_encon_dummy.c   |  193 ++++++
 drivers/gpu/drm/sdrm/sdrm_fb.c            |  191 ++++++
 drivers/gpu/drm/sdrm/sdrm_fbdev.c         |  238 ++++++++
 drivers/gpu/drm/sdrm/sdrm_gem.c           |  342 +++++++++++
 include/drm/drmP.h                        |    1 -
 include/drm/drm_crtc.h                    |   24 +-
 include/drm/sdrm.h                        |  102 ++++
 include/drm/sdrm_encon.h                  |   69 +++
 28 files changed, 3831 insertions(+), 172 deletions(-)
 create mode 100644 drivers/gpu/drm/imx/Kconfig
 create mode 100644 drivers/gpu/drm/imx/Makefile
 create mode 100644 drivers/gpu/drm/imx/imx-lcdc-crtc.c
 create mode 100644 drivers/gpu/drm/pxa/Kconfig
 create mode 100644 drivers/gpu/drm/pxa/Makefile
 create mode 100644 drivers/gpu/drm/pxa/pxa-lcdc-crtc.c
 create mode 100644 drivers/gpu/drm/sdrm/Kconfig
 create mode 100644 drivers/gpu/drm/sdrm/Makefile
 create mode 100644 drivers/gpu/drm/sdrm/sdrm.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm.h
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_encon.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_encon_dummy.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_fb.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_fbdev.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_gem.c
 create mode 100644 include/drm/sdrm.h
 create mode 100644 include/drm/sdrm_encon.h

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

* [PATCH 1/7] drm: remove legacy mode_group handling
  2012-04-11 15:33 ` Sascha Hauer
@ 2012-04-11 15:33   ` Sascha Hauer
  -1 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: linux-arm-kernel

During initialization of a drm device a struct drm_mode_group is
filled with the encoder/connector/crtc ids from the corresponding
lists, so the drm_mode_group contains the same data as already is
in the lists. Then in drm_mode_getresources either the lists or
the drm_mode_group are used. As both contain the same data,
we can remove the drm_mode_group and all code around it altogether
without losing functionality.

The legacy mode group handling was introduced for a later support
of for example two X servers running simultaneously in different
control groups on different heads, each with their own mode
configuration.

This code is present and unchanged since the initial drm commit
in 11/2008.  This code is not evolving and currently gets in the
way of implementing a more dynamic (de-)registration of encoders
and connectors. Instead of using an allocated array of pointers
to the components used in a particular control group a better way
would be to use a list instead. This would allow to add/remove
components to a control group without having to reallocate the
array of pointers.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/drm_crtc.c     |  158 +++++++++-------------------------------
 drivers/gpu/drm/drm_pci.c      |    8 --
 drivers/gpu/drm/drm_platform.c |    8 --
 drivers/gpu/drm/drm_usb.c      |    6 --
 include/drm/drmP.h             |    1 -
 include/drm/drm_crtc.h         |   24 +-----
 6 files changed, 35 insertions(+), 170 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index d3aaeb6..b14496e 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -944,50 +944,6 @@ void drm_mode_config_init(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_mode_config_init);
 
-int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group)
-{
-	uint32_t total_objects = 0;
-
-	total_objects += dev->mode_config.num_crtc;
-	total_objects += dev->mode_config.num_connector;
-	total_objects += dev->mode_config.num_encoder;
-
-	group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL);
-	if (!group->id_list)
-		return -ENOMEM;
-
-	group->num_crtcs = 0;
-	group->num_connectors = 0;
-	group->num_encoders = 0;
-	return 0;
-}
-
-int drm_mode_group_init_legacy_group(struct drm_device *dev,
-				     struct drm_mode_group *group)
-{
-	struct drm_crtc *crtc;
-	struct drm_encoder *encoder;
-	struct drm_connector *connector;
-	int ret;
-
-	if ((ret = drm_mode_group_init(dev, group)))
-		return ret;
-
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-		group->id_list[group->num_crtcs++] = crtc->base.id;
-
-	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
-		group->id_list[group->num_crtcs + group->num_encoders++] =
-		encoder->base.id;
-
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
-		group->id_list[group->num_crtcs + group->num_encoders +
-			       group->num_connectors++] = connector->base.id;
-
-	return 0;
-}
-EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
-
 /**
  * drm_mode_config_cleanup - free up DRM mode_config info
  * @dev: DRM device
@@ -1158,7 +1114,6 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
 	uint32_t __user *crtc_id;
 	uint32_t __user *connector_id;
 	uint32_t __user *encoder_id;
-	struct drm_mode_group *mode_group;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
@@ -1172,23 +1127,14 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
 	list_for_each(lh, &file_priv->fbs)
 		fb_count++;
 
-	mode_group = &file_priv->master->minor->mode_group;
-	if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
-
-		list_for_each(lh, &dev->mode_config.crtc_list)
-			crtc_count++;
-
-		list_for_each(lh, &dev->mode_config.connector_list)
-			connector_count++;
+	list_for_each(lh, &dev->mode_config.crtc_list)
+		crtc_count++;
 
-		list_for_each(lh, &dev->mode_config.encoder_list)
-			encoder_count++;
-	} else {
+	list_for_each(lh, &dev->mode_config.connector_list)
+		connector_count++;
 
-		crtc_count = mode_group->num_crtcs;
-		connector_count = mode_group->num_connectors;
-		encoder_count = mode_group->num_encoders;
-	}
+	list_for_each(lh, &dev->mode_config.encoder_list)
+		encoder_count++;
 
 	card_res->max_height = dev->mode_config.max_height;
 	card_res->min_height = dev->mode_config.min_height;
@@ -1214,25 +1160,14 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
 	if (card_res->count_crtcs >= crtc_count) {
 		copied = 0;
 		crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
-		if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
-			list_for_each_entry(crtc, &dev->mode_config.crtc_list,
-					    head) {
-				DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
-				if (put_user(crtc->base.id, crtc_id + copied)) {
-					ret = -EFAULT;
-					goto out;
-				}
-				copied++;
-			}
-		} else {
-			for (i = 0; i < mode_group->num_crtcs; i++) {
-				if (put_user(mode_group->id_list[i],
-					     crtc_id + copied)) {
-					ret = -EFAULT;
-					goto out;
-				}
-				copied++;
+		list_for_each_entry(crtc, &dev->mode_config.crtc_list,
+				    head) {
+			DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
+			if (put_user(crtc->base.id, crtc_id + copied)) {
+				ret = -EFAULT;
+				goto out;
 			}
+			copied++;
 		}
 	}
 	card_res->count_crtcs = crtc_count;
@@ -1241,29 +1176,17 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
 	if (card_res->count_encoders >= encoder_count) {
 		copied = 0;
 		encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
-		if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
-			list_for_each_entry(encoder,
-					    &dev->mode_config.encoder_list,
-					    head) {
-				DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id,
-						drm_get_encoder_name(encoder));
-				if (put_user(encoder->base.id, encoder_id +
-					     copied)) {
-					ret = -EFAULT;
-					goto out;
-				}
-				copied++;
-			}
-		} else {
-			for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) {
-				if (put_user(mode_group->id_list[i],
-					     encoder_id + copied)) {
-					ret = -EFAULT;
-					goto out;
-				}
-				copied++;
+		list_for_each_entry(encoder,
+				    &dev->mode_config.encoder_list,
+				    head) {
+			DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id,
+					drm_get_encoder_name(encoder));
+			if (put_user(encoder->base.id, encoder_id +
+				     copied)) {
+				ret = -EFAULT;
+				goto out;
 			}
-
+			copied++;
 		}
 	}
 	card_res->count_encoders = encoder_count;
@@ -1272,31 +1195,18 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
 	if (card_res->count_connectors >= connector_count) {
 		copied = 0;
 		connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
-		if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
-			list_for_each_entry(connector,
-					    &dev->mode_config.connector_list,
-					    head) {
-				DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-					connector->base.id,
-					drm_get_connector_name(connector));
-				if (put_user(connector->base.id,
-					     connector_id + copied)) {
-					ret = -EFAULT;
-					goto out;
-				}
-				copied++;
-			}
-		} else {
-			int start = mode_group->num_crtcs +
-				mode_group->num_encoders;
-			for (i = start; i < start + mode_group->num_connectors; i++) {
-				if (put_user(mode_group->id_list[i],
-					     connector_id + copied)) {
-					ret = -EFAULT;
-					goto out;
-				}
-				copied++;
+		list_for_each_entry(connector,
+				    &dev->mode_config.connector_list,
+				    head) {
+			DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+				connector->base.id,
+				drm_get_connector_name(connector));
+			if (put_user(connector->base.id,
+				     connector_id + copied)) {
+				ret = -EFAULT;
+				goto out;
 			}
+			copied++;
 		}
 	}
 	card_res->count_connectors = connector_count;
diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c
index 13f3d93..d5424c5 100644
--- a/drivers/gpu/drm/drm_pci.c
+++ b/drivers/gpu/drm/drm_pci.c
@@ -357,14 +357,6 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
 			goto err_g4;
 	}
 
-	/* setup the grouping for the legacy output */
-	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-		ret = drm_mode_group_init_legacy_group(dev,
-						&dev->primary->mode_group);
-		if (ret)
-			goto err_g4;
-	}
-
 	list_add_tail(&dev->driver_item, &driver->device_list);
 
 	DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n",
diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c
index 82431dc..0c36d63 100644
--- a/drivers/gpu/drm/drm_platform.c
+++ b/drivers/gpu/drm/drm_platform.c
@@ -80,14 +80,6 @@ int drm_get_platform_dev(struct platform_device *platdev,
 			goto err_g3;
 	}
 
-	/* setup the grouping for the legacy output */
-	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-		ret = drm_mode_group_init_legacy_group(dev,
-				&dev->primary->mode_group);
-		if (ret)
-			goto err_g3;
-	}
-
 	list_add_tail(&dev->driver_item, &driver->device_list);
 
 	mutex_unlock(&drm_global_mutex);
diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c
index c8c83da..8c7be4a 100644
--- a/drivers/gpu/drm/drm_usb.c
+++ b/drivers/gpu/drm/drm_usb.c
@@ -43,12 +43,6 @@ int drm_get_usb_dev(struct usb_interface *interface,
 			goto err_g3;
 	}
 
-	/* setup the grouping for the legacy output */
-	ret = drm_mode_group_init_legacy_group(dev,
-					       &dev->primary->mode_group);
-	if (ret)
-		goto err_g3;
-
 	list_add_tail(&dev->driver_item, &driver->device_list);
 
 	mutex_unlock(&drm_global_mutex);
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index dd73104..fe7884f 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -1032,7 +1032,6 @@ struct drm_minor {
 
 	struct drm_master *master; /* currently active master for this node */
 	struct list_head master_list;
-	struct drm_mode_group mode_group;
 };
 
 /* mode specified on the command line */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index e250eda..0ac6be0 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -692,28 +692,6 @@ struct drm_mode_config_funcs {
 };
 
 /**
- * drm_mode_group - group of mode setting resources for potential sub-grouping
- * @num_crtcs: CRTC count
- * @num_encoders: encoder count
- * @num_connectors: connector count
- * @id_list: list of KMS object IDs in this group
- *
- * Currently this simply tracks the global mode setting state.  But in the
- * future it could allow groups of objects to be set aside into independent
- * control groups for use by different user level processes (e.g. two X servers
- * running simultaneously on different heads, each with their own mode
- * configuration and freedom of mode setting).
- */
-struct drm_mode_group {
-	uint32_t num_crtcs;
-	uint32_t num_encoders;
-	uint32_t num_connectors;
-
-	/* list of object IDs for this group */
-	uint32_t *id_list;
-};
-
-/**
  * drm_mode_config - Mode configuration control structure
  * @mutex: mutex protecting KMS related lists and structures
  * @idr_mutex: mutex for KMS ID allocation and management
@@ -851,7 +829,7 @@ extern char *drm_get_dvi_i_select_name(int val);
 extern char *drm_get_tv_subconnector_name(int val);
 extern char *drm_get_tv_select_name(int val);
 extern void drm_fb_release(struct drm_file *file_priv);
-extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
+#define HAVE_MODEGROUP_REMOVED
 extern struct edid *drm_get_edid(struct drm_connector *connector,
 				 struct i2c_adapter *adapter);
 extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
-- 
1.7.9.5

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

* [PATCH 1/7] drm: remove legacy mode_group handling
@ 2012-04-11 15:33   ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: dri-devel; +Cc: Sascha Hauer, linux-arm-kernel, kernel

During initialization of a drm device a struct drm_mode_group is
filled with the encoder/connector/crtc ids from the corresponding
lists, so the drm_mode_group contains the same data as already is
in the lists. Then in drm_mode_getresources either the lists or
the drm_mode_group are used. As both contain the same data,
we can remove the drm_mode_group and all code around it altogether
without losing functionality.

The legacy mode group handling was introduced for a later support
of for example two X servers running simultaneously in different
control groups on different heads, each with their own mode
configuration.

This code is present and unchanged since the initial drm commit
in 11/2008.  This code is not evolving and currently gets in the
way of implementing a more dynamic (de-)registration of encoders
and connectors. Instead of using an allocated array of pointers
to the components used in a particular control group a better way
would be to use a list instead. This would allow to add/remove
components to a control group without having to reallocate the
array of pointers.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/drm_crtc.c     |  158 +++++++++-------------------------------
 drivers/gpu/drm/drm_pci.c      |    8 --
 drivers/gpu/drm/drm_platform.c |    8 --
 drivers/gpu/drm/drm_usb.c      |    6 --
 include/drm/drmP.h             |    1 -
 include/drm/drm_crtc.h         |   24 +-----
 6 files changed, 35 insertions(+), 170 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index d3aaeb6..b14496e 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -944,50 +944,6 @@ void drm_mode_config_init(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_mode_config_init);
 
-int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group)
-{
-	uint32_t total_objects = 0;
-
-	total_objects += dev->mode_config.num_crtc;
-	total_objects += dev->mode_config.num_connector;
-	total_objects += dev->mode_config.num_encoder;
-
-	group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL);
-	if (!group->id_list)
-		return -ENOMEM;
-
-	group->num_crtcs = 0;
-	group->num_connectors = 0;
-	group->num_encoders = 0;
-	return 0;
-}
-
-int drm_mode_group_init_legacy_group(struct drm_device *dev,
-				     struct drm_mode_group *group)
-{
-	struct drm_crtc *crtc;
-	struct drm_encoder *encoder;
-	struct drm_connector *connector;
-	int ret;
-
-	if ((ret = drm_mode_group_init(dev, group)))
-		return ret;
-
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-		group->id_list[group->num_crtcs++] = crtc->base.id;
-
-	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
-		group->id_list[group->num_crtcs + group->num_encoders++] =
-		encoder->base.id;
-
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
-		group->id_list[group->num_crtcs + group->num_encoders +
-			       group->num_connectors++] = connector->base.id;
-
-	return 0;
-}
-EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
-
 /**
  * drm_mode_config_cleanup - free up DRM mode_config info
  * @dev: DRM device
@@ -1158,7 +1114,6 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
 	uint32_t __user *crtc_id;
 	uint32_t __user *connector_id;
 	uint32_t __user *encoder_id;
-	struct drm_mode_group *mode_group;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
@@ -1172,23 +1127,14 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
 	list_for_each(lh, &file_priv->fbs)
 		fb_count++;
 
-	mode_group = &file_priv->master->minor->mode_group;
-	if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
-
-		list_for_each(lh, &dev->mode_config.crtc_list)
-			crtc_count++;
-
-		list_for_each(lh, &dev->mode_config.connector_list)
-			connector_count++;
+	list_for_each(lh, &dev->mode_config.crtc_list)
+		crtc_count++;
 
-		list_for_each(lh, &dev->mode_config.encoder_list)
-			encoder_count++;
-	} else {
+	list_for_each(lh, &dev->mode_config.connector_list)
+		connector_count++;
 
-		crtc_count = mode_group->num_crtcs;
-		connector_count = mode_group->num_connectors;
-		encoder_count = mode_group->num_encoders;
-	}
+	list_for_each(lh, &dev->mode_config.encoder_list)
+		encoder_count++;
 
 	card_res->max_height = dev->mode_config.max_height;
 	card_res->min_height = dev->mode_config.min_height;
@@ -1214,25 +1160,14 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
 	if (card_res->count_crtcs >= crtc_count) {
 		copied = 0;
 		crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
-		if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
-			list_for_each_entry(crtc, &dev->mode_config.crtc_list,
-					    head) {
-				DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
-				if (put_user(crtc->base.id, crtc_id + copied)) {
-					ret = -EFAULT;
-					goto out;
-				}
-				copied++;
-			}
-		} else {
-			for (i = 0; i < mode_group->num_crtcs; i++) {
-				if (put_user(mode_group->id_list[i],
-					     crtc_id + copied)) {
-					ret = -EFAULT;
-					goto out;
-				}
-				copied++;
+		list_for_each_entry(crtc, &dev->mode_config.crtc_list,
+				    head) {
+			DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
+			if (put_user(crtc->base.id, crtc_id + copied)) {
+				ret = -EFAULT;
+				goto out;
 			}
+			copied++;
 		}
 	}
 	card_res->count_crtcs = crtc_count;
@@ -1241,29 +1176,17 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
 	if (card_res->count_encoders >= encoder_count) {
 		copied = 0;
 		encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
-		if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
-			list_for_each_entry(encoder,
-					    &dev->mode_config.encoder_list,
-					    head) {
-				DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id,
-						drm_get_encoder_name(encoder));
-				if (put_user(encoder->base.id, encoder_id +
-					     copied)) {
-					ret = -EFAULT;
-					goto out;
-				}
-				copied++;
-			}
-		} else {
-			for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) {
-				if (put_user(mode_group->id_list[i],
-					     encoder_id + copied)) {
-					ret = -EFAULT;
-					goto out;
-				}
-				copied++;
+		list_for_each_entry(encoder,
+				    &dev->mode_config.encoder_list,
+				    head) {
+			DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id,
+					drm_get_encoder_name(encoder));
+			if (put_user(encoder->base.id, encoder_id +
+				     copied)) {
+				ret = -EFAULT;
+				goto out;
 			}
-
+			copied++;
 		}
 	}
 	card_res->count_encoders = encoder_count;
@@ -1272,31 +1195,18 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
 	if (card_res->count_connectors >= connector_count) {
 		copied = 0;
 		connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
-		if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
-			list_for_each_entry(connector,
-					    &dev->mode_config.connector_list,
-					    head) {
-				DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-					connector->base.id,
-					drm_get_connector_name(connector));
-				if (put_user(connector->base.id,
-					     connector_id + copied)) {
-					ret = -EFAULT;
-					goto out;
-				}
-				copied++;
-			}
-		} else {
-			int start = mode_group->num_crtcs +
-				mode_group->num_encoders;
-			for (i = start; i < start + mode_group->num_connectors; i++) {
-				if (put_user(mode_group->id_list[i],
-					     connector_id + copied)) {
-					ret = -EFAULT;
-					goto out;
-				}
-				copied++;
+		list_for_each_entry(connector,
+				    &dev->mode_config.connector_list,
+				    head) {
+			DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+				connector->base.id,
+				drm_get_connector_name(connector));
+			if (put_user(connector->base.id,
+				     connector_id + copied)) {
+				ret = -EFAULT;
+				goto out;
 			}
+			copied++;
 		}
 	}
 	card_res->count_connectors = connector_count;
diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c
index 13f3d93..d5424c5 100644
--- a/drivers/gpu/drm/drm_pci.c
+++ b/drivers/gpu/drm/drm_pci.c
@@ -357,14 +357,6 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
 			goto err_g4;
 	}
 
-	/* setup the grouping for the legacy output */
-	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-		ret = drm_mode_group_init_legacy_group(dev,
-						&dev->primary->mode_group);
-		if (ret)
-			goto err_g4;
-	}
-
 	list_add_tail(&dev->driver_item, &driver->device_list);
 
 	DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n",
diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c
index 82431dc..0c36d63 100644
--- a/drivers/gpu/drm/drm_platform.c
+++ b/drivers/gpu/drm/drm_platform.c
@@ -80,14 +80,6 @@ int drm_get_platform_dev(struct platform_device *platdev,
 			goto err_g3;
 	}
 
-	/* setup the grouping for the legacy output */
-	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-		ret = drm_mode_group_init_legacy_group(dev,
-				&dev->primary->mode_group);
-		if (ret)
-			goto err_g3;
-	}
-
 	list_add_tail(&dev->driver_item, &driver->device_list);
 
 	mutex_unlock(&drm_global_mutex);
diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c
index c8c83da..8c7be4a 100644
--- a/drivers/gpu/drm/drm_usb.c
+++ b/drivers/gpu/drm/drm_usb.c
@@ -43,12 +43,6 @@ int drm_get_usb_dev(struct usb_interface *interface,
 			goto err_g3;
 	}
 
-	/* setup the grouping for the legacy output */
-	ret = drm_mode_group_init_legacy_group(dev,
-					       &dev->primary->mode_group);
-	if (ret)
-		goto err_g3;
-
 	list_add_tail(&dev->driver_item, &driver->device_list);
 
 	mutex_unlock(&drm_global_mutex);
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index dd73104..fe7884f 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -1032,7 +1032,6 @@ struct drm_minor {
 
 	struct drm_master *master; /* currently active master for this node */
 	struct list_head master_list;
-	struct drm_mode_group mode_group;
 };
 
 /* mode specified on the command line */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index e250eda..0ac6be0 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -692,28 +692,6 @@ struct drm_mode_config_funcs {
 };
 
 /**
- * drm_mode_group - group of mode setting resources for potential sub-grouping
- * @num_crtcs: CRTC count
- * @num_encoders: encoder count
- * @num_connectors: connector count
- * @id_list: list of KMS object IDs in this group
- *
- * Currently this simply tracks the global mode setting state.  But in the
- * future it could allow groups of objects to be set aside into independent
- * control groups for use by different user level processes (e.g. two X servers
- * running simultaneously on different heads, each with their own mode
- * configuration and freedom of mode setting).
- */
-struct drm_mode_group {
-	uint32_t num_crtcs;
-	uint32_t num_encoders;
-	uint32_t num_connectors;
-
-	/* list of object IDs for this group */
-	uint32_t *id_list;
-};
-
-/**
  * drm_mode_config - Mode configuration control structure
  * @mutex: mutex protecting KMS related lists and structures
  * @idr_mutex: mutex for KMS ID allocation and management
@@ -851,7 +829,7 @@ extern char *drm_get_dvi_i_select_name(int val);
 extern char *drm_get_tv_subconnector_name(int val);
 extern char *drm_get_tv_select_name(int val);
 extern void drm_fb_release(struct drm_file *file_priv);
-extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
+#define HAVE_MODEGROUP_REMOVED
 extern struct edid *drm_get_edid(struct drm_connector *connector,
 				 struct i2c_adapter *adapter);
 extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
-- 
1.7.9.5

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

* [PATCH 2/7] drm: make gamma_set optional
  2012-04-11 15:33 ` Sascha Hauer
@ 2012-04-11 15:33   ` Sascha Hauer
  -1 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/drm_crtc.c |    5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index b14496e..75f66a5 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -3108,6 +3108,11 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
 		goto out;
 	}
 
+	if (!crtc->funcs->gamma_set) {
+		ret = -ENOSYS;
+		goto out;
+	}
+
 	crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
 
 out:
-- 
1.7.9.5

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

* [PATCH 2/7] drm: make gamma_set optional
@ 2012-04-11 15:33   ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-arm-kernel, kernel

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/drm_crtc.c |    5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index b14496e..75f66a5 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -3108,6 +3108,11 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
 		goto out;
 	}
 
+	if (!crtc->funcs->gamma_set) {
+		ret = -ENOSYS;
+		goto out;
+	}
+
 	crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
 
 out:
-- 
1.7.9.5

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

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-11 15:33 ` Sascha Hauer
@ 2012-04-11 15:33   ` Sascha Hauer
  -1 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds support for creating simple drm devices. The
basic idea of this patch is that drm drivers using the sdrm layer
no longer have to implement a full drm device but instead only
register crtcs, encoders and connectors with the sdrm layer. The
sdrm layer then registers a drm device with the drm core and
takes care of the drm device.

Adding this layer has advantages especially for embedded devices.
On these type of devices there is no single device (pci card on
PCs) with which the drm core can be initialized, instead there
are multiple devices. The crtc usually is on the SoC, but
encoders and connectors are usually board specific, they can be
on the SoC, but can also be externally connected via i2c or can
be completely transparent to software. Also the components used
to compose a drm device vary from board to board.

While the components of a drm device are highly board specific
with embedded systems the the general interfacing with the drm
layer tends to be identical across most SoCs which involves a lot
of code duplication. By making the crtc/encoder/connector helpers
mandatory much of this duplication can be avoided.

This patch is based on the exynos drm driver with the exynos_
prefix replaced with a sdrm_ prefix.

The 's' in sdrm has no special meaning, depending on what you
think about it it can stand for 'Simple', 'Stupid', or 'Saschas'
drm.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/Kconfig           |    2 +
 drivers/gpu/drm/Makefile          |    1 +
 drivers/gpu/drm/sdrm/Kconfig      |    8 +
 drivers/gpu/drm/sdrm/Makefile     |    2 +
 drivers/gpu/drm/sdrm/sdrm.c       |  904 +++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sdrm/sdrm.h       |   57 +++
 drivers/gpu/drm/sdrm/sdrm_fb.c    |  191 ++++++++
 drivers/gpu/drm/sdrm/sdrm_fbdev.c |  238 ++++++++++
 drivers/gpu/drm/sdrm/sdrm_gem.c   |  342 ++++++++++++++
 include/drm/sdrm.h                |  102 +++++
 10 files changed, 1847 insertions(+)
 create mode 100644 drivers/gpu/drm/sdrm/Kconfig
 create mode 100644 drivers/gpu/drm/sdrm/Makefile
 create mode 100644 drivers/gpu/drm/sdrm/sdrm.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm.h
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_fb.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_fbdev.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_gem.c
 create mode 100644 include/drm/sdrm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index e354bc0..93d9f79 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/sdrm/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index c20da5b..44c5949 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_SDRM) += sdrm/
 obj-y			+= i2c/
diff --git a/drivers/gpu/drm/sdrm/Kconfig b/drivers/gpu/drm/sdrm/Kconfig
new file mode 100644
index 0000000..2424b2c
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/Kconfig
@@ -0,0 +1,8 @@
+config DRM_SDRM
+	tristate
+	depends on DRM
+	select DRM_KMS_HELPER
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+
diff --git a/drivers/gpu/drm/sdrm/Makefile b/drivers/gpu/drm/sdrm/Makefile
new file mode 100644
index 0000000..c603f1b
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/Makefile
@@ -0,0 +1,2 @@
+drm-sdrm-objs := sdrm.o sdrm_gem.o sdrm_fbdev.o sdrm_fb.o
+obj-$(CONFIG_DRM_SDRM) += drm-sdrm.o
diff --git a/drivers/gpu/drm/sdrm/sdrm.c b/drivers/gpu/drm/sdrm/sdrm.c
new file mode 100644
index 0000000..2077419
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm.c
@@ -0,0 +1,904 @@
+/*
+ * 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 <drm/sdrm.h>
+#include "sdrm.h"
+
+#define DRIVER_NAME		"drm-generic"
+#define DRIVER_DESC		"drm generic graphics"
+#define DRIVER_DATE		"20110604"
+#define DRIVER_MAJOR		1
+#define DRIVER_MINOR		0
+#define DRIVER_PATCHLEVEL	0
+
+struct sdrm_device {
+	struct drm_device		*drm;
+	struct platform_device		*pdev;
+	struct drm_fb_helper		*fb_helper;
+	struct list_head		crtc_list;
+	struct list_head		encoder_list;
+	struct list_head		connector_list;
+	struct list_head		list;
+	const char			*name;
+	int				registered;
+	struct mutex			mutex;
+	struct drm_driver		driver;
+};
+
+struct sdrm_crtc {
+	struct drm_crtc			*crtc;
+	struct list_head		list;
+	struct sdrm_device		*sdrm;
+	int				registered;
+	int				pipe;
+	struct drm_crtc_helper_funcs	crtc_helper_funcs;
+	struct drm_crtc_funcs		crtc_funcs;
+	struct sdrm_crtc_helper_funcs	sdrm_helper_funcs;
+};
+
+struct sdrm_encoder {
+	struct drm_encoder		*encoder;
+	struct list_head		list;
+	struct sdrm_device		*sdrm;
+	int				registered;
+	struct module			*owner;
+};
+
+struct sdrm_connector {
+	struct drm_connector		*connector;
+	struct list_head		list;
+	struct sdrm_device		*sdrm;
+	int				registered;
+	struct module			*owner;
+};
+
+static int sdrm_driver_open(struct drm_device *drm, struct drm_file *file)
+{
+	struct sdrm_device *sdrm = drm->dev_private;
+
+	if (!try_module_get(sdrm->pdev->dev.driver->owner)) {
+		dev_err(drm->dev, "could not get module %s\n",
+				module_name(sdrm->pdev->dev.driver->owner));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void sdrm_driver_lastclose(struct drm_device *drm)
+{
+	struct sdrm_device *sdrm = drm->dev_private;
+
+	module_put(sdrm->pdev->dev.driver->owner);
+
+	/*
+	 * This shouldn't be here. If multiple drm applications (i.e. two
+	 * xservers) are running and the active one crashes then we will
+	 * only restore fbdev mode when the other one exits aswell. Anyway,
+	 * this is what all other drivers so for now we do it aswell.
+	 */
+	drm_fb_helper_restore_fbdev_mode(sdrm->fb_helper);
+}
+
+static int sdrm_driver_unload(struct drm_device *drm)
+{
+	struct sdrm_device *sdrm = drm->dev_private;
+
+	sdrm_fbdev_fini(sdrm->fb_helper);
+	sdrm->fb_helper = NULL;
+
+	drm_mode_config_cleanup(sdrm->drm);
+	drm_kms_helper_poll_fini(sdrm->drm);
+
+	return 0;
+}
+
+static int sdrm_suspend(struct drm_device *drm, pm_message_t state)
+{
+	/* TODO */
+
+	return 0;
+}
+
+static int sdrm_resume(struct drm_device *drm)
+{
+	/* TODO */
+
+	return 0;
+}
+
+/*
+ * We don't care at all for crtc numbers, but the core expects the
+ * crtcs to be numbered
+ */
+static struct sdrm_crtc *sdrm_crtc_by_num(struct sdrm_device *sdrm, int num)
+{
+	struct sdrm_crtc *sdrm_crtc;
+
+	list_for_each_entry(sdrm_crtc, &sdrm->crtc_list, list)
+		if (sdrm_crtc->pipe == num)
+			return sdrm_crtc;
+	return NULL;
+}
+
+int sdrm_crtc_vblank_get(struct sdrm_crtc *sdrm_crtc)
+{
+	return drm_vblank_get(sdrm_crtc->sdrm->drm, sdrm_crtc->pipe);
+}
+EXPORT_SYMBOL_GPL(sdrm_crtc_vblank_get);
+
+void sdrm_crtc_vblank_put(struct sdrm_crtc *sdrm_crtc)
+{
+	drm_vblank_put(sdrm_crtc->sdrm->drm, sdrm_crtc->pipe);
+}
+EXPORT_SYMBOL_GPL(sdrm_crtc_vblank_put);
+
+void sdrm_handle_vblank(struct sdrm_crtc *sdrm_crtc)
+{
+	struct sdrm_device *sdrm = sdrm_crtc->sdrm;
+
+	if (sdrm->registered)
+		drm_handle_vblank(sdrm_crtc->sdrm->drm, sdrm_crtc->pipe);
+}
+EXPORT_SYMBOL_GPL(sdrm_handle_vblank);
+
+static int sdrm_enable_vblank(struct drm_device *drm, int crtc)
+{
+	struct sdrm_device *sdrm = drm->dev_private;
+	struct sdrm_crtc *sdrm_crtc;
+	int ret;
+
+	sdrm_crtc = sdrm_crtc_by_num(sdrm, crtc);
+	if (!sdrm_crtc)
+		return -EINVAL;
+
+	if (!sdrm_crtc->sdrm_helper_funcs.enable_vblank)
+		return -ENOSYS;
+
+	ret = sdrm_crtc->sdrm_helper_funcs.enable_vblank(sdrm_crtc->crtc);
+	return ret;
+}
+
+static void sdrm_disable_vblank(struct drm_device *drm, int crtc)
+{
+	struct sdrm_device *sdrm = drm->dev_private;
+	struct sdrm_crtc *sdrm_crtc;
+
+	sdrm_crtc = sdrm_crtc_by_num(sdrm, crtc);
+	if (!sdrm_crtc)
+		return;
+
+	if (!sdrm_crtc->sdrm_helper_funcs.disable_vblank)
+		return;
+
+	sdrm_crtc->sdrm_helper_funcs.disable_vblank(sdrm_crtc->crtc);
+}
+
+static struct vm_operations_struct sdrm_gem_vm_ops = {
+	.fault = sdrm_gem_fault,
+	.open = drm_gem_vm_open,
+	.close = drm_gem_vm_close,
+};
+
+static struct drm_ioctl_desc sdrm_ioctls[] = {
+	/* none so far */
+};
+
+static const struct file_operations sdrm_driver_fops = {
+	 .owner = THIS_MODULE,
+	 .open = drm_open,
+	 .release = drm_release,
+	 .unlocked_ioctl = drm_ioctl,
+	 .mmap = sdrm_gem_mmap,
+	 .poll = drm_poll,
+	 .fasync = drm_fasync,
+	 .read = drm_read,
+	 .llseek = noop_llseek,
+};
+
+static int sdrm_get_irq(struct drm_device *dev)
+{
+	/*
+	 * Return an arbitrary number to make the core happy.
+	 * We can't return anything meaningful here since drm
+	 * devices in general have multiple irqs
+	 */
+	return 1234;
+}
+
+static struct drm_bus drm_platform_bus = {
+	.bus_type = DRIVER_BUS_PLATFORM,
+	.get_irq = sdrm_get_irq,
+};
+
+static struct drm_driver sdrm_driver_template = {
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM,
+	.unload			= sdrm_driver_unload,
+	.open			= sdrm_driver_open,
+	.lastclose		= sdrm_driver_lastclose,
+	.gem_free_object	= sdrm_gem_free_object,
+	.gem_vm_ops		= &sdrm_gem_vm_ops,
+	.dumb_create		= sdrm_gem_dumb_create,
+	.dumb_map_offset	= sdrm_gem_dumb_map_offset,
+	.dumb_destroy		= sdrm_gem_dumb_destroy,
+
+	.suspend		= sdrm_suspend,
+	.resume			= sdrm_resume,
+
+	.get_vblank_counter	= drm_vblank_count,
+	.enable_vblank		= sdrm_enable_vblank,
+	.disable_vblank		= sdrm_disable_vblank,
+	.reclaim_buffers	= drm_core_reclaim_buffers,
+	.ioctls			= sdrm_ioctls,
+	.num_ioctls		= ARRAY_SIZE(sdrm_ioctls),
+	.bus			= &drm_platform_bus,
+	.fops			= &sdrm_driver_fops,
+	.name			= DRIVER_NAME,
+	.desc			= DRIVER_DESC,
+	.date			= DRIVER_DATE,
+	.major			= DRIVER_MAJOR,
+	.minor			= DRIVER_MINOR,
+	.patchlevel		= DRIVER_PATCHLEVEL,
+};
+
+static LIST_HEAD(sdrm_device_list);
+static DEFINE_MUTEX(sdrm_list_mutex);
+
+/*
+ * sdrm_device_get - find or allocate sdrm device with unique name
+ *
+ * This function returns the sdrm device with the unique name 'name'
+ * If this already exists, return it, otherwise allocate a new
+ * object.
+ */
+static struct sdrm_device *sdrm_device_get(const char *name)
+{
+	struct sdrm_device *sdrm;
+
+	mutex_lock(&sdrm_list_mutex);
+
+	list_for_each_entry(sdrm, &sdrm_device_list, list)
+		if (!strcmp(sdrm->name, name))
+			goto out;
+
+	mutex_unlock(&sdrm_list_mutex);
+
+	sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL);
+	if (!sdrm)
+		goto out;
+
+	mutex_init(&sdrm->mutex);
+	INIT_LIST_HEAD(&sdrm->crtc_list);
+	INIT_LIST_HEAD(&sdrm->connector_list);
+	INIT_LIST_HEAD(&sdrm->encoder_list);
+
+	sdrm->name = kstrdup(name, GFP_KERNEL);
+	if (!sdrm->name) {
+		kfree(sdrm);
+		goto out;
+	}
+
+	mutex_lock(&sdrm_list_mutex);
+
+	list_add_tail(&sdrm->list, &sdrm_device_list);
+out:
+	mutex_unlock(&sdrm_list_mutex);
+
+	return sdrm;
+}
+
+/*
+ * sdrm_device_maybe_release - free sdrm device if noone is using it
+ *
+ * If we have no crtcs, connectors and encoders registered for this
+ * device we can free it. Caller must hold sdrm->mutex.
+ */
+static void sdrm_device_maybe_release(struct sdrm_device *sdrm)
+{
+	if (!list_empty(&sdrm->crtc_list))
+		return;
+	if (!list_empty(&sdrm->connector_list))
+		return;
+	if (!list_empty(&sdrm->encoder_list))
+		return;
+
+	mutex_lock(&sdrm_list_mutex);
+	list_del(&sdrm->list);
+	mutex_unlock(&sdrm_list_mutex);
+
+	kfree(sdrm->name);
+	kfree(sdrm);
+}
+
+/*
+ * I think this legacy modegroup handling should be removed and we
+ * have a patch for this which also defines HAVE_MODEGROUP_REMOVED
+ * For now ifdef this here to make this work with and without this
+ * patch.
+ */
+#ifndef HAVE_MODEGROUP_REMOVED
+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;
+}
+#else
+static int drm_mode_group_reinit(struct drm_device *dev)
+{
+	return 0;
+}
+#endif
+
+/*
+ * register an encoder to the drm core
+ */
+static int sdrm_encoder_register(struct sdrm_encoder *sdrm_encoder)
+{
+	struct sdrm_device *sdrm = sdrm_encoder->sdrm;
+
+	if (!try_module_get(sdrm_encoder->owner))
+		return -ENODEV;
+
+	drm_encoder_init(sdrm->drm, sdrm_encoder->encoder,
+			sdrm_encoder->encoder->funcs,
+			DRM_MODE_ENCODER_TMDS);
+
+	drm_mode_group_reinit(sdrm->drm);
+
+	sdrm_encoder->registered = 1;
+
+	return 0;
+}
+
+/*
+ * unregister an encoder from the drm core
+ */
+static void sdrm_encoder_unregister(struct sdrm_encoder *sdrm_encoder)
+{
+	struct sdrm_device *sdrm = sdrm_encoder->sdrm;
+
+	if (!sdrm_encoder->registered)
+		return;
+
+	if (sdrm->registered)
+		drm_encoder_cleanup(sdrm_encoder->encoder);
+
+	drm_mode_group_reinit(sdrm->drm);
+
+	sdrm_encoder->registered = 0;
+
+	module_put(sdrm_encoder->owner);
+}
+
+/*
+ * register a connector to the drm core
+ */
+static int sdrm_connector_register(struct sdrm_connector *sdrm_connector)
+{
+	struct sdrm_device *sdrm = sdrm_connector->sdrm;
+	int ret;
+
+	if (!try_module_get(sdrm_connector->owner))
+		return -ENODEV;
+
+	drm_connector_init(sdrm->drm, sdrm_connector->connector,
+			sdrm_connector->connector->funcs,
+			DRM_MODE_CONNECTOR_VGA);
+	drm_mode_group_reinit(sdrm->drm);
+	ret = drm_sysfs_connector_add(sdrm_connector->connector);
+	if (ret)
+		goto err;
+
+	sdrm_connector->registered = 1;
+
+	return 0;
+err:
+	module_put(sdrm_connector->owner);
+
+	return ret;
+}
+
+/*
+ * unregister a connector from the drm core
+ */
+static void sdrm_connector_unregister(struct sdrm_connector *sdrm_connector)
+{
+	struct sdrm_device *sdrm = sdrm_connector->sdrm;
+
+	if (!sdrm_connector->registered)
+		return;
+
+	if (sdrm->registered) {
+		drm_sysfs_connector_remove(sdrm_connector->connector);
+		drm_connector_cleanup(sdrm_connector->connector);
+	}
+
+	drm_mode_group_reinit(sdrm->drm);
+
+	sdrm_connector->registered = 0;
+
+	module_put(sdrm_connector->owner);
+}
+
+/*
+ * register a crtc to the drm core
+ */
+static int sdrm_crtc_register(struct sdrm_crtc *sdrm_crtc)
+{
+	struct sdrm_device *sdrm = sdrm_crtc->sdrm;
+	int ret;
+
+	drm_crtc_init(sdrm->drm, sdrm_crtc->crtc, &sdrm_crtc->crtc_funcs);
+	ret = drm_mode_crtc_set_gamma_size(sdrm_crtc->crtc, 256);
+	if (ret)
+		return ret;
+
+	drm_crtc_helper_add(sdrm_crtc->crtc, &sdrm_crtc->crtc_helper_funcs);
+
+	sdrm_crtc->registered = 1;
+
+	return 0;
+}
+
+static void sdrm_crtc_unregister(struct sdrm_crtc *sdrm_crtc)
+{
+	/*
+	 * The hard work has already been done in driver unload
+	 */
+	sdrm_crtc->registered = 0;
+}
+
+/*
+ * sdrm_uninit - unitialize all crtcs, encoders, connectors
+ *
+ * This is called in case initialization fails or as a cleanup
+ * after unitializing a drm device. Caller must hold sdrm->mutex
+ */
+static void sdrm_uninit(struct sdrm_device *sdrm)
+{
+	struct sdrm_crtc *sdrm_crtc;
+	struct sdrm_encoder *sdrm_encoder;
+	struct sdrm_connector *sdrm_connector;
+
+	list_for_each_entry(sdrm_crtc, &sdrm->crtc_list, list)
+		sdrm_crtc_unregister(sdrm_crtc);
+
+	list_for_each_entry(sdrm_connector, &sdrm->connector_list, list)
+		sdrm_connector_unregister(sdrm_connector);
+
+	list_for_each_entry(sdrm_encoder, &sdrm->encoder_list, list)
+		sdrm_encoder_unregister(sdrm_encoder);
+
+	sdrm->registered = 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.
+ */
+int sdrm_init_drm(const char *name, struct platform_device *pdev,
+		int preferred_bpp)
+{
+	struct sdrm_device *sdrm;
+	struct drm_device *drm;
+	struct sdrm_crtc *sdrm_crtc;
+	struct sdrm_encoder *sdrm_encoder;
+	struct sdrm_connector *sdrm_connector;
+	int ret;
+	int num_crtcs = 0;
+
+	sdrm = sdrm_device_get(name);
+	if (!sdrm)
+		return -ENOMEM;
+
+	drm = kzalloc(sizeof(struct drm_device), GFP_KERNEL);
+	if (!drm)
+		return -ENOMEM;
+
+	sdrm->drm = drm;
+
+	memcpy(&sdrm->driver, &sdrm_driver_template,
+			sizeof(sdrm_driver_template));
+
+	drm->dev_private = sdrm;
+
+	ret = drm_fill_in_dev(drm, NULL, &sdrm->driver);
+	if (ret) {
+		printk(KERN_ERR "DRM: Fill_in_dev failed.\n");
+		goto err_fill_in;
+	}
+
+	/*
+	 * 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 spsdrmific driver own one instead bsdrmause
+	 *      drm framework supports only one irq handler and
+	 *      drivers can well take care of their interrupts
+	 */
+	drm->irq_enabled = 1;
+
+	mutex_lock(&drm_global_mutex);
+
+	ret = drm_get_minor(drm, &drm->control, DRM_MINOR_CONTROL);
+	if (ret)
+		goto err_get_minor1;
+
+	ret = drm_get_minor(drm, &drm->primary, DRM_MINOR_LEGACY);
+	if (ret)
+		goto err_get_minor2;
+
+	mutex_unlock(&drm_global_mutex);
+
+	drm_mode_config_init(drm);
+	sdrm_mode_config_init(drm);
+
+	mutex_lock(&sdrm->mutex);
+
+	/* we need at least one crtc */
+	if (list_empty(&sdrm->crtc_list)) {
+		ret = -EINVAL;
+		goto err_init;
+	}
+
+	/* Register the subdevices we have so far */
+
+	list_for_each_entry(sdrm_encoder, &sdrm->encoder_list, list) {
+		ret = sdrm_encoder_register(sdrm_encoder);
+		if (ret)
+			goto err_init;
+	}
+
+	list_for_each_entry(sdrm_connector, &sdrm->connector_list, list) {
+		ret = sdrm_connector_register(sdrm_connector);
+		if (ret)
+			goto err_init;
+	}
+
+	list_for_each_entry(sdrm_crtc, &sdrm->crtc_list, list) {
+		sdrm_crtc->pipe = num_crtcs;
+		num_crtcs++;
+		ret = sdrm_crtc_register(sdrm_crtc);
+		if (ret)
+			goto err_init;
+	}
+
+	sdrm->pdev = pdev;
+	sdrm->drm->platformdev = pdev;
+	sdrm->drm->dev = &pdev->dev;
+
+	sdrm->fb_helper = sdrm_fbdev_init(sdrm->drm, preferred_bpp);
+	if (!sdrm->fb_helper) {
+		printk("sdrm_fbdev_init failed\n");
+		ret = -ENOMEM;
+		goto err_init;
+	}
+
+	drm_kms_helper_poll_init(sdrm->drm);
+
+#ifndef HAVE_MODEGROUP_REMOVED
+	mutex_lock(&drm_global_mutex);
+
+	/* setup the grouping for the legacy output */
+	ret = drm_mode_group_init_legacy_group(sdrm->drm,
+			&sdrm->drm->primary->mode_group);
+	mutex_unlock(&drm_global_mutex);
+
+	if (ret)
+		return ret;
+#endif
+	ret = drm_vblank_init(sdrm->drm, num_crtcs);
+	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)
+	 */
+	sdrm->drm->vblank_disable_allowed = 1;
+
+	sdrm->registered = 1;
+
+	mutex_unlock(&sdrm->mutex);
+
+	return 0;
+
+err_init:
+	sdrm_uninit(sdrm);
+	mutex_unlock(&sdrm->mutex);
+err_get_minor2:
+	drm_put_minor(&drm->control);
+err_get_minor1:
+err_fill_in:
+	mutex_unlock(&drm_global_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sdrm_init_drm);
+
+/*
+ * Called by the CRTC driver to uninitialize the driver.
+ */
+int sdrm_exit_drm(const char *name)
+{
+	struct sdrm_device *sdrm;
+
+	sdrm = sdrm_device_get(name);
+	if (!sdrm)
+		return -ENODEV;
+
+	sdrm_uninit(sdrm);
+	/*
+	 * The drm core never does anything with this list, yet it removes
+	 * this entry, so initialize it
+	 */
+	INIT_LIST_HEAD(&sdrm->drm->driver_item);
+	drm_put_dev(sdrm->drm);
+
+	sdrm->drm = NULL;
+	sdrm->registered = 0;
+	sdrm->pdev = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdrm_exit_drm);
+
+/*
+ * sdrm_add_crtc - add a new crtc
+ *
+ * The return value if !NULL is a cookie for the caller to pass to
+ * sdrm_remove_crtc later.
+ */
+struct sdrm_crtc *sdrm_add_crtc(const char *name, struct drm_crtc *crtc,
+		const struct drm_crtc_funcs *crtc_funcs,
+		const struct drm_crtc_helper_funcs *crtc_helper_funcs,
+		const struct sdrm_crtc_helper_funcs *sdrm_helper_funcs)
+{
+	struct sdrm_device *sdrm;
+	struct sdrm_crtc *sdrm_crtc;
+
+	sdrm = sdrm_device_get(name);
+	if (!sdrm)
+		return NULL; /* -ENOMEM */
+
+	if (sdrm->registered)
+		return NULL; /* -EBUSY */
+
+	sdrm_crtc = kzalloc(sizeof(*sdrm_crtc), GFP_KERNEL);
+	if (!sdrm_crtc)
+		return NULL; /* -ENOMEM */
+
+	sdrm_crtc->crtc_funcs = *crtc_funcs;
+	sdrm_crtc->crtc_helper_funcs = *crtc_helper_funcs;
+	sdrm_crtc->sdrm_helper_funcs = *sdrm_helper_funcs;
+
+	WARN_ON(crtc_funcs->set_config);
+	WARN_ON(crtc_funcs->destroy);
+
+	sdrm_crtc->crtc_funcs.set_config = drm_crtc_helper_set_config;
+	sdrm_crtc->crtc_funcs.destroy = drm_crtc_cleanup;
+
+	sdrm_crtc->crtc = crtc;
+	sdrm_crtc->sdrm = sdrm;
+
+	mutex_lock(&sdrm->mutex);
+
+	list_add_tail(&sdrm_crtc->list, &sdrm->crtc_list);
+
+	mutex_unlock(&sdrm->mutex);
+
+	return sdrm_crtc;
+}
+EXPORT_SYMBOL_GPL(sdrm_add_crtc);
+
+/*
+ * sdrm_remove_crtc - remove a crtc
+ *
+ * Can only be called on inactive drm devices since the drm
+ * core can't handle dynamic crtcs
+ */
+int sdrm_remove_crtc(struct sdrm_crtc *sdrm_crtc)
+{
+	struct sdrm_device *sdrm = sdrm_crtc->sdrm;
+
+	if (sdrm->registered)
+		return -EBUSY;
+
+	mutex_lock(&sdrm->mutex);
+
+	sdrm_crtc_unregister(sdrm_crtc);
+
+	list_del(&sdrm_crtc->list);
+
+	kfree(sdrm_crtc);
+
+	sdrm_device_maybe_release(sdrm);
+
+	mutex_unlock(&sdrm->mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdrm_remove_crtc);
+
+/*
+ * sdrm_add_encoder - add a new encoder
+ *
+ * The return value if !NULL is a cookie for the caller to pass to
+ * sdrm_remove_crtc later.
+ *
+ * Can be called on both active and inactive devices.
+ */
+struct sdrm_encoder *sdrm_add_encoder(const char *name,
+		struct drm_encoder *encoder, struct module *owner)
+{
+	struct sdrm_device *sdrm;
+	struct sdrm_encoder *sdrm_encoder;
+	int ret;
+
+	sdrm = sdrm_device_get(name);
+	if (!sdrm)
+		return NULL;
+
+	sdrm_encoder = kzalloc(sizeof(struct sdrm_encoder), GFP_KERNEL);
+	if (!sdrm_encoder)
+		return NULL;
+	sdrm_encoder->encoder = encoder;
+	sdrm_encoder->sdrm = sdrm;
+	sdrm_encoder->owner = owner;
+
+	mutex_lock(&sdrm->mutex);
+
+	if (sdrm->registered) {
+		/*
+		 * If the drm device is up register the encoder,
+		 * otherwise it will be done in sdrm_init_drm()
+		 */
+		ret = sdrm_encoder_register(sdrm_encoder);
+		if (ret) {
+			kfree(sdrm_encoder);
+			goto err;
+		}
+	}
+
+	list_add_tail(&sdrm_encoder->list, &sdrm->encoder_list);
+err:
+	mutex_unlock(&sdrm->mutex);
+
+	return sdrm_encoder;
+}
+EXPORT_SYMBOL_GPL(sdrm_add_encoder);
+
+/*
+ * sdrm_remove_encoder - remove an encoder
+ *
+ * Can be called on both active and inactive devices.
+ */
+int sdrm_remove_encoder(struct sdrm_encoder *sdrm_encoder)
+{
+	struct sdrm_device *sdrm = sdrm_encoder->sdrm;
+
+	mutex_lock(&sdrm->mutex);
+
+	sdrm_encoder_unregister(sdrm_encoder);
+
+	list_del(&sdrm_encoder->list);
+
+	kfree(sdrm_encoder);
+
+	sdrm_device_maybe_release(sdrm);
+
+	mutex_unlock(&sdrm->mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdrm_remove_encoder);
+
+/*
+ * sdrm_add_connector - add a connector
+ *
+ * Can be called on both active and inactive devices.
+ */
+struct sdrm_connector *sdrm_add_connector(const char *name,
+		struct drm_connector *connector, struct module *owner)
+{
+	struct sdrm_device *sdrm;
+	struct sdrm_connector *sdrm_connector;
+	int ret;
+
+	sdrm = sdrm_device_get(name);
+	if (!sdrm)
+		return NULL;
+
+	sdrm_connector = kzalloc(sizeof(struct sdrm_connector), GFP_KERNEL);
+	if (!sdrm_connector)
+		return NULL;
+
+	sdrm_connector->connector = connector;
+	sdrm_connector->sdrm = sdrm;
+	sdrm_connector->owner = owner;
+
+	mutex_lock(&sdrm->mutex);
+
+	if (sdrm->registered) {
+		/*
+		 * If the drm device is up register the connector,
+		 * otherwise it will be done in sdrm_init_drm()
+		 */
+		ret = sdrm_connector_register(sdrm_connector);
+		if (ret) {
+			kfree(sdrm_connector);
+			sdrm_connector = NULL;
+			goto err;
+		}
+	}
+
+	list_add_tail(&sdrm_connector->list, &sdrm->connector_list);
+err:
+	mutex_unlock(&sdrm->mutex);
+
+	return sdrm_connector;
+}
+EXPORT_SYMBOL_GPL(sdrm_add_connector);
+
+/*
+ * sdrm_remove_connector - remove a connector
+ *
+ * Can be called on both active and inactive devices.
+ */
+int sdrm_remove_connector(struct sdrm_connector *sdrm_connector)
+{
+	struct sdrm_device *sdrm = sdrm_connector->sdrm;
+
+	mutex_lock(&sdrm->mutex);
+
+	sdrm_connector_unregister(sdrm_connector);
+
+	list_del(&sdrm_connector->list);
+
+	kfree(sdrm_connector);
+
+	sdrm_device_maybe_release(sdrm);
+
+	mutex_unlock(&sdrm->mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdrm_remove_connector);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sdrm/sdrm.h b/drivers/gpu/drm/sdrm/sdrm.h
new file mode 100644
index 0000000..e5d3a63
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm.h
@@ -0,0 +1,57 @@
+#ifndef _SDRM_H_
+#define _SDRM_H_
+
+void sdrm_mode_config_init(struct drm_device *drm);
+
+struct drm_fb_helper *sdrm_fbdev_init(struct drm_device *drm,
+		int preferred_bpp);
+void sdrm_fbdev_fini(struct drm_fb_helper *);
+
+#define to_sdrm_gem_obj(x)	container_of(x,\
+			struct sdrm_gem_obj, base)
+
+struct sdrm_gem_obj {
+	struct drm_gem_object base;
+	struct sdrm_buf_entry *entry;
+};
+
+/* unmap a buffer from user space. */
+int sdrm_gem_munmap_ioctl(struct drm_device *drm, void *data,
+		struct drm_file *file_priv);
+
+/* initialize gem object. */
+int sdrm_gem_init_object(struct drm_gem_object *obj);
+
+/* free gem object. */
+void sdrm_gem_free_object(struct drm_gem_object *gem_obj);
+
+/* create memory region for drm framebuffer. */
+int sdrm_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 sdrm_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 sdrm_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 sdrm_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 sdrm_gem_dumb_destroy(struct drm_file *file_priv,
+		struct drm_device *drm, unsigned int handle);
+
+/* allocate physical memory. */
+struct sdrm_buf_entry *sdrm_buf_create(struct drm_device *drm,
+		unsigned int size);
+
+/* remove allocated physical memory. */
+void sdrm_buf_destroy(struct drm_device *drm, struct sdrm_buf_entry *entry);
+
+#endif /* _SDRM_H_ */
diff --git a/drivers/gpu/drm/sdrm/sdrm_fb.c b/drivers/gpu/drm/sdrm/sdrm_fb.c
new file mode 100644
index 0000000..7f9c69d
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm_fb.c
@@ -0,0 +1,191 @@
+/*
+ * simple drm driver
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix
+ *
+ * Based on Samsung Exynos code
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/sdrm.h>
+
+#include "sdrm.h"
+
+#define to_sdrm_fb(x)	container_of(x, struct sdrm_fb, fb)
+
+/*
+ * sdrm specific framebuffer structure.
+ *
+ * @fb: drm framebuffer obejct.
+ * @sdrm_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 sdrm_fb {
+	struct drm_framebuffer		fb;
+	struct sdrm_gem_obj	*sdrm_gem_obj;
+	struct sdrm_buf_entry	*entry;
+};
+
+static void sdrm_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct sdrm_fb *sdrm_fb = to_sdrm_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 (!sdrm_fb->sdrm_gem_obj && sdrm_fb->entry)
+		sdrm_buf_destroy(fb->dev, sdrm_fb->entry);
+
+	kfree(sdrm_fb);
+}
+
+static int sdrm_fb_create_handle(struct drm_framebuffer *fb,
+		struct drm_file *file_priv, unsigned int *handle)
+{
+	struct sdrm_fb *sdrm_fb = to_sdrm_fb(fb);
+
+	return drm_gem_handle_create(file_priv,
+			&sdrm_fb->sdrm_gem_obj->base, handle);
+}
+
+static int sdrm_fb_dirty(struct drm_framebuffer *fb,
+		struct drm_file *file_priv, unsigned flags,
+		unsigned color, struct drm_clip_rect *clips,
+		unsigned num_clips)
+{
+	/* TODO */
+
+	return 0;
+}
+
+static struct drm_framebuffer_funcs sdrm_fb_funcs = {
+	.destroy	= sdrm_fb_destroy,
+	.create_handle	= sdrm_fb_create_handle,
+	.dirty		= sdrm_fb_dirty,
+};
+
+static struct drm_framebuffer *sdrm_fb_create(struct drm_device *dev,
+		struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	struct sdrm_fb *sdrm_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);
+
+	sdrm_fb = kzalloc(sizeof(*sdrm_fb), GFP_KERNEL);
+	if (!sdrm_fb) {
+		dev_err(dev->dev, "failed to allocate drm framebuffer.\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	fb = &sdrm_fb->fb;
+	ret = drm_framebuffer_init(dev, fb, &sdrm_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 sdrm_fbdev_create in which
+	 * case we only create a framebuffer without a handle.
+	 */
+	if (!file_priv) {
+		struct sdrm_buf_entry *entry;
+
+		entry = sdrm_buf_create(dev, size);
+		if (IS_ERR(entry)) {
+			ret = PTR_ERR(entry);
+			goto err_buffer;
+		}
+
+		sdrm_fb->entry = entry;
+	} else {
+		obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
+		if (!obj) {
+			ret = -EINVAL;
+			goto err_buffer;
+		}
+
+		sdrm_fb->sdrm_gem_obj = to_sdrm_gem_obj(obj);
+
+		drm_gem_object_unreference_unlocked(obj);
+
+		sdrm_fb->entry = sdrm_fb->sdrm_gem_obj->entry;
+	}
+
+	drm_helper_mode_fill_fb_struct(fb, mode_cmd);
+
+	return fb;
+
+err_buffer:
+	drm_framebuffer_cleanup(fb);
+
+err_init:
+	kfree(sdrm_fb);
+
+	return ERR_PTR(ret);
+}
+
+struct sdrm_buf_entry *sdrm_fb_get_buf(struct drm_framebuffer *fb)
+{
+	struct sdrm_fb *sdrm_fb = to_sdrm_fb(fb);
+	struct sdrm_buf_entry *entry;
+
+	entry = sdrm_fb->entry;
+
+	return entry;
+}
+EXPORT_SYMBOL_GPL(sdrm_fb_get_buf);
+
+static struct drm_mode_config_funcs sdrm_mode_config_funcs = {
+	.fb_create = sdrm_fb_create,
+};
+
+void sdrm_mode_config_init(struct drm_device *dev)
+{
+	dev->mode_config.min_width = 0;
+	dev->mode_config.min_height = 0;
+
+	/*
+	 * 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 = &sdrm_mode_config_funcs;
+}
diff --git a/drivers/gpu/drm/sdrm/sdrm_fbdev.c b/drivers/gpu/drm/sdrm/sdrm_fbdev.c
new file mode 100644
index 0000000..8f3e3e0
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm_fbdev.c
@@ -0,0 +1,238 @@
+/*
+ * simple drm driver
+ *
+ * Copyright (C) 2011 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 <drm/sdrm.h>
+
+#include "sdrm.h"
+
+#define MAX_CONNECTOR		4
+#define PREFERRED_BPP		16
+
+static struct fb_ops sdrm_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 sdrm_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 sdrm_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 = sdrm_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 sdrm_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 = &sdrm_fb_ops;
+
+	ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+	if (ret)
+		goto err_alloc_cmap;
+
+	ret = sdrm_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 sdrm_fbdev_probe(struct drm_fb_helper *helper,
+				   struct drm_fb_helper_surface_size *sizes)
+{
+	int ret;
+
+	BUG_ON(helper->fb);
+
+	ret = sdrm_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 sdrm_fb_helper_funcs = {
+	.fb_probe = sdrm_fbdev_probe,
+};
+
+struct drm_fb_helper *sdrm_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 = &sdrm_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;
+}
+
+void sdrm_fbdev_fini(struct drm_fb_helper *helper)
+{
+	/* release linux framebuffer */
+	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);
+
+		framebuffer_release(info);
+	}
+
+	drm_fb_helper_fini(helper);
+
+	kfree(helper);
+}
diff --git a/drivers/gpu/drm/sdrm/sdrm_gem.c b/drivers/gpu/drm/sdrm/sdrm_gem.c
new file mode 100644
index 0000000..4f98627
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm_gem.c
@@ -0,0 +1,342 @@
+/*
+ * simple drm driver
+ *
+ * Copyright (C) 2011 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/sdrm.h>
+
+#include "sdrm.h"
+
+static int lowlevel_buffer_allocate(struct drm_device *drm,
+		struct sdrm_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 sdrm_buf_entry *entry)
+{
+	dma_free_writecombine(drm->dev, entry->size, entry->vaddr,
+			entry->paddr);
+}
+
+struct sdrm_buf_entry *sdrm_buf_create(struct drm_device *drm,
+		unsigned int size)
+{
+	struct sdrm_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 sdrm_buf_destroy(struct drm_device *drm,
+		struct sdrm_buf_entry *entry)
+{
+	lowlevel_buffer_free(drm, entry);
+
+	kfree(entry);
+	entry = NULL;
+}
+
+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 sdrm_gem_obj *sdrm_gem_create(struct drm_device *drm,
+		unsigned int size)
+{
+	struct sdrm_gem_obj *sdrm_gem_obj;
+	struct sdrm_buf_entry *entry;
+	struct drm_gem_object *obj;
+	int ret;
+
+	size = roundup(size, PAGE_SIZE);
+
+	sdrm_gem_obj = kzalloc(sizeof(*sdrm_gem_obj), GFP_KERNEL);
+	if (!sdrm_gem_obj)
+		return ERR_PTR(-ENOMEM);
+
+	/* allocate the new buffer object and memory region. */
+	entry = sdrm_buf_create(drm, size);
+	if (!entry) {
+		ret = -ENOMEM;
+		goto err_alloc;
+	}
+
+	sdrm_gem_obj->entry = entry;
+
+	obj = &sdrm_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 sdrm_gem_obj;
+
+err_create_mmap_offset:
+	drm_gem_object_release(obj);
+
+err_obj_init:
+	sdrm_buf_destroy(drm, sdrm_gem_obj->entry);
+
+err_alloc:
+	kfree(sdrm_gem_obj);
+
+	return ERR_PTR(ret);
+}
+
+static struct sdrm_gem_obj *sdrm_gem_create_with_handle(struct drm_file *file_priv,
+		struct drm_device *drm, unsigned int size,
+		unsigned int *handle)
+{
+	struct sdrm_gem_obj *sdrm_gem_obj;
+	struct drm_gem_object *obj;
+	int ret;
+
+	sdrm_gem_obj = sdrm_gem_create(drm, size);
+	if (IS_ERR(sdrm_gem_obj))
+		return sdrm_gem_obj;
+
+	obj = &sdrm_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 sdrm_gem_obj;
+
+err_handle_create:
+	sdrm_gem_free_object(obj);
+
+	return ERR_PTR(ret);
+}
+
+static int sdrm_gem_mmap_buffer(struct file *filp,
+		struct vm_area_struct *vma)
+{
+	struct drm_gem_object *obj = filp->private_data;
+	struct sdrm_gem_obj *sdrm_gem_obj = to_sdrm_gem_obj(obj);
+	struct sdrm_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 = sdrm_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 = sdrm_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 sdrm_gem_fops = {
+	.mmap = sdrm_gem_mmap_buffer,
+};
+
+int sdrm_gem_init_object(struct drm_gem_object *obj)
+{
+	return 0;
+}
+
+void sdrm_gem_free_object(struct drm_gem_object *gem_obj)
+{
+	struct sdrm_gem_obj *sdrm_gem_obj;
+
+	if (gem_obj->map_list.map)
+		drm_gem_free_mmap_offset(gem_obj);
+
+	drm_gem_object_release(gem_obj);
+
+	sdrm_gem_obj = to_sdrm_gem_obj(gem_obj);
+
+	sdrm_buf_destroy(gem_obj->dev, sdrm_gem_obj->entry);
+
+	kfree(sdrm_gem_obj);
+}
+
+int sdrm_gem_dumb_create(struct drm_file *file_priv,
+		struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+	struct sdrm_gem_obj *sdrm_gem_obj;
+
+	/* FIXME: This should be configured by the crtc driver */
+	args->pitch = args->width * args->bpp >> 3;
+	args->size = args->pitch * args->height;
+
+	sdrm_gem_obj = sdrm_gem_create_with_handle(file_priv, dev, args->size,
+			&args->handle);
+	if (IS_ERR(sdrm_gem_obj))
+		return PTR_ERR(sdrm_gem_obj);
+
+	return 0;
+}
+
+int sdrm_gem_dumb_map_offset(struct drm_file *file_priv,
+		struct drm_device *drm, uint32_t handle, uint64_t *offset)
+{
+	struct sdrm_gem_obj *sdrm_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;
+	}
+
+	sdrm_gem_obj = to_sdrm_gem_obj(obj);
+
+	*offset = get_gem_mmap_offset(&sdrm_gem_obj->base);
+
+	drm_gem_object_unreference(obj);
+
+	mutex_unlock(&drm->struct_mutex);
+
+	return 0;
+}
+
+int sdrm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	struct drm_gem_object *obj = vma->vm_private_data;
+	struct sdrm_gem_obj *sdrm_gem_obj = to_sdrm_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 = (sdrm_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 sdrm_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 sdrm_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/include/drm/sdrm.h b/include/drm/sdrm.h
new file mode 100644
index 0000000..ad4f5f6
--- /dev/null
+++ b/include/drm/sdrm.h
@@ -0,0 +1,102 @@
+#ifndef __DRM_SDRM_H
+#define __DRM_SDRM_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.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 sdrm_gem_create {
+	unsigned int size;
+	unsigned int flags;
+	unsigned int handle;
+};
+
+/**
+ * A structure for getting buffer offset.
+ *
+ * @handle: a pointer to gem object created.
+ * @pad: just padding to be 64-bit aligned.
+ * @offset: relatived offset value of the memory region allocated.
+ *	- this value should be set by user.
+ */
+struct sdrm_gem_map_off {
+	unsigned int handle;
+	unsigned int pad;
+	uint64_t offset;
+};
+
+/**
+ * A structure for mapping buffer.
+ *
+ * @handle: a handle to gem object created.
+ * @size: memory size to be mapped.
+ * @mapped: having user virtual address mmaped.
+ *	- this variable would be filled by exynos gem module
+ *	of kernel side with user virtual address which is allocated
+ *	by do_mmap().
+ */
+struct sdrm_gem_mmap {
+	unsigned int handle;
+	unsigned int size;
+	uint64_t mapped;
+};
+
+struct sdrm_device;
+struct sdrm_crtc;
+
+struct sdrm_crtc_helper_funcs {
+	int (*enable_vblank)(struct drm_crtc *crtc);
+	void (*disable_vblank)(struct drm_crtc *crtc);
+};
+
+struct sdrm_crtc *sdrm_add_crtc(const char *name, struct drm_crtc *crtc,
+		const struct drm_crtc_funcs *crtc_funcs,
+		const struct drm_crtc_helper_funcs *crtc_helper_funcs,
+		const struct sdrm_crtc_helper_funcs *ec_helper_funcs);
+int sdrm_remove_crtc(struct sdrm_crtc *);
+int sdrm_init_drm(const char *name, struct platform_device *pdev,
+		int preferred_bpp);
+int sdrm_exit_drm(const char *name);
+
+int sdrm_crtc_vblank_get(struct sdrm_crtc *sdrm_crtc);
+void sdrm_crtc_vblank_put(struct sdrm_crtc *sdrm_crtc);
+void sdrm_handle_vblank(struct sdrm_crtc *sdrm_crtc);
+
+/*
+ * sdrm drm buffer entry structure.
+ *
+ * @paddr: physical address of allocated memory.
+ * @vaddr: kernel virtual address of allocated memory.
+ * @size: size of allocated memory.
+ */
+struct sdrm_buf_entry {
+	dma_addr_t paddr;
+	void __iomem *vaddr;
+	unsigned int size;
+
+	dma_addr_t r_paddr;
+	void __iomem *r_vaddr;
+	unsigned int r_size;
+};
+
+/* get physical memory information of a drm framebuffer. */
+struct sdrm_buf_entry *sdrm_fb_get_buf(struct drm_framebuffer *fb);
+
+struct sdrm_encoder;
+struct sdrm_encoder *sdrm_add_encoder(const char *drmname,
+		struct drm_encoder *encoder, struct module *owner);
+int sdrm_remove_encoder(struct sdrm_encoder *);
+
+struct sdrm_connector;
+struct sdrm_connector *sdrm_add_connector(const char *drmname,
+		struct drm_connector *connector, struct module *owner);
+int sdrm_remove_connector(struct sdrm_connector *);
+
+#endif /* __DRM_SDRM_H */
-- 
1.7.9.5

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

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-11 15:33   ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: dri-devel; +Cc: Sascha Hauer, linux-arm-kernel, kernel

This patch adds support for creating simple drm devices. The
basic idea of this patch is that drm drivers using the sdrm layer
no longer have to implement a full drm device but instead only
register crtcs, encoders and connectors with the sdrm layer. The
sdrm layer then registers a drm device with the drm core and
takes care of the drm device.

Adding this layer has advantages especially for embedded devices.
On these type of devices there is no single device (pci card on
PCs) with which the drm core can be initialized, instead there
are multiple devices. The crtc usually is on the SoC, but
encoders and connectors are usually board specific, they can be
on the SoC, but can also be externally connected via i2c or can
be completely transparent to software. Also the components used
to compose a drm device vary from board to board.

While the components of a drm device are highly board specific
with embedded systems the the general interfacing with the drm
layer tends to be identical across most SoCs which involves a lot
of code duplication. By making the crtc/encoder/connector helpers
mandatory much of this duplication can be avoided.

This patch is based on the exynos drm driver with the exynos_
prefix replaced with a sdrm_ prefix.

The 's' in sdrm has no special meaning, depending on what you
think about it it can stand for 'Simple', 'Stupid', or 'Saschas'
drm.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/Kconfig           |    2 +
 drivers/gpu/drm/Makefile          |    1 +
 drivers/gpu/drm/sdrm/Kconfig      |    8 +
 drivers/gpu/drm/sdrm/Makefile     |    2 +
 drivers/gpu/drm/sdrm/sdrm.c       |  904 +++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sdrm/sdrm.h       |   57 +++
 drivers/gpu/drm/sdrm/sdrm_fb.c    |  191 ++++++++
 drivers/gpu/drm/sdrm/sdrm_fbdev.c |  238 ++++++++++
 drivers/gpu/drm/sdrm/sdrm_gem.c   |  342 ++++++++++++++
 include/drm/sdrm.h                |  102 +++++
 10 files changed, 1847 insertions(+)
 create mode 100644 drivers/gpu/drm/sdrm/Kconfig
 create mode 100644 drivers/gpu/drm/sdrm/Makefile
 create mode 100644 drivers/gpu/drm/sdrm/sdrm.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm.h
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_fb.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_fbdev.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_gem.c
 create mode 100644 include/drm/sdrm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index e354bc0..93d9f79 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/sdrm/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index c20da5b..44c5949 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_SDRM) += sdrm/
 obj-y			+= i2c/
diff --git a/drivers/gpu/drm/sdrm/Kconfig b/drivers/gpu/drm/sdrm/Kconfig
new file mode 100644
index 0000000..2424b2c
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/Kconfig
@@ -0,0 +1,8 @@
+config DRM_SDRM
+	tristate
+	depends on DRM
+	select DRM_KMS_HELPER
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+
diff --git a/drivers/gpu/drm/sdrm/Makefile b/drivers/gpu/drm/sdrm/Makefile
new file mode 100644
index 0000000..c603f1b
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/Makefile
@@ -0,0 +1,2 @@
+drm-sdrm-objs := sdrm.o sdrm_gem.o sdrm_fbdev.o sdrm_fb.o
+obj-$(CONFIG_DRM_SDRM) += drm-sdrm.o
diff --git a/drivers/gpu/drm/sdrm/sdrm.c b/drivers/gpu/drm/sdrm/sdrm.c
new file mode 100644
index 0000000..2077419
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm.c
@@ -0,0 +1,904 @@
+/*
+ * 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 <drm/sdrm.h>
+#include "sdrm.h"
+
+#define DRIVER_NAME		"drm-generic"
+#define DRIVER_DESC		"drm generic graphics"
+#define DRIVER_DATE		"20110604"
+#define DRIVER_MAJOR		1
+#define DRIVER_MINOR		0
+#define DRIVER_PATCHLEVEL	0
+
+struct sdrm_device {
+	struct drm_device		*drm;
+	struct platform_device		*pdev;
+	struct drm_fb_helper		*fb_helper;
+	struct list_head		crtc_list;
+	struct list_head		encoder_list;
+	struct list_head		connector_list;
+	struct list_head		list;
+	const char			*name;
+	int				registered;
+	struct mutex			mutex;
+	struct drm_driver		driver;
+};
+
+struct sdrm_crtc {
+	struct drm_crtc			*crtc;
+	struct list_head		list;
+	struct sdrm_device		*sdrm;
+	int				registered;
+	int				pipe;
+	struct drm_crtc_helper_funcs	crtc_helper_funcs;
+	struct drm_crtc_funcs		crtc_funcs;
+	struct sdrm_crtc_helper_funcs	sdrm_helper_funcs;
+};
+
+struct sdrm_encoder {
+	struct drm_encoder		*encoder;
+	struct list_head		list;
+	struct sdrm_device		*sdrm;
+	int				registered;
+	struct module			*owner;
+};
+
+struct sdrm_connector {
+	struct drm_connector		*connector;
+	struct list_head		list;
+	struct sdrm_device		*sdrm;
+	int				registered;
+	struct module			*owner;
+};
+
+static int sdrm_driver_open(struct drm_device *drm, struct drm_file *file)
+{
+	struct sdrm_device *sdrm = drm->dev_private;
+
+	if (!try_module_get(sdrm->pdev->dev.driver->owner)) {
+		dev_err(drm->dev, "could not get module %s\n",
+				module_name(sdrm->pdev->dev.driver->owner));
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void sdrm_driver_lastclose(struct drm_device *drm)
+{
+	struct sdrm_device *sdrm = drm->dev_private;
+
+	module_put(sdrm->pdev->dev.driver->owner);
+
+	/*
+	 * This shouldn't be here. If multiple drm applications (i.e. two
+	 * xservers) are running and the active one crashes then we will
+	 * only restore fbdev mode when the other one exits aswell. Anyway,
+	 * this is what all other drivers so for now we do it aswell.
+	 */
+	drm_fb_helper_restore_fbdev_mode(sdrm->fb_helper);
+}
+
+static int sdrm_driver_unload(struct drm_device *drm)
+{
+	struct sdrm_device *sdrm = drm->dev_private;
+
+	sdrm_fbdev_fini(sdrm->fb_helper);
+	sdrm->fb_helper = NULL;
+
+	drm_mode_config_cleanup(sdrm->drm);
+	drm_kms_helper_poll_fini(sdrm->drm);
+
+	return 0;
+}
+
+static int sdrm_suspend(struct drm_device *drm, pm_message_t state)
+{
+	/* TODO */
+
+	return 0;
+}
+
+static int sdrm_resume(struct drm_device *drm)
+{
+	/* TODO */
+
+	return 0;
+}
+
+/*
+ * We don't care at all for crtc numbers, but the core expects the
+ * crtcs to be numbered
+ */
+static struct sdrm_crtc *sdrm_crtc_by_num(struct sdrm_device *sdrm, int num)
+{
+	struct sdrm_crtc *sdrm_crtc;
+
+	list_for_each_entry(sdrm_crtc, &sdrm->crtc_list, list)
+		if (sdrm_crtc->pipe == num)
+			return sdrm_crtc;
+	return NULL;
+}
+
+int sdrm_crtc_vblank_get(struct sdrm_crtc *sdrm_crtc)
+{
+	return drm_vblank_get(sdrm_crtc->sdrm->drm, sdrm_crtc->pipe);
+}
+EXPORT_SYMBOL_GPL(sdrm_crtc_vblank_get);
+
+void sdrm_crtc_vblank_put(struct sdrm_crtc *sdrm_crtc)
+{
+	drm_vblank_put(sdrm_crtc->sdrm->drm, sdrm_crtc->pipe);
+}
+EXPORT_SYMBOL_GPL(sdrm_crtc_vblank_put);
+
+void sdrm_handle_vblank(struct sdrm_crtc *sdrm_crtc)
+{
+	struct sdrm_device *sdrm = sdrm_crtc->sdrm;
+
+	if (sdrm->registered)
+		drm_handle_vblank(sdrm_crtc->sdrm->drm, sdrm_crtc->pipe);
+}
+EXPORT_SYMBOL_GPL(sdrm_handle_vblank);
+
+static int sdrm_enable_vblank(struct drm_device *drm, int crtc)
+{
+	struct sdrm_device *sdrm = drm->dev_private;
+	struct sdrm_crtc *sdrm_crtc;
+	int ret;
+
+	sdrm_crtc = sdrm_crtc_by_num(sdrm, crtc);
+	if (!sdrm_crtc)
+		return -EINVAL;
+
+	if (!sdrm_crtc->sdrm_helper_funcs.enable_vblank)
+		return -ENOSYS;
+
+	ret = sdrm_crtc->sdrm_helper_funcs.enable_vblank(sdrm_crtc->crtc);
+	return ret;
+}
+
+static void sdrm_disable_vblank(struct drm_device *drm, int crtc)
+{
+	struct sdrm_device *sdrm = drm->dev_private;
+	struct sdrm_crtc *sdrm_crtc;
+
+	sdrm_crtc = sdrm_crtc_by_num(sdrm, crtc);
+	if (!sdrm_crtc)
+		return;
+
+	if (!sdrm_crtc->sdrm_helper_funcs.disable_vblank)
+		return;
+
+	sdrm_crtc->sdrm_helper_funcs.disable_vblank(sdrm_crtc->crtc);
+}
+
+static struct vm_operations_struct sdrm_gem_vm_ops = {
+	.fault = sdrm_gem_fault,
+	.open = drm_gem_vm_open,
+	.close = drm_gem_vm_close,
+};
+
+static struct drm_ioctl_desc sdrm_ioctls[] = {
+	/* none so far */
+};
+
+static const struct file_operations sdrm_driver_fops = {
+	 .owner = THIS_MODULE,
+	 .open = drm_open,
+	 .release = drm_release,
+	 .unlocked_ioctl = drm_ioctl,
+	 .mmap = sdrm_gem_mmap,
+	 .poll = drm_poll,
+	 .fasync = drm_fasync,
+	 .read = drm_read,
+	 .llseek = noop_llseek,
+};
+
+static int sdrm_get_irq(struct drm_device *dev)
+{
+	/*
+	 * Return an arbitrary number to make the core happy.
+	 * We can't return anything meaningful here since drm
+	 * devices in general have multiple irqs
+	 */
+	return 1234;
+}
+
+static struct drm_bus drm_platform_bus = {
+	.bus_type = DRIVER_BUS_PLATFORM,
+	.get_irq = sdrm_get_irq,
+};
+
+static struct drm_driver sdrm_driver_template = {
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM,
+	.unload			= sdrm_driver_unload,
+	.open			= sdrm_driver_open,
+	.lastclose		= sdrm_driver_lastclose,
+	.gem_free_object	= sdrm_gem_free_object,
+	.gem_vm_ops		= &sdrm_gem_vm_ops,
+	.dumb_create		= sdrm_gem_dumb_create,
+	.dumb_map_offset	= sdrm_gem_dumb_map_offset,
+	.dumb_destroy		= sdrm_gem_dumb_destroy,
+
+	.suspend		= sdrm_suspend,
+	.resume			= sdrm_resume,
+
+	.get_vblank_counter	= drm_vblank_count,
+	.enable_vblank		= sdrm_enable_vblank,
+	.disable_vblank		= sdrm_disable_vblank,
+	.reclaim_buffers	= drm_core_reclaim_buffers,
+	.ioctls			= sdrm_ioctls,
+	.num_ioctls		= ARRAY_SIZE(sdrm_ioctls),
+	.bus			= &drm_platform_bus,
+	.fops			= &sdrm_driver_fops,
+	.name			= DRIVER_NAME,
+	.desc			= DRIVER_DESC,
+	.date			= DRIVER_DATE,
+	.major			= DRIVER_MAJOR,
+	.minor			= DRIVER_MINOR,
+	.patchlevel		= DRIVER_PATCHLEVEL,
+};
+
+static LIST_HEAD(sdrm_device_list);
+static DEFINE_MUTEX(sdrm_list_mutex);
+
+/*
+ * sdrm_device_get - find or allocate sdrm device with unique name
+ *
+ * This function returns the sdrm device with the unique name 'name'
+ * If this already exists, return it, otherwise allocate a new
+ * object.
+ */
+static struct sdrm_device *sdrm_device_get(const char *name)
+{
+	struct sdrm_device *sdrm;
+
+	mutex_lock(&sdrm_list_mutex);
+
+	list_for_each_entry(sdrm, &sdrm_device_list, list)
+		if (!strcmp(sdrm->name, name))
+			goto out;
+
+	mutex_unlock(&sdrm_list_mutex);
+
+	sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL);
+	if (!sdrm)
+		goto out;
+
+	mutex_init(&sdrm->mutex);
+	INIT_LIST_HEAD(&sdrm->crtc_list);
+	INIT_LIST_HEAD(&sdrm->connector_list);
+	INIT_LIST_HEAD(&sdrm->encoder_list);
+
+	sdrm->name = kstrdup(name, GFP_KERNEL);
+	if (!sdrm->name) {
+		kfree(sdrm);
+		goto out;
+	}
+
+	mutex_lock(&sdrm_list_mutex);
+
+	list_add_tail(&sdrm->list, &sdrm_device_list);
+out:
+	mutex_unlock(&sdrm_list_mutex);
+
+	return sdrm;
+}
+
+/*
+ * sdrm_device_maybe_release - free sdrm device if noone is using it
+ *
+ * If we have no crtcs, connectors and encoders registered for this
+ * device we can free it. Caller must hold sdrm->mutex.
+ */
+static void sdrm_device_maybe_release(struct sdrm_device *sdrm)
+{
+	if (!list_empty(&sdrm->crtc_list))
+		return;
+	if (!list_empty(&sdrm->connector_list))
+		return;
+	if (!list_empty(&sdrm->encoder_list))
+		return;
+
+	mutex_lock(&sdrm_list_mutex);
+	list_del(&sdrm->list);
+	mutex_unlock(&sdrm_list_mutex);
+
+	kfree(sdrm->name);
+	kfree(sdrm);
+}
+
+/*
+ * I think this legacy modegroup handling should be removed and we
+ * have a patch for this which also defines HAVE_MODEGROUP_REMOVED
+ * For now ifdef this here to make this work with and without this
+ * patch.
+ */
+#ifndef HAVE_MODEGROUP_REMOVED
+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;
+}
+#else
+static int drm_mode_group_reinit(struct drm_device *dev)
+{
+	return 0;
+}
+#endif
+
+/*
+ * register an encoder to the drm core
+ */
+static int sdrm_encoder_register(struct sdrm_encoder *sdrm_encoder)
+{
+	struct sdrm_device *sdrm = sdrm_encoder->sdrm;
+
+	if (!try_module_get(sdrm_encoder->owner))
+		return -ENODEV;
+
+	drm_encoder_init(sdrm->drm, sdrm_encoder->encoder,
+			sdrm_encoder->encoder->funcs,
+			DRM_MODE_ENCODER_TMDS);
+
+	drm_mode_group_reinit(sdrm->drm);
+
+	sdrm_encoder->registered = 1;
+
+	return 0;
+}
+
+/*
+ * unregister an encoder from the drm core
+ */
+static void sdrm_encoder_unregister(struct sdrm_encoder *sdrm_encoder)
+{
+	struct sdrm_device *sdrm = sdrm_encoder->sdrm;
+
+	if (!sdrm_encoder->registered)
+		return;
+
+	if (sdrm->registered)
+		drm_encoder_cleanup(sdrm_encoder->encoder);
+
+	drm_mode_group_reinit(sdrm->drm);
+
+	sdrm_encoder->registered = 0;
+
+	module_put(sdrm_encoder->owner);
+}
+
+/*
+ * register a connector to the drm core
+ */
+static int sdrm_connector_register(struct sdrm_connector *sdrm_connector)
+{
+	struct sdrm_device *sdrm = sdrm_connector->sdrm;
+	int ret;
+
+	if (!try_module_get(sdrm_connector->owner))
+		return -ENODEV;
+
+	drm_connector_init(sdrm->drm, sdrm_connector->connector,
+			sdrm_connector->connector->funcs,
+			DRM_MODE_CONNECTOR_VGA);
+	drm_mode_group_reinit(sdrm->drm);
+	ret = drm_sysfs_connector_add(sdrm_connector->connector);
+	if (ret)
+		goto err;
+
+	sdrm_connector->registered = 1;
+
+	return 0;
+err:
+	module_put(sdrm_connector->owner);
+
+	return ret;
+}
+
+/*
+ * unregister a connector from the drm core
+ */
+static void sdrm_connector_unregister(struct sdrm_connector *sdrm_connector)
+{
+	struct sdrm_device *sdrm = sdrm_connector->sdrm;
+
+	if (!sdrm_connector->registered)
+		return;
+
+	if (sdrm->registered) {
+		drm_sysfs_connector_remove(sdrm_connector->connector);
+		drm_connector_cleanup(sdrm_connector->connector);
+	}
+
+	drm_mode_group_reinit(sdrm->drm);
+
+	sdrm_connector->registered = 0;
+
+	module_put(sdrm_connector->owner);
+}
+
+/*
+ * register a crtc to the drm core
+ */
+static int sdrm_crtc_register(struct sdrm_crtc *sdrm_crtc)
+{
+	struct sdrm_device *sdrm = sdrm_crtc->sdrm;
+	int ret;
+
+	drm_crtc_init(sdrm->drm, sdrm_crtc->crtc, &sdrm_crtc->crtc_funcs);
+	ret = drm_mode_crtc_set_gamma_size(sdrm_crtc->crtc, 256);
+	if (ret)
+		return ret;
+
+	drm_crtc_helper_add(sdrm_crtc->crtc, &sdrm_crtc->crtc_helper_funcs);
+
+	sdrm_crtc->registered = 1;
+
+	return 0;
+}
+
+static void sdrm_crtc_unregister(struct sdrm_crtc *sdrm_crtc)
+{
+	/*
+	 * The hard work has already been done in driver unload
+	 */
+	sdrm_crtc->registered = 0;
+}
+
+/*
+ * sdrm_uninit - unitialize all crtcs, encoders, connectors
+ *
+ * This is called in case initialization fails or as a cleanup
+ * after unitializing a drm device. Caller must hold sdrm->mutex
+ */
+static void sdrm_uninit(struct sdrm_device *sdrm)
+{
+	struct sdrm_crtc *sdrm_crtc;
+	struct sdrm_encoder *sdrm_encoder;
+	struct sdrm_connector *sdrm_connector;
+
+	list_for_each_entry(sdrm_crtc, &sdrm->crtc_list, list)
+		sdrm_crtc_unregister(sdrm_crtc);
+
+	list_for_each_entry(sdrm_connector, &sdrm->connector_list, list)
+		sdrm_connector_unregister(sdrm_connector);
+
+	list_for_each_entry(sdrm_encoder, &sdrm->encoder_list, list)
+		sdrm_encoder_unregister(sdrm_encoder);
+
+	sdrm->registered = 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.
+ */
+int sdrm_init_drm(const char *name, struct platform_device *pdev,
+		int preferred_bpp)
+{
+	struct sdrm_device *sdrm;
+	struct drm_device *drm;
+	struct sdrm_crtc *sdrm_crtc;
+	struct sdrm_encoder *sdrm_encoder;
+	struct sdrm_connector *sdrm_connector;
+	int ret;
+	int num_crtcs = 0;
+
+	sdrm = sdrm_device_get(name);
+	if (!sdrm)
+		return -ENOMEM;
+
+	drm = kzalloc(sizeof(struct drm_device), GFP_KERNEL);
+	if (!drm)
+		return -ENOMEM;
+
+	sdrm->drm = drm;
+
+	memcpy(&sdrm->driver, &sdrm_driver_template,
+			sizeof(sdrm_driver_template));
+
+	drm->dev_private = sdrm;
+
+	ret = drm_fill_in_dev(drm, NULL, &sdrm->driver);
+	if (ret) {
+		printk(KERN_ERR "DRM: Fill_in_dev failed.\n");
+		goto err_fill_in;
+	}
+
+	/*
+	 * 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 spsdrmific driver own one instead bsdrmause
+	 *      drm framework supports only one irq handler and
+	 *      drivers can well take care of their interrupts
+	 */
+	drm->irq_enabled = 1;
+
+	mutex_lock(&drm_global_mutex);
+
+	ret = drm_get_minor(drm, &drm->control, DRM_MINOR_CONTROL);
+	if (ret)
+		goto err_get_minor1;
+
+	ret = drm_get_minor(drm, &drm->primary, DRM_MINOR_LEGACY);
+	if (ret)
+		goto err_get_minor2;
+
+	mutex_unlock(&drm_global_mutex);
+
+	drm_mode_config_init(drm);
+	sdrm_mode_config_init(drm);
+
+	mutex_lock(&sdrm->mutex);
+
+	/* we need at least one crtc */
+	if (list_empty(&sdrm->crtc_list)) {
+		ret = -EINVAL;
+		goto err_init;
+	}
+
+	/* Register the subdevices we have so far */
+
+	list_for_each_entry(sdrm_encoder, &sdrm->encoder_list, list) {
+		ret = sdrm_encoder_register(sdrm_encoder);
+		if (ret)
+			goto err_init;
+	}
+
+	list_for_each_entry(sdrm_connector, &sdrm->connector_list, list) {
+		ret = sdrm_connector_register(sdrm_connector);
+		if (ret)
+			goto err_init;
+	}
+
+	list_for_each_entry(sdrm_crtc, &sdrm->crtc_list, list) {
+		sdrm_crtc->pipe = num_crtcs;
+		num_crtcs++;
+		ret = sdrm_crtc_register(sdrm_crtc);
+		if (ret)
+			goto err_init;
+	}
+
+	sdrm->pdev = pdev;
+	sdrm->drm->platformdev = pdev;
+	sdrm->drm->dev = &pdev->dev;
+
+	sdrm->fb_helper = sdrm_fbdev_init(sdrm->drm, preferred_bpp);
+	if (!sdrm->fb_helper) {
+		printk("sdrm_fbdev_init failed\n");
+		ret = -ENOMEM;
+		goto err_init;
+	}
+
+	drm_kms_helper_poll_init(sdrm->drm);
+
+#ifndef HAVE_MODEGROUP_REMOVED
+	mutex_lock(&drm_global_mutex);
+
+	/* setup the grouping for the legacy output */
+	ret = drm_mode_group_init_legacy_group(sdrm->drm,
+			&sdrm->drm->primary->mode_group);
+	mutex_unlock(&drm_global_mutex);
+
+	if (ret)
+		return ret;
+#endif
+	ret = drm_vblank_init(sdrm->drm, num_crtcs);
+	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)
+	 */
+	sdrm->drm->vblank_disable_allowed = 1;
+
+	sdrm->registered = 1;
+
+	mutex_unlock(&sdrm->mutex);
+
+	return 0;
+
+err_init:
+	sdrm_uninit(sdrm);
+	mutex_unlock(&sdrm->mutex);
+err_get_minor2:
+	drm_put_minor(&drm->control);
+err_get_minor1:
+err_fill_in:
+	mutex_unlock(&drm_global_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sdrm_init_drm);
+
+/*
+ * Called by the CRTC driver to uninitialize the driver.
+ */
+int sdrm_exit_drm(const char *name)
+{
+	struct sdrm_device *sdrm;
+
+	sdrm = sdrm_device_get(name);
+	if (!sdrm)
+		return -ENODEV;
+
+	sdrm_uninit(sdrm);
+	/*
+	 * The drm core never does anything with this list, yet it removes
+	 * this entry, so initialize it
+	 */
+	INIT_LIST_HEAD(&sdrm->drm->driver_item);
+	drm_put_dev(sdrm->drm);
+
+	sdrm->drm = NULL;
+	sdrm->registered = 0;
+	sdrm->pdev = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdrm_exit_drm);
+
+/*
+ * sdrm_add_crtc - add a new crtc
+ *
+ * The return value if !NULL is a cookie for the caller to pass to
+ * sdrm_remove_crtc later.
+ */
+struct sdrm_crtc *sdrm_add_crtc(const char *name, struct drm_crtc *crtc,
+		const struct drm_crtc_funcs *crtc_funcs,
+		const struct drm_crtc_helper_funcs *crtc_helper_funcs,
+		const struct sdrm_crtc_helper_funcs *sdrm_helper_funcs)
+{
+	struct sdrm_device *sdrm;
+	struct sdrm_crtc *sdrm_crtc;
+
+	sdrm = sdrm_device_get(name);
+	if (!sdrm)
+		return NULL; /* -ENOMEM */
+
+	if (sdrm->registered)
+		return NULL; /* -EBUSY */
+
+	sdrm_crtc = kzalloc(sizeof(*sdrm_crtc), GFP_KERNEL);
+	if (!sdrm_crtc)
+		return NULL; /* -ENOMEM */
+
+	sdrm_crtc->crtc_funcs = *crtc_funcs;
+	sdrm_crtc->crtc_helper_funcs = *crtc_helper_funcs;
+	sdrm_crtc->sdrm_helper_funcs = *sdrm_helper_funcs;
+
+	WARN_ON(crtc_funcs->set_config);
+	WARN_ON(crtc_funcs->destroy);
+
+	sdrm_crtc->crtc_funcs.set_config = drm_crtc_helper_set_config;
+	sdrm_crtc->crtc_funcs.destroy = drm_crtc_cleanup;
+
+	sdrm_crtc->crtc = crtc;
+	sdrm_crtc->sdrm = sdrm;
+
+	mutex_lock(&sdrm->mutex);
+
+	list_add_tail(&sdrm_crtc->list, &sdrm->crtc_list);
+
+	mutex_unlock(&sdrm->mutex);
+
+	return sdrm_crtc;
+}
+EXPORT_SYMBOL_GPL(sdrm_add_crtc);
+
+/*
+ * sdrm_remove_crtc - remove a crtc
+ *
+ * Can only be called on inactive drm devices since the drm
+ * core can't handle dynamic crtcs
+ */
+int sdrm_remove_crtc(struct sdrm_crtc *sdrm_crtc)
+{
+	struct sdrm_device *sdrm = sdrm_crtc->sdrm;
+
+	if (sdrm->registered)
+		return -EBUSY;
+
+	mutex_lock(&sdrm->mutex);
+
+	sdrm_crtc_unregister(sdrm_crtc);
+
+	list_del(&sdrm_crtc->list);
+
+	kfree(sdrm_crtc);
+
+	sdrm_device_maybe_release(sdrm);
+
+	mutex_unlock(&sdrm->mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdrm_remove_crtc);
+
+/*
+ * sdrm_add_encoder - add a new encoder
+ *
+ * The return value if !NULL is a cookie for the caller to pass to
+ * sdrm_remove_crtc later.
+ *
+ * Can be called on both active and inactive devices.
+ */
+struct sdrm_encoder *sdrm_add_encoder(const char *name,
+		struct drm_encoder *encoder, struct module *owner)
+{
+	struct sdrm_device *sdrm;
+	struct sdrm_encoder *sdrm_encoder;
+	int ret;
+
+	sdrm = sdrm_device_get(name);
+	if (!sdrm)
+		return NULL;
+
+	sdrm_encoder = kzalloc(sizeof(struct sdrm_encoder), GFP_KERNEL);
+	if (!sdrm_encoder)
+		return NULL;
+	sdrm_encoder->encoder = encoder;
+	sdrm_encoder->sdrm = sdrm;
+	sdrm_encoder->owner = owner;
+
+	mutex_lock(&sdrm->mutex);
+
+	if (sdrm->registered) {
+		/*
+		 * If the drm device is up register the encoder,
+		 * otherwise it will be done in sdrm_init_drm()
+		 */
+		ret = sdrm_encoder_register(sdrm_encoder);
+		if (ret) {
+			kfree(sdrm_encoder);
+			goto err;
+		}
+	}
+
+	list_add_tail(&sdrm_encoder->list, &sdrm->encoder_list);
+err:
+	mutex_unlock(&sdrm->mutex);
+
+	return sdrm_encoder;
+}
+EXPORT_SYMBOL_GPL(sdrm_add_encoder);
+
+/*
+ * sdrm_remove_encoder - remove an encoder
+ *
+ * Can be called on both active and inactive devices.
+ */
+int sdrm_remove_encoder(struct sdrm_encoder *sdrm_encoder)
+{
+	struct sdrm_device *sdrm = sdrm_encoder->sdrm;
+
+	mutex_lock(&sdrm->mutex);
+
+	sdrm_encoder_unregister(sdrm_encoder);
+
+	list_del(&sdrm_encoder->list);
+
+	kfree(sdrm_encoder);
+
+	sdrm_device_maybe_release(sdrm);
+
+	mutex_unlock(&sdrm->mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdrm_remove_encoder);
+
+/*
+ * sdrm_add_connector - add a connector
+ *
+ * Can be called on both active and inactive devices.
+ */
+struct sdrm_connector *sdrm_add_connector(const char *name,
+		struct drm_connector *connector, struct module *owner)
+{
+	struct sdrm_device *sdrm;
+	struct sdrm_connector *sdrm_connector;
+	int ret;
+
+	sdrm = sdrm_device_get(name);
+	if (!sdrm)
+		return NULL;
+
+	sdrm_connector = kzalloc(sizeof(struct sdrm_connector), GFP_KERNEL);
+	if (!sdrm_connector)
+		return NULL;
+
+	sdrm_connector->connector = connector;
+	sdrm_connector->sdrm = sdrm;
+	sdrm_connector->owner = owner;
+
+	mutex_lock(&sdrm->mutex);
+
+	if (sdrm->registered) {
+		/*
+		 * If the drm device is up register the connector,
+		 * otherwise it will be done in sdrm_init_drm()
+		 */
+		ret = sdrm_connector_register(sdrm_connector);
+		if (ret) {
+			kfree(sdrm_connector);
+			sdrm_connector = NULL;
+			goto err;
+		}
+	}
+
+	list_add_tail(&sdrm_connector->list, &sdrm->connector_list);
+err:
+	mutex_unlock(&sdrm->mutex);
+
+	return sdrm_connector;
+}
+EXPORT_SYMBOL_GPL(sdrm_add_connector);
+
+/*
+ * sdrm_remove_connector - remove a connector
+ *
+ * Can be called on both active and inactive devices.
+ */
+int sdrm_remove_connector(struct sdrm_connector *sdrm_connector)
+{
+	struct sdrm_device *sdrm = sdrm_connector->sdrm;
+
+	mutex_lock(&sdrm->mutex);
+
+	sdrm_connector_unregister(sdrm_connector);
+
+	list_del(&sdrm_connector->list);
+
+	kfree(sdrm_connector);
+
+	sdrm_device_maybe_release(sdrm);
+
+	mutex_unlock(&sdrm->mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdrm_remove_connector);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sdrm/sdrm.h b/drivers/gpu/drm/sdrm/sdrm.h
new file mode 100644
index 0000000..e5d3a63
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm.h
@@ -0,0 +1,57 @@
+#ifndef _SDRM_H_
+#define _SDRM_H_
+
+void sdrm_mode_config_init(struct drm_device *drm);
+
+struct drm_fb_helper *sdrm_fbdev_init(struct drm_device *drm,
+		int preferred_bpp);
+void sdrm_fbdev_fini(struct drm_fb_helper *);
+
+#define to_sdrm_gem_obj(x)	container_of(x,\
+			struct sdrm_gem_obj, base)
+
+struct sdrm_gem_obj {
+	struct drm_gem_object base;
+	struct sdrm_buf_entry *entry;
+};
+
+/* unmap a buffer from user space. */
+int sdrm_gem_munmap_ioctl(struct drm_device *drm, void *data,
+		struct drm_file *file_priv);
+
+/* initialize gem object. */
+int sdrm_gem_init_object(struct drm_gem_object *obj);
+
+/* free gem object. */
+void sdrm_gem_free_object(struct drm_gem_object *gem_obj);
+
+/* create memory region for drm framebuffer. */
+int sdrm_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 sdrm_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 sdrm_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 sdrm_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 sdrm_gem_dumb_destroy(struct drm_file *file_priv,
+		struct drm_device *drm, unsigned int handle);
+
+/* allocate physical memory. */
+struct sdrm_buf_entry *sdrm_buf_create(struct drm_device *drm,
+		unsigned int size);
+
+/* remove allocated physical memory. */
+void sdrm_buf_destroy(struct drm_device *drm, struct sdrm_buf_entry *entry);
+
+#endif /* _SDRM_H_ */
diff --git a/drivers/gpu/drm/sdrm/sdrm_fb.c b/drivers/gpu/drm/sdrm/sdrm_fb.c
new file mode 100644
index 0000000..7f9c69d
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm_fb.c
@@ -0,0 +1,191 @@
+/*
+ * simple drm driver
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix
+ *
+ * Based on Samsung Exynos code
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/sdrm.h>
+
+#include "sdrm.h"
+
+#define to_sdrm_fb(x)	container_of(x, struct sdrm_fb, fb)
+
+/*
+ * sdrm specific framebuffer structure.
+ *
+ * @fb: drm framebuffer obejct.
+ * @sdrm_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 sdrm_fb {
+	struct drm_framebuffer		fb;
+	struct sdrm_gem_obj	*sdrm_gem_obj;
+	struct sdrm_buf_entry	*entry;
+};
+
+static void sdrm_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct sdrm_fb *sdrm_fb = to_sdrm_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 (!sdrm_fb->sdrm_gem_obj && sdrm_fb->entry)
+		sdrm_buf_destroy(fb->dev, sdrm_fb->entry);
+
+	kfree(sdrm_fb);
+}
+
+static int sdrm_fb_create_handle(struct drm_framebuffer *fb,
+		struct drm_file *file_priv, unsigned int *handle)
+{
+	struct sdrm_fb *sdrm_fb = to_sdrm_fb(fb);
+
+	return drm_gem_handle_create(file_priv,
+			&sdrm_fb->sdrm_gem_obj->base, handle);
+}
+
+static int sdrm_fb_dirty(struct drm_framebuffer *fb,
+		struct drm_file *file_priv, unsigned flags,
+		unsigned color, struct drm_clip_rect *clips,
+		unsigned num_clips)
+{
+	/* TODO */
+
+	return 0;
+}
+
+static struct drm_framebuffer_funcs sdrm_fb_funcs = {
+	.destroy	= sdrm_fb_destroy,
+	.create_handle	= sdrm_fb_create_handle,
+	.dirty		= sdrm_fb_dirty,
+};
+
+static struct drm_framebuffer *sdrm_fb_create(struct drm_device *dev,
+		struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	struct sdrm_fb *sdrm_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);
+
+	sdrm_fb = kzalloc(sizeof(*sdrm_fb), GFP_KERNEL);
+	if (!sdrm_fb) {
+		dev_err(dev->dev, "failed to allocate drm framebuffer.\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	fb = &sdrm_fb->fb;
+	ret = drm_framebuffer_init(dev, fb, &sdrm_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 sdrm_fbdev_create in which
+	 * case we only create a framebuffer without a handle.
+	 */
+	if (!file_priv) {
+		struct sdrm_buf_entry *entry;
+
+		entry = sdrm_buf_create(dev, size);
+		if (IS_ERR(entry)) {
+			ret = PTR_ERR(entry);
+			goto err_buffer;
+		}
+
+		sdrm_fb->entry = entry;
+	} else {
+		obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
+		if (!obj) {
+			ret = -EINVAL;
+			goto err_buffer;
+		}
+
+		sdrm_fb->sdrm_gem_obj = to_sdrm_gem_obj(obj);
+
+		drm_gem_object_unreference_unlocked(obj);
+
+		sdrm_fb->entry = sdrm_fb->sdrm_gem_obj->entry;
+	}
+
+	drm_helper_mode_fill_fb_struct(fb, mode_cmd);
+
+	return fb;
+
+err_buffer:
+	drm_framebuffer_cleanup(fb);
+
+err_init:
+	kfree(sdrm_fb);
+
+	return ERR_PTR(ret);
+}
+
+struct sdrm_buf_entry *sdrm_fb_get_buf(struct drm_framebuffer *fb)
+{
+	struct sdrm_fb *sdrm_fb = to_sdrm_fb(fb);
+	struct sdrm_buf_entry *entry;
+
+	entry = sdrm_fb->entry;
+
+	return entry;
+}
+EXPORT_SYMBOL_GPL(sdrm_fb_get_buf);
+
+static struct drm_mode_config_funcs sdrm_mode_config_funcs = {
+	.fb_create = sdrm_fb_create,
+};
+
+void sdrm_mode_config_init(struct drm_device *dev)
+{
+	dev->mode_config.min_width = 0;
+	dev->mode_config.min_height = 0;
+
+	/*
+	 * 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 = &sdrm_mode_config_funcs;
+}
diff --git a/drivers/gpu/drm/sdrm/sdrm_fbdev.c b/drivers/gpu/drm/sdrm/sdrm_fbdev.c
new file mode 100644
index 0000000..8f3e3e0
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm_fbdev.c
@@ -0,0 +1,238 @@
+/*
+ * simple drm driver
+ *
+ * Copyright (C) 2011 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 <drm/sdrm.h>
+
+#include "sdrm.h"
+
+#define MAX_CONNECTOR		4
+#define PREFERRED_BPP		16
+
+static struct fb_ops sdrm_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 sdrm_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 sdrm_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 = sdrm_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 sdrm_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 = &sdrm_fb_ops;
+
+	ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+	if (ret)
+		goto err_alloc_cmap;
+
+	ret = sdrm_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 sdrm_fbdev_probe(struct drm_fb_helper *helper,
+				   struct drm_fb_helper_surface_size *sizes)
+{
+	int ret;
+
+	BUG_ON(helper->fb);
+
+	ret = sdrm_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 sdrm_fb_helper_funcs = {
+	.fb_probe = sdrm_fbdev_probe,
+};
+
+struct drm_fb_helper *sdrm_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 = &sdrm_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;
+}
+
+void sdrm_fbdev_fini(struct drm_fb_helper *helper)
+{
+	/* release linux framebuffer */
+	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);
+
+		framebuffer_release(info);
+	}
+
+	drm_fb_helper_fini(helper);
+
+	kfree(helper);
+}
diff --git a/drivers/gpu/drm/sdrm/sdrm_gem.c b/drivers/gpu/drm/sdrm/sdrm_gem.c
new file mode 100644
index 0000000..4f98627
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm_gem.c
@@ -0,0 +1,342 @@
+/*
+ * simple drm driver
+ *
+ * Copyright (C) 2011 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/sdrm.h>
+
+#include "sdrm.h"
+
+static int lowlevel_buffer_allocate(struct drm_device *drm,
+		struct sdrm_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 sdrm_buf_entry *entry)
+{
+	dma_free_writecombine(drm->dev, entry->size, entry->vaddr,
+			entry->paddr);
+}
+
+struct sdrm_buf_entry *sdrm_buf_create(struct drm_device *drm,
+		unsigned int size)
+{
+	struct sdrm_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 sdrm_buf_destroy(struct drm_device *drm,
+		struct sdrm_buf_entry *entry)
+{
+	lowlevel_buffer_free(drm, entry);
+
+	kfree(entry);
+	entry = NULL;
+}
+
+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 sdrm_gem_obj *sdrm_gem_create(struct drm_device *drm,
+		unsigned int size)
+{
+	struct sdrm_gem_obj *sdrm_gem_obj;
+	struct sdrm_buf_entry *entry;
+	struct drm_gem_object *obj;
+	int ret;
+
+	size = roundup(size, PAGE_SIZE);
+
+	sdrm_gem_obj = kzalloc(sizeof(*sdrm_gem_obj), GFP_KERNEL);
+	if (!sdrm_gem_obj)
+		return ERR_PTR(-ENOMEM);
+
+	/* allocate the new buffer object and memory region. */
+	entry = sdrm_buf_create(drm, size);
+	if (!entry) {
+		ret = -ENOMEM;
+		goto err_alloc;
+	}
+
+	sdrm_gem_obj->entry = entry;
+
+	obj = &sdrm_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 sdrm_gem_obj;
+
+err_create_mmap_offset:
+	drm_gem_object_release(obj);
+
+err_obj_init:
+	sdrm_buf_destroy(drm, sdrm_gem_obj->entry);
+
+err_alloc:
+	kfree(sdrm_gem_obj);
+
+	return ERR_PTR(ret);
+}
+
+static struct sdrm_gem_obj *sdrm_gem_create_with_handle(struct drm_file *file_priv,
+		struct drm_device *drm, unsigned int size,
+		unsigned int *handle)
+{
+	struct sdrm_gem_obj *sdrm_gem_obj;
+	struct drm_gem_object *obj;
+	int ret;
+
+	sdrm_gem_obj = sdrm_gem_create(drm, size);
+	if (IS_ERR(sdrm_gem_obj))
+		return sdrm_gem_obj;
+
+	obj = &sdrm_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 sdrm_gem_obj;
+
+err_handle_create:
+	sdrm_gem_free_object(obj);
+
+	return ERR_PTR(ret);
+}
+
+static int sdrm_gem_mmap_buffer(struct file *filp,
+		struct vm_area_struct *vma)
+{
+	struct drm_gem_object *obj = filp->private_data;
+	struct sdrm_gem_obj *sdrm_gem_obj = to_sdrm_gem_obj(obj);
+	struct sdrm_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 = sdrm_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 = sdrm_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 sdrm_gem_fops = {
+	.mmap = sdrm_gem_mmap_buffer,
+};
+
+int sdrm_gem_init_object(struct drm_gem_object *obj)
+{
+	return 0;
+}
+
+void sdrm_gem_free_object(struct drm_gem_object *gem_obj)
+{
+	struct sdrm_gem_obj *sdrm_gem_obj;
+
+	if (gem_obj->map_list.map)
+		drm_gem_free_mmap_offset(gem_obj);
+
+	drm_gem_object_release(gem_obj);
+
+	sdrm_gem_obj = to_sdrm_gem_obj(gem_obj);
+
+	sdrm_buf_destroy(gem_obj->dev, sdrm_gem_obj->entry);
+
+	kfree(sdrm_gem_obj);
+}
+
+int sdrm_gem_dumb_create(struct drm_file *file_priv,
+		struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+	struct sdrm_gem_obj *sdrm_gem_obj;
+
+	/* FIXME: This should be configured by the crtc driver */
+	args->pitch = args->width * args->bpp >> 3;
+	args->size = args->pitch * args->height;
+
+	sdrm_gem_obj = sdrm_gem_create_with_handle(file_priv, dev, args->size,
+			&args->handle);
+	if (IS_ERR(sdrm_gem_obj))
+		return PTR_ERR(sdrm_gem_obj);
+
+	return 0;
+}
+
+int sdrm_gem_dumb_map_offset(struct drm_file *file_priv,
+		struct drm_device *drm, uint32_t handle, uint64_t *offset)
+{
+	struct sdrm_gem_obj *sdrm_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;
+	}
+
+	sdrm_gem_obj = to_sdrm_gem_obj(obj);
+
+	*offset = get_gem_mmap_offset(&sdrm_gem_obj->base);
+
+	drm_gem_object_unreference(obj);
+
+	mutex_unlock(&drm->struct_mutex);
+
+	return 0;
+}
+
+int sdrm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	struct drm_gem_object *obj = vma->vm_private_data;
+	struct sdrm_gem_obj *sdrm_gem_obj = to_sdrm_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 = (sdrm_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 sdrm_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 sdrm_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/include/drm/sdrm.h b/include/drm/sdrm.h
new file mode 100644
index 0000000..ad4f5f6
--- /dev/null
+++ b/include/drm/sdrm.h
@@ -0,0 +1,102 @@
+#ifndef __DRM_SDRM_H
+#define __DRM_SDRM_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.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 sdrm_gem_create {
+	unsigned int size;
+	unsigned int flags;
+	unsigned int handle;
+};
+
+/**
+ * A structure for getting buffer offset.
+ *
+ * @handle: a pointer to gem object created.
+ * @pad: just padding to be 64-bit aligned.
+ * @offset: relatived offset value of the memory region allocated.
+ *	- this value should be set by user.
+ */
+struct sdrm_gem_map_off {
+	unsigned int handle;
+	unsigned int pad;
+	uint64_t offset;
+};
+
+/**
+ * A structure for mapping buffer.
+ *
+ * @handle: a handle to gem object created.
+ * @size: memory size to be mapped.
+ * @mapped: having user virtual address mmaped.
+ *	- this variable would be filled by exynos gem module
+ *	of kernel side with user virtual address which is allocated
+ *	by do_mmap().
+ */
+struct sdrm_gem_mmap {
+	unsigned int handle;
+	unsigned int size;
+	uint64_t mapped;
+};
+
+struct sdrm_device;
+struct sdrm_crtc;
+
+struct sdrm_crtc_helper_funcs {
+	int (*enable_vblank)(struct drm_crtc *crtc);
+	void (*disable_vblank)(struct drm_crtc *crtc);
+};
+
+struct sdrm_crtc *sdrm_add_crtc(const char *name, struct drm_crtc *crtc,
+		const struct drm_crtc_funcs *crtc_funcs,
+		const struct drm_crtc_helper_funcs *crtc_helper_funcs,
+		const struct sdrm_crtc_helper_funcs *ec_helper_funcs);
+int sdrm_remove_crtc(struct sdrm_crtc *);
+int sdrm_init_drm(const char *name, struct platform_device *pdev,
+		int preferred_bpp);
+int sdrm_exit_drm(const char *name);
+
+int sdrm_crtc_vblank_get(struct sdrm_crtc *sdrm_crtc);
+void sdrm_crtc_vblank_put(struct sdrm_crtc *sdrm_crtc);
+void sdrm_handle_vblank(struct sdrm_crtc *sdrm_crtc);
+
+/*
+ * sdrm drm buffer entry structure.
+ *
+ * @paddr: physical address of allocated memory.
+ * @vaddr: kernel virtual address of allocated memory.
+ * @size: size of allocated memory.
+ */
+struct sdrm_buf_entry {
+	dma_addr_t paddr;
+	void __iomem *vaddr;
+	unsigned int size;
+
+	dma_addr_t r_paddr;
+	void __iomem *r_vaddr;
+	unsigned int r_size;
+};
+
+/* get physical memory information of a drm framebuffer. */
+struct sdrm_buf_entry *sdrm_fb_get_buf(struct drm_framebuffer *fb);
+
+struct sdrm_encoder;
+struct sdrm_encoder *sdrm_add_encoder(const char *drmname,
+		struct drm_encoder *encoder, struct module *owner);
+int sdrm_remove_encoder(struct sdrm_encoder *);
+
+struct sdrm_connector;
+struct sdrm_connector *sdrm_add_connector(const char *drmname,
+		struct drm_connector *connector, struct module *owner);
+int sdrm_remove_connector(struct sdrm_connector *);
+
+#endif /* __DRM_SDRM_H */
-- 
1.7.9.5

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

* [PATCH 4/7] DRM: Add sdrm 1:1 encoder - connector helper
  2012-04-11 15:33 ` Sascha Hauer
@ 2012-04-11 15:33   ` Sascha Hauer
  -1 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: linux-arm-kernel

Having a 1:1 relationship between an encoder and a connector is a
very common case. This patch allows for registering a combination
of both in a single device. It should allow implementing all
necessary callbacks for both the encoder and the connector, but
most calls are optional leaving the simplest encoder - connector
which is purely dummy and only passes a drm mode to the core.
This is a common case on embedded systems where the parallel data
lines are directly connected to a single display.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/sdrm/Kconfig            |    3 +
 drivers/gpu/drm/sdrm/Makefile           |    3 +
 drivers/gpu/drm/sdrm/sdrm_encon.c       |  211 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/sdrm/sdrm_encon_dummy.c |  193 ++++++++++++++++++++++++++++
 include/drm/sdrm_encon.h                |   69 ++++++++++
 5 files changed, 479 insertions(+)
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_encon.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_encon_dummy.c
 create mode 100644 include/drm/sdrm_encon.h

diff --git a/drivers/gpu/drm/sdrm/Kconfig b/drivers/gpu/drm/sdrm/Kconfig
index 2424b2c..456ac07 100644
--- a/drivers/gpu/drm/sdrm/Kconfig
+++ b/drivers/gpu/drm/sdrm/Kconfig
@@ -6,3 +6,6 @@ config DRM_SDRM
 	select FB_CFB_COPYAREA
 	select FB_CFB_IMAGEBLIT
 
+config DRM_SDRM_ENCON
+	tristate
+	depends on DRM_SDRM
diff --git a/drivers/gpu/drm/sdrm/Makefile b/drivers/gpu/drm/sdrm/Makefile
index c603f1b..cf57be2 100644
--- a/drivers/gpu/drm/sdrm/Makefile
+++ b/drivers/gpu/drm/sdrm/Makefile
@@ -1,2 +1,5 @@
 drm-sdrm-objs := sdrm.o sdrm_gem.o sdrm_fbdev.o sdrm_fb.o
 obj-$(CONFIG_DRM_SDRM) += drm-sdrm.o
+
+drm-sdrm-encon-objs := sdrm_encon_dummy.o sdrm_encon.o
+obj-$(CONFIG_DRM_SDRM_ENCON) += drm-sdrm-encon.o
diff --git a/drivers/gpu/drm/sdrm/sdrm_encon.c b/drivers/gpu/drm/sdrm/sdrm_encon.c
new file mode 100644
index 0000000..16f7e4c
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm_encon.c
@@ -0,0 +1,211 @@
+/*
+ * Implementation of a 1:1 relationship for drm encoders and connectors
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+#include <linux/module.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder_slave.h>
+#include <drm/sdrm_encon.h>
+#include <drm/sdrm.h>
+
+#define con_to_encon(x) container_of(x, struct drm_encoder_connector, connector)
+#define enc_to_encon(x) container_of(x, struct drm_encoder_connector, encoder)
+
+static enum drm_connector_status connector_detect(struct drm_connector *connector,
+		bool force)
+{
+	struct drm_encoder_connector *encon = con_to_encon(connector);
+
+	if (encon->funcs->detect)
+		return encon->funcs->detect(encon);
+
+	return connector_status_connected;
+}
+
+static int connector_set_property(struct drm_connector *connector,
+		struct drm_property *property, uint64_t val)
+{
+	struct drm_encoder_connector *encon = con_to_encon(connector);
+
+	if (encon->funcs->set_property)
+		return encon->funcs->set_property(encon, property, val);
+
+	return -EINVAL;
+}
+
+static void connector_destroy(struct drm_connector *connector)
+{
+	/* do not free here */
+}
+
+static int connector_get_modes(struct drm_connector *connector)
+{
+	struct drm_encoder_connector *encon = con_to_encon(connector);
+
+	if (encon->funcs->get_modes)
+		return encon->funcs->get_modes(encon);
+
+	return 0;
+}
+
+static int connector_mode_valid(struct drm_connector *connector,
+			  struct drm_display_mode *mode)
+{
+	struct drm_encoder_connector *encon = con_to_encon(connector);
+
+	if (encon->funcs && encon->funcs->mode_valid)
+		return encon->funcs->mode_valid(encon, mode);
+
+	return 0;
+}
+
+static struct drm_encoder *connector_best_encoder(struct drm_connector *connector)
+{
+	struct drm_encoder_connector *encon = con_to_encon(connector);
+
+	return &encon->encoder;
+}
+
+static void encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_encoder_connector *encon = enc_to_encon(encoder);
+
+	if (encon->funcs->dpms)
+		encon->funcs->dpms(encon, mode);
+}
+
+static bool encoder_mode_fixup(struct drm_encoder *encoder,
+			   struct drm_display_mode *mode,
+			   struct drm_display_mode *adjusted_mode)
+{
+	struct drm_encoder_connector *encon = enc_to_encon(encoder);
+
+	if (encon->funcs->mode_fixup)
+		return encon->funcs->mode_fixup(encon, mode, adjusted_mode);
+
+	return true;
+}
+
+static void encoder_prepare(struct drm_encoder *encoder)
+{
+	struct drm_encoder_connector *encon = enc_to_encon(encoder);
+
+	if (encon->funcs->prepare)
+		encon->funcs->prepare(encon);
+}
+
+static void encoder_commit(struct drm_encoder *encoder)
+{
+	struct drm_encoder_connector *encon = enc_to_encon(encoder);
+
+	if (encon->funcs->commit)
+		encon->funcs->commit(encon);
+}
+
+static void encoder_mode_set(struct drm_encoder *encoder,
+			 struct drm_display_mode *mode,
+			 struct drm_display_mode *adjusted_mode)
+{
+	struct drm_encoder_connector *encon = enc_to_encon(encoder);
+
+	if (encon->funcs->mode_set)
+		encon->funcs->mode_set(encon, mode, adjusted_mode);
+}
+
+static void encoder_disable(struct drm_encoder *encoder)
+{
+}
+
+static void encoder_destroy(struct drm_encoder *encoder)
+{
+	/* do not free here */
+}
+
+struct drm_connector_funcs connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = connector_detect,
+	.set_property = connector_set_property,
+	.destroy = connector_destroy,
+};
+
+struct drm_connector_helper_funcs connector_helper_funcs = {
+	.get_modes = connector_get_modes,
+	.mode_valid = connector_mode_valid,
+	.best_encoder = connector_best_encoder,
+};
+
+static struct drm_encoder_funcs encoder_funcs = {
+	.destroy = encoder_destroy,
+};
+
+static struct drm_encoder_helper_funcs encoder_helper_funcs = {
+	.dpms = encoder_dpms,
+	.mode_fixup = encoder_mode_fixup,
+	.prepare = encoder_prepare,
+	.commit = encoder_commit,
+	.mode_set = encoder_mode_set,
+	.disable = encoder_disable,
+};
+
+int drm_encoder_connector_register(const char *drm_name, struct drm_encoder_connector *encon)
+{
+	drm_mode_connector_attach_encoder(&encon->connector, &encon->encoder);
+
+	encon->connector.funcs = &connector_funcs;
+	encon->encoder.funcs = &encoder_funcs;
+	drm_encoder_helper_add(&encon->encoder, &encoder_helper_funcs);
+	encon->sdrm_encoder = sdrm_add_encoder(drm_name, &encon->encoder,
+			encon->owner);
+	if (!encon->sdrm_encoder) {
+		pr_err("%s: adding encoder failed\n", __func__);
+		return -EINVAL;
+	}
+
+	drm_connector_helper_add(&encon->connector, &connector_helper_funcs);
+
+	encon->sdrm_connector = sdrm_add_connector(drm_name, &encon->connector,
+			encon->owner);
+	if (!encon->sdrm_connector) {
+		pr_err("%s: adding connector failed\n", __func__);
+		return -EINVAL;
+	}
+
+	encon->connector.encoder = &encon->encoder;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(drm_encoder_connector_register);
+
+void drm_encoder_connector_unregister(struct drm_encoder_connector *c)
+{
+	struct drm_connector *connector = &c->connector;
+	struct drm_encoder *encoder = &c->encoder;
+
+	drm_sysfs_connector_remove(connector);
+	drm_mode_connector_detach_encoder(connector, encoder);
+	drm_encoder_cleanup(encoder);
+	drm_connector_cleanup(connector);
+}
+EXPORT_SYMBOL_GPL(drm_encoder_connector_unregister);
+
+MODULE_DESCRIPTION("drm encoder/connector driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sdrm/sdrm_encon_dummy.c b/drivers/gpu/drm/sdrm/sdrm_encon_dummy.c
new file mode 100644
index 0000000..a88b884
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm_encon_dummy.c
@@ -0,0 +1,193 @@
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <drm/drmP.h>
+#include <drm/sdrm_encon.h>
+#include <drm/sdrm.h>
+
+struct sdrm_encon_dummy_private {
+	struct drm_encoder_connector encon;
+	struct drm_display_mode *modes;
+	int num_modes;
+};
+
+static int dummy_get_modes(struct drm_encoder_connector *encon)
+{
+	struct sdrm_encon_dummy_private *priv = encon->encon_private;
+	struct drm_display_mode *mode;
+	struct drm_device *drm = encon->connector.dev;
+	int i, ret = 0;
+
+	if (!priv->num_modes)
+		return 0;
+
+	for (i = 0; i < priv->num_modes; i++) {
+		mode = drm_mode_duplicate(drm, &priv->modes[i]);
+		if (mode == NULL)
+			return 0;
+
+		mode->base.type = DRM_MODE_OBJECT_MODE;
+
+		drm_mode_probed_add(&encon->connector, mode);
+		ret++; 
+	}
+	return ret;
+}
+
+static struct drm_encoder_connector_funcs dummy_funcs = {
+	.get_modes = dummy_get_modes,
+};
+
+/*
+ * sdrm_encon_add_dummy - add a dummy encoder/connector
+ *
+ * All callbacks of a dummy encoder/connector are no-ops, only
+ * an array of modes can be provided.
+ */
+struct drm_encoder_connector *sdrm_encon_add_dummy(struct sdrm_encon_dummy *dummy)
+{
+	struct sdrm_encon_dummy_private *priv;
+	struct drm_encoder_connector *encon;
+	int ret;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	encon = &priv->encon;
+	priv->modes = dummy->modes;
+	priv->num_modes = dummy->num_modes;
+	encon->funcs = &dummy_funcs;
+	encon->encon_private = priv;
+	encon->connector.encoder = &encon->encoder;
+	encon->encoder.possible_crtcs = dummy->possible_crtcs;
+	encon->encoder.possible_clones = dummy->possible_clones;
+	encon->owner = dummy->owner;
+
+	ret = drm_encoder_connector_register(dummy->drm_name, encon);
+	if (ret) {
+		kfree(priv);
+		return NULL;
+	}
+
+	return encon;
+}
+EXPORT_SYMBOL_GPL(sdrm_encon_add_dummy);
+
+int sdrm_encon_remove_dummy(struct drm_encoder_connector *encon)
+{
+	struct sdrm_encon_dummy_private *priv = encon->encon_private;
+
+	sdrm_remove_connector(encon->sdrm_connector);
+	sdrm_remove_encoder(encon->sdrm_encoder);
+
+	kfree(priv);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdrm_encon_remove_dummy);
+
+struct sdrm_encon_pdev_priv {
+	struct sdrm_encon_dummy dummy;
+	struct drm_encoder_connector *encon;
+	unsigned int flags;
+	int gpio_dpms;
+	int gpio_backlight;	
+};
+
+static void sdrm_encon_pdev_gpio_dpms(struct sdrm_encon_dummy *dummy, int mode)
+{
+	struct sdrm_encon_pdev_priv *priv = container_of(dummy,
+			struct sdrm_encon_pdev_priv, dummy);
+	int gpio_backlight = 0, gpio_dpms = 0;
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		printk("%s: on\n", __func__);
+		gpio_backlight = 1;
+		gpio_dpms = 1;
+	default:
+		printk("%s: off\n", __func__);
+		break;
+	}
+
+	if (priv->flags == DRM_ENCON_DUMMY_BL_GPIO_ACTIVE_LOW)
+		gpio_backlight = !gpio_backlight;
+	if (priv->flags == DRM_ENCON_DUMMY_DPMS_GPIO_ACTIVE_LOW)
+		gpio_dpms = !gpio_dpms;
+
+	if (priv->flags & DRM_ENCON_DUMMY_USE_DPMS_GPIO)
+		gpio_direction_output(priv->gpio_dpms, gpio_dpms);
+
+	if (priv->flags & DRM_ENCON_DUMMY_USE_BL_GPIO)
+		gpio_direction_output(priv->gpio_backlight, gpio_backlight);
+}
+
+static int __devinit sdrm_encon_platform_probe(struct platform_device *pdev)
+{
+	struct sdrm_encon_dummy_pdata *pdata = pdev->dev.platform_data;
+	struct sdrm_encon_pdev_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dummy.owner = THIS_MODULE;
+	priv->dummy.drm_name = pdata->drm_name;
+	priv->dummy.possible_crtcs = pdata->possible_crtcs;
+	priv->dummy.possible_clones = pdata->possible_clones;
+	priv->dummy.modes = pdata->modes;
+	priv->dummy.num_modes = pdata->num_modes;
+	priv->flags = pdata->flags;
+
+	if (pdata->flags & DRM_ENCON_DUMMY_USE_BL_GPIO) {
+		priv->gpio_backlight = pdata->gpio_backlight;
+		ret = gpio_request(priv->gpio_backlight, "backlight");
+		if (ret)
+			return ret;
+	}
+
+	if (pdata->flags & DRM_ENCON_DUMMY_USE_DPMS_GPIO) {
+		priv->gpio_dpms = pdata->gpio_dpms;
+		priv->dummy.dpms = sdrm_encon_pdev_gpio_dpms;
+		ret = gpio_request(priv->gpio_dpms, "dpms");
+		if (ret) {
+			if (pdata->flags & DRM_ENCON_DUMMY_USE_BL_GPIO)
+				gpio_free(priv->gpio_backlight);
+			return ret;
+		}
+	}
+
+	priv->encon = sdrm_encon_add_dummy(&priv->dummy);
+	if (!priv->encon)
+		return -EINVAL;
+
+	platform_set_drvdata(pdev, priv);
+
+	return 0;
+}
+
+static int __devexit sdrm_encon_platform_remove(struct platform_device *pdev)
+{
+	struct sdrm_encon_pdev_priv *priv = platform_get_drvdata(pdev);
+
+	sdrm_encon_remove_dummy(priv->encon);
+
+	return 0;
+}
+
+static struct platform_driver sdrm_encon_driver = {
+	.probe		= sdrm_encon_platform_probe,
+	.remove		= __devexit_p(sdrm_encon_platform_remove),
+	.driver		= {
+		.name	= "drm-encon-dummy",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(sdrm_encon_driver);
+
+MODULE_DESCRIPTION("drm encoder/connector dummy driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
diff --git a/include/drm/sdrm_encon.h b/include/drm/sdrm_encon.h
new file mode 100644
index 0000000..6a426df
--- /dev/null
+++ b/include/drm/sdrm_encon.h
@@ -0,0 +1,69 @@
+#ifndef __DRM_ENCON_H
+#define __DRM_ENCON_H
+
+struct drm_encoder_connector;
+
+struct drm_encoder_connector_funcs {
+	int (*get_modes)(struct drm_encoder_connector *encon);
+	int (*mode_valid)(struct drm_encoder_connector *encon,
+			struct drm_display_mode *mode);
+	void (*mode_set)(struct drm_encoder_connector *encon,
+			 struct drm_display_mode *mode,
+			 struct drm_display_mode *adjusted_mode);
+	void (*dpms)(struct drm_encoder_connector *encon, int mode);
+	enum drm_connector_status (*detect)(struct drm_encoder_connector *encon);
+	void (*commit)(struct drm_encoder_connector *encon);
+	bool (*mode_fixup)(struct drm_encoder_connector *encon,
+			struct drm_display_mode *mode,
+			struct drm_display_mode *adjusted_mode);
+	void (*prepare)(struct drm_encoder_connector *encon);
+	int (*set_property)(struct drm_encoder_connector *encon,
+			struct drm_property *property, uint64_t val);
+};
+
+struct drm_encoder_connector {
+	struct drm_connector connector;
+	struct sdrm_connector *sdrm_connector;
+	struct drm_encoder encoder;
+	struct sdrm_encoder *sdrm_encoder;
+	struct drm_encoder_connector_funcs *funcs;
+	struct module *owner;
+	void *encon_private;
+};
+
+void drm_encoder_connector_cleanup(struct drm_device *drm,
+		struct drm_encoder_connector *c);
+int drm_encoder_connector_register(const char *drm_name,
+		struct drm_encoder_connector *encon);
+void drm_encoder_connector_unregister(struct drm_encoder_connector *encon);
+
+struct sdrm_encon_dummy {
+	char *drm_name;
+	u32 possible_crtcs;
+	u32 possible_clones;
+	struct drm_display_mode *modes;
+	int num_modes;
+	struct module *owner;
+	void (*dpms)(struct sdrm_encon_dummy *dummy, int mode);
+	void *driver_priv;
+};
+
+struct drm_encoder_connector *sdrm_encon_add_dummy(struct sdrm_encon_dummy *);
+int sdrm_encon_remove_dummy(struct drm_encoder_connector *encon);
+
+struct sdrm_encon_dummy_pdata {
+	char *drm_name;
+	u32 possible_crtcs;
+	u32 possible_clones;
+	struct drm_display_mode *modes;
+	int num_modes;
+	int gpio_dpms;
+	int gpio_backlight;
+#define DRM_ENCON_DUMMY_USE_BL_GPIO		(1 << 0)
+#define DRM_ENCON_DUMMY_BL_GPIO_ACTIVE_LOW	(1 << 1)
+#define DRM_ENCON_DUMMY_USE_DPMS_GPIO		(1 << 2)
+#define DRM_ENCON_DUMMY_DPMS_GPIO_ACTIVE_LOW	(1 << 3)
+	unsigned flags;
+};
+
+#endif /* __DRM_ENCON_H */
-- 
1.7.9.5

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

* [PATCH 4/7] DRM: Add sdrm 1:1 encoder - connector helper
@ 2012-04-11 15:33   ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: dri-devel; +Cc: Sascha Hauer, linux-arm-kernel, kernel

Having a 1:1 relationship between an encoder and a connector is a
very common case. This patch allows for registering a combination
of both in a single device. It should allow implementing all
necessary callbacks for both the encoder and the connector, but
most calls are optional leaving the simplest encoder - connector
which is purely dummy and only passes a drm mode to the core.
This is a common case on embedded systems where the parallel data
lines are directly connected to a single display.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/gpu/drm/sdrm/Kconfig            |    3 +
 drivers/gpu/drm/sdrm/Makefile           |    3 +
 drivers/gpu/drm/sdrm/sdrm_encon.c       |  211 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/sdrm/sdrm_encon_dummy.c |  193 ++++++++++++++++++++++++++++
 include/drm/sdrm_encon.h                |   69 ++++++++++
 5 files changed, 479 insertions(+)
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_encon.c
 create mode 100644 drivers/gpu/drm/sdrm/sdrm_encon_dummy.c
 create mode 100644 include/drm/sdrm_encon.h

diff --git a/drivers/gpu/drm/sdrm/Kconfig b/drivers/gpu/drm/sdrm/Kconfig
index 2424b2c..456ac07 100644
--- a/drivers/gpu/drm/sdrm/Kconfig
+++ b/drivers/gpu/drm/sdrm/Kconfig
@@ -6,3 +6,6 @@ config DRM_SDRM
 	select FB_CFB_COPYAREA
 	select FB_CFB_IMAGEBLIT
 
+config DRM_SDRM_ENCON
+	tristate
+	depends on DRM_SDRM
diff --git a/drivers/gpu/drm/sdrm/Makefile b/drivers/gpu/drm/sdrm/Makefile
index c603f1b..cf57be2 100644
--- a/drivers/gpu/drm/sdrm/Makefile
+++ b/drivers/gpu/drm/sdrm/Makefile
@@ -1,2 +1,5 @@
 drm-sdrm-objs := sdrm.o sdrm_gem.o sdrm_fbdev.o sdrm_fb.o
 obj-$(CONFIG_DRM_SDRM) += drm-sdrm.o
+
+drm-sdrm-encon-objs := sdrm_encon_dummy.o sdrm_encon.o
+obj-$(CONFIG_DRM_SDRM_ENCON) += drm-sdrm-encon.o
diff --git a/drivers/gpu/drm/sdrm/sdrm_encon.c b/drivers/gpu/drm/sdrm/sdrm_encon.c
new file mode 100644
index 0000000..16f7e4c
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm_encon.c
@@ -0,0 +1,211 @@
+/*
+ * Implementation of a 1:1 relationship for drm encoders and connectors
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+#include <linux/module.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder_slave.h>
+#include <drm/sdrm_encon.h>
+#include <drm/sdrm.h>
+
+#define con_to_encon(x) container_of(x, struct drm_encoder_connector, connector)
+#define enc_to_encon(x) container_of(x, struct drm_encoder_connector, encoder)
+
+static enum drm_connector_status connector_detect(struct drm_connector *connector,
+		bool force)
+{
+	struct drm_encoder_connector *encon = con_to_encon(connector);
+
+	if (encon->funcs->detect)
+		return encon->funcs->detect(encon);
+
+	return connector_status_connected;
+}
+
+static int connector_set_property(struct drm_connector *connector,
+		struct drm_property *property, uint64_t val)
+{
+	struct drm_encoder_connector *encon = con_to_encon(connector);
+
+	if (encon->funcs->set_property)
+		return encon->funcs->set_property(encon, property, val);
+
+	return -EINVAL;
+}
+
+static void connector_destroy(struct drm_connector *connector)
+{
+	/* do not free here */
+}
+
+static int connector_get_modes(struct drm_connector *connector)
+{
+	struct drm_encoder_connector *encon = con_to_encon(connector);
+
+	if (encon->funcs->get_modes)
+		return encon->funcs->get_modes(encon);
+
+	return 0;
+}
+
+static int connector_mode_valid(struct drm_connector *connector,
+			  struct drm_display_mode *mode)
+{
+	struct drm_encoder_connector *encon = con_to_encon(connector);
+
+	if (encon->funcs && encon->funcs->mode_valid)
+		return encon->funcs->mode_valid(encon, mode);
+
+	return 0;
+}
+
+static struct drm_encoder *connector_best_encoder(struct drm_connector *connector)
+{
+	struct drm_encoder_connector *encon = con_to_encon(connector);
+
+	return &encon->encoder;
+}
+
+static void encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_encoder_connector *encon = enc_to_encon(encoder);
+
+	if (encon->funcs->dpms)
+		encon->funcs->dpms(encon, mode);
+}
+
+static bool encoder_mode_fixup(struct drm_encoder *encoder,
+			   struct drm_display_mode *mode,
+			   struct drm_display_mode *adjusted_mode)
+{
+	struct drm_encoder_connector *encon = enc_to_encon(encoder);
+
+	if (encon->funcs->mode_fixup)
+		return encon->funcs->mode_fixup(encon, mode, adjusted_mode);
+
+	return true;
+}
+
+static void encoder_prepare(struct drm_encoder *encoder)
+{
+	struct drm_encoder_connector *encon = enc_to_encon(encoder);
+
+	if (encon->funcs->prepare)
+		encon->funcs->prepare(encon);
+}
+
+static void encoder_commit(struct drm_encoder *encoder)
+{
+	struct drm_encoder_connector *encon = enc_to_encon(encoder);
+
+	if (encon->funcs->commit)
+		encon->funcs->commit(encon);
+}
+
+static void encoder_mode_set(struct drm_encoder *encoder,
+			 struct drm_display_mode *mode,
+			 struct drm_display_mode *adjusted_mode)
+{
+	struct drm_encoder_connector *encon = enc_to_encon(encoder);
+
+	if (encon->funcs->mode_set)
+		encon->funcs->mode_set(encon, mode, adjusted_mode);
+}
+
+static void encoder_disable(struct drm_encoder *encoder)
+{
+}
+
+static void encoder_destroy(struct drm_encoder *encoder)
+{
+	/* do not free here */
+}
+
+struct drm_connector_funcs connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = connector_detect,
+	.set_property = connector_set_property,
+	.destroy = connector_destroy,
+};
+
+struct drm_connector_helper_funcs connector_helper_funcs = {
+	.get_modes = connector_get_modes,
+	.mode_valid = connector_mode_valid,
+	.best_encoder = connector_best_encoder,
+};
+
+static struct drm_encoder_funcs encoder_funcs = {
+	.destroy = encoder_destroy,
+};
+
+static struct drm_encoder_helper_funcs encoder_helper_funcs = {
+	.dpms = encoder_dpms,
+	.mode_fixup = encoder_mode_fixup,
+	.prepare = encoder_prepare,
+	.commit = encoder_commit,
+	.mode_set = encoder_mode_set,
+	.disable = encoder_disable,
+};
+
+int drm_encoder_connector_register(const char *drm_name, struct drm_encoder_connector *encon)
+{
+	drm_mode_connector_attach_encoder(&encon->connector, &encon->encoder);
+
+	encon->connector.funcs = &connector_funcs;
+	encon->encoder.funcs = &encoder_funcs;
+	drm_encoder_helper_add(&encon->encoder, &encoder_helper_funcs);
+	encon->sdrm_encoder = sdrm_add_encoder(drm_name, &encon->encoder,
+			encon->owner);
+	if (!encon->sdrm_encoder) {
+		pr_err("%s: adding encoder failed\n", __func__);
+		return -EINVAL;
+	}
+
+	drm_connector_helper_add(&encon->connector, &connector_helper_funcs);
+
+	encon->sdrm_connector = sdrm_add_connector(drm_name, &encon->connector,
+			encon->owner);
+	if (!encon->sdrm_connector) {
+		pr_err("%s: adding connector failed\n", __func__);
+		return -EINVAL;
+	}
+
+	encon->connector.encoder = &encon->encoder;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(drm_encoder_connector_register);
+
+void drm_encoder_connector_unregister(struct drm_encoder_connector *c)
+{
+	struct drm_connector *connector = &c->connector;
+	struct drm_encoder *encoder = &c->encoder;
+
+	drm_sysfs_connector_remove(connector);
+	drm_mode_connector_detach_encoder(connector, encoder);
+	drm_encoder_cleanup(encoder);
+	drm_connector_cleanup(connector);
+}
+EXPORT_SYMBOL_GPL(drm_encoder_connector_unregister);
+
+MODULE_DESCRIPTION("drm encoder/connector driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sdrm/sdrm_encon_dummy.c b/drivers/gpu/drm/sdrm/sdrm_encon_dummy.c
new file mode 100644
index 0000000..a88b884
--- /dev/null
+++ b/drivers/gpu/drm/sdrm/sdrm_encon_dummy.c
@@ -0,0 +1,193 @@
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <drm/drmP.h>
+#include <drm/sdrm_encon.h>
+#include <drm/sdrm.h>
+
+struct sdrm_encon_dummy_private {
+	struct drm_encoder_connector encon;
+	struct drm_display_mode *modes;
+	int num_modes;
+};
+
+static int dummy_get_modes(struct drm_encoder_connector *encon)
+{
+	struct sdrm_encon_dummy_private *priv = encon->encon_private;
+	struct drm_display_mode *mode;
+	struct drm_device *drm = encon->connector.dev;
+	int i, ret = 0;
+
+	if (!priv->num_modes)
+		return 0;
+
+	for (i = 0; i < priv->num_modes; i++) {
+		mode = drm_mode_duplicate(drm, &priv->modes[i]);
+		if (mode == NULL)
+			return 0;
+
+		mode->base.type = DRM_MODE_OBJECT_MODE;
+
+		drm_mode_probed_add(&encon->connector, mode);
+		ret++; 
+	}
+	return ret;
+}
+
+static struct drm_encoder_connector_funcs dummy_funcs = {
+	.get_modes = dummy_get_modes,
+};
+
+/*
+ * sdrm_encon_add_dummy - add a dummy encoder/connector
+ *
+ * All callbacks of a dummy encoder/connector are no-ops, only
+ * an array of modes can be provided.
+ */
+struct drm_encoder_connector *sdrm_encon_add_dummy(struct sdrm_encon_dummy *dummy)
+{
+	struct sdrm_encon_dummy_private *priv;
+	struct drm_encoder_connector *encon;
+	int ret;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	encon = &priv->encon;
+	priv->modes = dummy->modes;
+	priv->num_modes = dummy->num_modes;
+	encon->funcs = &dummy_funcs;
+	encon->encon_private = priv;
+	encon->connector.encoder = &encon->encoder;
+	encon->encoder.possible_crtcs = dummy->possible_crtcs;
+	encon->encoder.possible_clones = dummy->possible_clones;
+	encon->owner = dummy->owner;
+
+	ret = drm_encoder_connector_register(dummy->drm_name, encon);
+	if (ret) {
+		kfree(priv);
+		return NULL;
+	}
+
+	return encon;
+}
+EXPORT_SYMBOL_GPL(sdrm_encon_add_dummy);
+
+int sdrm_encon_remove_dummy(struct drm_encoder_connector *encon)
+{
+	struct sdrm_encon_dummy_private *priv = encon->encon_private;
+
+	sdrm_remove_connector(encon->sdrm_connector);
+	sdrm_remove_encoder(encon->sdrm_encoder);
+
+	kfree(priv);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdrm_encon_remove_dummy);
+
+struct sdrm_encon_pdev_priv {
+	struct sdrm_encon_dummy dummy;
+	struct drm_encoder_connector *encon;
+	unsigned int flags;
+	int gpio_dpms;
+	int gpio_backlight;	
+};
+
+static void sdrm_encon_pdev_gpio_dpms(struct sdrm_encon_dummy *dummy, int mode)
+{
+	struct sdrm_encon_pdev_priv *priv = container_of(dummy,
+			struct sdrm_encon_pdev_priv, dummy);
+	int gpio_backlight = 0, gpio_dpms = 0;
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		printk("%s: on\n", __func__);
+		gpio_backlight = 1;
+		gpio_dpms = 1;
+	default:
+		printk("%s: off\n", __func__);
+		break;
+	}
+
+	if (priv->flags == DRM_ENCON_DUMMY_BL_GPIO_ACTIVE_LOW)
+		gpio_backlight = !gpio_backlight;
+	if (priv->flags == DRM_ENCON_DUMMY_DPMS_GPIO_ACTIVE_LOW)
+		gpio_dpms = !gpio_dpms;
+
+	if (priv->flags & DRM_ENCON_DUMMY_USE_DPMS_GPIO)
+		gpio_direction_output(priv->gpio_dpms, gpio_dpms);
+
+	if (priv->flags & DRM_ENCON_DUMMY_USE_BL_GPIO)
+		gpio_direction_output(priv->gpio_backlight, gpio_backlight);
+}
+
+static int __devinit sdrm_encon_platform_probe(struct platform_device *pdev)
+{
+	struct sdrm_encon_dummy_pdata *pdata = pdev->dev.platform_data;
+	struct sdrm_encon_pdev_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dummy.owner = THIS_MODULE;
+	priv->dummy.drm_name = pdata->drm_name;
+	priv->dummy.possible_crtcs = pdata->possible_crtcs;
+	priv->dummy.possible_clones = pdata->possible_clones;
+	priv->dummy.modes = pdata->modes;
+	priv->dummy.num_modes = pdata->num_modes;
+	priv->flags = pdata->flags;
+
+	if (pdata->flags & DRM_ENCON_DUMMY_USE_BL_GPIO) {
+		priv->gpio_backlight = pdata->gpio_backlight;
+		ret = gpio_request(priv->gpio_backlight, "backlight");
+		if (ret)
+			return ret;
+	}
+
+	if (pdata->flags & DRM_ENCON_DUMMY_USE_DPMS_GPIO) {
+		priv->gpio_dpms = pdata->gpio_dpms;
+		priv->dummy.dpms = sdrm_encon_pdev_gpio_dpms;
+		ret = gpio_request(priv->gpio_dpms, "dpms");
+		if (ret) {
+			if (pdata->flags & DRM_ENCON_DUMMY_USE_BL_GPIO)
+				gpio_free(priv->gpio_backlight);
+			return ret;
+		}
+	}
+
+	priv->encon = sdrm_encon_add_dummy(&priv->dummy);
+	if (!priv->encon)
+		return -EINVAL;
+
+	platform_set_drvdata(pdev, priv);
+
+	return 0;
+}
+
+static int __devexit sdrm_encon_platform_remove(struct platform_device *pdev)
+{
+	struct sdrm_encon_pdev_priv *priv = platform_get_drvdata(pdev);
+
+	sdrm_encon_remove_dummy(priv->encon);
+
+	return 0;
+}
+
+static struct platform_driver sdrm_encon_driver = {
+	.probe		= sdrm_encon_platform_probe,
+	.remove		= __devexit_p(sdrm_encon_platform_remove),
+	.driver		= {
+		.name	= "drm-encon-dummy",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(sdrm_encon_driver);
+
+MODULE_DESCRIPTION("drm encoder/connector dummy driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
diff --git a/include/drm/sdrm_encon.h b/include/drm/sdrm_encon.h
new file mode 100644
index 0000000..6a426df
--- /dev/null
+++ b/include/drm/sdrm_encon.h
@@ -0,0 +1,69 @@
+#ifndef __DRM_ENCON_H
+#define __DRM_ENCON_H
+
+struct drm_encoder_connector;
+
+struct drm_encoder_connector_funcs {
+	int (*get_modes)(struct drm_encoder_connector *encon);
+	int (*mode_valid)(struct drm_encoder_connector *encon,
+			struct drm_display_mode *mode);
+	void (*mode_set)(struct drm_encoder_connector *encon,
+			 struct drm_display_mode *mode,
+			 struct drm_display_mode *adjusted_mode);
+	void (*dpms)(struct drm_encoder_connector *encon, int mode);
+	enum drm_connector_status (*detect)(struct drm_encoder_connector *encon);
+	void (*commit)(struct drm_encoder_connector *encon);
+	bool (*mode_fixup)(struct drm_encoder_connector *encon,
+			struct drm_display_mode *mode,
+			struct drm_display_mode *adjusted_mode);
+	void (*prepare)(struct drm_encoder_connector *encon);
+	int (*set_property)(struct drm_encoder_connector *encon,
+			struct drm_property *property, uint64_t val);
+};
+
+struct drm_encoder_connector {
+	struct drm_connector connector;
+	struct sdrm_connector *sdrm_connector;
+	struct drm_encoder encoder;
+	struct sdrm_encoder *sdrm_encoder;
+	struct drm_encoder_connector_funcs *funcs;
+	struct module *owner;
+	void *encon_private;
+};
+
+void drm_encoder_connector_cleanup(struct drm_device *drm,
+		struct drm_encoder_connector *c);
+int drm_encoder_connector_register(const char *drm_name,
+		struct drm_encoder_connector *encon);
+void drm_encoder_connector_unregister(struct drm_encoder_connector *encon);
+
+struct sdrm_encon_dummy {
+	char *drm_name;
+	u32 possible_crtcs;
+	u32 possible_clones;
+	struct drm_display_mode *modes;
+	int num_modes;
+	struct module *owner;
+	void (*dpms)(struct sdrm_encon_dummy *dummy, int mode);
+	void *driver_priv;
+};
+
+struct drm_encoder_connector *sdrm_encon_add_dummy(struct sdrm_encon_dummy *);
+int sdrm_encon_remove_dummy(struct drm_encoder_connector *encon);
+
+struct sdrm_encon_dummy_pdata {
+	char *drm_name;
+	u32 possible_crtcs;
+	u32 possible_clones;
+	struct drm_display_mode *modes;
+	int num_modes;
+	int gpio_dpms;
+	int gpio_backlight;
+#define DRM_ENCON_DUMMY_USE_BL_GPIO		(1 << 0)
+#define DRM_ENCON_DUMMY_BL_GPIO_ACTIVE_LOW	(1 << 1)
+#define DRM_ENCON_DUMMY_USE_DPMS_GPIO		(1 << 2)
+#define DRM_ENCON_DUMMY_DPMS_GPIO_ACTIVE_LOW	(1 << 3)
+	unsigned flags;
+};
+
+#endif /* __DRM_ENCON_H */
-- 
1.7.9.5

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

* [PATCH 5/7] DRM: add i.MX kms simple driver
  2012-04-11 15:33 ` Sascha Hauer
@ 2012-04-11 15:33   ` Sascha Hauer
  -1 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: linux-arm-kernel

This adds a sdrm driver for the Freescale i.MX LCDC controller. It
is found on i.MX1, i.MX21, i.MX25 and i.MX27. Currently only the
base framebuffer is supported, no overlay. This has been tested
on an i.MX27 custom board with the xf86 modesetting driver.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm/plat-mxc/include/mach/imxfb.h |    8 +-
 drivers/gpu/drm/Kconfig                |    2 +
 drivers/gpu/drm/Makefile               |    1 +
 drivers/gpu/drm/imx/Kconfig            |    8 +
 drivers/gpu/drm/imx/Makefile           |    1 +
 drivers/gpu/drm/imx/imx-lcdc-crtc.c    |  506 ++++++++++++++++++++++++++++++++
 6 files changed, 525 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/imx/Kconfig
 create mode 100644 drivers/gpu/drm/imx/Makefile
 create mode 100644 drivers/gpu/drm/imx/imx-lcdc-crtc.c

diff --git a/arch/arm/plat-mxc/include/mach/imxfb.h b/arch/arm/plat-mxc/include/mach/imxfb.h
index 9de8f06..5d4aa61 100644
--- a/arch/arm/plat-mxc/include/mach/imxfb.h
+++ b/arch/arm/plat-mxc/include/mach/imxfb.h
@@ -80,5 +80,11 @@ struct imx_fb_platform_data {
 	void (*backlight_power)(int);
 };
 
-void set_imx_fb_info(struct imx_fb_platform_data *);
+/* drm platform data */
+struct imx_drm_platform_data {
+	u32 lscr1;
+	u32 pcr;
+	u32 pwmr;
+};
+
 #endif /* ifndef __MACH_IMXFB_H__ */
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 93d9f79..4650d65 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -188,3 +188,5 @@ source "drivers/gpu/drm/gma500/Kconfig"
 source "drivers/gpu/drm/udl/Kconfig"
 
 source "drivers/gpu/drm/sdrm/Kconfig"
+
+source "drivers/gpu/drm/imx/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 44c5949..b0c283e 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -43,4 +43,5 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_SDRM) += sdrm/
+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..d5ad7ed
--- /dev/null
+++ b/drivers/gpu/drm/imx/Kconfig
@@ -0,0 +1,8 @@
+config DRM_IMX_LCDC
+	tristate "DRM Support for Freescale i.MX1 and i.MX2"
+	depends on DRM && ARCH_MXC
+	select DRM_SDRM
+	select DRM_SDRM_ENCON
+	help
+	  Choose this if you have a i.MX1, i.MX21, i.MX25 or i.MX27 processor.
+
diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
new file mode 100644
index 0000000..cc84f65
--- /dev/null
+++ b/drivers/gpu/drm/imx/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DRM_IMX_LCDC) += imx-lcdc-crtc.o
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..620b04b
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-lcdc-crtc.c
@@ -0,0 +1,506 @@
+/*
+ * i.MX 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 <drm/sdrm.h>
+#include <drm/sdrm_encon.h>
+#include <mach/hardware.h>
+#include <mach/imxfb.h>
+#include <generated/mach-types.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 sdrm_crtc	*sdrm_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 sdrm_buf_entry *entry;
+	struct drm_framebuffer *fb = crtc->fb;
+	unsigned long phys;
+
+	entry = sdrm_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 sdrm_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);
+		sdrm_handle_pageflip(imx_crtc);
+		sdrm_crtc_vblank_put(imx_crtc->sdrm_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;
+	sdrm_crtc_vblank_get(imx_crtc->sdrm_crtc);
+
+	return 0;
+}
+
+static const struct drm_crtc_funcs imx_crtc_funcs = {
+	.page_flip = imx_page_flip,
+};
+
+static const struct sdrm_crtc_helper_funcs imx_sdrm_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;
+	struct imx_drm_platform_data *pdata = pdev->dev.platform_data;
+
+	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;
+	}
+
+	imx_crtc->sdrm_crtc = sdrm_add_crtc(dev_name(&pdev->dev),
+			&imx_crtc->base, &imx_crtc_funcs, &imx_helper_funcs,
+			&imx_sdrm_helper);
+	if (!imx_crtc->sdrm_crtc) {
+		ret = -EINVAL;
+		goto err_clk;
+	}
+
+	clk_prepare_enable(imx_crtc->clk);
+	imx_crtc->enabled = 1;
+
+	platform_set_drvdata(pdev, imx_crtc);
+
+	imx_crtc->pcr = pdata->pcr & PDATA_PCR;
+
+	if (imx_crtc->pcr != pdata->pcr)
+		dev_err(&pdev->dev, "invalid bits set in pcr: 0x%08x\n",
+				pdata->pcr & ~PDATA_PCR);
+
+	imx_crtc->lscr1 = pdata->lscr1;
+	imx_crtc->pwmr = pdata->pwmr;
+
+	ret = sdrm_init_drm(dev_name(&pdev->dev), pdev, 16);
+	if (ret) {
+		dev_err(&pdev->dev, "init drm failed with %d\n", ret);
+		goto err_init;
+	}
+
+	return 0;
+
+err_init:
+	sdrm_remove_crtc(imx_crtc->sdrm_crtc);
+	clk_disable_unprepare(imx_crtc->clk);
+err_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);
+
+	sdrm_exit_drm(dev_name(&pdev->dev));
+
+	writel(0, imx_crtc->regs + LCDC_LIER);
+
+	clk_disable_unprepare(imx_crtc->clk);
+	clk_put(imx_crtc->clk);
+
+	sdrm_remove_crtc(imx_crtc->sdrm_crtc);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver imx_crtc_driver = {
+	.remove		= __devexit_p(imx_crtc_remove),
+	.probe		= imx_crtc_probe,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(imx_crtc_driver);
+
+MODULE_DESCRIPTION("Freescale i.MX framebuffer driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5

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

* [PATCH 5/7] DRM: add i.MX kms simple driver
@ 2012-04-11 15:33   ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: dri-devel; +Cc: Sascha Hauer, linux-arm-kernel, kernel

This adds a sdrm driver for the Freescale i.MX LCDC controller. It
is found on i.MX1, i.MX21, i.MX25 and i.MX27. Currently only the
base framebuffer is supported, no overlay. This has been tested
on an i.MX27 custom board with the xf86 modesetting driver.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm/plat-mxc/include/mach/imxfb.h |    8 +-
 drivers/gpu/drm/Kconfig                |    2 +
 drivers/gpu/drm/Makefile               |    1 +
 drivers/gpu/drm/imx/Kconfig            |    8 +
 drivers/gpu/drm/imx/Makefile           |    1 +
 drivers/gpu/drm/imx/imx-lcdc-crtc.c    |  506 ++++++++++++++++++++++++++++++++
 6 files changed, 525 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/imx/Kconfig
 create mode 100644 drivers/gpu/drm/imx/Makefile
 create mode 100644 drivers/gpu/drm/imx/imx-lcdc-crtc.c

diff --git a/arch/arm/plat-mxc/include/mach/imxfb.h b/arch/arm/plat-mxc/include/mach/imxfb.h
index 9de8f06..5d4aa61 100644
--- a/arch/arm/plat-mxc/include/mach/imxfb.h
+++ b/arch/arm/plat-mxc/include/mach/imxfb.h
@@ -80,5 +80,11 @@ struct imx_fb_platform_data {
 	void (*backlight_power)(int);
 };
 
-void set_imx_fb_info(struct imx_fb_platform_data *);
+/* drm platform data */
+struct imx_drm_platform_data {
+	u32 lscr1;
+	u32 pcr;
+	u32 pwmr;
+};
+
 #endif /* ifndef __MACH_IMXFB_H__ */
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 93d9f79..4650d65 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -188,3 +188,5 @@ source "drivers/gpu/drm/gma500/Kconfig"
 source "drivers/gpu/drm/udl/Kconfig"
 
 source "drivers/gpu/drm/sdrm/Kconfig"
+
+source "drivers/gpu/drm/imx/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 44c5949..b0c283e 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -43,4 +43,5 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_SDRM) += sdrm/
+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..d5ad7ed
--- /dev/null
+++ b/drivers/gpu/drm/imx/Kconfig
@@ -0,0 +1,8 @@
+config DRM_IMX_LCDC
+	tristate "DRM Support for Freescale i.MX1 and i.MX2"
+	depends on DRM && ARCH_MXC
+	select DRM_SDRM
+	select DRM_SDRM_ENCON
+	help
+	  Choose this if you have a i.MX1, i.MX21, i.MX25 or i.MX27 processor.
+
diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
new file mode 100644
index 0000000..cc84f65
--- /dev/null
+++ b/drivers/gpu/drm/imx/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DRM_IMX_LCDC) += imx-lcdc-crtc.o
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..620b04b
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-lcdc-crtc.c
@@ -0,0 +1,506 @@
+/*
+ * i.MX 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 <drm/sdrm.h>
+#include <drm/sdrm_encon.h>
+#include <mach/hardware.h>
+#include <mach/imxfb.h>
+#include <generated/mach-types.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 sdrm_crtc	*sdrm_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 sdrm_buf_entry *entry;
+	struct drm_framebuffer *fb = crtc->fb;
+	unsigned long phys;
+
+	entry = sdrm_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 sdrm_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);
+		sdrm_handle_pageflip(imx_crtc);
+		sdrm_crtc_vblank_put(imx_crtc->sdrm_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;
+	sdrm_crtc_vblank_get(imx_crtc->sdrm_crtc);
+
+	return 0;
+}
+
+static const struct drm_crtc_funcs imx_crtc_funcs = {
+	.page_flip = imx_page_flip,
+};
+
+static const struct sdrm_crtc_helper_funcs imx_sdrm_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;
+	struct imx_drm_platform_data *pdata = pdev->dev.platform_data;
+
+	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;
+	}
+
+	imx_crtc->sdrm_crtc = sdrm_add_crtc(dev_name(&pdev->dev),
+			&imx_crtc->base, &imx_crtc_funcs, &imx_helper_funcs,
+			&imx_sdrm_helper);
+	if (!imx_crtc->sdrm_crtc) {
+		ret = -EINVAL;
+		goto err_clk;
+	}
+
+	clk_prepare_enable(imx_crtc->clk);
+	imx_crtc->enabled = 1;
+
+	platform_set_drvdata(pdev, imx_crtc);
+
+	imx_crtc->pcr = pdata->pcr & PDATA_PCR;
+
+	if (imx_crtc->pcr != pdata->pcr)
+		dev_err(&pdev->dev, "invalid bits set in pcr: 0x%08x\n",
+				pdata->pcr & ~PDATA_PCR);
+
+	imx_crtc->lscr1 = pdata->lscr1;
+	imx_crtc->pwmr = pdata->pwmr;
+
+	ret = sdrm_init_drm(dev_name(&pdev->dev), pdev, 16);
+	if (ret) {
+		dev_err(&pdev->dev, "init drm failed with %d\n", ret);
+		goto err_init;
+	}
+
+	return 0;
+
+err_init:
+	sdrm_remove_crtc(imx_crtc->sdrm_crtc);
+	clk_disable_unprepare(imx_crtc->clk);
+err_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);
+
+	sdrm_exit_drm(dev_name(&pdev->dev));
+
+	writel(0, imx_crtc->regs + LCDC_LIER);
+
+	clk_disable_unprepare(imx_crtc->clk);
+	clk_put(imx_crtc->clk);
+
+	sdrm_remove_crtc(imx_crtc->sdrm_crtc);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver imx_crtc_driver = {
+	.remove		= __devexit_p(imx_crtc_remove),
+	.probe		= imx_crtc_probe,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(imx_crtc_driver);
+
+MODULE_DESCRIPTION("Freescale i.MX framebuffer driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5

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

* [PATCH 6/7] ARM i.MX27 pcm038: Add sdrm support
  2012-04-11 15:33 ` Sascha Hauer
@ 2012-04-11 15:33   ` Sascha Hauer
  -1 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm/mach-imx/pcm970-baseboard.c |   78 +++++++++++++++++++++++++++++++++-
 1 file changed, 77 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-imx/pcm970-baseboard.c b/arch/arm/mach-imx/pcm970-baseboard.c
index 99afbc3..17758f8 100644
--- a/arch/arm/mach-imx/pcm970-baseboard.c
+++ b/arch/arm/mach-imx/pcm970-baseboard.c
@@ -20,6 +20,9 @@
 #include <linux/irq.h>
 #include <linux/platform_device.h>
 #include <linux/can/platform/sja1000.h>
+#include <drm/drmP.h>
+#include <drm/sdrm_encon.h>
+#include <generated/mach-types.h>
 
 #include <asm/mach/arch.h>
 
@@ -224,8 +227,81 @@ void __init pcm970_baseboard_init(void)
 	mxc_gpio_setup_multiple_pins(pcm970_pins, ARRAY_SIZE(pcm970_pins),
 			"PCM970");
 
-	imx27_add_imx_fb(&pcm038_fb_data);
+//	imx27_add_imx_fb(&pcm038_fb_data);
 	mxc_gpio_mode(GPIO_PORTC | 28 | GPIO_GPIO | GPIO_IN);
 	imx27_add_mxc_mmc(1, &sdhc_pdata);
 	platform_device_register(&pcm970_sja1000);
 }
+
+static struct platform_device *__init imx_add_imx_drm(
+		struct imx_drm_platform_data *pdata)
+{
+	struct resource res[] = {
+		{
+			.start = MX27_LCDC_BASE_ADDR,
+			.end = MX27_LCDC_BASE_ADDR + SZ_4K - 1,
+			.flags = IORESOURCE_MEM,
+		}, {
+			.start = MX27_INT_LCDC,
+			.end = MX27_INT_LCDC,
+			.flags = IORESOURCE_IRQ,
+		},
+	};
+	return imx_add_platform_device_dmamask("imx-drm", -1,
+			res, ARRAY_SIZE(res),
+			pdata, sizeof(*pdata), DMA_BIT_MASK(32));
+}
+
+static struct drm_display_mode pcm038_modes[] = {
+	{
+		.name = "Sharp-LQ035Q7",
+		.vrefresh = 60,
+		.clock = 5300,
+		.hdisplay = 240,
+		.hsync_start = 240 + 16,
+		.hsync_end = 240 + 16 + 7,
+		.htotal = 240 + 16 + 7 + 5,
+		.vdisplay = 320,
+		.vsync_start = 320 + 9,
+		.vsync_end = 320 + 9 + 1,
+		.vtotal = 320 + 9 + 1 + 7,
+		.type = 0x0,
+		.flags = 0x0,
+	},
+};
+
+static struct sdrm_encon_dummy_pdata pcm038_encon_data = {
+	.drm_name = "imx-drm",
+	.possible_crtcs = 0x1,
+	.possible_clones = 0x1,
+	.modes = pcm038_modes,
+	.num_modes = ARRAY_SIZE(pcm038_modes),
+};
+
+static struct imx_drm_platform_data drm_pdata = {
+	/*
+	 * - HSYNC active high
+	 * - VSYNC active high
+	 * - clk notenabled while idle
+	 * - clock not inverted
+	 * - data not inverted
+	 * - data enable low active
+	 * - enable sharp mode
+	 */
+	.pcr		= 0xf00080c0,
+	.lscr1		= 0x00120300,
+	.pwmr		= 0x00a903ff,
+};
+
+static int add_drm(void)
+{
+	if (!machine_is_pcm038())
+		return 0;
+
+	imx_add_imx_drm(&drm_pdata);
+
+	platform_device_register_data(NULL, "drm-encon-dummy", 0,
+			&pcm038_encon_data, sizeof(pcm038_encon_data));
+	return 0;
+}
+device_initcall(add_drm);
-- 
1.7.9.5

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

* [PATCH 6/7] ARM i.MX27 pcm038: Add sdrm support
@ 2012-04-11 15:33   ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: dri-devel; +Cc: Sascha Hauer, linux-arm-kernel, kernel

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm/mach-imx/pcm970-baseboard.c |   78 +++++++++++++++++++++++++++++++++-
 1 file changed, 77 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-imx/pcm970-baseboard.c b/arch/arm/mach-imx/pcm970-baseboard.c
index 99afbc3..17758f8 100644
--- a/arch/arm/mach-imx/pcm970-baseboard.c
+++ b/arch/arm/mach-imx/pcm970-baseboard.c
@@ -20,6 +20,9 @@
 #include <linux/irq.h>
 #include <linux/platform_device.h>
 #include <linux/can/platform/sja1000.h>
+#include <drm/drmP.h>
+#include <drm/sdrm_encon.h>
+#include <generated/mach-types.h>
 
 #include <asm/mach/arch.h>
 
@@ -224,8 +227,81 @@ void __init pcm970_baseboard_init(void)
 	mxc_gpio_setup_multiple_pins(pcm970_pins, ARRAY_SIZE(pcm970_pins),
 			"PCM970");
 
-	imx27_add_imx_fb(&pcm038_fb_data);
+//	imx27_add_imx_fb(&pcm038_fb_data);
 	mxc_gpio_mode(GPIO_PORTC | 28 | GPIO_GPIO | GPIO_IN);
 	imx27_add_mxc_mmc(1, &sdhc_pdata);
 	platform_device_register(&pcm970_sja1000);
 }
+
+static struct platform_device *__init imx_add_imx_drm(
+		struct imx_drm_platform_data *pdata)
+{
+	struct resource res[] = {
+		{
+			.start = MX27_LCDC_BASE_ADDR,
+			.end = MX27_LCDC_BASE_ADDR + SZ_4K - 1,
+			.flags = IORESOURCE_MEM,
+		}, {
+			.start = MX27_INT_LCDC,
+			.end = MX27_INT_LCDC,
+			.flags = IORESOURCE_IRQ,
+		},
+	};
+	return imx_add_platform_device_dmamask("imx-drm", -1,
+			res, ARRAY_SIZE(res),
+			pdata, sizeof(*pdata), DMA_BIT_MASK(32));
+}
+
+static struct drm_display_mode pcm038_modes[] = {
+	{
+		.name = "Sharp-LQ035Q7",
+		.vrefresh = 60,
+		.clock = 5300,
+		.hdisplay = 240,
+		.hsync_start = 240 + 16,
+		.hsync_end = 240 + 16 + 7,
+		.htotal = 240 + 16 + 7 + 5,
+		.vdisplay = 320,
+		.vsync_start = 320 + 9,
+		.vsync_end = 320 + 9 + 1,
+		.vtotal = 320 + 9 + 1 + 7,
+		.type = 0x0,
+		.flags = 0x0,
+	},
+};
+
+static struct sdrm_encon_dummy_pdata pcm038_encon_data = {
+	.drm_name = "imx-drm",
+	.possible_crtcs = 0x1,
+	.possible_clones = 0x1,
+	.modes = pcm038_modes,
+	.num_modes = ARRAY_SIZE(pcm038_modes),
+};
+
+static struct imx_drm_platform_data drm_pdata = {
+	/*
+	 * - HSYNC active high
+	 * - VSYNC active high
+	 * - clk notenabled while idle
+	 * - clock not inverted
+	 * - data not inverted
+	 * - data enable low active
+	 * - enable sharp mode
+	 */
+	.pcr		= 0xf00080c0,
+	.lscr1		= 0x00120300,
+	.pwmr		= 0x00a903ff,
+};
+
+static int add_drm(void)
+{
+	if (!machine_is_pcm038())
+		return 0;
+
+	imx_add_imx_drm(&drm_pdata);
+
+	platform_device_register_data(NULL, "drm-encon-dummy", 0,
+			&pcm038_encon_data, sizeof(pcm038_encon_data));
+	return 0;
+}
+device_initcall(add_drm);
-- 
1.7.9.5

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

* [PATCH 7/7] DRM: add PXA kms simple driver
  2012-04-11 15:33 ` Sascha Hauer
@ 2012-04-11 15:33   ` Sascha Hauer
  -1 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: linux-arm-kernel

From: Philipp Zabel <p.zabel@pengutronix.de>

This adds a sdrm driver for the PXA LCDC controller.
Currently only the base framebuffer is supported, no overlay.
There is no support for smart panels and so far only RGB565
pixel format is supported.
Tested on PHYTEC PCM-990 development board.

Also adds EOFINT and SOFINT bit definitions for the LDCMD registers.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm/mach-pxa/include/mach/regs-lcd.h |    2 +
 drivers/gpu/drm/Kconfig                   |    2 +
 drivers/gpu/drm/Makefile                  |    1 +
 drivers/gpu/drm/pxa/Kconfig               |   12 +
 drivers/gpu/drm/pxa/Makefile              |    1 +
 drivers/gpu/drm/pxa/pxa-lcdc-crtc.c       |  845 +++++++++++++++++++++++++++++
 6 files changed, 863 insertions(+)
 create mode 100644 drivers/gpu/drm/pxa/Kconfig
 create mode 100644 drivers/gpu/drm/pxa/Makefile
 create mode 100644 drivers/gpu/drm/pxa/pxa-lcdc-crtc.c

diff --git a/arch/arm/mach-pxa/include/mach/regs-lcd.h b/arch/arm/mach-pxa/include/mach/regs-lcd.h
index f82dcea..69699ce 100644
--- a/arch/arm/mach-pxa/include/mach/regs-lcd.h
+++ b/arch/arm/mach-pxa/include/mach/regs-lcd.h
@@ -158,6 +158,8 @@
 #define LCSR1_EOF(x)	(1 << ((x) + 7))  /* End of Frame Status */
 #define LCSR1_SOF(x)	(1 << ((x) - 1))  /* Start of Frame Status */
 
+#define LDCMD_EOFINT	(1 << 21)	/* instructs DMA to set the EOF status bit */
+#define LCDMD_SOFINT	(1 << 22)	/* instructs DMA to set the SOF status bit */
 #define LDCMD_PAL	(1 << 26)	/* instructs DMA to load palette buffer */
 
 /* overlay control registers */
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 4650d65..6a68e33 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -190,3 +190,5 @@ source "drivers/gpu/drm/udl/Kconfig"
 source "drivers/gpu/drm/sdrm/Kconfig"
 
 source "drivers/gpu/drm/imx/Kconfig"
+
+source "drivers/gpu/drm/pxa/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index b0c283e..0f16044 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -44,4 +44,5 @@ obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_SDRM) += sdrm/
 obj-$(CONFIG_DRM_IMX) +=imx/
+obj-$(CONFIG_DRM_PXA) +=pxa/
 obj-y			+= i2c/
diff --git a/drivers/gpu/drm/pxa/Kconfig b/drivers/gpu/drm/pxa/Kconfig
new file mode 100644
index 0000000..bb2e7e0
--- /dev/null
+++ b/drivers/gpu/drm/pxa/Kconfig
@@ -0,0 +1,12 @@
+config DRM_PXA
+	tristate "DRM Support for Marvell PXA"
+	depends on DRM && ARCH_PXA
+
+config DRM_PXA_LCDC
+	tristate "DRM Support for PXA2xx and PXA3xx"
+	depends on DRM && DRM_PXA
+	select DRM_SDRM
+	select DRM_SDRM_ENCON
+	help
+	  Choose this if you have a PXA2xx or PXA3xx processor
+	  with a display connected to the internal LCD controller.
diff --git a/drivers/gpu/drm/pxa/Makefile b/drivers/gpu/drm/pxa/Makefile
new file mode 100644
index 0000000..bb5cf1f
--- /dev/null
+++ b/drivers/gpu/drm/pxa/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DRM_PXA_LCDC) += pxa-lcdc-crtc.o
diff --git a/drivers/gpu/drm/pxa/pxa-lcdc-crtc.c b/drivers/gpu/drm/pxa/pxa-lcdc-crtc.c
new file mode 100644
index 0000000..3d0e6e9
--- /dev/null
+++ b/drivers/gpu/drm/pxa/pxa-lcdc-crtc.c
@@ -0,0 +1,845 @@
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/sdrm.h>
+#include <drm/sdrm_encon.h>
+
+#include <mach/hardware.h>
+#include <mach/pxafb.h>
+
+/* PXA LCD DMA descriptor */
+struct pxafb_dma_descriptor {
+	unsigned int fdadr;
+	unsigned int fsadr;
+	unsigned int fidr;
+	unsigned int ldcmd;
+};
+
+enum {
+	PAL_NONE	= -1,
+	PAL_BASE	= 0,
+	PAL_OV1		= 1,
+	PAL_OV2		= 2,
+	PAL_MAX,
+};
+
+enum {
+	DMA_BASE	= 0,
+	DMA_UPPER	= 0,
+	DMA_LOWER	= 1,
+	DMA_OV1		= 1,
+	DMA_OV2_Y	= 2,
+	DMA_OV2_Cb	= 3,
+	DMA_OV2_Cr	= 4,
+	DMA_CURSOR	= 5,
+	DMA_CMD		= 6,
+	DMA_MAX,
+};
+
+/* maximum palette size - 256 entries, each 4 bytes long */
+#define PALETTE_SIZE	(256 * 4)
+#define CMD_BUFF_SIZE	(1024 * 50)
+
+/* NOTE: the palette and frame dma descriptors are doubled to allow
+ * the 2nd set for branch settings (FBRx)
+ */
+struct pxafb_dma_buff {
+	unsigned char palette[PAL_MAX * PALETTE_SIZE];
+	uint16_t cmd_buff[CMD_BUFF_SIZE];
+	struct pxafb_dma_descriptor pal_desc[PAL_MAX * 2];
+	struct pxafb_dma_descriptor dma_desc[DMA_MAX * 2];
+};
+
+struct pxa_crtc {
+	struct drm_crtc		base;
+	struct sdrm_crtc	*sdrm_crtc;
+	int			di_no;
+	int			enabled;
+
+	void __iomem		*mmio_base;
+
+	struct pxafb_dma_buff	*dma_buff;
+	size_t			dma_buff_size;
+	dma_addr_t		dma_buff_phys;
+	dma_addr_t		fdadr[DMA_MAX * 2];
+
+	unsigned long		video_mem_phys;	/* physical address of frame buffer */
+
+	u32			lccr0;
+	u32			lccr3;
+	u32			lccr4;
+	u32			cmap_inverse:1,
+				cmap_static:1,
+				unused:30;
+
+	u32			reg_lccr0;
+	u32			reg_lccr1;
+	u32			reg_lccr2;
+	u32			reg_lccr3;
+	u32			reg_lccr4;
+
+	struct completion	disable_done;
+
+	struct clk		*clk;
+	struct device		*dev;
+	int			vblank_enable;
+
+	struct drm_pending_vblank_event *page_flip_event;
+	struct drm_framebuffer	*newfb;
+
+	void (*lcd_power)(int, struct fb_var_screeninfo *);
+};
+
+#define to_pxa_crtc(x) container_of(x, struct pxa_crtc, base)
+
+static inline unsigned long
+lcd_readl(struct pxa_crtc *pxa_crtc, unsigned int off)
+{
+	return __raw_readl(pxa_crtc->mmio_base + off);
+}
+
+static inline void
+lcd_writel(struct pxa_crtc *pxa_crtc, unsigned int off, unsigned long val)
+{
+	__raw_writel(val, pxa_crtc->mmio_base + off);
+}
+
+static void pxa_crtc_load_lut(struct drm_crtc *crtc)
+{
+}
+
+const char *fourcc_to_str(u32 fourcc)
+{
+	static char buf[5];
+
+	*(u32 *)buf = fourcc;
+	buf[4] = 0;
+
+	return buf;
+}
+
+static inline void __pxa_crtc_lcd_power(struct pxa_crtc *pxa_crtc, int on)
+{
+	dev_dbg(pxa_crtc->dev, "LCD power o%s\n", on ? "n" : "ff");
+
+	if (pxa_crtc->lcd_power)
+		pxa_crtc->lcd_power(on, NULL); /* there is no fb_info */
+}
+
+static void pxa_crtc_enable_controller(struct pxa_crtc *pxa_crtc)
+{
+	dev_dbg(pxa_crtc->dev, "Enabling LCD controller\n");
+
+	/* enable LCD controller clock */
+	clk_prepare_enable(pxa_crtc->clk);
+
+	if (pxa_crtc->lccr0 & LCCR0_LCDT)
+		return;
+
+	/* Sequence from 11.7.10 */
+	lcd_writel(pxa_crtc, LCCR4, pxa_crtc->reg_lccr4);
+	lcd_writel(pxa_crtc, LCCR3, pxa_crtc->reg_lccr3);
+	lcd_writel(pxa_crtc, LCCR2, pxa_crtc->reg_lccr2);
+	lcd_writel(pxa_crtc, LCCR1, pxa_crtc->reg_lccr1);
+	lcd_writel(pxa_crtc, LCCR0, pxa_crtc->reg_lccr0 & ~LCCR0_ENB);
+
+	lcd_writel(pxa_crtc, FDADR0, pxa_crtc->fdadr[0]);
+	if (pxa_crtc->lccr0 & LCCR0_SDS)
+		lcd_writel(pxa_crtc, FDADR1, pxa_crtc->fdadr[1]);
+
+	lcd_writel(pxa_crtc, LCCR0, pxa_crtc->reg_lccr0 | LCCR0_ENB);
+}
+
+static void pxa_crtc_disable_controller(struct pxa_crtc *pxa_crtc)
+{
+	uint32_t lccr0;
+
+	dev_dbg(pxa_crtc->dev, "Disabling LCD controller\n");
+
+	/* Clear LCD Status Register */
+	lcd_writel(pxa_crtc, LCSR, 0xffffffff);
+
+	lccr0 = lcd_readl(pxa_crtc, LCCR0) & ~LCCR0_LDM;
+	lcd_writel(pxa_crtc, LCCR0, lccr0);
+	lcd_writel(pxa_crtc, LCCR0, lccr0 | LCCR0_DIS);
+
+	wait_for_completion_timeout(&pxa_crtc->disable_done, 200 * HZ / 1000);
+
+	/* disable LCD controller clock */
+	clk_disable_unprepare(pxa_crtc->clk);
+}
+
+static void pxa_crtc_decode_mach_info(struct pxa_crtc *pxa_crtc,
+				      struct pxafb_mach_info *inf)
+{
+	unsigned int lcd_conn = inf->lcd_conn;
+
+	pxa_crtc->cmap_inverse = inf->cmap_inverse;
+	pxa_crtc->cmap_static = inf->cmap_static;
+	pxa_crtc->lccr4 = inf->lccr4;
+
+	switch (lcd_conn & LCD_TYPE_MASK) {
+	case LCD_TYPE_MONO_STN:
+		pxa_crtc->lccr0 = LCCR0_CMS;
+		break;
+	case LCD_TYPE_MONO_DSTN:
+		pxa_crtc->lccr0 = LCCR0_CMS | LCCR0_SDS;
+		break;
+	case LCD_TYPE_COLOR_STN:
+		pxa_crtc->lccr0 = 0;
+		break;
+	case LCD_TYPE_COLOR_DSTN:
+		pxa_crtc->lccr0 = LCCR0_SDS;
+		break;
+	case LCD_TYPE_COLOR_TFT:
+		pxa_crtc->lccr0 = LCCR0_PAS;
+		break;
+	case LCD_TYPE_SMART_PANEL:
+		pxa_crtc->lccr0 = LCCR0_LCDT | LCCR0_PAS;
+		break;
+	default:
+		/* fall back to backward compatibility way */
+		pxa_crtc->lccr0 = inf->lccr0;
+		pxa_crtc->lccr3 = inf->lccr3;
+		return;
+	}
+
+	if (lcd_conn == LCD_MONO_STN_8BPP)
+		pxa_crtc->lccr0 |= LCCR0_DPD;
+
+	pxa_crtc->lccr0 |= (lcd_conn & LCD_ALTERNATE_MAPPING) ? LCCR0_LDDALT : 0;
+
+	pxa_crtc->lccr3 = LCCR3_Acb((inf->lcd_conn >> 10) & 0xff);
+	pxa_crtc->lccr3 |= (lcd_conn & LCD_BIAS_ACTIVE_LOW) ? LCCR3_OEP : 0;
+	pxa_crtc->lccr3 |= (lcd_conn & LCD_PCLK_EDGE_FALL)  ? LCCR3_PCP : 0;
+}
+
+/*
+ * Calculate the PCD value from the clock rate (in picoseconds).
+ * We take account of the PPCR clock setting.
+ * From PXA Developer's Manual:
+ *
+ *   PixelClock =      LCLK
+ *                -------------
+ *                2 ( PCD + 1 )
+ *
+ *   PCD =      LCLK
+ *         ------------- - 1
+ *         2(PixelClock)
+ *
+ * Where:
+ *   LCLK = LCD/Memory Clock
+ *   PCD = LCCR3[7:0]
+ *
+ * PixelClock here is in Hz while the pixclock argument given is the
+ * period in picoseconds. Hence PixelClock = 1 / ( pixclock * 10^-12 )
+ *
+ * The function get_lclk_frequency_10khz returns LCLK in units of
+ * 10khz. Calling the result of this function lclk gives us the
+ * following
+ *
+ *    PCD = (lclk * 10^4 ) * ( pixclock * 10^-12 )
+ *          -------------------------------------- - 1
+ *                          2
+ *
+ * Factoring the 10^4 and 10^-12 out gives 10^-8 == 1 / 100000000 as used below.
+ */
+static inline unsigned int get_pcd(struct pxa_crtc *pxa_crtc,
+				   unsigned int pixclock)
+{
+	unsigned long long pcd;
+
+	/* FIXME: Need to take into account Double Pixel Clock mode
+	 * (DPC) bit? or perhaps set it based on the various clock
+	 * speeds */
+	pcd = (unsigned long long)(clk_get_rate(pxa_crtc->clk) / 10000);
+	pcd *= pixclock;
+	do_div(pcd, 100000000 * 2);
+	/* no need for this, since we should subtract 1 anyway. they cancel */
+	/* pcd += 1; */ /* make up for integer math truncations */
+	return (unsigned int)pcd;
+}
+
+static int setup_frame_dma(struct pxa_crtc *pxa_crtc, int dma, int pal,
+			   unsigned long start, size_t size)
+{
+	struct pxafb_dma_descriptor *dma_desc;
+	unsigned int dma_desc_off;
+
+	if (dma < 0 || dma >= DMA_MAX * 2)
+		return -EINVAL;
+
+	dma_desc = &pxa_crtc->dma_buff->dma_desc[dma];
+	dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]);
+
+	dma_desc->fsadr = start;
+	dma_desc->fidr  = 0;
+	/* enable EOFINT unconditionally, but mask interrupt with LCCR0_EFM */
+	dma_desc->ldcmd = size | LDCMD_EOFINT;
+
+	if (pal < 0 || pal >= PAL_MAX * 2) {
+		dma_desc->fdadr = pxa_crtc->dma_buff_phys + dma_desc_off;
+		pxa_crtc->fdadr[dma] = pxa_crtc->dma_buff_phys + dma_desc_off;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void setup_base_frame(struct pxa_crtc *pxa_crtc, int branch)
+{
+	struct drm_framebuffer *fb = pxa_crtc->base.fb;
+	struct drm_display_mode *mode = &pxa_crtc->base.mode;
+	int nbytes, dma, pal, bpp = fb->bits_per_pixel;
+	unsigned long offset;
+
+	dma = DMA_BASE + (branch ? DMA_MAX : 0);
+	pal = (bpp >= 16) ? PAL_NONE : PAL_BASE + (branch ? PAL_MAX : 0);
+
+	nbytes = mode->crtc_hdisplay * mode->crtc_vdisplay * bpp / 8;
+	offset = pxa_crtc->video_mem_phys; /* no yoffset support yet */
+
+	if (pxa_crtc->lccr0 & LCCR0_SDS) {
+		nbytes = nbytes / 2;
+		setup_frame_dma(pxa_crtc, dma + 1, PAL_NONE,
+				offset + nbytes, nbytes);
+	}
+
+	setup_frame_dma(pxa_crtc, dma, pal, offset, nbytes);
+}
+
+static void setup_parallel_timing(struct pxa_crtc *pxa_crtc,
+				  struct drm_display_mode *mode)
+{
+	unsigned int lines_per_panel, pcd = get_pcd(pxa_crtc, mode->clock);
+	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;
+	int vsync_len = mode->vsync_end - mode->vsync_start;
+	int upper_margin = mode->vtotal - mode->vsync_end;
+	int lower_margin = mode->vsync_start - mode->vdisplay;
+
+	pxa_crtc->reg_lccr1 =
+		LCCR1_DisWdth(mode->hdisplay) +
+		LCCR1_HorSnchWdth(hsync_len) +
+		LCCR1_BegLnDel(left_margin) +
+		LCCR1_EndLnDel(right_margin);
+
+	/*
+	 * If we have a dual scan LCD, we need to halve
+	 * the YRES parameter.
+	 */
+	lines_per_panel = mode->vdisplay;
+	if ((pxa_crtc->lccr0 & LCCR0_SDS) == LCCR0_Dual)
+		lines_per_panel /= 2;
+
+	pxa_crtc->reg_lccr2 =
+		LCCR2_DisHght(lines_per_panel) +
+		LCCR2_VrtSnchWdth(vsync_len) +
+		LCCR2_BegFrmDel(upper_margin) +
+		LCCR2_EndFrmDel(lower_margin);
+
+	pxa_crtc->reg_lccr3 = pxa_crtc->lccr3 |
+		(mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		 LCCR3_HorSnchH : LCCR3_HorSnchL) |
+		(mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		 LCCR3_VrtSnchH : LCCR3_VrtSnchL);
+
+	if (pcd)
+		pxa_crtc->reg_lccr3 |= LCCR3_PixClkDiv(pcd);
+}
+
+/* calculate 4-bit BPP value for LCCR3 and OVLxC1 */
+static int pixel_format_to_bpp(struct drm_framebuffer *fb)
+{
+	int bpp = -EINVAL;
+
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_C8:
+		bpp = 3;
+		break;
+	case DRM_FORMAT_XRGB1555:
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_RGB565:
+		bpp = 4;
+		break;
+	case DRM_FORMAT_RGB888:
+		bpp = 9;
+		break;
+	}
+	return bpp;
+}
+
+/*
+ *  pxa_crtc_fb_to_lccr3():
+ *    Convert a bits per pixel value to the correct bit pattern for LCCR3
+ *
+ *  NOTE: for PXA27x with overlays support, the LCCR3_PDFOR_x bits have an
+ *  implication of the acutal use of transparency bit,  which we handle it
+ *  here separatedly. See PXA27x Developer's Manual, Section <<7.4.6 Pixel
+ *  Formats>> for the valid combination of PDFOR, PAL_FOR for various BPP.
+ *
+ *  Transparency for palette pixel formats is not supported at the moment.
+ */
+static uint32_t pxa_crtc_fb_to_lccr3(struct drm_framebuffer *fb)
+{
+	int bpp = pixel_format_to_bpp(fb);
+	uint32_t lccr3;
+	int transparency;
+
+	if (bpp < 0)
+		return 0;
+
+	lccr3 = LCCR3_BPP(bpp);
+
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_XRGB1555:
+		lccr3 |= LCCR3_PDFOR_3;
+		break;
+	case DRM_FORMAT_RGB565:
+		break;
+	case DRM_FORMAT_ARGB8888;
+	case DRM_FORMAT_XRGB8888;
+		lccr3 |= LCCR3_PDFOR_0;
+		break;
+	case DRM_FORMAT_RGB888;
+		lccr3 |= LCCR3_PDFOR_3;
+		break;
+	}
+	return lccr3;
+}
+
+static int pxa_drm_crtc_set(struct drm_crtc *crtc,
+		struct drm_display_mode *mode)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+	struct drm_framebuffer *fb = crtc->fb;
+	unsigned long flags;
+
+
+	/* Pixel clock */
+
+	/* Pixel format */
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_RGB565:
+		pr_info("depth: %d, bpp: %d\n", fb->depth, fb->bits_per_pixel);
+		break;
+	default:
+		dev_err(pxa_crtc->dev, "unsupported pixel format %s\n",
+				fourcc_to_str(fb->pixel_format));
+		return -EINVAL;
+	}
+
+	/* Update shadow copy atomically */
+	local_irq_save(flags);
+
+	setup_parallel_timing(pxa_crtc, mode);
+
+	setup_base_frame(pxa_crtc, 0);
+
+	pxa_crtc->reg_lccr0 = pxa_crtc->lccr0 |
+		(LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM |
+		 LCCR0_QDM | LCCR0_BM  | LCCR0_OUM);
+
+	pxa_crtc->reg_lccr3 |= pxa_crtc_fb_to_lccr3(fb);
+
+	pxa_crtc->reg_lccr4 = lcd_readl(pxa_crtc, LCCR4) & ~LCCR4_PAL_FOR_MASK;
+	pxa_crtc->reg_lccr4 |= (pxa_crtc->lccr4 & LCCR4_PAL_FOR_MASK);
+	local_irq_restore(flags);
+
+	/*
+	 * Only update the registers if the controller is enabled
+	 * and something has changed.
+	 */
+	if ((lcd_readl(pxa_crtc, LCCR0) != pxa_crtc->reg_lccr0) ||
+	    (lcd_readl(pxa_crtc, LCCR1) != pxa_crtc->reg_lccr1) ||
+	    (lcd_readl(pxa_crtc, LCCR2) != pxa_crtc->reg_lccr2) ||
+	    (lcd_readl(pxa_crtc, LCCR3) != pxa_crtc->reg_lccr3) ||
+	    (lcd_readl(pxa_crtc, LCCR4) != pxa_crtc->reg_lccr4) ||
+	    (lcd_readl(pxa_crtc, FDADR0) != pxa_crtc->fdadr[0]) ||
+	    ((pxa_crtc->lccr0 & LCCR0_SDS) &&
+	    (lcd_readl(pxa_crtc, FDADR1) != pxa_crtc->fdadr[1]))) {
+		if (pxa_crtc->enabled) {
+			pxa_crtc_disable_controller(pxa_crtc);
+			pxa_crtc_enable_controller(pxa_crtc);
+		}
+	}
+
+	return 0;
+}
+
+static int pxa_drm_set_base(struct drm_crtc *crtc, int x, int y)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+	struct sdrm_buf_entry *entry;
+	struct drm_framebuffer *fb = crtc->fb;
+	unsigned long phys;
+	int dma = DMA_MAX + DMA_BASE;
+
+	entry = sdrm_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(pxa_crtc->dev, "%s: phys: 0x%lx\n", __func__, phys);
+	dev_dbg(pxa_crtc->dev, "%s: xy: %dx%d\n", __func__, x, y);
+
+	pxa_crtc->video_mem_phys = entry->paddr;
+
+	setup_base_frame(pxa_crtc, 1);
+
+	if (pxa_crtc->lccr0 & LCCR0_SDS)
+		lcd_writel(pxa_crtc, FBR1, pxa_crtc->fdadr[dma + 1] | 0x1);
+
+	lcd_writel(pxa_crtc, FBR0, pxa_crtc->fdadr[dma] | 0x1);
+
+	return 0;
+}
+
+static int pxa_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 pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+
+	pxa_drm_set_base(crtc, x, y);
+
+	return pxa_drm_crtc_set(crtc, mode);
+}
+
+static void pxa_crtc_enable(struct pxa_crtc *pxa_crtc)
+{
+	if (!pxa_crtc->enabled) {
+		__pxa_crtc_lcd_power(pxa_crtc, 1);
+		pxa_crtc_enable_controller(pxa_crtc);
+	}
+	pxa_crtc->enabled = 1;
+}
+
+static void pxa_crtc_disable(struct pxa_crtc *pxa_crtc)
+{
+	if (pxa_crtc->enabled) {
+		pxa_crtc_disable_controller(pxa_crtc);
+		__pxa_crtc_lcd_power(pxa_crtc, 0);
+	}
+	pxa_crtc->enabled = 0;
+}
+
+static void pxa_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+
+	dev_dbg(pxa_crtc->dev, "%s mode: %d\n", __func__, mode);
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		pxa_crtc_enable(pxa_crtc);
+		break;
+	default:
+		pxa_crtc_disable(pxa_crtc);
+		break;
+	}
+}
+
+static bool pxa_crtc_mode_fixup(struct drm_crtc *crtc,
+				  struct drm_display_mode *mode,
+				  struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void pxa_crtc_prepare(struct drm_crtc *crtc)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+
+	pxa_crtc_disable(pxa_crtc);
+}
+
+static void pxa_crtc_commit(struct drm_crtc *crtc)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+
+	pxa_crtc_enable(pxa_crtc);
+}
+
+static struct drm_crtc_helper_funcs pxa_helper_funcs = {
+	.dpms = pxa_crtc_dpms,
+	.prepare = pxa_crtc_prepare,
+	.commit = pxa_crtc_commit,
+	.mode_fixup = pxa_crtc_mode_fixup,
+	.mode_set = pxa_crtc_mode_set,
+	.load_lut = pxa_crtc_load_lut,
+};
+
+static void sdrm_handle_pageflip(struct pxa_crtc *pxa_crtc)
+{
+	struct drm_pending_vblank_event *e;
+	struct timeval now;
+	unsigned long flags;
+	struct drm_device *drm = pxa_crtc->base.dev;
+
+	spin_lock_irqsave(&drm->event_lock, flags);
+
+	e = pxa_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;
+	pxa_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 pxa_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+	uint32_t lccr0;
+
+	pxa_crtc->reg_lccr0 &= ~LCCR0_EFM;
+
+	lccr0 = lcd_readl(pxa_crtc, LCCR0);
+	lccr0 &= ~LCCR0_EFM;
+	lcd_writel(pxa_crtc, LCCR0, lccr0);
+
+	return 0;
+}
+
+static void pxa_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+	uint32_t lccr0;
+
+	pxa_crtc->reg_lccr0 &= ~LCCR0_EFM;
+
+	lccr0 = lcd_readl(pxa_crtc, LCCR0);
+	lccr0 |= LCCR0_EFM;
+	lcd_writel(pxa_crtc, LCCR0, lccr0);
+}
+
+/*
+ * Handle LCDC interrupts.
+ */
+static irqreturn_t pxa_irq_handler(int irq, void *dev_id)
+{
+	struct pxa_crtc *pxa_crtc = dev_id;
+	struct drm_device *drm = pxa_crtc->base.dev;
+	unsigned int lccr0, lcsr;
+
+	lcsr = lcd_readl(pxa_crtc, LCSR);
+	if (lcsr & LCSR_LDD) {
+		lccr0 = lcd_readl(pxa_crtc, LCCR0);
+		lcd_writel(pxa_crtc, LCCR0, lccr0 | LCCR0_LDM);
+		complete(&pxa_crtc->disable_done);
+	}
+
+	if (lcsr & LCSR_BER)
+		dev_err(pxa_crtc->dev, "bus error caused by dma channel %d\n",
+			(lcsr >> 28) & 0x7);
+
+	if (lcsr & LCSR_EOF)
+		sdrm_handle_pageflip(pxa_crtc);
+
+	/* Acknowledge interrupt */
+	lcd_writel(pxa_crtc, LCSR, lcsr);
+
+	drm_handle_vblank(drm, 0);
+
+	if (pxa_crtc->newfb) {
+		pxa_crtc->base.fb = pxa_crtc->newfb;
+		pxa_crtc->newfb = NULL;
+		pxa_drm_set_base(&pxa_crtc->base, 0, 0);
+		sdrm_handle_pageflip(pxa_crtc);
+		sdrm_crtc_vblank_put(pxa_crtc->sdrm_crtc);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int pxa_page_flip(struct drm_crtc *crtc,
+			 struct drm_framebuffer *fb,
+			 struct drm_pending_vblank_event *event)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+
+	if (pxa_crtc->newfb)
+		return -EBUSY;
+
+	pxa_crtc->newfb = fb;
+	pxa_crtc->page_flip_event = event;
+	sdrm_crtc_vblank_get(pxa_crtc->sdrm_crtc);
+
+	return 0;
+}
+
+static const struct drm_crtc_funcs pxa_crtc_funcs = {
+	.page_flip = pxa_page_flip,
+};
+
+static const struct sdrm_crtc_helper_funcs pxa_sdrm_helper = {
+	.enable_vblank = pxa_crtc_enable_vblank,
+	.disable_vblank = pxa_crtc_disable_vblank,
+};
+
+#define DRIVER_NAME "pxa-lcdc-crtc"
+
+static int __devinit pxa_crtc_probe(struct platform_device *pdev)
+{
+	struct pxa_crtc *pxa_crtc;
+	struct resource *res;
+	int ret, irq;
+	struct pxafb_mach_info *pdata = pdev->dev.platform_data;
+
+	dev_dbg(&pdev->dev, "pxa_crtc_probe\n");
+
+	if (!pdata)
+		return -EINVAL;
+
+	dev_info(&pdev->dev, "got a %dx%dx%d LCD\n",
+			pdata->modes->xres,
+			pdata->modes->yres,
+			pdata->modes->bpp);
+	if (pdata->modes->xres == 0 ||
+	    pdata->modes->yres == 0 ||
+	    pdata->modes->bpp == 0) {
+		dev_err(&pdev->dev, "Invalid resolution or bit depth\n");
+		return -EINVAL;
+	}
+
+	pxa_crtc = devm_kzalloc(&pdev->dev, sizeof(*pxa_crtc), GFP_KERNEL);
+	if (!pxa_crtc) {
+		dev_dbg(&pdev->dev, "failed to allocate crtc\n");
+		return -ENOMEM;
+	}
+
+	pxa_crtc->dev = &pdev->dev;
+
+	init_completion(&pxa_crtc->disable_done);
+
+	pxa_crtc_decode_mach_info(pxa_crtc, pdata);
+
+	pxa_crtc->lcd_power = pdata->pxafb_lcd_power;
+
+	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;
+
+	pxa_crtc->mmio_base = devm_ioremap(&pdev->dev, res->start,
+				resource_size(res));
+	if (!pxa_crtc->mmio_base) {
+		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, pxa_irq_handler, 0, "pxa_drm",
+			pxa_crtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "irq request failed with %d.\n", ret);
+		return ret;
+	}
+
+	pxa_crtc->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(pxa_crtc->clk)) {
+		ret = PTR_ERR(pxa_crtc->clk);
+		dev_err(&pdev->dev, "unable to get clock: %d\n", ret);
+		return ret;
+	}
+
+	pxa_crtc->sdrm_crtc = sdrm_add_crtc(dev_name(&pdev->dev),
+			&pxa_crtc->base, &pxa_crtc_funcs, &pxa_helper_funcs,
+			&pxa_sdrm_helper);
+	if (!pxa_crtc->sdrm_crtc) {
+		dev_err(&pdev->dev, "failed to add crtc\n");
+		ret = -EINVAL;
+		goto err_clk;
+	}
+
+	platform_set_drvdata(pdev, pxa_crtc);
+
+	/* there are no devm helpers for dma_alloc_coherent */
+	pxa_crtc->dma_buff_size = PAGE_ALIGN(sizeof(struct pxafb_dma_buff));
+	pxa_crtc->dma_buff = dma_alloc_coherent(pxa_crtc->dev,
+				pxa_crtc->dma_buff_size,
+				&pxa_crtc->dma_buff_phys, GFP_KERNEL);
+	if (pxa_crtc->dma_buff == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate memory for DMA\n");
+		ret = -ENOMEM;
+		goto err_crtc;
+	}
+
+	ret = sdrm_init_drm(dev_name(&pdev->dev), pdev, 16);
+	if (ret) {
+		dev_err(&pdev->dev, "init drm failed with %d\n", ret);
+		goto err_free_dma;
+	}
+
+	return 0;
+
+err_free_dma:
+	dma_free_coherent(&pdev->dev, pxa_crtc->dma_buff_size,
+			pxa_crtc->dma_buff, pxa_crtc->dma_buff_phys);
+err_crtc:
+	sdrm_remove_crtc(pxa_crtc->sdrm_crtc);
+err_clk:
+	clk_put(pxa_crtc->clk);
+
+	return ret;
+}
+
+static int __devexit pxa_crtc_remove(struct platform_device *pdev)
+{
+	struct pxa_crtc *pxa_crtc = platform_get_drvdata(pdev);
+
+	sdrm_exit_drm(dev_name(&pdev->dev));
+
+	clk_disable_unprepare(pxa_crtc->clk);
+	clk_put(pxa_crtc->clk);
+
+	sdrm_remove_crtc(pxa_crtc->sdrm_crtc);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pxa_crtc_driver = {
+	.remove		= __devexit_p(pxa_crtc_remove),
+	.probe		= pxa_crtc_probe,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(pxa_crtc_driver);
+
+MODULE_DESCRIPTION("PXA LCDC framebuffer driver");
+MODULE_AUTHOR("Philipp Zabel, Pengutronix");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5

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

* [PATCH 7/7] DRM: add PXA kms simple driver
@ 2012-04-11 15:33   ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-11 15:33 UTC (permalink / raw)
  To: dri-devel; +Cc: Philipp Zabel, Sascha Hauer, linux-arm-kernel, kernel

From: Philipp Zabel <p.zabel@pengutronix.de>

This adds a sdrm driver for the PXA LCDC controller.
Currently only the base framebuffer is supported, no overlay.
There is no support for smart panels and so far only RGB565
pixel format is supported.
Tested on PHYTEC PCM-990 development board.

Also adds EOFINT and SOFINT bit definitions for the LDCMD registers.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm/mach-pxa/include/mach/regs-lcd.h |    2 +
 drivers/gpu/drm/Kconfig                   |    2 +
 drivers/gpu/drm/Makefile                  |    1 +
 drivers/gpu/drm/pxa/Kconfig               |   12 +
 drivers/gpu/drm/pxa/Makefile              |    1 +
 drivers/gpu/drm/pxa/pxa-lcdc-crtc.c       |  845 +++++++++++++++++++++++++++++
 6 files changed, 863 insertions(+)
 create mode 100644 drivers/gpu/drm/pxa/Kconfig
 create mode 100644 drivers/gpu/drm/pxa/Makefile
 create mode 100644 drivers/gpu/drm/pxa/pxa-lcdc-crtc.c

diff --git a/arch/arm/mach-pxa/include/mach/regs-lcd.h b/arch/arm/mach-pxa/include/mach/regs-lcd.h
index f82dcea..69699ce 100644
--- a/arch/arm/mach-pxa/include/mach/regs-lcd.h
+++ b/arch/arm/mach-pxa/include/mach/regs-lcd.h
@@ -158,6 +158,8 @@
 #define LCSR1_EOF(x)	(1 << ((x) + 7))  /* End of Frame Status */
 #define LCSR1_SOF(x)	(1 << ((x) - 1))  /* Start of Frame Status */
 
+#define LDCMD_EOFINT	(1 << 21)	/* instructs DMA to set the EOF status bit */
+#define LCDMD_SOFINT	(1 << 22)	/* instructs DMA to set the SOF status bit */
 #define LDCMD_PAL	(1 << 26)	/* instructs DMA to load palette buffer */
 
 /* overlay control registers */
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 4650d65..6a68e33 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -190,3 +190,5 @@ source "drivers/gpu/drm/udl/Kconfig"
 source "drivers/gpu/drm/sdrm/Kconfig"
 
 source "drivers/gpu/drm/imx/Kconfig"
+
+source "drivers/gpu/drm/pxa/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index b0c283e..0f16044 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -44,4 +44,5 @@ obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_SDRM) += sdrm/
 obj-$(CONFIG_DRM_IMX) +=imx/
+obj-$(CONFIG_DRM_PXA) +=pxa/
 obj-y			+= i2c/
diff --git a/drivers/gpu/drm/pxa/Kconfig b/drivers/gpu/drm/pxa/Kconfig
new file mode 100644
index 0000000..bb2e7e0
--- /dev/null
+++ b/drivers/gpu/drm/pxa/Kconfig
@@ -0,0 +1,12 @@
+config DRM_PXA
+	tristate "DRM Support for Marvell PXA"
+	depends on DRM && ARCH_PXA
+
+config DRM_PXA_LCDC
+	tristate "DRM Support for PXA2xx and PXA3xx"
+	depends on DRM && DRM_PXA
+	select DRM_SDRM
+	select DRM_SDRM_ENCON
+	help
+	  Choose this if you have a PXA2xx or PXA3xx processor
+	  with a display connected to the internal LCD controller.
diff --git a/drivers/gpu/drm/pxa/Makefile b/drivers/gpu/drm/pxa/Makefile
new file mode 100644
index 0000000..bb5cf1f
--- /dev/null
+++ b/drivers/gpu/drm/pxa/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DRM_PXA_LCDC) += pxa-lcdc-crtc.o
diff --git a/drivers/gpu/drm/pxa/pxa-lcdc-crtc.c b/drivers/gpu/drm/pxa/pxa-lcdc-crtc.c
new file mode 100644
index 0000000..3d0e6e9
--- /dev/null
+++ b/drivers/gpu/drm/pxa/pxa-lcdc-crtc.c
@@ -0,0 +1,845 @@
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/sdrm.h>
+#include <drm/sdrm_encon.h>
+
+#include <mach/hardware.h>
+#include <mach/pxafb.h>
+
+/* PXA LCD DMA descriptor */
+struct pxafb_dma_descriptor {
+	unsigned int fdadr;
+	unsigned int fsadr;
+	unsigned int fidr;
+	unsigned int ldcmd;
+};
+
+enum {
+	PAL_NONE	= -1,
+	PAL_BASE	= 0,
+	PAL_OV1		= 1,
+	PAL_OV2		= 2,
+	PAL_MAX,
+};
+
+enum {
+	DMA_BASE	= 0,
+	DMA_UPPER	= 0,
+	DMA_LOWER	= 1,
+	DMA_OV1		= 1,
+	DMA_OV2_Y	= 2,
+	DMA_OV2_Cb	= 3,
+	DMA_OV2_Cr	= 4,
+	DMA_CURSOR	= 5,
+	DMA_CMD		= 6,
+	DMA_MAX,
+};
+
+/* maximum palette size - 256 entries, each 4 bytes long */
+#define PALETTE_SIZE	(256 * 4)
+#define CMD_BUFF_SIZE	(1024 * 50)
+
+/* NOTE: the palette and frame dma descriptors are doubled to allow
+ * the 2nd set for branch settings (FBRx)
+ */
+struct pxafb_dma_buff {
+	unsigned char palette[PAL_MAX * PALETTE_SIZE];
+	uint16_t cmd_buff[CMD_BUFF_SIZE];
+	struct pxafb_dma_descriptor pal_desc[PAL_MAX * 2];
+	struct pxafb_dma_descriptor dma_desc[DMA_MAX * 2];
+};
+
+struct pxa_crtc {
+	struct drm_crtc		base;
+	struct sdrm_crtc	*sdrm_crtc;
+	int			di_no;
+	int			enabled;
+
+	void __iomem		*mmio_base;
+
+	struct pxafb_dma_buff	*dma_buff;
+	size_t			dma_buff_size;
+	dma_addr_t		dma_buff_phys;
+	dma_addr_t		fdadr[DMA_MAX * 2];
+
+	unsigned long		video_mem_phys;	/* physical address of frame buffer */
+
+	u32			lccr0;
+	u32			lccr3;
+	u32			lccr4;
+	u32			cmap_inverse:1,
+				cmap_static:1,
+				unused:30;
+
+	u32			reg_lccr0;
+	u32			reg_lccr1;
+	u32			reg_lccr2;
+	u32			reg_lccr3;
+	u32			reg_lccr4;
+
+	struct completion	disable_done;
+
+	struct clk		*clk;
+	struct device		*dev;
+	int			vblank_enable;
+
+	struct drm_pending_vblank_event *page_flip_event;
+	struct drm_framebuffer	*newfb;
+
+	void (*lcd_power)(int, struct fb_var_screeninfo *);
+};
+
+#define to_pxa_crtc(x) container_of(x, struct pxa_crtc, base)
+
+static inline unsigned long
+lcd_readl(struct pxa_crtc *pxa_crtc, unsigned int off)
+{
+	return __raw_readl(pxa_crtc->mmio_base + off);
+}
+
+static inline void
+lcd_writel(struct pxa_crtc *pxa_crtc, unsigned int off, unsigned long val)
+{
+	__raw_writel(val, pxa_crtc->mmio_base + off);
+}
+
+static void pxa_crtc_load_lut(struct drm_crtc *crtc)
+{
+}
+
+const char *fourcc_to_str(u32 fourcc)
+{
+	static char buf[5];
+
+	*(u32 *)buf = fourcc;
+	buf[4] = 0;
+
+	return buf;
+}
+
+static inline void __pxa_crtc_lcd_power(struct pxa_crtc *pxa_crtc, int on)
+{
+	dev_dbg(pxa_crtc->dev, "LCD power o%s\n", on ? "n" : "ff");
+
+	if (pxa_crtc->lcd_power)
+		pxa_crtc->lcd_power(on, NULL); /* there is no fb_info */
+}
+
+static void pxa_crtc_enable_controller(struct pxa_crtc *pxa_crtc)
+{
+	dev_dbg(pxa_crtc->dev, "Enabling LCD controller\n");
+
+	/* enable LCD controller clock */
+	clk_prepare_enable(pxa_crtc->clk);
+
+	if (pxa_crtc->lccr0 & LCCR0_LCDT)
+		return;
+
+	/* Sequence from 11.7.10 */
+	lcd_writel(pxa_crtc, LCCR4, pxa_crtc->reg_lccr4);
+	lcd_writel(pxa_crtc, LCCR3, pxa_crtc->reg_lccr3);
+	lcd_writel(pxa_crtc, LCCR2, pxa_crtc->reg_lccr2);
+	lcd_writel(pxa_crtc, LCCR1, pxa_crtc->reg_lccr1);
+	lcd_writel(pxa_crtc, LCCR0, pxa_crtc->reg_lccr0 & ~LCCR0_ENB);
+
+	lcd_writel(pxa_crtc, FDADR0, pxa_crtc->fdadr[0]);
+	if (pxa_crtc->lccr0 & LCCR0_SDS)
+		lcd_writel(pxa_crtc, FDADR1, pxa_crtc->fdadr[1]);
+
+	lcd_writel(pxa_crtc, LCCR0, pxa_crtc->reg_lccr0 | LCCR0_ENB);
+}
+
+static void pxa_crtc_disable_controller(struct pxa_crtc *pxa_crtc)
+{
+	uint32_t lccr0;
+
+	dev_dbg(pxa_crtc->dev, "Disabling LCD controller\n");
+
+	/* Clear LCD Status Register */
+	lcd_writel(pxa_crtc, LCSR, 0xffffffff);
+
+	lccr0 = lcd_readl(pxa_crtc, LCCR0) & ~LCCR0_LDM;
+	lcd_writel(pxa_crtc, LCCR0, lccr0);
+	lcd_writel(pxa_crtc, LCCR0, lccr0 | LCCR0_DIS);
+
+	wait_for_completion_timeout(&pxa_crtc->disable_done, 200 * HZ / 1000);
+
+	/* disable LCD controller clock */
+	clk_disable_unprepare(pxa_crtc->clk);
+}
+
+static void pxa_crtc_decode_mach_info(struct pxa_crtc *pxa_crtc,
+				      struct pxafb_mach_info *inf)
+{
+	unsigned int lcd_conn = inf->lcd_conn;
+
+	pxa_crtc->cmap_inverse = inf->cmap_inverse;
+	pxa_crtc->cmap_static = inf->cmap_static;
+	pxa_crtc->lccr4 = inf->lccr4;
+
+	switch (lcd_conn & LCD_TYPE_MASK) {
+	case LCD_TYPE_MONO_STN:
+		pxa_crtc->lccr0 = LCCR0_CMS;
+		break;
+	case LCD_TYPE_MONO_DSTN:
+		pxa_crtc->lccr0 = LCCR0_CMS | LCCR0_SDS;
+		break;
+	case LCD_TYPE_COLOR_STN:
+		pxa_crtc->lccr0 = 0;
+		break;
+	case LCD_TYPE_COLOR_DSTN:
+		pxa_crtc->lccr0 = LCCR0_SDS;
+		break;
+	case LCD_TYPE_COLOR_TFT:
+		pxa_crtc->lccr0 = LCCR0_PAS;
+		break;
+	case LCD_TYPE_SMART_PANEL:
+		pxa_crtc->lccr0 = LCCR0_LCDT | LCCR0_PAS;
+		break;
+	default:
+		/* fall back to backward compatibility way */
+		pxa_crtc->lccr0 = inf->lccr0;
+		pxa_crtc->lccr3 = inf->lccr3;
+		return;
+	}
+
+	if (lcd_conn == LCD_MONO_STN_8BPP)
+		pxa_crtc->lccr0 |= LCCR0_DPD;
+
+	pxa_crtc->lccr0 |= (lcd_conn & LCD_ALTERNATE_MAPPING) ? LCCR0_LDDALT : 0;
+
+	pxa_crtc->lccr3 = LCCR3_Acb((inf->lcd_conn >> 10) & 0xff);
+	pxa_crtc->lccr3 |= (lcd_conn & LCD_BIAS_ACTIVE_LOW) ? LCCR3_OEP : 0;
+	pxa_crtc->lccr3 |= (lcd_conn & LCD_PCLK_EDGE_FALL)  ? LCCR3_PCP : 0;
+}
+
+/*
+ * Calculate the PCD value from the clock rate (in picoseconds).
+ * We take account of the PPCR clock setting.
+ * From PXA Developer's Manual:
+ *
+ *   PixelClock =      LCLK
+ *                -------------
+ *                2 ( PCD + 1 )
+ *
+ *   PCD =      LCLK
+ *         ------------- - 1
+ *         2(PixelClock)
+ *
+ * Where:
+ *   LCLK = LCD/Memory Clock
+ *   PCD = LCCR3[7:0]
+ *
+ * PixelClock here is in Hz while the pixclock argument given is the
+ * period in picoseconds. Hence PixelClock = 1 / ( pixclock * 10^-12 )
+ *
+ * The function get_lclk_frequency_10khz returns LCLK in units of
+ * 10khz. Calling the result of this function lclk gives us the
+ * following
+ *
+ *    PCD = (lclk * 10^4 ) * ( pixclock * 10^-12 )
+ *          -------------------------------------- - 1
+ *                          2
+ *
+ * Factoring the 10^4 and 10^-12 out gives 10^-8 == 1 / 100000000 as used below.
+ */
+static inline unsigned int get_pcd(struct pxa_crtc *pxa_crtc,
+				   unsigned int pixclock)
+{
+	unsigned long long pcd;
+
+	/* FIXME: Need to take into account Double Pixel Clock mode
+	 * (DPC) bit? or perhaps set it based on the various clock
+	 * speeds */
+	pcd = (unsigned long long)(clk_get_rate(pxa_crtc->clk) / 10000);
+	pcd *= pixclock;
+	do_div(pcd, 100000000 * 2);
+	/* no need for this, since we should subtract 1 anyway. they cancel */
+	/* pcd += 1; */ /* make up for integer math truncations */
+	return (unsigned int)pcd;
+}
+
+static int setup_frame_dma(struct pxa_crtc *pxa_crtc, int dma, int pal,
+			   unsigned long start, size_t size)
+{
+	struct pxafb_dma_descriptor *dma_desc;
+	unsigned int dma_desc_off;
+
+	if (dma < 0 || dma >= DMA_MAX * 2)
+		return -EINVAL;
+
+	dma_desc = &pxa_crtc->dma_buff->dma_desc[dma];
+	dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]);
+
+	dma_desc->fsadr = start;
+	dma_desc->fidr  = 0;
+	/* enable EOFINT unconditionally, but mask interrupt with LCCR0_EFM */
+	dma_desc->ldcmd = size | LDCMD_EOFINT;
+
+	if (pal < 0 || pal >= PAL_MAX * 2) {
+		dma_desc->fdadr = pxa_crtc->dma_buff_phys + dma_desc_off;
+		pxa_crtc->fdadr[dma] = pxa_crtc->dma_buff_phys + dma_desc_off;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void setup_base_frame(struct pxa_crtc *pxa_crtc, int branch)
+{
+	struct drm_framebuffer *fb = pxa_crtc->base.fb;
+	struct drm_display_mode *mode = &pxa_crtc->base.mode;
+	int nbytes, dma, pal, bpp = fb->bits_per_pixel;
+	unsigned long offset;
+
+	dma = DMA_BASE + (branch ? DMA_MAX : 0);
+	pal = (bpp >= 16) ? PAL_NONE : PAL_BASE + (branch ? PAL_MAX : 0);
+
+	nbytes = mode->crtc_hdisplay * mode->crtc_vdisplay * bpp / 8;
+	offset = pxa_crtc->video_mem_phys; /* no yoffset support yet */
+
+	if (pxa_crtc->lccr0 & LCCR0_SDS) {
+		nbytes = nbytes / 2;
+		setup_frame_dma(pxa_crtc, dma + 1, PAL_NONE,
+				offset + nbytes, nbytes);
+	}
+
+	setup_frame_dma(pxa_crtc, dma, pal, offset, nbytes);
+}
+
+static void setup_parallel_timing(struct pxa_crtc *pxa_crtc,
+				  struct drm_display_mode *mode)
+{
+	unsigned int lines_per_panel, pcd = get_pcd(pxa_crtc, mode->clock);
+	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;
+	int vsync_len = mode->vsync_end - mode->vsync_start;
+	int upper_margin = mode->vtotal - mode->vsync_end;
+	int lower_margin = mode->vsync_start - mode->vdisplay;
+
+	pxa_crtc->reg_lccr1 =
+		LCCR1_DisWdth(mode->hdisplay) +
+		LCCR1_HorSnchWdth(hsync_len) +
+		LCCR1_BegLnDel(left_margin) +
+		LCCR1_EndLnDel(right_margin);
+
+	/*
+	 * If we have a dual scan LCD, we need to halve
+	 * the YRES parameter.
+	 */
+	lines_per_panel = mode->vdisplay;
+	if ((pxa_crtc->lccr0 & LCCR0_SDS) == LCCR0_Dual)
+		lines_per_panel /= 2;
+
+	pxa_crtc->reg_lccr2 =
+		LCCR2_DisHght(lines_per_panel) +
+		LCCR2_VrtSnchWdth(vsync_len) +
+		LCCR2_BegFrmDel(upper_margin) +
+		LCCR2_EndFrmDel(lower_margin);
+
+	pxa_crtc->reg_lccr3 = pxa_crtc->lccr3 |
+		(mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		 LCCR3_HorSnchH : LCCR3_HorSnchL) |
+		(mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		 LCCR3_VrtSnchH : LCCR3_VrtSnchL);
+
+	if (pcd)
+		pxa_crtc->reg_lccr3 |= LCCR3_PixClkDiv(pcd);
+}
+
+/* calculate 4-bit BPP value for LCCR3 and OVLxC1 */
+static int pixel_format_to_bpp(struct drm_framebuffer *fb)
+{
+	int bpp = -EINVAL;
+
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_C8:
+		bpp = 3;
+		break;
+	case DRM_FORMAT_XRGB1555:
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_RGB565:
+		bpp = 4;
+		break;
+	case DRM_FORMAT_RGB888:
+		bpp = 9;
+		break;
+	}
+	return bpp;
+}
+
+/*
+ *  pxa_crtc_fb_to_lccr3():
+ *    Convert a bits per pixel value to the correct bit pattern for LCCR3
+ *
+ *  NOTE: for PXA27x with overlays support, the LCCR3_PDFOR_x bits have an
+ *  implication of the acutal use of transparency bit,  which we handle it
+ *  here separatedly. See PXA27x Developer's Manual, Section <<7.4.6 Pixel
+ *  Formats>> for the valid combination of PDFOR, PAL_FOR for various BPP.
+ *
+ *  Transparency for palette pixel formats is not supported at the moment.
+ */
+static uint32_t pxa_crtc_fb_to_lccr3(struct drm_framebuffer *fb)
+{
+	int bpp = pixel_format_to_bpp(fb);
+	uint32_t lccr3;
+	int transparency;
+
+	if (bpp < 0)
+		return 0;
+
+	lccr3 = LCCR3_BPP(bpp);
+
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_XRGB1555:
+		lccr3 |= LCCR3_PDFOR_3;
+		break;
+	case DRM_FORMAT_RGB565:
+		break;
+	case DRM_FORMAT_ARGB8888;
+	case DRM_FORMAT_XRGB8888;
+		lccr3 |= LCCR3_PDFOR_0;
+		break;
+	case DRM_FORMAT_RGB888;
+		lccr3 |= LCCR3_PDFOR_3;
+		break;
+	}
+	return lccr3;
+}
+
+static int pxa_drm_crtc_set(struct drm_crtc *crtc,
+		struct drm_display_mode *mode)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+	struct drm_framebuffer *fb = crtc->fb;
+	unsigned long flags;
+
+
+	/* Pixel clock */
+
+	/* Pixel format */
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_RGB565:
+		pr_info("depth: %d, bpp: %d\n", fb->depth, fb->bits_per_pixel);
+		break;
+	default:
+		dev_err(pxa_crtc->dev, "unsupported pixel format %s\n",
+				fourcc_to_str(fb->pixel_format));
+		return -EINVAL;
+	}
+
+	/* Update shadow copy atomically */
+	local_irq_save(flags);
+
+	setup_parallel_timing(pxa_crtc, mode);
+
+	setup_base_frame(pxa_crtc, 0);
+
+	pxa_crtc->reg_lccr0 = pxa_crtc->lccr0 |
+		(LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM |
+		 LCCR0_QDM | LCCR0_BM  | LCCR0_OUM);
+
+	pxa_crtc->reg_lccr3 |= pxa_crtc_fb_to_lccr3(fb);
+
+	pxa_crtc->reg_lccr4 = lcd_readl(pxa_crtc, LCCR4) & ~LCCR4_PAL_FOR_MASK;
+	pxa_crtc->reg_lccr4 |= (pxa_crtc->lccr4 & LCCR4_PAL_FOR_MASK);
+	local_irq_restore(flags);
+
+	/*
+	 * Only update the registers if the controller is enabled
+	 * and something has changed.
+	 */
+	if ((lcd_readl(pxa_crtc, LCCR0) != pxa_crtc->reg_lccr0) ||
+	    (lcd_readl(pxa_crtc, LCCR1) != pxa_crtc->reg_lccr1) ||
+	    (lcd_readl(pxa_crtc, LCCR2) != pxa_crtc->reg_lccr2) ||
+	    (lcd_readl(pxa_crtc, LCCR3) != pxa_crtc->reg_lccr3) ||
+	    (lcd_readl(pxa_crtc, LCCR4) != pxa_crtc->reg_lccr4) ||
+	    (lcd_readl(pxa_crtc, FDADR0) != pxa_crtc->fdadr[0]) ||
+	    ((pxa_crtc->lccr0 & LCCR0_SDS) &&
+	    (lcd_readl(pxa_crtc, FDADR1) != pxa_crtc->fdadr[1]))) {
+		if (pxa_crtc->enabled) {
+			pxa_crtc_disable_controller(pxa_crtc);
+			pxa_crtc_enable_controller(pxa_crtc);
+		}
+	}
+
+	return 0;
+}
+
+static int pxa_drm_set_base(struct drm_crtc *crtc, int x, int y)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+	struct sdrm_buf_entry *entry;
+	struct drm_framebuffer *fb = crtc->fb;
+	unsigned long phys;
+	int dma = DMA_MAX + DMA_BASE;
+
+	entry = sdrm_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(pxa_crtc->dev, "%s: phys: 0x%lx\n", __func__, phys);
+	dev_dbg(pxa_crtc->dev, "%s: xy: %dx%d\n", __func__, x, y);
+
+	pxa_crtc->video_mem_phys = entry->paddr;
+
+	setup_base_frame(pxa_crtc, 1);
+
+	if (pxa_crtc->lccr0 & LCCR0_SDS)
+		lcd_writel(pxa_crtc, FBR1, pxa_crtc->fdadr[dma + 1] | 0x1);
+
+	lcd_writel(pxa_crtc, FBR0, pxa_crtc->fdadr[dma] | 0x1);
+
+	return 0;
+}
+
+static int pxa_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 pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+
+	pxa_drm_set_base(crtc, x, y);
+
+	return pxa_drm_crtc_set(crtc, mode);
+}
+
+static void pxa_crtc_enable(struct pxa_crtc *pxa_crtc)
+{
+	if (!pxa_crtc->enabled) {
+		__pxa_crtc_lcd_power(pxa_crtc, 1);
+		pxa_crtc_enable_controller(pxa_crtc);
+	}
+	pxa_crtc->enabled = 1;
+}
+
+static void pxa_crtc_disable(struct pxa_crtc *pxa_crtc)
+{
+	if (pxa_crtc->enabled) {
+		pxa_crtc_disable_controller(pxa_crtc);
+		__pxa_crtc_lcd_power(pxa_crtc, 0);
+	}
+	pxa_crtc->enabled = 0;
+}
+
+static void pxa_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+
+	dev_dbg(pxa_crtc->dev, "%s mode: %d\n", __func__, mode);
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		pxa_crtc_enable(pxa_crtc);
+		break;
+	default:
+		pxa_crtc_disable(pxa_crtc);
+		break;
+	}
+}
+
+static bool pxa_crtc_mode_fixup(struct drm_crtc *crtc,
+				  struct drm_display_mode *mode,
+				  struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void pxa_crtc_prepare(struct drm_crtc *crtc)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+
+	pxa_crtc_disable(pxa_crtc);
+}
+
+static void pxa_crtc_commit(struct drm_crtc *crtc)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+
+	pxa_crtc_enable(pxa_crtc);
+}
+
+static struct drm_crtc_helper_funcs pxa_helper_funcs = {
+	.dpms = pxa_crtc_dpms,
+	.prepare = pxa_crtc_prepare,
+	.commit = pxa_crtc_commit,
+	.mode_fixup = pxa_crtc_mode_fixup,
+	.mode_set = pxa_crtc_mode_set,
+	.load_lut = pxa_crtc_load_lut,
+};
+
+static void sdrm_handle_pageflip(struct pxa_crtc *pxa_crtc)
+{
+	struct drm_pending_vblank_event *e;
+	struct timeval now;
+	unsigned long flags;
+	struct drm_device *drm = pxa_crtc->base.dev;
+
+	spin_lock_irqsave(&drm->event_lock, flags);
+
+	e = pxa_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;
+	pxa_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 pxa_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+	uint32_t lccr0;
+
+	pxa_crtc->reg_lccr0 &= ~LCCR0_EFM;
+
+	lccr0 = lcd_readl(pxa_crtc, LCCR0);
+	lccr0 &= ~LCCR0_EFM;
+	lcd_writel(pxa_crtc, LCCR0, lccr0);
+
+	return 0;
+}
+
+static void pxa_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+	uint32_t lccr0;
+
+	pxa_crtc->reg_lccr0 &= ~LCCR0_EFM;
+
+	lccr0 = lcd_readl(pxa_crtc, LCCR0);
+	lccr0 |= LCCR0_EFM;
+	lcd_writel(pxa_crtc, LCCR0, lccr0);
+}
+
+/*
+ * Handle LCDC interrupts.
+ */
+static irqreturn_t pxa_irq_handler(int irq, void *dev_id)
+{
+	struct pxa_crtc *pxa_crtc = dev_id;
+	struct drm_device *drm = pxa_crtc->base.dev;
+	unsigned int lccr0, lcsr;
+
+	lcsr = lcd_readl(pxa_crtc, LCSR);
+	if (lcsr & LCSR_LDD) {
+		lccr0 = lcd_readl(pxa_crtc, LCCR0);
+		lcd_writel(pxa_crtc, LCCR0, lccr0 | LCCR0_LDM);
+		complete(&pxa_crtc->disable_done);
+	}
+
+	if (lcsr & LCSR_BER)
+		dev_err(pxa_crtc->dev, "bus error caused by dma channel %d\n",
+			(lcsr >> 28) & 0x7);
+
+	if (lcsr & LCSR_EOF)
+		sdrm_handle_pageflip(pxa_crtc);
+
+	/* Acknowledge interrupt */
+	lcd_writel(pxa_crtc, LCSR, lcsr);
+
+	drm_handle_vblank(drm, 0);
+
+	if (pxa_crtc->newfb) {
+		pxa_crtc->base.fb = pxa_crtc->newfb;
+		pxa_crtc->newfb = NULL;
+		pxa_drm_set_base(&pxa_crtc->base, 0, 0);
+		sdrm_handle_pageflip(pxa_crtc);
+		sdrm_crtc_vblank_put(pxa_crtc->sdrm_crtc);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int pxa_page_flip(struct drm_crtc *crtc,
+			 struct drm_framebuffer *fb,
+			 struct drm_pending_vblank_event *event)
+{
+	struct pxa_crtc *pxa_crtc = to_pxa_crtc(crtc);
+
+	if (pxa_crtc->newfb)
+		return -EBUSY;
+
+	pxa_crtc->newfb = fb;
+	pxa_crtc->page_flip_event = event;
+	sdrm_crtc_vblank_get(pxa_crtc->sdrm_crtc);
+
+	return 0;
+}
+
+static const struct drm_crtc_funcs pxa_crtc_funcs = {
+	.page_flip = pxa_page_flip,
+};
+
+static const struct sdrm_crtc_helper_funcs pxa_sdrm_helper = {
+	.enable_vblank = pxa_crtc_enable_vblank,
+	.disable_vblank = pxa_crtc_disable_vblank,
+};
+
+#define DRIVER_NAME "pxa-lcdc-crtc"
+
+static int __devinit pxa_crtc_probe(struct platform_device *pdev)
+{
+	struct pxa_crtc *pxa_crtc;
+	struct resource *res;
+	int ret, irq;
+	struct pxafb_mach_info *pdata = pdev->dev.platform_data;
+
+	dev_dbg(&pdev->dev, "pxa_crtc_probe\n");
+
+	if (!pdata)
+		return -EINVAL;
+
+	dev_info(&pdev->dev, "got a %dx%dx%d LCD\n",
+			pdata->modes->xres,
+			pdata->modes->yres,
+			pdata->modes->bpp);
+	if (pdata->modes->xres == 0 ||
+	    pdata->modes->yres == 0 ||
+	    pdata->modes->bpp == 0) {
+		dev_err(&pdev->dev, "Invalid resolution or bit depth\n");
+		return -EINVAL;
+	}
+
+	pxa_crtc = devm_kzalloc(&pdev->dev, sizeof(*pxa_crtc), GFP_KERNEL);
+	if (!pxa_crtc) {
+		dev_dbg(&pdev->dev, "failed to allocate crtc\n");
+		return -ENOMEM;
+	}
+
+	pxa_crtc->dev = &pdev->dev;
+
+	init_completion(&pxa_crtc->disable_done);
+
+	pxa_crtc_decode_mach_info(pxa_crtc, pdata);
+
+	pxa_crtc->lcd_power = pdata->pxafb_lcd_power;
+
+	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;
+
+	pxa_crtc->mmio_base = devm_ioremap(&pdev->dev, res->start,
+				resource_size(res));
+	if (!pxa_crtc->mmio_base) {
+		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, pxa_irq_handler, 0, "pxa_drm",
+			pxa_crtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "irq request failed with %d.\n", ret);
+		return ret;
+	}
+
+	pxa_crtc->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(pxa_crtc->clk)) {
+		ret = PTR_ERR(pxa_crtc->clk);
+		dev_err(&pdev->dev, "unable to get clock: %d\n", ret);
+		return ret;
+	}
+
+	pxa_crtc->sdrm_crtc = sdrm_add_crtc(dev_name(&pdev->dev),
+			&pxa_crtc->base, &pxa_crtc_funcs, &pxa_helper_funcs,
+			&pxa_sdrm_helper);
+	if (!pxa_crtc->sdrm_crtc) {
+		dev_err(&pdev->dev, "failed to add crtc\n");
+		ret = -EINVAL;
+		goto err_clk;
+	}
+
+	platform_set_drvdata(pdev, pxa_crtc);
+
+	/* there are no devm helpers for dma_alloc_coherent */
+	pxa_crtc->dma_buff_size = PAGE_ALIGN(sizeof(struct pxafb_dma_buff));
+	pxa_crtc->dma_buff = dma_alloc_coherent(pxa_crtc->dev,
+				pxa_crtc->dma_buff_size,
+				&pxa_crtc->dma_buff_phys, GFP_KERNEL);
+	if (pxa_crtc->dma_buff == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate memory for DMA\n");
+		ret = -ENOMEM;
+		goto err_crtc;
+	}
+
+	ret = sdrm_init_drm(dev_name(&pdev->dev), pdev, 16);
+	if (ret) {
+		dev_err(&pdev->dev, "init drm failed with %d\n", ret);
+		goto err_free_dma;
+	}
+
+	return 0;
+
+err_free_dma:
+	dma_free_coherent(&pdev->dev, pxa_crtc->dma_buff_size,
+			pxa_crtc->dma_buff, pxa_crtc->dma_buff_phys);
+err_crtc:
+	sdrm_remove_crtc(pxa_crtc->sdrm_crtc);
+err_clk:
+	clk_put(pxa_crtc->clk);
+
+	return ret;
+}
+
+static int __devexit pxa_crtc_remove(struct platform_device *pdev)
+{
+	struct pxa_crtc *pxa_crtc = platform_get_drvdata(pdev);
+
+	sdrm_exit_drm(dev_name(&pdev->dev));
+
+	clk_disable_unprepare(pxa_crtc->clk);
+	clk_put(pxa_crtc->clk);
+
+	sdrm_remove_crtc(pxa_crtc->sdrm_crtc);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pxa_crtc_driver = {
+	.remove		= __devexit_p(pxa_crtc_remove),
+	.probe		= pxa_crtc_probe,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(pxa_crtc_driver);
+
+MODULE_DESCRIPTION("PXA LCDC framebuffer driver");
+MODULE_AUTHOR("Philipp Zabel, Pengutronix");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5

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

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-11 15:33   ` Sascha Hauer
@ 2012-04-11 20:22     ` Alan Cox
  -1 siblings, 0 replies; 44+ messages in thread
From: Alan Cox @ 2012-04-11 20:22 UTC (permalink / raw)
  To: linux-arm-kernel

> +static int sdrm_suspend(struct drm_device *drm, pm_message_t state)
> +{
> +	/* TODO */
> +
> +	return 0;
> +}
> +
> +static int sdrm_resume(struct drm_device *drm)
> +{
> +	/* TODO */
> +
> +	return 0;
> +}

These probably need to call into the sdrm device specific handling.


> +static int sdrm_get_irq(struct drm_device *dev)
> +{
> +	/*
> +	 * Return an arbitrary number to make the core happy.
> +	 * We can't return anything meaningful here since drm
> +	 * devices in general have multiple irqs
> +	 */
> +	return 1234;
> +}

If there isn't a meaningful IRQ then surely 0 should be returned.
Actually I'd suggest returning sdrm->irq or similar, because some simple
DRM type use cases will have a single IRQ (notably 2 on older PC hardware)

> + * sdrm_device_get - find or allocate sdrm device with unique name
> + *
> + * This function returns the sdrm device with the unique name 'name'
> + * If this already exists, return it, otherwise allocate a new
> + * object.

This naming is a bit confusing because the kernel mid layers etc tend to
use _get and _put for ref counting not lookup ?


> +	/*
> +	 * 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 spsdrmific driver own one instead bsdrmause
> +	 *      drm framework supports only one irq handler and
> +	 *      drivers can well take care of their interrupts
> +	 */
> +	drm->irq_enabled = 1;

We've got a couple of assumptions here I think I'd question for generality

1. That its a platform device
2. That it can't use the standard IRQ helpers in some cases.

Probably it should take a struct device and a struct of the bits you'd
fish out from platform or pci or other device type. And yes probably
there would be a platform_ version that wraps it.


> +static int sdrm_fb_dirty(struct drm_framebuffer *fb,
> +		struct drm_file *file_priv, unsigned flags,
> +		unsigned color, struct drm_clip_rect *clips,
> +		unsigned num_clips)
> +{
> +	/* TODO */
> +
> +	return 0;
> +}

Probably a helper method.

> +static struct fb_ops sdrm_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,
> +};

If you re assuming any kind of gtt then you should probably allow for gtt
based scrolling eventually, but thats an optimisation.


> +int sdrm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> +{
> +	struct drm_gem_object *obj = vma->vm_private_data;
> +	struct sdrm_gem_obj *sdrm_gem_obj = to_sdrm_gem_obj(obj);
> +	struct drm_device *dev = obj->dev;
> +	unsigned long pfn;
> +	pgoff_t page_offset;
> +	int ret;

For dumb hardware take a look how gma500 and some other bits do this -
you can premap the entire buffer when you take the first fault, which for
a dumb fb is a good bet.



Looking at it from the point of view of x86 legacy devices then the
things I see are

- Device is quite possibly PCI (but may be platform eg vesa)
- Memory will probably be allocated in the PCI space
- Mappings are probably write combining but not on all hardware

There's probably a case for pinning/unpinning scanout buffers according
to whether they are used. On some hardware the io mapping needed is a
precious resource. Also for stuff with a fixed fb space it means you can
combine it with invalidating the mmap mappings of an object and copying
objects in/out of the frame buffer to provide the expected interfaces to
allocate/release framebuffers.

Alan

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

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-11 20:22     ` Alan Cox
  0 siblings, 0 replies; 44+ messages in thread
From: Alan Cox @ 2012-04-11 20:22 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: kernel, linux-arm-kernel, dri-devel

> +static int sdrm_suspend(struct drm_device *drm, pm_message_t state)
> +{
> +	/* TODO */
> +
> +	return 0;
> +}
> +
> +static int sdrm_resume(struct drm_device *drm)
> +{
> +	/* TODO */
> +
> +	return 0;
> +}

These probably need to call into the sdrm device specific handling.


> +static int sdrm_get_irq(struct drm_device *dev)
> +{
> +	/*
> +	 * Return an arbitrary number to make the core happy.
> +	 * We can't return anything meaningful here since drm
> +	 * devices in general have multiple irqs
> +	 */
> +	return 1234;
> +}

If there isn't a meaningful IRQ then surely 0 should be returned.
Actually I'd suggest returning sdrm->irq or similar, because some simple
DRM type use cases will have a single IRQ (notably 2 on older PC hardware)

> + * sdrm_device_get - find or allocate sdrm device with unique name
> + *
> + * This function returns the sdrm device with the unique name 'name'
> + * If this already exists, return it, otherwise allocate a new
> + * object.

This naming is a bit confusing because the kernel mid layers etc tend to
use _get and _put for ref counting not lookup ?


> +	/*
> +	 * 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 spsdrmific driver own one instead bsdrmause
> +	 *      drm framework supports only one irq handler and
> +	 *      drivers can well take care of their interrupts
> +	 */
> +	drm->irq_enabled = 1;

We've got a couple of assumptions here I think I'd question for generality

1. That its a platform device
2. That it can't use the standard IRQ helpers in some cases.

Probably it should take a struct device and a struct of the bits you'd
fish out from platform or pci or other device type. And yes probably
there would be a platform_ version that wraps it.


> +static int sdrm_fb_dirty(struct drm_framebuffer *fb,
> +		struct drm_file *file_priv, unsigned flags,
> +		unsigned color, struct drm_clip_rect *clips,
> +		unsigned num_clips)
> +{
> +	/* TODO */
> +
> +	return 0;
> +}

Probably a helper method.

> +static struct fb_ops sdrm_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,
> +};

If you re assuming any kind of gtt then you should probably allow for gtt
based scrolling eventually, but thats an optimisation.


> +int sdrm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> +{
> +	struct drm_gem_object *obj = vma->vm_private_data;
> +	struct sdrm_gem_obj *sdrm_gem_obj = to_sdrm_gem_obj(obj);
> +	struct drm_device *dev = obj->dev;
> +	unsigned long pfn;
> +	pgoff_t page_offset;
> +	int ret;

For dumb hardware take a look how gma500 and some other bits do this -
you can premap the entire buffer when you take the first fault, which for
a dumb fb is a good bet.



Looking at it from the point of view of x86 legacy devices then the
things I see are

- Device is quite possibly PCI (but may be platform eg vesa)
- Memory will probably be allocated in the PCI space
- Mappings are probably write combining but not on all hardware

There's probably a case for pinning/unpinning scanout buffers according
to whether they are used. On some hardware the io mapping needed is a
precious resource. Also for stuff with a fixed fb space it means you can
combine it with invalidating the mmap mappings of an object and copying
objects in/out of the frame buffer to provide the expected interfaces to
allocate/release framebuffers.

Alan

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

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-11 20:22     ` Alan Cox
@ 2012-04-12  8:58       ` Sascha Hauer
  -1 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-12  8:58 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 11, 2012 at 09:22:47PM +0100, Alan Cox wrote:
> > +static int sdrm_suspend(struct drm_device *drm, pm_message_t state)
> > +{
> > +	/* TODO */
> > +
> > +	return 0;
> > +}
> > +
> > +static int sdrm_resume(struct drm_device *drm)
> > +{
> > +	/* TODO */
> > +
> > +	return 0;
> > +}
> 
> These probably need to call into the sdrm device specific handling.
> 
> 
> > +static int sdrm_get_irq(struct drm_device *dev)
> > +{
> > +	/*
> > +	 * Return an arbitrary number to make the core happy.
> > +	 * We can't return anything meaningful here since drm
> > +	 * devices in general have multiple irqs
> > +	 */
> > +	return 1234;
> > +}
> 
> If there isn't a meaningful IRQ then surely 0 should be returned.
> Actually I'd suggest returning sdrm->irq or similar, because some simple
> DRM type use cases will have a single IRQ (notably 2 on older PC hardware)

Hm, At the moment I can't even trigger this function to be called. I can
simply return 0 here. Returning a real irq does not sound sane since I
want the interrupt handled internally. Noone else has any business using
it.

> 
> > + * sdrm_device_get - find or allocate sdrm device with unique name
> > + *
> > + * This function returns the sdrm device with the unique name 'name'
> > + * If this already exists, return it, otherwise allocate a new
> > + * object.
> 
> This naming is a bit confusing because the kernel mid layers etc tend to
> use _get and _put for ref counting not lookup ?

Ok, lookup sounds better. Will rename it.

> 
> 
> > +	/*
> > +	 * 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 spsdrmific driver own one instead bsdrmause
> > +	 *      drm framework supports only one irq handler and
> > +	 *      drivers can well take care of their interrupts
> > +	 */
> > +	drm->irq_enabled = 1;
> 
> We've got a couple of assumptions here I think I'd question for generality
> 
> 1. That its a platform device
> 2. That it can't use the standard IRQ helpers in some cases.
> 
> Probably it should take a struct device and a struct of the bits you'd
> fish out from platform or pci or other device type. And yes probably
> there would be a platform_ version that wraps it.

I had a look and it turned out that I don't need anything specific to a
platform_device, so I can simply pass in a regular struct device here.
Having a platform_device here seems to be a leftover from earlier
versions in which I used the drm_platform stubs.

> 
> 
> > +static int sdrm_fb_dirty(struct drm_framebuffer *fb,
> > +		struct drm_file *file_priv, unsigned flags,
> > +		unsigned color, struct drm_clip_rect *clips,
> > +		unsigned num_clips)
> > +{
> > +	/* TODO */
> > +
> > +	return 0;
> > +}
> 
> Probably a helper method.

Yes.

> 
> > +static struct fb_ops sdrm_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,
> > +};
> 
> If you re assuming any kind of gtt then you should probably allow for gtt
> based scrolling eventually, but thats an optimisation.

I'll keep that for later.

> 
> 
> > +int sdrm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> > +{
> > +	struct drm_gem_object *obj = vma->vm_private_data;
> > +	struct sdrm_gem_obj *sdrm_gem_obj = to_sdrm_gem_obj(obj);
> > +	struct drm_device *dev = obj->dev;
> > +	unsigned long pfn;
> > +	pgoff_t page_offset;
> > +	int ret;
> 
> For dumb hardware take a look how gma500 and some other bits do this -
> you can premap the entire buffer when you take the first fault, which for
> a dumb fb is a good bet.
> 
> 
> 
> Looking at it from the point of view of x86 legacy devices then the
> things I see are
> 
> - Device is quite possibly PCI (but may be platform eg vesa)
> - Memory will probably be allocated in the PCI space
> - Mappings are probably write combining but not on all hardware
> 
> There's probably a case for pinning/unpinning scanout buffers according
> to whether they are used. On some hardware the io mapping needed is a
> precious resource. Also for stuff with a fixed fb space it means you can
> combine it with invalidating the mmap mappings of an object and copying
> objects in/out of the frame buffer to provide the expected interfaces to
> allocate/release framebuffers.

I'll have a look. Unfortunately my knowledge of these things is quite
limited. I am hoping a bit for Thierry here since he has a iommu on
Tegra and maybe this helps making the GEM support more generic.

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] 44+ messages in thread

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-12  8:58       ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-12  8:58 UTC (permalink / raw)
  To: Alan Cox; +Cc: kernel, linux-arm-kernel, dri-devel

On Wed, Apr 11, 2012 at 09:22:47PM +0100, Alan Cox wrote:
> > +static int sdrm_suspend(struct drm_device *drm, pm_message_t state)
> > +{
> > +	/* TODO */
> > +
> > +	return 0;
> > +}
> > +
> > +static int sdrm_resume(struct drm_device *drm)
> > +{
> > +	/* TODO */
> > +
> > +	return 0;
> > +}
> 
> These probably need to call into the sdrm device specific handling.
> 
> 
> > +static int sdrm_get_irq(struct drm_device *dev)
> > +{
> > +	/*
> > +	 * Return an arbitrary number to make the core happy.
> > +	 * We can't return anything meaningful here since drm
> > +	 * devices in general have multiple irqs
> > +	 */
> > +	return 1234;
> > +}
> 
> If there isn't a meaningful IRQ then surely 0 should be returned.
> Actually I'd suggest returning sdrm->irq or similar, because some simple
> DRM type use cases will have a single IRQ (notably 2 on older PC hardware)

Hm, At the moment I can't even trigger this function to be called. I can
simply return 0 here. Returning a real irq does not sound sane since I
want the interrupt handled internally. Noone else has any business using
it.

> 
> > + * sdrm_device_get - find or allocate sdrm device with unique name
> > + *
> > + * This function returns the sdrm device with the unique name 'name'
> > + * If this already exists, return it, otherwise allocate a new
> > + * object.
> 
> This naming is a bit confusing because the kernel mid layers etc tend to
> use _get and _put for ref counting not lookup ?

Ok, lookup sounds better. Will rename it.

> 
> 
> > +	/*
> > +	 * 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 spsdrmific driver own one instead bsdrmause
> > +	 *      drm framework supports only one irq handler and
> > +	 *      drivers can well take care of their interrupts
> > +	 */
> > +	drm->irq_enabled = 1;
> 
> We've got a couple of assumptions here I think I'd question for generality
> 
> 1. That its a platform device
> 2. That it can't use the standard IRQ helpers in some cases.
> 
> Probably it should take a struct device and a struct of the bits you'd
> fish out from platform or pci or other device type. And yes probably
> there would be a platform_ version that wraps it.

I had a look and it turned out that I don't need anything specific to a
platform_device, so I can simply pass in a regular struct device here.
Having a platform_device here seems to be a leftover from earlier
versions in which I used the drm_platform stubs.

> 
> 
> > +static int sdrm_fb_dirty(struct drm_framebuffer *fb,
> > +		struct drm_file *file_priv, unsigned flags,
> > +		unsigned color, struct drm_clip_rect *clips,
> > +		unsigned num_clips)
> > +{
> > +	/* TODO */
> > +
> > +	return 0;
> > +}
> 
> Probably a helper method.

Yes.

> 
> > +static struct fb_ops sdrm_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,
> > +};
> 
> If you re assuming any kind of gtt then you should probably allow for gtt
> based scrolling eventually, but thats an optimisation.

I'll keep that for later.

> 
> 
> > +int sdrm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> > +{
> > +	struct drm_gem_object *obj = vma->vm_private_data;
> > +	struct sdrm_gem_obj *sdrm_gem_obj = to_sdrm_gem_obj(obj);
> > +	struct drm_device *dev = obj->dev;
> > +	unsigned long pfn;
> > +	pgoff_t page_offset;
> > +	int ret;
> 
> For dumb hardware take a look how gma500 and some other bits do this -
> you can premap the entire buffer when you take the first fault, which for
> a dumb fb is a good bet.
> 
> 
> 
> Looking at it from the point of view of x86 legacy devices then the
> things I see are
> 
> - Device is quite possibly PCI (but may be platform eg vesa)
> - Memory will probably be allocated in the PCI space
> - Mappings are probably write combining but not on all hardware
> 
> There's probably a case for pinning/unpinning scanout buffers according
> to whether they are used. On some hardware the io mapping needed is a
> precious resource. Also for stuff with a fixed fb space it means you can
> combine it with invalidating the mmap mappings of an object and copying
> objects in/out of the frame buffer to provide the expected interfaces to
> allocate/release framebuffers.

I'll have a look. Unfortunately my knowledge of these things is quite
limited. I am hoping a bit for Thierry here since he has a iommu on
Tegra and maybe this helps making the GEM support more generic.

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] 44+ messages in thread

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-11 15:33   ` Sascha Hauer
@ 2012-04-20 10:02     ` Dave Airlie
  -1 siblings, 0 replies; 44+ messages in thread
From: Dave Airlie @ 2012-04-20 10:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 11, 2012 at 4:33 PM, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> This patch adds support for creating simple drm devices. The
> basic idea of this patch is that drm drivers using the sdrm layer
> no longer have to implement a full drm device but instead only
> register crtcs, encoders and connectors with the sdrm layer. The
> sdrm layer then registers a drm device with the drm core and
> takes care of the drm device.

I'm sorry to say I totally hate this on every level. I think I said to
you before that midlayers are not the answer, and this is a hella big
midlayer.

I understand the SoC architecture but I can't think this is the way forward.

The problems I see from a highlevel:

This layer is highly restrictive on its users, I get the feeling once
you get to implement 2-3 complete drivers or try and implement a
driver say on x86 that should work in a similar fashion you are going
to realise you made things overly generic and the driver can't change
it. Then the drivers will start to workaround the midlayer and we'll
end up worse off. I don't really want to pick on specifics but the
taking over of the fb ops is on example,

I think this should work as a set of helpers that might work in place
of the current set of helpers. The current helpers are very directed
towards desktop x86 experience, so a new set of these might be better.

I get the feeling the drm can just be a virtual platform device of
some sort, then it reads the device tree and binds all the information
on what crtc/encoders are available,

Also the mode group stuff isn't legacy, the render nodes stuff posted
is what is intended to use it for, again it may not be useful on ARM,
but on desktop it has a very useful use case.

I'm sorry to not provide the answer I would fine acceptable, maybe if
I had a week of time to write something I could figure it out, maybe
someone else can give advice on how this sort of thing might look,
Linearo/ARM guys can some of you guys look at this?

Dave.

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

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-20 10:02     ` Dave Airlie
  0 siblings, 0 replies; 44+ messages in thread
From: Dave Airlie @ 2012-04-20 10:02 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: kernel, linux-arm-kernel, dri-devel

On Wed, Apr 11, 2012 at 4:33 PM, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> This patch adds support for creating simple drm devices. The
> basic idea of this patch is that drm drivers using the sdrm layer
> no longer have to implement a full drm device but instead only
> register crtcs, encoders and connectors with the sdrm layer. The
> sdrm layer then registers a drm device with the drm core and
> takes care of the drm device.

I'm sorry to say I totally hate this on every level. I think I said to
you before that midlayers are not the answer, and this is a hella big
midlayer.

I understand the SoC architecture but I can't think this is the way forward.

The problems I see from a highlevel:

This layer is highly restrictive on its users, I get the feeling once
you get to implement 2-3 complete drivers or try and implement a
driver say on x86 that should work in a similar fashion you are going
to realise you made things overly generic and the driver can't change
it. Then the drivers will start to workaround the midlayer and we'll
end up worse off. I don't really want to pick on specifics but the
taking over of the fb ops is on example,

I think this should work as a set of helpers that might work in place
of the current set of helpers. The current helpers are very directed
towards desktop x86 experience, so a new set of these might be better.

I get the feeling the drm can just be a virtual platform device of
some sort, then it reads the device tree and binds all the information
on what crtc/encoders are available,

Also the mode group stuff isn't legacy, the render nodes stuff posted
is what is intended to use it for, again it may not be useful on ARM,
but on desktop it has a very useful use case.

I'm sorry to not provide the answer I would fine acceptable, maybe if
I had a week of time to write something I could figure it out, maybe
someone else can give advice on how this sort of thing might look,
Linearo/ARM guys can some of you guys look at this?

Dave.

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

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-20 10:02     ` Dave Airlie
@ 2012-04-20 12:38       ` Thierry Reding
  -1 siblings, 0 replies; 44+ messages in thread
From: Thierry Reding @ 2012-04-20 12:38 UTC (permalink / raw)
  To: linux-arm-kernel

* Dave Airlie wrote:
> I get the feeling the drm can just be a virtual platform device of
> some sort, then it reads the device tree and binds all the information
> on what crtc/encoders are available,

That's pretty much what I've come up with in the second round of Tegra DRM
patches. Basically display controllers and outputs (RGB, HDMI, TVO, DSI) get
separate drivers and register themselves with the DRM driver which then looks
at the device tree to see which display controllers to register as CRTCs and
parses a list of connector nodes to create encoder/connector pairs that
define the physical connectors and their corresponding outputs.

I did take a brief look at the SDRM patches as well and they didn't quite
seem to fit what was needed for Tegra. But if time allows I'll take a closer
look.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120420/704fc456/attachment.sig>

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

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-20 12:38       ` Thierry Reding
  0 siblings, 0 replies; 44+ messages in thread
From: Thierry Reding @ 2012-04-20 12:38 UTC (permalink / raw)
  To: Dave Airlie; +Cc: dri-devel, Sascha Hauer, linux-arm-kernel, kernel


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

* Dave Airlie wrote:
> I get the feeling the drm can just be a virtual platform device of
> some sort, then it reads the device tree and binds all the information
> on what crtc/encoders are available,

That's pretty much what I've come up with in the second round of Tegra DRM
patches. Basically display controllers and outputs (RGB, HDMI, TVO, DSI) get
separate drivers and register themselves with the DRM driver which then looks
at the device tree to see which display controllers to register as CRTCs and
parses a list of connector nodes to create encoder/connector pairs that
define the physical connectors and their corresponding outputs.

I did take a brief look at the SDRM patches as well and they didn't quite
seem to fit what was needed for Tegra. But if time allows I'll take a closer
look.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 198 bytes --]

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

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-20 10:02     ` Dave Airlie
@ 2012-04-20 13:10       ` Sascha Hauer
  -1 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-20 13:10 UTC (permalink / raw)
  To: linux-arm-kernel

[Added some embedded graphics maintainers to Cc who might be interested
in this]

On Fri, Apr 20, 2012 at 11:02:02AM +0100, Dave Airlie wrote:
> On Wed, Apr 11, 2012 at 4:33 PM, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> > This patch adds support for creating simple drm devices. The
> > basic idea of this patch is that drm drivers using the sdrm layer
> > no longer have to implement a full drm device but instead only
> > register crtcs, encoders and connectors with the sdrm layer. The
> > sdrm layer then registers a drm device with the drm core and
> > takes care of the drm device.
> 
> I'm sorry to say I totally hate this on every level. I think I said to
> you before that midlayers are not the answer, and this is a hella big
> midlayer.
> 
> I understand the SoC architecture but I can't think this is the way forward.
> 
> The problems I see from a highlevel:
> 
> This layer is highly restrictive on its users, I get the feeling once
> you get to implement 2-3 complete drivers or try and implement a
> driver say on x86 that should work in a similar fashion you are going
> to realise you made things overly generic and the driver can't change
> it.

That's maybe where our philosophies clash. I'd say that drivers should just
have enough freedom to get their job done whereas you want to give
drivers the freedom to do anything they want. I come from an area where
we have dozens of drivers which mostly are quite similar, in the ARM
world we currently try to get the duplication out of the drivers because
otherwise we can't handle the sheer amount of devices we have.

> Then the drivers will start to workaround the midlayer and we'll
> end up worse off. I don't really want to pick on specifics but the
> taking over of the fb ops is on example,

The layer can be extended when we need it, no need to work around it. I
took over the fb ops because currently I don't need to adjust them. I
know that some devices have accelerated blit operations and this means
that we may add a way to overwrite the ops later. Same with the ioctls.
I didn't provide a way to add device specific ioctls now, partly because
currently I don't need them, partly to get this series out.

> 
> I think this should work as a set of helpers that might work in place
> of the current set of helpers. The current helpers are very directed
> towards desktop x86 experience, so a new set of these might be better.

Hm, this means duplicating the helpers. The KMS support is to a great
extend defined by the helpers. Duplicating them would mean more code
fragmentation and different behaviour from a users point of view. I'd
rather not go this way.

> 
> I get the feeling the drm can just be a virtual platform device of
> some sort, then it reads the device tree and binds all the information
> on what crtc/encoders are available,

We can do that. Currently I use a string matching mechanism to tie
together the different pieces of a device, but sooner or later it will
be devicetree anyway, so no problem to convert this sooner instead of
later. The only problem I see with this is that for example X86 will not
have devicetree, so I see a value in not binding whatever we come up
with too tight to devicetree.

> 
> Also the mode group stuff isn't legacy, the render nodes stuff posted
> is what is intended to use it for, again it may not be useful on ARM,
> but on desktop it has a very useful use case.

I didn't remove them because they are not useful, but because currently
I couldn't add an encoder/connector to a active drm device (see how the
exynos driver currently works around this, I doubt it will work this way
once the legacy_mode_group handling is actually used). The fact that this
is currently unused motivated me to remove it. That said, I can live without
it, no problem.

> 
> I'm sorry to not provide the answer I would fine acceptable, maybe if
> I had a week of time to write something I could figure it out, maybe
> someone else can give advice on how this sort of thing might look,
> Linearo/ARM guys can some of you guys look at this?

Take these patches as my try to show that something has to change to
make drm stuff more widely usable on embedded devices. As said, there
are dozens of different devices out there, many of them are dumb
framebuffer devices like the PXA and i.MX driver I posted, others are
more advanced like the exynos, tegra and the newer i.MX devices. Some of
the bigger players can effort to write (and maintain?) a 300KB driver,
others can not. Those who cannot currently stay in drivers/video, but
this has limitations when it comes to overlay, hot pluggable monitors,
multiple crtcs, etc.

I'd like to find a solution for this which makes us all happy. In the
end reducing the amount of code duplication also helps you as a
maintainer.

(BTW each driver in drm has this layer somewhere in it. If I had hidden
it in imx specific functions I probably wouldn't have raised any
questions, but I don't want to go that way)

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] 44+ messages in thread

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-20 13:10       ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-20 13:10 UTC (permalink / raw)
  To: Dave Airlie; +Cc: dri-devel, Rob Clark, kernel, linux-arm-kernel

[Added some embedded graphics maintainers to Cc who might be interested
in this]

On Fri, Apr 20, 2012 at 11:02:02AM +0100, Dave Airlie wrote:
> On Wed, Apr 11, 2012 at 4:33 PM, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> > This patch adds support for creating simple drm devices. The
> > basic idea of this patch is that drm drivers using the sdrm layer
> > no longer have to implement a full drm device but instead only
> > register crtcs, encoders and connectors with the sdrm layer. The
> > sdrm layer then registers a drm device with the drm core and
> > takes care of the drm device.
> 
> I'm sorry to say I totally hate this on every level. I think I said to
> you before that midlayers are not the answer, and this is a hella big
> midlayer.
> 
> I understand the SoC architecture but I can't think this is the way forward.
> 
> The problems I see from a highlevel:
> 
> This layer is highly restrictive on its users, I get the feeling once
> you get to implement 2-3 complete drivers or try and implement a
> driver say on x86 that should work in a similar fashion you are going
> to realise you made things overly generic and the driver can't change
> it.

That's maybe where our philosophies clash. I'd say that drivers should just
have enough freedom to get their job done whereas you want to give
drivers the freedom to do anything they want. I come from an area where
we have dozens of drivers which mostly are quite similar, in the ARM
world we currently try to get the duplication out of the drivers because
otherwise we can't handle the sheer amount of devices we have.

> Then the drivers will start to workaround the midlayer and we'll
> end up worse off. I don't really want to pick on specifics but the
> taking over of the fb ops is on example,

The layer can be extended when we need it, no need to work around it. I
took over the fb ops because currently I don't need to adjust them. I
know that some devices have accelerated blit operations and this means
that we may add a way to overwrite the ops later. Same with the ioctls.
I didn't provide a way to add device specific ioctls now, partly because
currently I don't need them, partly to get this series out.

> 
> I think this should work as a set of helpers that might work in place
> of the current set of helpers. The current helpers are very directed
> towards desktop x86 experience, so a new set of these might be better.

Hm, this means duplicating the helpers. The KMS support is to a great
extend defined by the helpers. Duplicating them would mean more code
fragmentation and different behaviour from a users point of view. I'd
rather not go this way.

> 
> I get the feeling the drm can just be a virtual platform device of
> some sort, then it reads the device tree and binds all the information
> on what crtc/encoders are available,

We can do that. Currently I use a string matching mechanism to tie
together the different pieces of a device, but sooner or later it will
be devicetree anyway, so no problem to convert this sooner instead of
later. The only problem I see with this is that for example X86 will not
have devicetree, so I see a value in not binding whatever we come up
with too tight to devicetree.

> 
> Also the mode group stuff isn't legacy, the render nodes stuff posted
> is what is intended to use it for, again it may not be useful on ARM,
> but on desktop it has a very useful use case.

I didn't remove them because they are not useful, but because currently
I couldn't add an encoder/connector to a active drm device (see how the
exynos driver currently works around this, I doubt it will work this way
once the legacy_mode_group handling is actually used). The fact that this
is currently unused motivated me to remove it. That said, I can live without
it, no problem.

> 
> I'm sorry to not provide the answer I would fine acceptable, maybe if
> I had a week of time to write something I could figure it out, maybe
> someone else can give advice on how this sort of thing might look,
> Linearo/ARM guys can some of you guys look at this?

Take these patches as my try to show that something has to change to
make drm stuff more widely usable on embedded devices. As said, there
are dozens of different devices out there, many of them are dumb
framebuffer devices like the PXA and i.MX driver I posted, others are
more advanced like the exynos, tegra and the newer i.MX devices. Some of
the bigger players can effort to write (and maintain?) a 300KB driver,
others can not. Those who cannot currently stay in drivers/video, but
this has limitations when it comes to overlay, hot pluggable monitors,
multiple crtcs, etc.

I'd like to find a solution for this which makes us all happy. In the
end reducing the amount of code duplication also helps you as a
maintainer.

(BTW each driver in drm has this layer somewhere in it. If I had hidden
it in imx specific functions I probably wouldn't have raised any
questions, but I don't want to go that way)

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] 44+ messages in thread

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-20 12:38       ` Thierry Reding
@ 2012-04-20 13:20         ` Sascha Hauer
  -1 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-20 13:20 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 20, 2012 at 02:38:43PM +0200, Thierry Reding wrote:
> * Dave Airlie wrote:
> > I get the feeling the drm can just be a virtual platform device of
> > some sort, then it reads the device tree and binds all the information
> > on what crtc/encoders are available,
> 
> That's pretty much what I've come up with in the second round of Tegra DRM
> patches. Basically display controllers and outputs (RGB, HDMI, TVO, DSI) get
> separate drivers and register themselves with the DRM driver which then looks
> at the device tree to see which display controllers to register as CRTCs and
> parses a list of connector nodes to create encoder/connector pairs that
> define the physical connectors and their corresponding outputs.
> 
> I did take a brief look at the SDRM patches as well and they didn't quite
> seem to fit what was needed for Tegra. But if time allows I'll take a closer
> look.

Can you elaborate which parts don't fit? I am very interested to
improve this situation, and I think having code to share will be a
benefit for us all.

I know that the patches I wrote are no one-solution-fits-for-all yet,
they are mainly something to show that drm drivers do not have to be
huge complex drivers.

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] 44+ messages in thread

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-20 13:20         ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-20 13:20 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel, linux-arm-kernel, kernel

On Fri, Apr 20, 2012 at 02:38:43PM +0200, Thierry Reding wrote:
> * Dave Airlie wrote:
> > I get the feeling the drm can just be a virtual platform device of
> > some sort, then it reads the device tree and binds all the information
> > on what crtc/encoders are available,
> 
> That's pretty much what I've come up with in the second round of Tegra DRM
> patches. Basically display controllers and outputs (RGB, HDMI, TVO, DSI) get
> separate drivers and register themselves with the DRM driver which then looks
> at the device tree to see which display controllers to register as CRTCs and
> parses a list of connector nodes to create encoder/connector pairs that
> define the physical connectors and their corresponding outputs.
> 
> I did take a brief look at the SDRM patches as well and they didn't quite
> seem to fit what was needed for Tegra. But if time allows I'll take a closer
> look.

Can you elaborate which parts don't fit? I am very interested to
improve this situation, and I think having code to share will be a
benefit for us all.

I know that the patches I wrote are no one-solution-fits-for-all yet,
they are mainly something to show that drm drivers do not have to be
huge complex drivers.

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] 44+ messages in thread

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-20 13:10       ` Sascha Hauer
@ 2012-04-20 13:33         ` Daniel Vetter
  -1 siblings, 0 replies; 44+ messages in thread
From: Daniel Vetter @ 2012-04-20 13:33 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 20, 2012 at 03:10:05PM +0200, Sascha Hauer wrote:
> (BTW each driver in drm has this layer somewhere in it. If I had hidden
> it in imx specific functions I probably wouldn't have raised any
> questions, but I don't want to go that way)

That's _exactly_ what you should be doing. And once you have more than one
driver that works in a similar way, you can extract the common code as
helper functions to make life easier. Like Rob&Alan did with a few gem
helpers they needed in omapdrm/gma500.

For your case it sounds like a new set of modeset helper functions
tailored for the embedded use-case would be good (as Dave Airlie
suggested). Adding yet another middle-layer (like sdrm is) that forces
drivers to go through it usually ends up in tears. And drm core
unfortunately still has too much middle-layer heritage: For an awful lot
of setup and teardown issues it's the core of the problme - because
drivers can't control when drm sets up and tears down certain things, it's
done at the wrong time (for certain drivers at least). Same problem when
the abstraction doesn't quite fit.

Helper functions leave the driver in full control of what's going on, and
working around hw-specific madness with ease.

https://lwn.net/Articles/336262/ is the canonical reference for why a lot
of kernel people are allergic to anything that looks like a middle-layer.

Yours, Daniel
-- 
Daniel Vetter
Mail: daniel at ffwll.ch
Mobile: +41 (0)79 365 57 48

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

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-20 13:33         ` Daniel Vetter
  0 siblings, 0 replies; 44+ messages in thread
From: Daniel Vetter @ 2012-04-20 13:33 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-arm-kernel, kernel, dri-devel, Rob Clark

On Fri, Apr 20, 2012 at 03:10:05PM +0200, Sascha Hauer wrote:
> (BTW each driver in drm has this layer somewhere in it. If I had hidden
> it in imx specific functions I probably wouldn't have raised any
> questions, but I don't want to go that way)

That's _exactly_ what you should be doing. And once you have more than one
driver that works in a similar way, you can extract the common code as
helper functions to make life easier. Like Rob&Alan did with a few gem
helpers they needed in omapdrm/gma500.

For your case it sounds like a new set of modeset helper functions
tailored for the embedded use-case would be good (as Dave Airlie
suggested). Adding yet another middle-layer (like sdrm is) that forces
drivers to go through it usually ends up in tears. And drm core
unfortunately still has too much middle-layer heritage: For an awful lot
of setup and teardown issues it's the core of the problme - because
drivers can't control when drm sets up and tears down certain things, it's
done at the wrong time (for certain drivers at least). Same problem when
the abstraction doesn't quite fit.

Helper functions leave the driver in full control of what's going on, and
working around hw-specific madness with ease.

https://lwn.net/Articles/336262/ is the canonical reference for why a lot
of kernel people are allergic to anything that looks like a middle-layer.

Yours, Daniel
-- 
Daniel Vetter
Mail: daniel@ffwll.ch
Mobile: +41 (0)79 365 57 48

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

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-20 12:38       ` Thierry Reding
@ 2012-04-20 14:25         ` Mark Brown
  -1 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2012-04-20 14:25 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 20, 2012 at 02:38:43PM +0200, Thierry Reding wrote:
> * Dave Airlie wrote:
> > I get the feeling the drm can just be a virtual platform device of
> > some sort, then it reads the device tree and binds all the information
> > on what crtc/encoders are available,

> That's pretty much what I've come up with in the second round of Tegra DRM
> patches. Basically display controllers and outputs (RGB, HDMI, TVO, DSI) get
> separate drivers and register themselves with the DRM driver which then looks
> at the device tree to see which display controllers to register as CRTCs and
> parses a list of connector nodes to create encoder/connector pairs that
> define the physical connectors and their corresponding outputs.

> I did take a brief look at the SDRM patches as well and they didn't quite
> seem to fit what was needed for Tegra. But if time allows I'll take a closer
> look.

This sounds an awful lot like how ASoC hangs together...

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

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-20 14:25         ` Mark Brown
  0 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2012-04-20 14:25 UTC (permalink / raw)
  To: Thierry Reding
  Cc: kernel, Sascha Hauer, Dave Airlie, linux-arm-kernel, dri-devel

On Fri, Apr 20, 2012 at 02:38:43PM +0200, Thierry Reding wrote:
> * Dave Airlie wrote:
> > I get the feeling the drm can just be a virtual platform device of
> > some sort, then it reads the device tree and binds all the information
> > on what crtc/encoders are available,

> That's pretty much what I've come up with in the second round of Tegra DRM
> patches. Basically display controllers and outputs (RGB, HDMI, TVO, DSI) get
> separate drivers and register themselves with the DRM driver which then looks
> at the device tree to see which display controllers to register as CRTCs and
> parses a list of connector nodes to create encoder/connector pairs that
> define the physical connectors and their corresponding outputs.

> I did take a brief look at the SDRM patches as well and they didn't quite
> seem to fit what was needed for Tegra. But if time allows I'll take a closer
> look.

This sounds an awful lot like how ASoC hangs together...

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

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-20 14:25         ` Mark Brown
@ 2012-04-20 14:49           ` Thierry Reding
  -1 siblings, 0 replies; 44+ messages in thread
From: Thierry Reding @ 2012-04-20 14:49 UTC (permalink / raw)
  To: linux-arm-kernel

* Mark Brown wrote:
> On Fri, Apr 20, 2012 at 02:38:43PM +0200, Thierry Reding wrote:
> > * Dave Airlie wrote:
> > > I get the feeling the drm can just be a virtual platform device of
> > > some sort, then it reads the device tree and binds all the information
> > > on what crtc/encoders are available,
> 
> > That's pretty much what I've come up with in the second round of Tegra DRM
> > patches. Basically display controllers and outputs (RGB, HDMI, TVO, DSI) get
> > separate drivers and register themselves with the DRM driver which then looks
> > at the device tree to see which display controllers to register as CRTCs and
> > parses a list of connector nodes to create encoder/connector pairs that
> > define the physical connectors and their corresponding outputs.
> 
> > I did take a brief look at the SDRM patches as well and they didn't quite
> > seem to fit what was needed for Tegra. But if time allows I'll take a closer
> > look.
> 
> This sounds an awful lot like how ASoC hangs together...

What in particular sounds awful?

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120420/a3a3e867/attachment.sig>

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

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-20 14:49           ` Thierry Reding
  0 siblings, 0 replies; 44+ messages in thread
From: Thierry Reding @ 2012-04-20 14:49 UTC (permalink / raw)
  To: Mark Brown; +Cc: kernel, Sascha Hauer, Dave Airlie, linux-arm-kernel, dri-devel


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

* Mark Brown wrote:
> On Fri, Apr 20, 2012 at 02:38:43PM +0200, Thierry Reding wrote:
> > * Dave Airlie wrote:
> > > I get the feeling the drm can just be a virtual platform device of
> > > some sort, then it reads the device tree and binds all the information
> > > on what crtc/encoders are available,
> 
> > That's pretty much what I've come up with in the second round of Tegra DRM
> > patches. Basically display controllers and outputs (RGB, HDMI, TVO, DSI) get
> > separate drivers and register themselves with the DRM driver which then looks
> > at the device tree to see which display controllers to register as CRTCs and
> > parses a list of connector nodes to create encoder/connector pairs that
> > define the physical connectors and their corresponding outputs.
> 
> > I did take a brief look at the SDRM patches as well and they didn't quite
> > seem to fit what was needed for Tegra. But if time allows I'll take a closer
> > look.
> 
> This sounds an awful lot like how ASoC hangs together...

What in particular sounds awful?

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 198 bytes --]

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

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-20 14:49           ` Thierry Reding
@ 2012-04-20 15:06             ` Mark Brown
  -1 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2012-04-20 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 20, 2012 at 04:49:43PM +0200, Thierry Reding wrote:
> * Mark Brown wrote:

> > This sounds an awful lot like how ASoC hangs together...

> What in particular sounds awful?

Nothing - "an awful" is an English idiom for "very".
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120420/2cae7345/attachment-0001.sig>

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

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-20 15:06             ` Mark Brown
  0 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2012-04-20 15:06 UTC (permalink / raw)
  To: Thierry Reding
  Cc: kernel, Sascha Hauer, Dave Airlie, linux-arm-kernel, dri-devel


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

On Fri, Apr 20, 2012 at 04:49:43PM +0200, Thierry Reding wrote:
> * Mark Brown wrote:

> > This sounds an awful lot like how ASoC hangs together...

> What in particular sounds awful?

Nothing - "an awful" is an English idiom for "very".

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

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

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-20 15:06             ` Mark Brown
@ 2012-04-20 15:13               ` Thierry Reding
  -1 siblings, 0 replies; 44+ messages in thread
From: Thierry Reding @ 2012-04-20 15:13 UTC (permalink / raw)
  To: linux-arm-kernel

* Mark Brown wrote:
> On Fri, Apr 20, 2012 at 04:49:43PM +0200, Thierry Reding wrote:
> > * Mark Brown wrote:
> 
> > > This sounds an awful lot like how ASoC hangs together...
> 
> > What in particular sounds awful?
> 
> Nothing - "an awful" is an English idiom for "very".

I know =) But it has a somewhat negative connotation, from which I deduced
that you somehow thought it wasn't a good solution.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120420/0367c77e/attachment.sig>

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

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-20 15:13               ` Thierry Reding
  0 siblings, 0 replies; 44+ messages in thread
From: Thierry Reding @ 2012-04-20 15:13 UTC (permalink / raw)
  To: Mark Brown; +Cc: kernel, linux-arm-kernel, dri-devel


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

* Mark Brown wrote:
> On Fri, Apr 20, 2012 at 04:49:43PM +0200, Thierry Reding wrote:
> > * Mark Brown wrote:
> 
> > > This sounds an awful lot like how ASoC hangs together...
> 
> > What in particular sounds awful?
> 
> Nothing - "an awful" is an English idiom for "very".

I know =) But it has a somewhat negative connotation, from which I deduced
that you somehow thought it wasn't a good solution.

Thierry

[-- Attachment #1.2: 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] 44+ messages in thread

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-20 14:25         ` Mark Brown
@ 2012-04-20 15:15           ` Sascha Hauer
  -1 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-20 15:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 20, 2012 at 03:25:58PM +0100, Mark Brown wrote:
> On Fri, Apr 20, 2012 at 02:38:43PM +0200, Thierry Reding wrote:
> > * Dave Airlie wrote:
> > > I get the feeling the drm can just be a virtual platform device of
> > > some sort, then it reads the device tree and binds all the information
> > > on what crtc/encoders are available,
> 
> > That's pretty much what I've come up with in the second round of Tegra DRM
> > patches. Basically display controllers and outputs (RGB, HDMI, TVO, DSI) get
> > separate drivers and register themselves with the DRM driver which then looks
> > at the device tree to see which display controllers to register as CRTCs and
> > parses a list of connector nodes to create encoder/connector pairs that
> > define the physical connectors and their corresponding outputs.
> 
> > I did take a brief look at the SDRM patches as well and they didn't quite
> > seem to fit what was needed for Tegra. But if time allows I'll take a closer
> > look.
> 
> This sounds an awful lot like how ASoC hangs together...

Very much, yes. In ASoC and DRM we both have several physical devices spread
around the SoC which form a logical device. I assume that before ASoC existed
also everyone had a single PCI device which could be used to collect the
information together.

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] 44+ messages in thread

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-20 15:15           ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-20 15:15 UTC (permalink / raw)
  To: Mark Brown; +Cc: kernel, linux-arm-kernel, dri-devel

On Fri, Apr 20, 2012 at 03:25:58PM +0100, Mark Brown wrote:
> On Fri, Apr 20, 2012 at 02:38:43PM +0200, Thierry Reding wrote:
> > * Dave Airlie wrote:
> > > I get the feeling the drm can just be a virtual platform device of
> > > some sort, then it reads the device tree and binds all the information
> > > on what crtc/encoders are available,
> 
> > That's pretty much what I've come up with in the second round of Tegra DRM
> > patches. Basically display controllers and outputs (RGB, HDMI, TVO, DSI) get
> > separate drivers and register themselves with the DRM driver which then looks
> > at the device tree to see which display controllers to register as CRTCs and
> > parses a list of connector nodes to create encoder/connector pairs that
> > define the physical connectors and their corresponding outputs.
> 
> > I did take a brief look at the SDRM patches as well and they didn't quite
> > seem to fit what was needed for Tegra. But if time allows I'll take a closer
> > look.
> 
> This sounds an awful lot like how ASoC hangs together...

Very much, yes. In ASoC and DRM we both have several physical devices spread
around the SoC which form a logical device. I assume that before ASoC existed
also everyone had a single PCI device which could be used to collect the
information together.

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] 44+ messages in thread

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-20 15:15           ` Sascha Hauer
@ 2012-04-20 15:20             ` Mark Brown
  -1 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2012-04-20 15:20 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 20, 2012 at 05:15:18PM +0200, Sascha Hauer wrote:
> On Fri, Apr 20, 2012 at 03:25:58PM +0100, Mark Brown wrote:

> > This sounds an awful lot like how ASoC hangs together...

> Very much, yes. In ASoC and DRM we both have several physical devices spread
> around the SoC which form a logical device. I assume that before ASoC existed
> also everyone had a single PCI device which could be used to collect the
> information together.

Yeah, it's a similar issue - on PC hardware we tend to have a single
integrated device which does everything (at least from the point of view
of the outside world, physically things may sometimes be split).
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120420/61d9507c/attachment.sig>

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

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-20 15:20             ` Mark Brown
  0 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2012-04-20 15:20 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: kernel, Thierry Reding, Dave Airlie, linux-arm-kernel, dri-devel


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

On Fri, Apr 20, 2012 at 05:15:18PM +0200, Sascha Hauer wrote:
> On Fri, Apr 20, 2012 at 03:25:58PM +0100, Mark Brown wrote:

> > This sounds an awful lot like how ASoC hangs together...

> Very much, yes. In ASoC and DRM we both have several physical devices spread
> around the SoC which form a logical device. I assume that before ASoC existed
> also everyone had a single PCI device which could be used to collect the
> information together.

Yeah, it's a similar issue - on PC hardware we tend to have a single
integrated device which does everything (at least from the point of view
of the outside world, physically things may sometimes be split).

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

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

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 3/7] DRM: add sdrm layer for general embedded system support
  2012-04-20 13:33         ` Daniel Vetter
@ 2012-04-21  8:18           ` Sascha Hauer
  -1 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-21  8:18 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 20, 2012 at 03:33:14PM +0200, Daniel Vetter wrote:
> On Fri, Apr 20, 2012 at 03:10:05PM +0200, Sascha Hauer wrote:
> > (BTW each driver in drm has this layer somewhere in it. If I had hidden
> > it in imx specific functions I probably wouldn't have raised any
> > questions, but I don't want to go that way)
> 
> That's _exactly_ what you should be doing. And once you have more than one
> driver that works in a similar way, you can extract the common code as
> helper functions to make life easier.

I already have three drivers working in a similar way from which I
posted two. I could easily throw in some more into the pot. Also the
code is based on the exynos driver, so I think it's suitable for this
aswell.

> 
> For your case it sounds like a new set of modeset helper functions
> tailored for the embedded use-case would be good (as Dave Airlie
> suggested).

One of my problems is that currently drm is based on the assumption that
there is a single device which offers all needed resources and
information to form a drm device. On embedded systems this is simply not
the case, we have our resources all around the SoC. I have physical
devices which are crtcs, encoders or connectors, but drm does not
provide a way to glue them together. You can find this aswell in the
exynos driver if you grep for exynos_drm_subdrv_register. If you follow
this function you'll see that a good bunch of the driver actually
handles the management of these subdevices. Do you have a suggestion
solving the involved code duplication with helper functions?

Also sooner or later it will happen that the same hdmi controller is
used on two otherwise different SoCs. Currently the driver can't be
shared between SoCs because each hdmi driver implements exynos or
nouveau specific callbacks. I guess the answer is to put the common hdmi
driver code into helper functions and to implement a middle layer in
each drm driver wishing to use it.

> Adding yet another middle-layer (like sdrm is) that forces
> drivers to go through it usually ends up in tears. And drm core
> unfortunately still has too much middle-layer heritage: For an awful lot
> of setup and teardown issues it's the core of the problme - because
> drivers can't control when drm sets up and tears down certain things, it's
> done at the wrong time (for certain drivers at least). Same problem when
> the abstraction doesn't quite fit.
> 
> Helper functions leave the driver in full control of what's going on, and
> working around hw-specific madness with ease.
> 
> https://lwn.net/Articles/336262/ is the canonical reference for why a lot
> of kernel people are allergic to anything that looks like a middle-layer.

I have read the article when it was featured on LWN. While I agree to
several things I have my problems with it. Take for example the MMC
core. A MMC driver mainly has to implement two callbacks, .request and
.set_ios. Noone has ever asked to get direct access from the driver to
the underlying block device and eventually pass control to MMC helper
functions. This makes the MMC core a middle layer sitting between the
blockdevice and the driver.  With drm instead it's normal that ioctls
fall straight through to the driver. This leads to such funny things
that the kernel itself cannot control the device to bring a console on
the screen without dedicated help from the driver.

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] 44+ messages in thread

* Re: [PATCH 3/7] DRM: add sdrm layer for general embedded system support
@ 2012-04-21  8:18           ` Sascha Hauer
  0 siblings, 0 replies; 44+ messages in thread
From: Sascha Hauer @ 2012-04-21  8:18 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: linux-arm-kernel, kernel, dri-devel, Rob Clark

On Fri, Apr 20, 2012 at 03:33:14PM +0200, Daniel Vetter wrote:
> On Fri, Apr 20, 2012 at 03:10:05PM +0200, Sascha Hauer wrote:
> > (BTW each driver in drm has this layer somewhere in it. If I had hidden
> > it in imx specific functions I probably wouldn't have raised any
> > questions, but I don't want to go that way)
> 
> That's _exactly_ what you should be doing. And once you have more than one
> driver that works in a similar way, you can extract the common code as
> helper functions to make life easier.

I already have three drivers working in a similar way from which I
posted two. I could easily throw in some more into the pot. Also the
code is based on the exynos driver, so I think it's suitable for this
aswell.

> 
> For your case it sounds like a new set of modeset helper functions
> tailored for the embedded use-case would be good (as Dave Airlie
> suggested).

One of my problems is that currently drm is based on the assumption that
there is a single device which offers all needed resources and
information to form a drm device. On embedded systems this is simply not
the case, we have our resources all around the SoC. I have physical
devices which are crtcs, encoders or connectors, but drm does not
provide a way to glue them together. You can find this aswell in the
exynos driver if you grep for exynos_drm_subdrv_register. If you follow
this function you'll see that a good bunch of the driver actually
handles the management of these subdevices. Do you have a suggestion
solving the involved code duplication with helper functions?

Also sooner or later it will happen that the same hdmi controller is
used on two otherwise different SoCs. Currently the driver can't be
shared between SoCs because each hdmi driver implements exynos or
nouveau specific callbacks. I guess the answer is to put the common hdmi
driver code into helper functions and to implement a middle layer in
each drm driver wishing to use it.

> Adding yet another middle-layer (like sdrm is) that forces
> drivers to go through it usually ends up in tears. And drm core
> unfortunately still has too much middle-layer heritage: For an awful lot
> of setup and teardown issues it's the core of the problme - because
> drivers can't control when drm sets up and tears down certain things, it's
> done at the wrong time (for certain drivers at least). Same problem when
> the abstraction doesn't quite fit.
> 
> Helper functions leave the driver in full control of what's going on, and
> working around hw-specific madness with ease.
> 
> https://lwn.net/Articles/336262/ is the canonical reference for why a lot
> of kernel people are allergic to anything that looks like a middle-layer.

I have read the article when it was featured on LWN. While I agree to
several things I have my problems with it. Take for example the MMC
core. A MMC driver mainly has to implement two callbacks, .request and
.set_ios. Noone has ever asked to get direct access from the driver to
the underlying block device and eventually pass control to MMC helper
functions. This makes the MMC core a middle layer sitting between the
blockdevice and the driver.  With drm instead it's normal that ioctls
fall straight through to the driver. This leads to such funny things
that the kernel itself cannot control the device to bring a console on
the screen without dedicated help from the driver.

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] 44+ messages in thread

end of thread, other threads:[~2012-04-21  8:18 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-04-11 15:33 [RFC] DRM helpers for embedded systems Sascha Hauer
2012-04-11 15:33 ` Sascha Hauer
2012-04-11 15:33 ` [PATCH 1/7] drm: remove legacy mode_group handling Sascha Hauer
2012-04-11 15:33   ` Sascha Hauer
2012-04-11 15:33 ` [PATCH 2/7] drm: make gamma_set optional Sascha Hauer
2012-04-11 15:33   ` Sascha Hauer
2012-04-11 15:33 ` [PATCH 3/7] DRM: add sdrm layer for general embedded system support Sascha Hauer
2012-04-11 15:33   ` Sascha Hauer
2012-04-11 20:22   ` Alan Cox
2012-04-11 20:22     ` Alan Cox
2012-04-12  8:58     ` Sascha Hauer
2012-04-12  8:58       ` Sascha Hauer
2012-04-20 10:02   ` Dave Airlie
2012-04-20 10:02     ` Dave Airlie
2012-04-20 12:38     ` Thierry Reding
2012-04-20 12:38       ` Thierry Reding
2012-04-20 13:20       ` Sascha Hauer
2012-04-20 13:20         ` Sascha Hauer
2012-04-20 14:25       ` Mark Brown
2012-04-20 14:25         ` Mark Brown
2012-04-20 14:49         ` Thierry Reding
2012-04-20 14:49           ` Thierry Reding
2012-04-20 15:06           ` Mark Brown
2012-04-20 15:06             ` Mark Brown
2012-04-20 15:13             ` Thierry Reding
2012-04-20 15:13               ` Thierry Reding
2012-04-20 15:15         ` Sascha Hauer
2012-04-20 15:15           ` Sascha Hauer
2012-04-20 15:20           ` Mark Brown
2012-04-20 15:20             ` Mark Brown
2012-04-20 13:10     ` Sascha Hauer
2012-04-20 13:10       ` Sascha Hauer
2012-04-20 13:33       ` Daniel Vetter
2012-04-20 13:33         ` Daniel Vetter
2012-04-21  8:18         ` Sascha Hauer
2012-04-21  8:18           ` Sascha Hauer
2012-04-11 15:33 ` [PATCH 4/7] DRM: Add sdrm 1:1 encoder - connector helper Sascha Hauer
2012-04-11 15:33   ` Sascha Hauer
2012-04-11 15:33 ` [PATCH 5/7] DRM: add i.MX kms simple driver Sascha Hauer
2012-04-11 15:33   ` Sascha Hauer
2012-04-11 15:33 ` [PATCH 6/7] ARM i.MX27 pcm038: Add sdrm support Sascha Hauer
2012-04-11 15:33   ` Sascha Hauer
2012-04-11 15:33 ` [PATCH 7/7] DRM: add PXA kms simple driver Sascha Hauer
2012-04-11 15:33   ` 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.