All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Herrmann <dh.herrmann@gmail.com>
To: dri-devel@lists.freedesktop.org
Subject: [PATCH v5 6/7] drm: add SimpleDRM driver
Date: Fri,  2 Sep 2016 10:22:44 +0200	[thread overview]
Message-ID: <20160902082245.7119-7-dh.herrmann@gmail.com> (raw)
In-Reply-To: <20160902082245.7119-1-dh.herrmann@gmail.com>

The SimpleDRM driver binds to simple-framebuffer devices and provides a
DRM/KMS API. It provides only a single CRTC+encoder+connector combination
plus one initial mode.

Userspace can create dumb-buffers which can be blit into the real
framebuffer similar to UDL. No access to the real framebuffer is allowed
(compared to earlier version of this driver) to avoid security issues.
Furthermore, this way we can support arbitrary modes as long as we have a
conversion-helper.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 MAINTAINERS                                  |   6 +
 drivers/gpu/drm/Kconfig                      |   2 +
 drivers/gpu/drm/Makefile                     |   1 +
 drivers/gpu/drm/simpledrm/Kconfig            |  19 ++
 drivers/gpu/drm/simpledrm/Makefile           |   8 +
 drivers/gpu/drm/simpledrm/simpledrm.h        |  83 +++++
 drivers/gpu/drm/simpledrm/simpledrm_damage.c | 194 +++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_drv.c    | 464 +++++++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_gem.c    | 109 +++++++
 drivers/gpu/drm/simpledrm/simpledrm_kms.c    | 263 +++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_of.c     | 265 +++++++++++++++
 11 files changed, 1414 insertions(+)
 create mode 100644 drivers/gpu/drm/simpledrm/Kconfig
 create mode 100644 drivers/gpu/drm/simpledrm/Makefile
 create mode 100644 drivers/gpu/drm/simpledrm/simpledrm.h
 create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_damage.c
 create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_drv.c
 create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_gem.c
 create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_kms.c
 create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_of.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 0bbe4b1..408863d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4140,6 +4140,12 @@ S:	Orphan / Obsolete
 F:	drivers/gpu/drm/savage/
 F:	include/uapi/drm/savage_drm.h
 
+DRM DRIVER FOR SIMPLE FRAMEBUFFER DEVICES
+M:	David Herrmann <dh.herrmann@gmail.com>
+L:	dri-devel@lists.freedesktop.org
+S:	Maintained
+F:	drivers/gpu/drm/simpledrm/
+
 DRM DRIVER FOR SIS VIDEO CARDS
 S:	Orphan / Obsolete
 F:	drivers/gpu/drm/sis/
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f27f9b5..61cbcd1 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -291,3 +291,5 @@ source "drivers/gpu/drm/arc/Kconfig"
 source "drivers/gpu/drm/hisilicon/Kconfig"
 
 source "drivers/gpu/drm/mediatek/Kconfig"
+
+source "drivers/gpu/drm/simpledrm/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 0238bf8..3e6fe99 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -83,3 +83,4 @@ obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/
 obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/
 obj-$(CONFIG_DRM_ARCPGU)+= arc/
 obj-y			+= hisilicon/
+obj-y			+= simpledrm/
diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig
new file mode 100644
index 0000000..f45b25d
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/Kconfig
@@ -0,0 +1,19 @@
+config DRM_SIMPLEDRM
+	tristate "Simple firmware framebuffer DRM driver"
+	depends on DRM
+	select DRM_KMS_HELPER
+	help
+	  SimpleDRM can run on all systems with pre-initialized graphics
+	  hardware. It uses a framebuffer that was initialized during
+	  firmware boot. No page-flipping, modesetting or other advanced
+	  features are available. However, other DRM drivers can be loaded
+	  later and take over from SimpleDRM if they provide real hardware
+	  support.
+
+	  SimpleDRM supports "simple-framebuffer" DeviceTree objects and
+	  compatible platform framebuffers.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called simpledrm.
diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile
new file mode 100644
index 0000000..d7b179d
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/Makefile
@@ -0,0 +1,8 @@
+simpledrm-y := \
+	simpledrm_damage.o \
+	simpledrm_drv.o \
+	simpledrm_gem.o \
+	simpledrm_kms.o \
+	simpledrm_of.o
+
+obj-$(CONFIG_DRM_SIMPLEDRM) := simpledrm.o
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h
new file mode 100644
index 0000000..ed6d725
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm.h
@@ -0,0 +1,83 @@
+#ifndef __SDRM_SIMPLEDRM_H
+#define __SDRM_SIMPLEDRM_H
+
+/*
+ * Copyright (C) 2012-2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+
+struct clk;
+struct regulator;
+struct simplefb_format;
+
+struct sdrm_hw {
+	struct mutex lock;
+	u32 width;
+	u32 height;
+	u32 stride;
+	u32 bpp;
+	u32 format;
+	unsigned long base;
+	unsigned long size;
+	void *map;
+};
+
+struct sdrm_bo {
+	struct drm_gem_object base;
+	struct page **pages;
+	void *vmapping;
+};
+
+struct sdrm_fb {
+	struct drm_framebuffer base;
+	struct sdrm_bo *bo;
+};
+
+struct sdrm_device {
+	atomic_t n_used;
+	struct drm_device *ddev;
+	struct sdrm_hw *hw;
+
+	size_t n_clks;
+	size_t n_regulators;
+	struct clk **clks;
+	struct regulator **regulators;
+
+	struct drm_simple_display_pipe pipe;
+	struct drm_connector conn;
+};
+
+void sdrm_of_bootstrap(void);
+int sdrm_of_bind(struct sdrm_device *sdrm);
+void sdrm_of_unbind(struct sdrm_device *sdrm);
+
+int sdrm_kms_bind(struct sdrm_device *sdrm);
+void sdrm_kms_unbind(struct sdrm_device *sdrm);
+
+void sdrm_dirty(struct sdrm_fb *fb, u32 x, u32 y, u32 width, u32 height);
+
+struct sdrm_bo *sdrm_bo_new(struct drm_device *ddev, size_t size);
+void sdrm_bo_free(struct drm_gem_object *obj);
+int sdrm_bo_vmap(struct sdrm_bo *bo);
+
+int sdrm_dumb_create(struct drm_file *dfile,
+		     struct drm_device *ddev,
+		     struct drm_mode_create_dumb *arg);
+int sdrm_dumb_map_offset(struct drm_file *dfile,
+			 struct drm_device *ddev,
+			 uint32_t handle,
+			 uint64_t *offset);
+
+#endif /* __SDRM_SIMPLEDRM_H */
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_damage.c b/drivers/gpu/drm/simpledrm/simpledrm_damage.c
new file mode 100644
index 0000000..4f7af5d
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_damage.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2012-2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <asm/unaligned.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <linux/kernel.h>
+#include "simpledrm.h"
+
+static inline void sdrm_put(u8 *dst, u32 four_cc, u16 r, u16 g, u16 b)
+{
+	switch (four_cc) {
+	case DRM_FORMAT_RGB565:
+		r >>= 11;
+		g >>= 10;
+		b >>= 11;
+		put_unaligned((u16)((r << 11) | (g << 5) | b), (u16 *)dst);
+		break;
+	case DRM_FORMAT_XRGB1555:
+	case DRM_FORMAT_ARGB1555:
+		r >>= 11;
+		g >>= 11;
+		b >>= 11;
+		put_unaligned((u16)((r << 10) | (g << 5) | b), (u16 *)dst);
+		break;
+	case DRM_FORMAT_RGB888:
+		r >>= 8;
+		g >>= 8;
+		b >>= 8;
+#ifdef __LITTLE_ENDIAN
+		dst[2] = r;
+		dst[1] = g;
+		dst[0] = b;
+#elif defined(__BIG_ENDIAN)
+		dst[0] = r;
+		dst[1] = g;
+		dst[2] = b;
+#endif
+		break;
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+		r >>= 8;
+		g >>= 8;
+		b >>= 8;
+		put_unaligned((u32)((r << 16) | (g << 8) | b), (u32 *)dst);
+		break;
+	case DRM_FORMAT_ABGR8888:
+		r >>= 8;
+		g >>= 8;
+		b >>= 8;
+		put_unaligned((u32)((b << 16) | (g << 8) | r), (u32 *)dst);
+		break;
+	case DRM_FORMAT_XRGB2101010:
+	case DRM_FORMAT_ARGB2101010:
+		r >>= 4;
+		g >>= 4;
+		b >>= 4;
+		put_unaligned((u32)((r << 20) | (g << 10) | b), (u32 *)dst);
+		break;
+	}
+}
+
+static void sdrm_blit_from_xrgb8888(const u8 *src,
+				    u32 src_stride,
+				    u32 src_bpp,
+				    u8 *dst,
+				    u32 dst_stride,
+				    u32 dst_bpp,
+				    u32 dst_four_cc,
+				    u32 width,
+				    u32 height)
+{
+	u32 val, i;
+
+	while (height--) {
+		for (i = 0; i < width; ++i) {
+			val = get_unaligned((const u32 *)&src[i * src_bpp]);
+			sdrm_put(&dst[i * dst_bpp], dst_four_cc,
+				 (val & 0x00ff0000U) >> 8,
+				 (val & 0x0000ff00U),
+				 (val & 0x000000ffU) << 8);
+		}
+
+		src += src_stride;
+		dst += dst_stride;
+	}
+}
+
+static void sdrm_blit_from_rgb565(const u8 *src,
+				  u32 src_stride,
+				  u32 src_bpp,
+				  u8 *dst,
+				  u32 dst_stride,
+				  u32 dst_bpp,
+				  u32 dst_four_cc,
+				  u32 width,
+				  u32 height)
+{
+	u32 val, i;
+
+	while (height--) {
+		for (i = 0; i < width; ++i) {
+			val = get_unaligned((const u16 *)&src[i * src_bpp]);
+			sdrm_put(&dst[i * dst_bpp], dst_four_cc,
+				 (val & 0xf800),
+				 (val & 0x07e0) << 5,
+				 (val & 0x001f) << 11);
+		}
+
+		src += src_stride;
+		dst += dst_stride;
+	}
+}
+
+static void sdrm_blit_lines(const u8 *src,
+			    u32 src_stride,
+			    u8 *dst,
+			    u32 dst_stride,
+			    u32 bpp,
+			    u32 width,
+			    u32 height)
+{
+	u32 len;
+
+	len = width * bpp;
+
+	while (height--) {
+		memcpy(dst, src, len);
+		src += src_stride;
+		dst += dst_stride;
+	}
+}
+
+static void sdrm_blit(struct sdrm_hw *hw,
+		      struct sdrm_fb *fb,
+		      u32 x,
+		      u32 y,
+		      u32 width,
+		      u32 height)
+{
+	u32 src_bpp, dst_bpp;
+	u8 *src, *dst;
+
+	src = fb->bo->vmapping;
+	src_bpp = DIV_ROUND_UP(fb->base.bits_per_pixel, 8);
+	src += fb->base.offsets[0] + y * fb->base.pitches[0] + x * src_bpp;
+
+	dst = hw->map;
+	dst_bpp = DIV_ROUND_UP(hw->bpp, 8);
+	dst += y * hw->stride + x * dst_bpp;
+
+	if (fb->base.pixel_format == hw->format) {
+		/* if formats are identical, do a line-by-line copy.. */
+		sdrm_blit_lines(src, fb->base.pitches[0],
+				dst, hw->stride, src_bpp, width, height);
+	} else {
+		/* ..otherwise call slow blit-function */
+		switch (fb->base.pixel_format) {
+		case DRM_FORMAT_ARGB8888:
+		case DRM_FORMAT_XRGB8888:
+			sdrm_blit_from_xrgb8888(src, fb->base.pitches[0],
+						src_bpp, dst, hw->stride,
+						dst_bpp, hw->format,
+						width, height);
+			break;
+		case DRM_FORMAT_RGB565:
+			sdrm_blit_from_rgb565(src, fb->base.pitches[0],
+					      src_bpp, dst, hw->stride,
+					      dst_bpp, hw->format,
+					      width, height);
+			break;
+		}
+	}
+}
+
+void sdrm_dirty(struct sdrm_fb *fb, u32 x, u32 y, u32 width, u32 height)
+{
+	struct sdrm_device *sdrm = fb->base.dev->dev_private;
+
+	if (WARN_ON(!fb->bo->vmapping))
+		return;
+
+	mutex_lock(&sdrm->hw->lock);
+	if (sdrm->hw->map)
+		sdrm_blit(sdrm->hw, fb, x, y, width, height);
+	mutex_unlock(&sdrm->hw->lock);
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
new file mode 100644
index 0000000..d569120
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2012-2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <drm/drmP.h>
+#include <linux/atomic.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/string.h>
+#include "simpledrm.h"
+
+static struct drm_driver sdrm_drm_driver;
+static DEFINE_MUTEX(sdrm_lock);
+
+static int sdrm_hw_identify(struct platform_device *pdev,
+			    struct simplefb_platform_data *modep,
+			    struct simplefb_format *formatp,
+			    struct resource **memp,
+			    u32 *bppp)
+{
+	static const struct simplefb_format valid_formats[] = SIMPLEFB_FORMATS;
+	struct simplefb_platform_data pm = {}, *mode = pdev->dev.platform_data;
+	struct device_node *np = pdev->dev.of_node;
+	const struct simplefb_format *format = NULL;
+	struct resource *mem;
+	unsigned int depth;
+	int r, bpp;
+	size_t i;
+
+	if (!mode) {
+		if (!np)
+			return -ENODEV;
+
+		mode = &pm;
+
+		r = of_property_read_u32(np, "width", &mode->width);
+		if (r >= 0)
+			r = of_property_read_u32(np, "height", &mode->height);
+		if (r >= 0)
+			r = of_property_read_u32(np, "stride", &mode->stride);
+		if (r >= 0)
+			r = of_property_read_string(np, "format",
+						    &mode->format);
+		if (r < 0)
+			return r;
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem)
+		return -ENODEV;
+
+	for (i = 0; i < ARRAY_SIZE(valid_formats); ++i) {
+		if (!strcmp(mode->format, valid_formats[i].name)) {
+			format = &valid_formats[i];
+			break;
+		}
+	}
+
+	if (!format)
+		return -ENODEV;
+
+	switch (format->fourcc) {
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_XRGB1555:
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_XRGB2101010:
+	case DRM_FORMAT_ARGB2101010:
+		/*
+		 * You must adjust sdrm_put() whenever you add a new format
+		 * here, otherwise, blitting operations will not work.
+		 * Furthermore, include/linux/platform_data/simplefb.h needs
+		 * to be adjusted so the platform-device actually allows this
+		 * format.
+		 */
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	drm_fb_get_bpp_depth(format->fourcc, &depth, &bpp);
+	if (!bpp)
+		return -ENODEV;
+	if (resource_size(mem) < mode->stride * mode->height)
+		return -ENODEV;
+	if ((bpp + 7) / 8 * mode->width > mode->stride)
+		return -ENODEV;
+
+	*modep = *mode;
+	*formatp = *format;
+	*memp = mem;
+	*bppp = bpp;
+	return 0;
+}
+
+static struct sdrm_hw *sdrm_hw_new(const struct simplefb_platform_data *mode,
+				   const struct simplefb_format *format,
+				   const struct resource *mem,
+				   u32 bpp)
+{
+	struct sdrm_hw *hw;
+
+	hw = kzalloc(sizeof(*hw), GFP_KERNEL);
+	if (!hw)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&hw->lock);
+	hw->width = mode->width;
+	hw->height = mode->height;
+	hw->stride = mode->stride;
+	hw->bpp = bpp;
+	hw->format = format->fourcc;
+	hw->base = mem->start;
+	hw->size = resource_size(mem);
+
+	return hw;
+}
+
+static struct sdrm_hw *sdrm_hw_free(struct sdrm_hw *hw)
+{
+	if (!hw)
+		return NULL;
+
+	WARN_ON(hw->map);
+	mutex_destroy(&hw->lock);
+	kfree(hw);
+
+	return NULL;
+}
+
+static int sdrm_hw_bind(struct sdrm_hw *hw)
+{
+	mutex_lock(&hw->lock);
+	if (!hw->map)
+		hw->map = ioremap_wc(hw->base, hw->size);
+	mutex_unlock(&hw->lock);
+
+	return hw->map ? 0 : -EIO;
+}
+
+static void sdrm_hw_unbind(struct sdrm_hw *hw)
+{
+	if (!hw)
+		return;
+
+	mutex_lock(&hw->lock);
+	if (hw->map) {
+		iounmap(hw->map);
+		hw->map = NULL;
+	}
+	mutex_unlock(&hw->lock);
+}
+
+static struct sdrm_device *sdrm_device_free(struct sdrm_device *sdrm)
+{
+	if (!sdrm)
+		return NULL;
+
+	WARN_ON(atomic_read(&sdrm->n_used) != INT_MIN);
+	sdrm->hw = sdrm_hw_free(sdrm->hw);
+	drm_dev_unref(sdrm->ddev);
+	kfree(sdrm);
+
+	return NULL;
+}
+
+static struct sdrm_device *sdrm_device_new(struct platform_device *pdev,
+					   struct sdrm_hw *hw)
+{
+	struct sdrm_device *sdrm;
+	int r;
+
+	sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL);
+	if (!sdrm)
+		return ERR_PTR(-ENOMEM);
+
+	atomic_set(&sdrm->n_used, INT_MIN);
+
+	sdrm->ddev = drm_dev_alloc(&sdrm_drm_driver, &pdev->dev);
+	if (!sdrm->ddev) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	sdrm->ddev->dev_private = sdrm;
+	sdrm->hw = hw;
+
+	return sdrm;
+
+error:
+	sdrm_device_free(sdrm);
+	return ERR_PTR(r);
+}
+
+static void sdrm_device_unbind(struct sdrm_device *sdrm)
+{
+	if (sdrm) {
+		sdrm_kms_unbind(sdrm);
+		sdrm_hw_unbind(sdrm->hw);
+		sdrm_of_unbind(sdrm);
+	}
+}
+
+static int sdrm_device_bind(struct sdrm_device *sdrm)
+{
+	int r;
+
+	r = sdrm_of_bind(sdrm);
+	if (r < 0)
+		goto error;
+
+	r = sdrm_hw_bind(sdrm->hw);
+	if (r < 0)
+		goto error;
+
+	r = sdrm_kms_bind(sdrm);
+	if (r < 0)
+		goto error;
+
+	return 0;
+
+error:
+	sdrm_device_unbind(sdrm);
+	return r;
+}
+
+static int sdrm_device_acquire(struct sdrm_device *sdrm)
+{
+	return (sdrm && atomic_inc_unless_negative(&sdrm->n_used))
+		? 0 : -ENODEV;
+}
+
+static void sdrm_device_release(struct sdrm_device *sdrm)
+{
+	if (sdrm && atomic_dec_return(&sdrm->n_used) == INT_MIN) {
+		sdrm_device_unbind(sdrm);
+		sdrm_device_free(sdrm);
+	}
+}
+
+static int sdrm_fop_open(struct inode *inode, struct file *file)
+{
+	struct drm_device *ddev;
+	int r;
+
+	mutex_lock(&sdrm_lock);
+	r = drm_open(inode, file);
+	if (r >= 0) {
+		ddev = file->private_data;
+		r = sdrm_device_acquire(ddev->dev_private);
+		if (r < 0)
+			drm_release(inode, file);
+	}
+	mutex_unlock(&sdrm_lock);
+
+	return r;
+}
+
+static int sdrm_fop_release(struct inode *inode, struct file *file)
+{
+	struct drm_file *dfile = file->private_data;
+	struct drm_device *ddev = dfile->minor->dev;
+	struct sdrm_device *sdrm = ddev->dev_private;
+	int res;
+
+	res = drm_release(inode, file);
+	sdrm_device_release(sdrm);
+	return res;
+}
+
+static int sdrm_fop_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct drm_file *dfile = file->private_data;
+	struct drm_device *dev = dfile->minor->dev;
+	struct drm_gem_object *obj = NULL;
+	struct drm_vma_offset_node *node;
+	int r;
+
+	drm_vma_offset_lock_lookup(dev->vma_offset_manager);
+	node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager,
+						  vma->vm_pgoff,
+						  vma_pages(vma));
+	if (likely(node)) {
+		obj = container_of(node, struct drm_gem_object, vma_node);
+		if (!kref_get_unless_zero(&obj->refcount))
+			obj = NULL;
+	}
+	drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
+
+	if (!obj)
+		return -EINVAL;
+
+	if (!drm_vma_node_is_allowed(node, dfile)) {
+		drm_gem_object_unreference_unlocked(obj);
+		return -EACCES;
+	}
+
+	if (vma->vm_file)
+		fput(vma->vm_file);
+	vma->vm_file = get_file(obj->filp);
+	vma->vm_pgoff = 0;
+
+	r = obj->filp->f_op->mmap(obj->filp, vma);
+	drm_gem_object_unreference_unlocked(obj);
+	return r;
+}
+
+static int sdrm_simplefb_probe(struct platform_device *pdev)
+{
+	struct simplefb_platform_data hw_mode;
+	struct simplefb_format hw_format;
+	struct sdrm_device *sdrm = NULL;
+	struct sdrm_hw *hw = NULL;
+	struct resource *hw_mem;
+	u32 hw_bpp;
+	int r;
+
+	r = sdrm_hw_identify(pdev, &hw_mode, &hw_format, &hw_mem, &hw_bpp);
+	if (r < 0)
+		goto error;
+
+	hw = sdrm_hw_new(&hw_mode, &hw_format, hw_mem, hw_bpp);
+	if (IS_ERR(hw)) {
+		r = PTR_ERR(hw);
+		hw = NULL;
+		goto error;
+	}
+
+	sdrm = sdrm_device_new(pdev, hw);
+	if (IS_ERR(sdrm)) {
+		r = PTR_ERR(sdrm);
+		sdrm = NULL;
+		goto error;
+	}
+	hw = NULL;
+	platform_set_drvdata(pdev, sdrm);
+
+	r = sdrm_device_bind(sdrm);
+	if (r < 0)
+		goto error;
+
+	/* mark device as enabled and acquire bus ref */
+	WARN_ON(atomic_read(&sdrm->n_used) != INT_MIN);
+	atomic_set(&sdrm->n_used, 1);
+
+	r = drm_dev_register(sdrm->ddev, 0);
+	if (r < 0) {
+		/* mark device as disabled and drop bus ref */
+		WARN_ON(atomic_add_return(INT_MIN, &sdrm->n_used) != INT_MIN);
+		sdrm_device_release(sdrm);
+		return r;
+	}
+
+	dev_info(sdrm->ddev->dev, "initialized %s on minor %d\n",
+		 sdrm->ddev->driver->name, sdrm->ddev->primary->index);
+
+	return 0;
+
+error:
+	sdrm_device_unbind(sdrm);
+	sdrm_device_free(sdrm);
+	sdrm_hw_free(hw);
+	return r;
+}
+
+static int sdrm_simplefb_remove(struct platform_device *pdev)
+{
+	struct sdrm_device *sdrm = platform_get_drvdata(pdev);
+
+	/* mark device as disabled */
+	atomic_add(INT_MIN, &sdrm->n_used);
+	sdrm_hw_unbind(sdrm->hw);
+
+	mutex_lock(&sdrm_lock);
+	drm_dev_unregister(sdrm->ddev);
+	sdrm_device_release(sdrm);
+	mutex_unlock(&sdrm_lock);
+
+	return 0;
+}
+
+static const struct file_operations sdrm_drm_fops = {
+	.owner = THIS_MODULE,
+	.open = sdrm_fop_open,
+	.release = sdrm_fop_release,
+	.mmap = sdrm_fop_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+	.unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = drm_compat_ioctl,
+#endif
+	.llseek = noop_llseek,
+};
+
+static struct drm_driver sdrm_drm_driver = {
+	.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+	.fops = &sdrm_drm_fops,
+
+	.gem_free_object = sdrm_bo_free,
+
+	.dumb_create = sdrm_dumb_create,
+	.dumb_map_offset = sdrm_dumb_map_offset,
+	.dumb_destroy = drm_gem_dumb_destroy,
+
+	.name = "simpledrm",
+	.desc = "Simple firmware framebuffer DRM driver",
+	.date = "20160901",
+};
+
+static const struct of_device_id sdrm_simplefb_of_match[] = {
+	{ .compatible = "simple-framebuffer", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, sdrm_simplefb_of_match);
+
+static struct platform_driver sdrm_simplefb_driver = {
+	.probe = sdrm_simplefb_probe,
+	.remove = sdrm_simplefb_remove,
+	.driver = {
+		.name = "simple-framebuffer",
+		.mod_name = KBUILD_MODNAME,
+		.owner = THIS_MODULE,
+		.of_match_table = sdrm_simplefb_of_match,
+	},
+};
+
+static int __init sdrm_init(void)
+{
+	int r;
+
+	r = platform_driver_register(&sdrm_simplefb_driver);
+	if (r < 0)
+		return r;
+
+	sdrm_of_bootstrap();
+	return 0;
+}
+
+static void __exit sdrm_exit(void)
+{
+	platform_driver_unregister(&sdrm_simplefb_driver);
+}
+
+module_init(sdrm_init);
+module_exit(sdrm_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Simple firmware framebuffer DRM driver");
+MODULE_ALIAS("platform:simple-framebuffer");
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_gem.c b/drivers/gpu/drm/simpledrm/simpledrm_gem.c
new file mode 100644
index 0000000..4aaae6e
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2012-2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <drm/drmP.h>
+#include <drm/drm_gem.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include "simpledrm.h"
+
+struct sdrm_bo *sdrm_bo_new(struct drm_device *ddev, size_t size)
+{
+	struct sdrm_bo *bo;
+
+	WARN_ON(!size || (size & ~PAGE_MASK) != 0);
+
+	bo = kzalloc(sizeof(*bo), GFP_KERNEL);
+	if (!bo)
+		return NULL;
+
+	if (drm_gem_object_init(ddev, &bo->base, size)) {
+		kfree(bo);
+		return NULL;
+	}
+
+	return bo;
+}
+
+void sdrm_bo_free(struct drm_gem_object *dobj)
+{
+	struct sdrm_bo *bo = container_of(dobj, struct sdrm_bo, base);
+
+	if (bo->vmapping)
+		vunmap(bo->vmapping);
+	if (bo->pages)
+		drm_gem_put_pages(dobj, bo->pages, false, false);
+	drm_gem_object_release(dobj);
+	kfree(bo);
+}
+
+int sdrm_bo_vmap(struct sdrm_bo *bo)
+{
+	int r;
+
+	if (!bo->pages) {
+		bo->pages = drm_gem_get_pages(&bo->base);
+		if (IS_ERR(bo->pages)) {
+			r = PTR_ERR(bo->pages);
+			bo->pages = NULL;
+			return r;
+		}
+	}
+
+	if (!bo->vmapping) {
+		bo->vmapping = vmap(bo->pages, bo->base.size / PAGE_SIZE, 0,
+				    PAGE_KERNEL);
+		if (!bo->vmapping)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+int sdrm_dumb_create(struct drm_file *dfile,
+		     struct drm_device *ddev,
+		     struct drm_mode_create_dumb *args)
+{
+	struct sdrm_bo *bo;
+	int r;
+
+	/* overflow checks are done by DRM core */
+	args->pitch = (args->bpp + 7) / 8 * args->width;
+	args->size = PAGE_ALIGN(args->pitch * args->height);
+
+	bo = sdrm_bo_new(ddev, args->size);
+	if (!bo)
+		return -ENOMEM;
+
+	r = drm_gem_handle_create(dfile, &bo->base, &args->handle);
+	drm_gem_object_unreference_unlocked(&bo->base);
+	return r;
+}
+
+int sdrm_dumb_map_offset(struct drm_file *dfile,
+			 struct drm_device *ddev,
+			 uint32_t handle,
+			 uint64_t *offset)
+{
+	struct drm_gem_object *dobj;
+	int r;
+
+	dobj = drm_gem_object_lookup(dfile, handle);
+	if (!dobj)
+		return -ENOENT;
+
+	r = drm_gem_create_mmap_offset(dobj);
+	if (r >= 0)
+		*offset = drm_vma_node_offset_addr(&dobj->vma_node);
+	drm_gem_object_unreference_unlocked(dobj);
+	return r;
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
new file mode 100644
index 0000000..00101c9
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2012-2016 Red Hat, Inc.
+ * Copyright (C) 2016 Noralf Trønnes
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include "simpledrm.h"
+
+static const uint32_t sdrm_formats[] = {
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XRGB8888,
+};
+
+static int sdrm_conn_get_modes(struct drm_connector *conn)
+{
+	struct sdrm_device *sdrm = conn->dev->dev_private;
+	struct drm_display_mode *mode;
+
+	mode = drm_cvt_mode(sdrm->ddev, sdrm->hw->width, sdrm->hw->height,
+			    60, false, false, false);
+	if (!mode)
+		return 0;
+
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(conn, mode);
+
+	return 1;
+}
+
+static const struct drm_connector_helper_funcs sdrm_conn_hfuncs = {
+	.get_modes	= sdrm_conn_get_modes,
+	.best_encoder	= drm_atomic_helper_best_encoder,
+};
+
+static enum drm_connector_status sdrm_conn_detect(struct drm_connector *conn,
+						  bool force)
+{
+	/*
+	 * We simulate an always connected monitor. simple-fb doesn't
+	 * provide any way to detect whether the connector is active. Hence,
+	 * signal DRM core that it is always connected.
+	 */
+	return connector_status_connected;
+}
+
+static const struct drm_connector_funcs sdrm_conn_ops = {
+	.dpms			= drm_atomic_helper_connector_dpms,
+	.reset			= drm_atomic_helper_connector_reset,
+	.detect			= sdrm_conn_detect,
+	.fill_modes		= drm_helper_probe_single_connector_modes,
+	.destroy		= drm_connector_cleanup,
+	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
+};
+
+static void sdrm_crtc_send_vblank_event(struct drm_crtc *crtc)
+{
+	if (crtc->state && crtc->state->event) {
+		spin_lock_irq(&crtc->dev->event_lock);
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+		crtc->state->event = NULL;
+	}
+}
+
+void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
+			      struct drm_plane_state *plane_state)
+{
+	struct drm_framebuffer *dfb = pipe->plane.state->fb;
+	struct sdrm_fb *fb;
+
+	sdrm_crtc_send_vblank_event(&pipe->crtc);
+
+	if (dfb) {
+		fb = container_of(dfb, struct sdrm_fb, base);
+		pipe->plane.fb = dfb;
+		sdrm_dirty(fb, 0, 0, dfb->width, dfb->height);
+	}
+}
+
+static void sdrm_display_pipe_enable(struct drm_simple_display_pipe *pipe,
+				     struct drm_crtc_state *crtc_state)
+{
+	sdrm_crtc_send_vblank_event(&pipe->crtc);
+}
+
+static void sdrm_display_pipe_disable(struct drm_simple_display_pipe *pipe)
+{
+	sdrm_crtc_send_vblank_event(&pipe->crtc);
+}
+
+static const struct drm_simple_display_pipe_funcs sdrm_pipe_funcs = {
+	.update		= sdrm_display_pipe_update,
+	.enable		= sdrm_display_pipe_enable,
+	.disable	= sdrm_display_pipe_disable,
+};
+
+static int sdrm_fb_create_handle(struct drm_framebuffer *dfb,
+				 struct drm_file *dfile,
+				 unsigned int *handle)
+{
+	struct sdrm_fb *fb = container_of(dfb, struct sdrm_fb, base);
+
+	return drm_gem_handle_create(dfile, &fb->bo->base, handle);
+}
+
+static int sdrm_fb_dirty(struct drm_framebuffer *dfb,
+			 struct drm_file *dfile,
+			 unsigned int flags,
+			 unsigned int color,
+			 struct drm_clip_rect *clips,
+			 unsigned int n_clips)
+{
+	struct sdrm_fb *fb = container_of(dfb, struct sdrm_fb, base);
+	struct sdrm_device *sdrm = dfb->dev->dev_private;
+	unsigned int i;
+
+	drm_modeset_lock_all(sdrm->ddev);
+	if (dfb == sdrm->pipe.plane.fb) {
+		if (!clips || !n_clips) {
+			sdrm_dirty(fb, 0, 0, dfb->width, dfb->height);
+		} else {
+			for (i = 0; i < n_clips; i++) {
+				if (clips[i].x1 > clips[i].x2 ||
+				    clips[i].x2 > dfb->width ||
+				    clips[i].y1 > clips[i].y2 ||
+				    clips[i].y2 > dfb->height)
+					continue;
+
+				sdrm_dirty(fb, clips[i].x1, clips[i].y1,
+					   clips[i].x2 - clips[i].x1,
+					   clips[i].y2 - clips[i].y1);
+			}
+		}
+	}
+	drm_modeset_unlock_all(sdrm->ddev);
+
+	return 0;
+}
+
+static void sdrm_fb_destroy(struct drm_framebuffer *dfb)
+{
+	struct sdrm_fb *fb = container_of(dfb, struct sdrm_fb, base);
+
+	drm_framebuffer_cleanup(dfb);
+	drm_gem_object_unreference_unlocked(&fb->bo->base);
+	kfree(fb);
+}
+
+static const struct drm_framebuffer_funcs sdrm_fb_ops = {
+	.create_handle		= sdrm_fb_create_handle,
+	.dirty			= sdrm_fb_dirty,
+	.destroy		= sdrm_fb_destroy,
+};
+
+static struct drm_framebuffer *
+sdrm_fb_create(struct drm_device *ddev,
+	       struct drm_file *dfile,
+	       const struct drm_mode_fb_cmd2 *cmd)
+{
+	struct drm_gem_object *dobj;
+	struct sdrm_fb *fb = NULL;
+	struct sdrm_bo *bo;
+	int r;
+
+	if (cmd->flags)
+		return ERR_PTR(-EINVAL);
+
+	dobj = drm_gem_object_lookup(dfile, cmd->handles[0]);
+	if (!dobj)
+		return ERR_PTR(-EINVAL);
+
+	bo = container_of(dobj, struct sdrm_bo, base);
+
+	r = sdrm_bo_vmap(bo);
+	if (r < 0)
+		goto error;
+
+	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+	if (!fb) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	fb->bo = bo;
+	drm_helper_mode_fill_fb_struct(&fb->base, cmd);
+
+	r = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops);
+	if (r < 0)
+		goto error;
+
+	DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id,
+		      drm_get_format_name(fb->base.pixel_format));
+
+	return &fb->base;
+
+error:
+	kfree(fb);
+	drm_gem_object_unreference_unlocked(dobj);
+	return ERR_PTR(r);
+}
+
+static const struct drm_mode_config_funcs sdrm_mode_config_ops = {
+	.fb_create		= sdrm_fb_create,
+	.atomic_check		= drm_atomic_helper_check,
+	.atomic_commit		= drm_atomic_helper_commit,
+};
+
+int sdrm_kms_bind(struct sdrm_device *sdrm)
+{
+	struct drm_connector *conn = &sdrm->conn;
+	struct drm_device *ddev = sdrm->ddev;
+	int r;
+
+	drm_mode_config_init(ddev);
+	ddev->mode_config.min_width = sdrm->hw->width;
+	ddev->mode_config.max_width = sdrm->hw->width;
+	ddev->mode_config.min_height = sdrm->hw->height;
+	ddev->mode_config.max_height = sdrm->hw->height;
+	ddev->mode_config.preferred_depth = sdrm->hw->bpp;
+	ddev->mode_config.funcs = &sdrm_mode_config_ops;
+	drm_connector_helper_add(conn, &sdrm_conn_hfuncs);
+
+	r = drm_connector_init(ddev, conn, &sdrm_conn_ops,
+			       DRM_MODE_CONNECTOR_VIRTUAL);
+	if (r < 0)
+		goto error;
+
+	r = drm_simple_display_pipe_init(ddev, &sdrm->pipe, &sdrm_pipe_funcs,
+					 sdrm_formats,
+					 ARRAY_SIZE(sdrm_formats), conn);
+	if (r < 0)
+		goto error;
+
+	drm_mode_config_reset(ddev);
+	return 0;
+
+error:
+	drm_mode_config_cleanup(ddev);
+	return r;
+}
+
+void sdrm_kms_unbind(struct sdrm_device *sdrm)
+{
+	if (sdrm->ddev->mode_config.funcs)
+		drm_mode_config_cleanup(sdrm->ddev);
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_of.c b/drivers/gpu/drm/simpledrm/simpledrm_of.c
new file mode 100644
index 0000000..5620000
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_of.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2012-2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <drm/drmP.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include "simpledrm.h"
+
+#ifdef CONFIG_COMMON_CLK
+
+static int sdrm_of_bind_clocks(struct sdrm_device *sdrm,
+			       struct device_node *np)
+{
+	struct clk *clock;
+	size_t i, n;
+	int r;
+
+	n = of_clk_get_parent_count(np);
+	if (n < 1)
+		return 0;
+
+	sdrm->clks = kcalloc(n, sizeof(*sdrm->clks), GFP_KERNEL);
+	if (!sdrm->clks)
+		return -ENOMEM;
+
+	for (i = 0; i < n; ++i) {
+		clock = of_clk_get(np, i);
+		if (!IS_ERR(clock)) {
+			sdrm->clks[sdrm->n_clks++] = clock;
+		} else if (PTR_ERR(clock) == -EPROBE_DEFER) {
+			r = -EPROBE_DEFER;
+			goto error;
+		} else {
+			dev_err(sdrm->ddev->dev, "cannot find clock %zu: %ld\n",
+				i, PTR_ERR(clock));
+		}
+	}
+
+	for (i = 0; i < sdrm->n_clks; ++i) {
+		if (!sdrm->clks[i])
+			continue;
+
+		r = clk_prepare_enable(sdrm->clks[i]);
+		if (r < 0) {
+			dev_err(sdrm->ddev->dev,
+				"cannot find clock %zu: %d\n", i, r);
+			clk_put(sdrm->clks[i]);
+			sdrm->clks[i] = NULL;
+		}
+	}
+
+	return 0;
+
+error:
+	for (i = 0; i < sdrm->n_clks; ++i)
+		clk_put(sdrm->clks[i]);
+	kfree(sdrm->clks);
+	sdrm->clks = NULL;
+	sdrm->n_clks = 0;
+	return r;
+}
+
+static void sdrm_of_unbind_clocks(struct sdrm_device *sdrm)
+{
+	size_t i;
+
+	for (i = 0; i < sdrm->n_clks; ++i) {
+		clk_disable_unprepare(sdrm->clks[i]);
+		clk_put(sdrm->clks[i]);
+	}
+
+	kfree(sdrm->clks);
+	sdrm->clks = NULL;
+	sdrm->n_clks = 0;
+}
+
+#else /* CONFIG_COMMON_CLK */
+
+static int sdrm_of_bind_clocks(struct sdrm_device *sdrm,
+			       struct device_node *np)
+{
+	return 0;
+}
+
+static void sdrm_of_unbind_clocks(struct sdrm_device *sdrm)
+{
+}
+
+#endif /* CONFIG_COMMON_CLK */
+
+#ifdef CONFIG_REGULATOR
+
+static int sdrm_of_bind_regulators(struct sdrm_device *sdrm,
+				   struct device_node *np)
+{
+	struct regulator *regulator;
+	struct property *prop;
+	char *p, *name;
+	size_t i, n;
+	int r;
+
+	n = 0;
+	for_each_property_of_node(np, prop) {
+		p = strstr(prop->name, "-supply");
+		if (p && p != prop->name)
+			++n;
+	}
+
+	if (n < 1)
+		return 0;
+
+	sdrm->regulators = kcalloc(n, sizeof(*sdrm->regulators), GFP_KERNEL);
+	if (!sdrm->regulators)
+		return -ENOMEM;
+
+	for_each_property_of_node(np, prop) {
+		p = strstr(prop->name, "-supply");
+		if (!p || p == prop->name)
+			continue;
+
+		name = kstrndup(prop->name, p - prop->name, GFP_TEMPORARY);
+		if (!name)
+			continue;
+
+		regulator = regulator_get_optional(sdrm->ddev->dev, name);
+		kfree(name);
+
+		if (!IS_ERR(regulator)) {
+			sdrm->regulators[sdrm->n_regulators++] = regulator;
+		} else if (PTR_ERR(regulator) == -EPROBE_DEFER) {
+			r = -EPROBE_DEFER;
+			goto error;
+		} else {
+			dev_warn(sdrm->ddev->dev,
+				 "cannot find regulator %s: %ld\n",
+				 prop->name, PTR_ERR(regulator));
+		}
+	}
+
+	for (i = 0; i < sdrm->n_regulators; ++i) {
+		if (!sdrm->regulators[i])
+			continue;
+
+		r = regulator_enable(sdrm->regulators[i]);
+		if (r < 0) {
+			dev_warn(sdrm->ddev->dev,
+				 "cannot enable regulator %zu: %d\n", i, r);
+			regulator_put(sdrm->regulators[i]);
+			sdrm->regulators[i] = NULL;
+		}
+	}
+
+	return 0;
+
+error:
+	for (i = 0; i < sdrm->n_regulators; ++i)
+		if (sdrm->regulators[i])
+			regulator_put(sdrm->regulators[i]);
+	kfree(sdrm->regulators);
+	sdrm->regulators = NULL;
+	sdrm->n_regulators = 0;
+	return r;
+}
+
+static void sdrm_of_unbind_regulators(struct sdrm_device *sdrm)
+{
+	size_t i;
+
+	for (i = 0; i < sdrm->n_regulators; ++i) {
+		if (sdrm->regulators[i]) {
+			regulator_disable(sdrm->regulators[i]);
+			regulator_put(sdrm->regulators[i]);
+		}
+	}
+
+	kfree(sdrm->regulators);
+	sdrm->regulators = NULL;
+	sdrm->n_regulators = 0;
+}
+
+#else /* CONFIG_REGULATORS */
+
+static int sdrm_of_bind_regulators(struct sdrm_device *sdrm,
+				   struct device_node *np)
+{
+	return 0;
+}
+
+static void sdrm_of_unbind_regulators(struct sdrm_device *sdrm)
+{
+}
+
+#endif /* CONFIG_REGULATORS */
+
+#ifdef CONFIG_OF
+
+void sdrm_of_bootstrap(void)
+{
+#ifdef CONFIG_OF_ADDRESS
+	struct device_node *np;
+
+	for_each_compatible_node(np, NULL, "simple-framebuffer")
+		of_platform_device_create(np, NULL, NULL);
+#endif
+}
+
+int sdrm_of_bind(struct sdrm_device *sdrm)
+{
+	int r;
+
+	if (WARN_ON(sdrm->n_clks > 0 || sdrm->n_regulators > 0))
+		return 0;
+	if (!sdrm->ddev->dev->of_node)
+		return 0;
+
+	r = sdrm_of_bind_clocks(sdrm, sdrm->ddev->dev->of_node);
+	if (r < 0)
+		goto error;
+
+	r = sdrm_of_bind_regulators(sdrm, sdrm->ddev->dev->of_node);
+	if (r < 0)
+		goto error;
+
+	return 0;
+
+error:
+	sdrm_of_unbind(sdrm);
+	return r;
+}
+
+void sdrm_of_unbind(struct sdrm_device *sdrm)
+{
+	sdrm_of_unbind_regulators(sdrm);
+	sdrm_of_unbind_clocks(sdrm);
+}
+
+#else /* CONFIG_OF */
+
+void sdrm_of_bootstrap(void)
+{
+}
+
+int sdrm_of_bind(struct sdrm_device *sdrm)
+{
+	return 0;
+}
+
+void sdrm_of_unbind(struct sdrm_device *sdrm)
+{
+}
+
+#endif /* CONFIG_OF */
-- 
2.9.3

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

  parent reply	other threads:[~2016-09-02  8:23 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-09-02  8:22 [PATCH v5 0/7] drm: add simpledrm driver David Herrmann
2016-09-02  8:22 ` [PATCH v5 1/7] x86/sysfb: add support for 64bit EFI lfb_base David Herrmann
2016-09-02 10:20   ` Tom Gundersen
2016-09-02  8:22 ` [PATCH v5 2/7] x86/sysfb: fix lfb_size calculation David Herrmann
2016-09-02 10:20   ` Tom Gundersen
2016-09-02  8:22 ` [PATCH v5 3/7] of/platform: expose of_platform_device_destroy() David Herrmann
2016-09-02 10:21   ` Tom Gundersen
2016-09-02  8:22 ` [PATCH v5 4/7] video: add generic framebuffer eviction David Herrmann
2016-09-02 10:21   ` Tom Gundersen
2016-09-03 12:06   ` Noralf Trønnes
2016-09-05 11:19     ` David Herrmann
2016-09-05 16:36       ` Noralf Trønnes
2016-09-02  8:22 ` [PATCH v5 5/7] drm: switch to sysfb_evict_conflicts() David Herrmann
2016-09-03 12:13   ` Noralf Trønnes
2016-09-02  8:22 ` David Herrmann [this message]
2016-09-02 12:45   ` [PATCH v5 6/7] drm: add SimpleDRM driver Tom Gundersen
2016-09-03 12:01   ` Noralf Trønnes
2016-09-03 12:05     ` David Herrmann
2016-09-05 16:39   ` Noralf Trønnes
2016-09-02  8:22 ` [PATCH v5 7/7] drm/simpledrm: add fbdev fallback support David Herrmann
2016-09-03 12:04   ` Noralf Trønnes
2016-09-03 17:15     ` Noralf Trønnes
2016-09-05 11:21       ` David Herrmann
2021-03-10  2:50 ` [PATCH v5 0/7] drm: add simpledrm driver nerdopolis
2021-03-10  9:10   ` Thomas Zimmermann
2021-03-10 13:52     ` nerdopolis
2021-03-12  3:49     ` nerdopolis
2021-03-12  8:03       ` Thomas Zimmermann
2021-03-12 13:25         ` nerdopolis

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160902082245.7119-7-dh.herrmann@gmail.com \
    --to=dh.herrmann@gmail.com \
    --cc=dri-devel@lists.freedesktop.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.