All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] drm: add SimpleDRM driver
@ 2016-08-04 14:03 ` Noralf Trønnes
  0 siblings, 0 replies; 31+ messages in thread
From: Noralf Trønnes @ 2016-08-04 14:03 UTC (permalink / raw)
  To: dri-devel; +Cc: dh.herrmann, linux-kernel, Noralf Trønnes

This patchset adds the simpledrm driver by David Herrmann based on a
patchset[1] from 2014. That patchset also included patches for kicking
out simpledrm by real drivers. I have stayed away from that since it
involves another subsystem and I would probably be unable to answer any
questions about the implementation.

I have done my best to bring simpledrm up to speed. However I was unable to
loose drm_legacy_mmap() in sdrm_drm_mmap() since I don't understand much of
the gem code.

I was left with some questions after doing this:

- Is there any reason why simpledrm can't use drm_gem_cma_helper?
  One obvious difference is that of contiguous memory allocation:

  sdrm_gem_get_pages():
	obj->pages = drm_malloc_ab(num, sizeof(*obj->pages));
	for (i = 0; i < num; ++i) {
		obj->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
	obj->vmapping = vmap(obj->pages, num, 0, PAGE_KERNEL);

  drm_gem_cma_create():
        cma_obj->vaddr = dma_alloc_wc(drm->dev, size, &cma_obj->paddr,
                                      GFP_KERNEL | __GFP_NOWARN);


- Could we set this range to the actual width/height of the native framebuffer?

  sdrm_drm_modeset_init():
	ddev->mode_config.min_width = 1;
	ddev->mode_config.min_height = 1;
	ddev->mode_config.max_width = 8192;
	ddev->mode_config.max_height = 8192;


- Is there a usecase for offsetting the dumb buffer when blitted onto the
  native buffer?

  sdrm_blit():
	/* get scanout offsets */
	xoff = 0;
	yoff = 0;
	if (sdrm->pipe.plane.fb == fb) {
		xoff = sdrm->pipe.crtc.x;
		yoff = sdrm->pipe.crtc.y;
	}


I have tested simpledrm on a Raspberry Pi B+ with U-boot setting up the
framebuffer and producing this node:

        framebuffer@1e887000 {
                compatible = "simple-framebuffer";
                reg = <0x1e887000 0x36c600>;
                format = "r5g6b5";
                width = <1824>;
                height = <984>;
                stride = <3648>;
                status = "okay";
        };

I have only tested with fbcon and modetest (XR24,RG16).


Noralf.


Changes from previous version[2]:
- Remove FB_SIMPLE=n dependency to avoid kconfig recursive error
- Changed module name to match kconfig help text: sdrm -> simpledrm
- Use drm_simple_display_pipe
- Replace deprecated drm_platform_init()
- sdrm_dumb_create(): drm_gem_object_unreference() -> *_unlocked()
- sdrm_dumb_map_offset(): drm_gem_object_lookup() remove drm_device parameter
- sdrm_drm_mmap() changes:
  Remove struct_mutex locking
  Add drm_vma_offset_{lock,unlock}_lookup()
  drm_mmap() -> drm_legacy_mmap()
- dma_buf_begin_cpu_access() doesn't require start and length anymore
- Use drm_cvt_mode() instead of open coding a mode
- Fix format conversion. In the intermediate step, store the 8/6/5 bit color
  value in the upper part of the 16-bit color variable, not the lower.
- Support clips == NULL in sdrm_dirty()
- Set mode_config.preferred_depth
- Attach mode_config.dirty_info_property to connector
fbdev:
- Remove the DRM_SIMPLEDRM_FBDEV kconfig option and use DRM_FBDEV_EMULATION
- Suspend fbcon/fbdev when the pipeline is enabled, resume in lastclose
- Add FBINFO_CAN_FORCE_OUTPUT flag so we get oops'es on the console

[1] https://lists.freedesktop.org/archives/dri-devel/2014-January/052584.html
[2] https://lists.freedesktop.org/archives/dri-devel/2014-January/052594.html


Further history:

[PATCH v4 0/6] SimpleDRM Driver
https://lists.freedesktop.org/archives/dri-devel/2013-September/044638.html

[PATCH v2 00/14] Platform Framebuffers and SimpleDRM
https://lists.freedesktop.org/archives/dri-devel/2013-July/041090.html

[RFC 0/6] SimpleDRM Driver (was: dvbe driver)
https://lists.freedesktop.org/archives/dri-devel/2013-June/040386.html

[PATCH 0/9] System Framebuffer Bus (sysfb)
https://lists.freedesktop.org/archives/dri-devel/2013-February/035013.html


Noralf Trønnes (2):
  drm: add SimpleDRM driver
  drm: simpledrm: add fbdev fallback support

 drivers/gpu/drm/Kconfig                      |   2 +
 drivers/gpu/drm/Makefile                     |   1 +
 drivers/gpu/drm/simpledrm/Kconfig            |  22 ++
 drivers/gpu/drm/simpledrm/Makefile           |   5 +
 drivers/gpu/drm/simpledrm/simpledrm.h        | 111 ++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_damage.c | 314 +++++++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_drv.c    | 298 +++++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c  | 160 ++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_gem.c    | 276 +++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_kms.c    | 288 ++++++++++++++++++++++++
 10 files changed, 1477 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_fbdev.c
 create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_gem.c
 create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_kms.c

--
2.8.2

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

* [PATCH 0/2] drm: add SimpleDRM driver
@ 2016-08-04 14:03 ` Noralf Trønnes
  0 siblings, 0 replies; 31+ messages in thread
From: Noralf Trønnes @ 2016-08-04 14:03 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-kernel

This patchset adds the simpledrm driver by David Herrmann based on a
patchset[1] from 2014. That patchset also included patches for kicking
out simpledrm by real drivers. I have stayed away from that since it
involves another subsystem and I would probably be unable to answer any
questions about the implementation.

I have done my best to bring simpledrm up to speed. However I was unable to
loose drm_legacy_mmap() in sdrm_drm_mmap() since I don't understand much of
the gem code.

I was left with some questions after doing this:

- Is there any reason why simpledrm can't use drm_gem_cma_helper?
  One obvious difference is that of contiguous memory allocation:

  sdrm_gem_get_pages():
	obj->pages = drm_malloc_ab(num, sizeof(*obj->pages));
	for (i = 0; i < num; ++i) {
		obj->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
	obj->vmapping = vmap(obj->pages, num, 0, PAGE_KERNEL);

  drm_gem_cma_create():
        cma_obj->vaddr = dma_alloc_wc(drm->dev, size, &cma_obj->paddr,
                                      GFP_KERNEL | __GFP_NOWARN);


- Could we set this range to the actual width/height of the native framebuffer?

  sdrm_drm_modeset_init():
	ddev->mode_config.min_width = 1;
	ddev->mode_config.min_height = 1;
	ddev->mode_config.max_width = 8192;
	ddev->mode_config.max_height = 8192;


- Is there a usecase for offsetting the dumb buffer when blitted onto the
  native buffer?

  sdrm_blit():
	/* get scanout offsets */
	xoff = 0;
	yoff = 0;
	if (sdrm->pipe.plane.fb == fb) {
		xoff = sdrm->pipe.crtc.x;
		yoff = sdrm->pipe.crtc.y;
	}


I have tested simpledrm on a Raspberry Pi B+ with U-boot setting up the
framebuffer and producing this node:

        framebuffer@1e887000 {
                compatible = "simple-framebuffer";
                reg = <0x1e887000 0x36c600>;
                format = "r5g6b5";
                width = <1824>;
                height = <984>;
                stride = <3648>;
                status = "okay";
        };

I have only tested with fbcon and modetest (XR24,RG16).


Noralf.


Changes from previous version[2]:
- Remove FB_SIMPLE=n dependency to avoid kconfig recursive error
- Changed module name to match kconfig help text: sdrm -> simpledrm
- Use drm_simple_display_pipe
- Replace deprecated drm_platform_init()
- sdrm_dumb_create(): drm_gem_object_unreference() -> *_unlocked()
- sdrm_dumb_map_offset(): drm_gem_object_lookup() remove drm_device parameter
- sdrm_drm_mmap() changes:
  Remove struct_mutex locking
  Add drm_vma_offset_{lock,unlock}_lookup()
  drm_mmap() -> drm_legacy_mmap()
- dma_buf_begin_cpu_access() doesn't require start and length anymore
- Use drm_cvt_mode() instead of open coding a mode
- Fix format conversion. In the intermediate step, store the 8/6/5 bit color
  value in the upper part of the 16-bit color variable, not the lower.
- Support clips == NULL in sdrm_dirty()
- Set mode_config.preferred_depth
- Attach mode_config.dirty_info_property to connector
fbdev:
- Remove the DRM_SIMPLEDRM_FBDEV kconfig option and use DRM_FBDEV_EMULATION
- Suspend fbcon/fbdev when the pipeline is enabled, resume in lastclose
- Add FBINFO_CAN_FORCE_OUTPUT flag so we get oops'es on the console

[1] https://lists.freedesktop.org/archives/dri-devel/2014-January/052584.html
[2] https://lists.freedesktop.org/archives/dri-devel/2014-January/052594.html


Further history:

[PATCH v4 0/6] SimpleDRM Driver
https://lists.freedesktop.org/archives/dri-devel/2013-September/044638.html

[PATCH v2 00/14] Platform Framebuffers and SimpleDRM
https://lists.freedesktop.org/archives/dri-devel/2013-July/041090.html

[RFC 0/6] SimpleDRM Driver (was: dvbe driver)
https://lists.freedesktop.org/archives/dri-devel/2013-June/040386.html

[PATCH 0/9] System Framebuffer Bus (sysfb)
https://lists.freedesktop.org/archives/dri-devel/2013-February/035013.html


Noralf Trønnes (2):
  drm: add SimpleDRM driver
  drm: simpledrm: add fbdev fallback support

 drivers/gpu/drm/Kconfig                      |   2 +
 drivers/gpu/drm/Makefile                     |   1 +
 drivers/gpu/drm/simpledrm/Kconfig            |  22 ++
 drivers/gpu/drm/simpledrm/Makefile           |   5 +
 drivers/gpu/drm/simpledrm/simpledrm.h        | 111 ++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_damage.c | 314 +++++++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_drv.c    | 298 +++++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c  | 160 ++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_gem.c    | 276 +++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_kms.c    | 288 ++++++++++++++++++++++++
 10 files changed, 1477 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_fbdev.c
 create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_gem.c
 create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_kms.c

--
2.8.2

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

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

* [PATCH 1/2] drm: add SimpleDRM driver
  2016-08-04 14:03 ` Noralf Trønnes
@ 2016-08-04 14:03   ` Noralf Trønnes
  -1 siblings, 0 replies; 31+ messages in thread
From: Noralf Trønnes @ 2016-08-04 14:03 UTC (permalink / raw)
  To: dri-devel; +Cc: dh.herrmann, linux-kernel, Noralf Trønnes

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.

The driver was originally written by David Herrmann in 2014.
My main contribution is to make use of drm_simple_kms_helper and
rework the probe path to avoid use of the deprecated drm_platform_init()
and drm_driver.{load,unload}().

Cc: dh.herrmann@gmail.com
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---

Changes from previous version:
- Remove FB_SIMPLE=n dependency to avoid kconfig recursive error
- Changed module name to match kconfig help text: sdrm -> simpledrm
- Use drm_simple_display_pipe
- Replace deprecated drm_platform_init()
- sdrm_dumb_create(): drm_gem_object_unreference() -> *_unlocked()
- sdrm_dumb_map_offset(): drm_gem_object_lookup() remove drm_device parameter
- sdrm_drm_mmap() changes:
  Remove struct_mutex locking
  Add drm_vma_offset_{lock,unlock}_lookup()
  drm_mmap() -> drm_legacy_mmap()
- dma_buf_begin_cpu_access() doesn't require start and length anymore
- Use drm_cvt_mode() instead of open coding a mode
- Fix format conversion. In the intermediate step, store the 8/6/5 bit color
  value in the upper part of the 16-bit color variable, not the lower.
- Support clips == NULL in sdrm_dirty()
- Set mode_config.preferred_depth
- Attach mode_config.dirty_info_property to connector

 drivers/gpu/drm/Kconfig                      |   2 +
 drivers/gpu/drm/Makefile                     |   1 +
 drivers/gpu/drm/simpledrm/Kconfig            |  19 ++
 drivers/gpu/drm/simpledrm/Makefile           |   4 +
 drivers/gpu/drm/simpledrm/simpledrm.h        |  87 ++++++++
 drivers/gpu/drm/simpledrm/simpledrm_damage.c | 314 +++++++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_drv.c    | 295 +++++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_gem.c    | 276 +++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_kms.c    | 276 +++++++++++++++++++++++
 9 files changed, 1274 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

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index fc35731..a54cc8d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -290,3 +290,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 e3dba6f..eba32ad 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-$(CONFIG_DRM_SIMPLEDRM) += 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..f6a62dc
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/Makefile
@@ -0,0 +1,4 @@
+simpledrm-y :=	simpledrm_drv.o simpledrm_kms.o simpledrm_gem.o \
+		simpledrm_damage.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..e841655
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm.h
@@ -0,0 +1,87 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef SDRM_DRV_H
+#define SDRM_DRV_H
+
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_simple_kms_helper.h>
+
+struct sdrm_device;
+struct sdrm_gem_object;
+struct sdrm_framebuffer;
+
+struct sdrm_device {
+	struct drm_device *ddev;
+	struct drm_simple_display_pipe pipe;
+	struct drm_connector conn;
+
+	/* framebuffer information */
+	const struct simplefb_format *fb_sformat;
+	u32 fb_format;
+	u32 fb_width;
+	u32 fb_height;
+	u32 fb_stride;
+	u32 fb_bpp;
+	unsigned long fb_base;
+	unsigned long fb_size;
+	void *fb_map;
+};
+
+int sdrm_drm_modeset_init(struct sdrm_device *sdrm);
+int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma);
+
+int sdrm_dirty(struct drm_framebuffer *fb,
+	       struct drm_file *file,
+	       unsigned int flags, unsigned int color,
+	       struct drm_clip_rect *clips,
+	       unsigned int num_clips);
+int sdrm_dirty_all_locked(struct sdrm_device *sdrm);
+int sdrm_dirty_all_unlocked(struct sdrm_device *sdrm);
+
+struct sdrm_gem_object {
+	struct drm_gem_object base;
+	struct sg_table *sg;
+	struct page **pages;
+	void *vmapping;
+};
+
+#define to_sdrm_bo(x) container_of(x, struct sdrm_gem_object, base)
+
+struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev,
+					      size_t size);
+struct drm_gem_object *sdrm_gem_prime_import(struct drm_device *ddev,
+					     struct dma_buf *dma_buf);
+void sdrm_gem_free_object(struct drm_gem_object *obj);
+int sdrm_gem_get_pages(struct sdrm_gem_object *obj);
+
+int sdrm_dumb_create(struct drm_file *file_priv, struct drm_device *ddev,
+		     struct drm_mode_create_dumb *arg);
+int sdrm_dumb_destroy(struct drm_file *file_priv, struct drm_device *ddev,
+		      uint32_t handle);
+int sdrm_dumb_map_offset(struct drm_file *file_priv, struct drm_device *ddev,
+			 uint32_t handle, uint64_t *offset);
+
+struct sdrm_framebuffer {
+	struct drm_framebuffer base;
+	struct sdrm_gem_object *obj;
+};
+
+#define to_sdrm_fb(x) container_of(x, struct sdrm_framebuffer, base)
+
+#endif /* SDRM_DRV_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..e87dac7
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_damage.c
@@ -0,0 +1,314 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/dma-buf.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.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_framebuffer *sfb, u32 x, u32 y,
+		      u32 width, u32 height)
+{
+	struct drm_framebuffer *fb = &sfb->base;
+	struct drm_device *ddev = fb->dev;
+	struct sdrm_device *sdrm = ddev->dev_private;
+	u32 src_bpp, dst_bpp, xoff, yoff, x2, y2;
+	u8 *src, *dst;
+
+	/* already unmapped; ongoing handover? */
+	if (!sdrm->fb_map)
+		return;
+
+	/* empty dirty-region, nothing to do */
+	if (!width || !height)
+		return;
+	if (x >= fb->width || y >= fb->height)
+		return;
+
+	/* sanity checks */
+	if (x + width < x)
+		width = fb->width - x;
+	if (y + height < y)
+		height = fb->height - y;
+
+	/* get scanout offsets */
+	xoff = 0;
+	yoff = 0;
+	if (sdrm->pipe.plane.fb == fb) {
+		xoff = sdrm->pipe.crtc.x;
+		yoff = sdrm->pipe.crtc.y;
+	}
+
+	/* get intersection of dirty region and scan-out region */
+	x2 = min(x + width, xoff + sdrm->fb_width);
+	y2 = min(y + height, yoff + sdrm->fb_height);
+	x = max(x, xoff);
+	y = max(y, yoff);
+	if (x2 <= x || y2 <= y)
+		return;
+	width = x2 - x;
+	height = y2 - y;
+
+	/* get buffer offsets */
+	src = sfb->obj->vmapping;
+	dst = sdrm->fb_map;
+
+	/* bo is guaranteed to be big enough; size checks not needed */
+	src_bpp = (fb->bits_per_pixel + 7) / 8;
+	src += fb->offsets[0] + y * fb->pitches[0] + x * src_bpp;
+
+	dst_bpp = (sdrm->fb_bpp + 7) / 8;
+	dst += (y - yoff) * sdrm->fb_stride + (x - xoff) * dst_bpp;
+
+	/* if formats are identical, do a line-by-line copy.. */
+	if (fb->pixel_format == sdrm->fb_format) {
+		sdrm_blit_lines(src, fb->pitches[0],
+				dst, sdrm->fb_stride,
+				src_bpp, width, height);
+		return;
+	}
+
+	/* ..otherwise call slow blit-function */
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_ARGB8888:
+		/* fallthrough */
+	case DRM_FORMAT_XRGB8888:
+		sdrm_blit_from_xrgb8888(src, fb->pitches[0], src_bpp,
+					dst, sdrm->fb_stride, dst_bpp,
+					sdrm->fb_format, width, height);
+		break;
+	case DRM_FORMAT_RGB565:
+		sdrm_blit_from_rgb565(src, fb->pitches[0], src_bpp,
+				      dst, sdrm->fb_stride, dst_bpp,
+				      sdrm->fb_format, width, height);
+		break;
+	}
+}
+
+static int sdrm_begin_access(struct sdrm_framebuffer *sfb)
+{
+	int r;
+
+	r = sdrm_gem_get_pages(sfb->obj);
+	if (r)
+		return r;
+
+	if (!sfb->obj->base.import_attach)
+		return 0;
+
+	return dma_buf_begin_cpu_access(sfb->obj->base.import_attach->dmabuf,
+					DMA_FROM_DEVICE);
+}
+
+static void sdrm_end_access(struct sdrm_framebuffer *sfb)
+{
+	if (!sfb->obj->base.import_attach)
+		return;
+
+	dma_buf_end_cpu_access(sfb->obj->base.import_attach->dmabuf,
+			       DMA_FROM_DEVICE);
+}
+
+int sdrm_dirty(struct drm_framebuffer *fb,
+	       struct drm_file *file,
+	       unsigned int flags, unsigned int color,
+	       struct drm_clip_rect *clips,
+	       unsigned int num_clips)
+{
+	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
+	struct drm_device *ddev = fb->dev;
+	struct sdrm_device *sdrm = ddev->dev_private;
+	struct drm_clip_rect full_clip;
+	unsigned int i;
+	int r;
+
+	if (!clips || !num_clips) {
+		full_clip.x1 = 0;
+		full_clip.x2 = fb->width;
+		full_clip.y1 = 0;
+		full_clip.y2 = fb->height;
+		clips = &full_clip;
+		num_clips = 1;
+	}
+
+	drm_modeset_lock_all(ddev);
+
+	if (sdrm->pipe.plane.fb != fb) {
+		r = 0;
+		goto unlock;
+	}
+
+	r = sdrm_begin_access(sfb);
+	if (r)
+		goto unlock;
+
+	for (i = 0; i < num_clips; i++) {
+		if (clips[i].x2 <= clips[i].x1 ||
+		    clips[i].y2 <= clips[i].y1)
+			continue;
+
+		sdrm_blit(sfb, clips[i].x1, clips[i].y1,
+			  clips[i].x2 - clips[i].x1,
+			  clips[i].y2 - clips[i].y1);
+	}
+
+	sdrm_end_access(sfb);
+
+unlock:
+	drm_modeset_unlock_all(ddev);
+	return 0;
+}
+
+int sdrm_dirty_all_locked(struct sdrm_device *sdrm)
+{
+	struct drm_framebuffer *fb;
+	struct sdrm_framebuffer *sfb;
+	int r;
+
+	fb = sdrm->pipe.plane.fb;
+	if (!fb)
+		return 0;
+
+	sfb = to_sdrm_fb(fb);
+	r = sdrm_begin_access(sfb);
+	if (r)
+		return r;
+
+	sdrm_blit(sfb, 0, 0, fb->width, fb->height);
+
+	sdrm_end_access(sfb);
+
+	return 0;
+}
+
+int sdrm_dirty_all_unlocked(struct sdrm_device *sdrm)
+{
+	int r;
+
+	drm_modeset_lock_all(sdrm->ddev);
+	r = sdrm_dirty_all_locked(sdrm);
+	drm_modeset_unlock_all(sdrm->ddev);
+
+	return r;
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
new file mode 100644
index 0000000..26b4ca9
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
@@ -0,0 +1,295 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include "simpledrm.h"
+
+static const struct file_operations sdrm_drm_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.mmap = sdrm_drm_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+	.unlocked_ioctl = drm_ioctl,
+	.release = drm_release,
+#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_PRIME |
+			   DRIVER_ATOMIC,
+	.fops = &sdrm_drm_fops,
+
+	.gem_free_object = sdrm_gem_free_object,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_prime_import = sdrm_gem_prime_import,
+
+	.dumb_create = sdrm_dumb_create,
+	.dumb_map_offset = sdrm_dumb_map_offset,
+	.dumb_destroy = sdrm_dumb_destroy,
+
+	.name = "simpledrm",
+	.desc = "Simple firmware framebuffer DRM driver",
+	.date = "20130601",
+	.major = 0,
+	.minor = 0,
+	.patchlevel = 1,
+};
+
+static int parse_dt(struct platform_device *pdev,
+		    struct simplefb_platform_data *mode)
+{
+	struct device_node *np = pdev->dev.of_node;
+	const char *format;
+	int ret;
+
+	if (!np)
+		return -ENODEV;
+
+	ret = of_property_read_u32(np, "width", &mode->width);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't parse width property\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "height", &mode->height);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't parse height property\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "stride", &mode->stride);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't parse stride property\n");
+		return ret;
+	}
+
+	ret = of_property_read_string(np, "format", &format);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't parse format property\n");
+		return ret;
+	}
+	mode->format = format;
+
+	return 0;
+}
+
+static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS;
+
+int sdrm_pdev_init(struct sdrm_device *sdrm)
+{
+	struct platform_device *pdev = sdrm->ddev->platformdev;
+	struct simplefb_platform_data *mode = pdev->dev.platform_data;
+	struct simplefb_platform_data pmode;
+	struct resource *mem;
+	unsigned int depth;
+	int ret, i, bpp;
+
+	if (!mode) {
+		mode = &pmode;
+		ret = parse_dt(pdev, mode);
+		if (ret)
+			return ret;
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(sdrm->ddev->dev, "No memory resource\n");
+		return -ENODEV;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(simplefb_formats); ++i) {
+		if (strcmp(mode->format, simplefb_formats[i].name))
+			continue;
+
+		sdrm->fb_sformat = &simplefb_formats[i];
+		sdrm->fb_format = simplefb_formats[i].fourcc;
+		sdrm->fb_width = mode->width;
+		sdrm->fb_height = mode->height;
+		sdrm->fb_stride = mode->stride;
+		sdrm->fb_base = mem->start;
+		sdrm->fb_size = resource_size(mem);
+		break;
+	}
+
+	if (i >= ARRAY_SIZE(simplefb_formats)) {
+		dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format);
+		return -ENODEV;
+	}
+
+	switch (sdrm->fb_format) {
+	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:
+		dev_err(sdrm->ddev->dev, "Unsupported format %s\n",
+			mode->format);
+		return -ENODEV;
+	}
+
+	drm_fb_get_bpp_depth(sdrm->fb_format, &depth, &bpp);
+	if (!bpp) {
+		dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format);
+		return -ENODEV;
+	}
+
+	if (sdrm->fb_size < sdrm->fb_stride * sdrm->fb_height) {
+		dev_err(sdrm->ddev->dev, "FB too small\n");
+		return -ENODEV;
+	} else if ((bpp + 7) / 8 * sdrm->fb_width > sdrm->fb_stride) {
+		dev_err(sdrm->ddev->dev, "Invalid stride\n");
+		return -ENODEV;
+	}
+
+	sdrm->fb_bpp = bpp;
+
+	sdrm->fb_map = ioremap_wc(sdrm->fb_base, sdrm->fb_size);
+	if (!sdrm->fb_map) {
+		dev_err(sdrm->ddev->dev, "cannot remap VMEM\n");
+		return -EIO;
+	}
+
+	DRM_DEBUG_KMS("format: %s\n", drm_get_format_name(sdrm->fb_format));
+
+	return 0;
+}
+
+void sdrm_pdev_destroy(struct sdrm_device *sdrm)
+{
+	if (sdrm->fb_map) {
+		iounmap(sdrm->fb_map);
+		sdrm->fb_map = NULL;
+	}
+}
+
+static int sdrm_simplefb_probe(struct platform_device *pdev)
+{
+	struct sdrm_device *sdrm;
+	struct drm_device *ddev;
+	int ret;
+
+	ddev = drm_dev_alloc(&sdrm_drm_driver, &pdev->dev);
+	if (!ddev)
+		return -ENOMEM;
+
+	sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL);
+	if (!sdrm)
+		goto err_free;
+
+	ddev->platformdev = pdev;
+	ddev->dev_private = sdrm;
+	sdrm->ddev = ddev;
+
+	ret = sdrm_pdev_init(sdrm);
+	if (ret)
+		goto err_free;
+
+	ret = sdrm_drm_modeset_init(sdrm);
+	if (ret)
+		goto err_destroy;
+
+	ret = drm_dev_register(ddev, 0);
+	if (ret)
+		goto err_cleanup;
+
+	platform_set_drvdata(pdev, ddev);
+
+	DRM_INFO("Initialized %s on minor %d\n", ddev->driver->name,
+		 ddev->primary->index);
+
+	return 0;
+
+err_cleanup:
+	drm_mode_config_cleanup(ddev);
+err_destroy:
+	sdrm_pdev_destroy(sdrm);
+err_free:
+	drm_dev_unref(ddev);
+	kfree(sdrm);
+
+	return ret;
+}
+
+static int sdrm_simplefb_remove(struct platform_device *pdev)
+{
+	struct drm_device *ddev = platform_get_drvdata(pdev);
+	struct sdrm_device *sdrm = ddev->dev_private;
+
+	drm_dev_unregister(ddev);
+	drm_mode_config_cleanup(ddev);
+
+	/* protect fb_map removal against sdrm_blit() */
+	drm_modeset_lock_all(ddev);
+	sdrm_pdev_destroy(sdrm);
+	drm_modeset_unlock_all(ddev);
+
+	drm_dev_unref(ddev);
+	kfree(sdrm);
+
+	return 0;
+}
+
+static const struct of_device_id simplefb_of_match[] = {
+	{ .compatible = "simple-framebuffer", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, 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 = simplefb_of_match,
+	},
+};
+
+static int __init sdrm_init(void)
+{
+	return platform_driver_register(&sdrm_simplefb_driver);
+}
+
+static void __exit sdrm_exit(void)
+{
+	platform_driver_unregister(&sdrm_simplefb_driver);
+}
+
+module_init(sdrm_init);
+module_exit(sdrm_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
+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..2d59632
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c
@@ -0,0 +1,276 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include <drm/drm_legacy.h>
+#include "simpledrm.h"
+
+int sdrm_gem_get_pages(struct sdrm_gem_object *obj)
+{
+	size_t num, i;
+
+	if (obj->vmapping)
+		return 0;
+
+	if (obj->base.import_attach) {
+		obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf);
+		return !obj->vmapping ? -ENOMEM : 0;
+	}
+
+	num = obj->base.size >> PAGE_SHIFT;
+	obj->pages = drm_malloc_ab(num, sizeof(*obj->pages));
+	if (!obj->pages)
+		return -ENOMEM;
+
+	for (i = 0; i < num; ++i) {
+		obj->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
+		if (!obj->pages[i])
+			goto error;
+	}
+
+	obj->vmapping = vmap(obj->pages, num, 0, PAGE_KERNEL);
+	if (!obj->vmapping)
+		goto error;
+
+	return 0;
+
+error:
+	while (i > 0)
+		__free_pages(obj->pages[--i], 0);
+
+	drm_free_large(obj->pages);
+	obj->pages = NULL;
+	return -ENOMEM;
+}
+
+static void sdrm_gem_put_pages(struct sdrm_gem_object *obj)
+{
+	size_t num, i;
+
+	if (!obj->vmapping)
+		return;
+
+	if (obj->base.import_attach) {
+		dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping);
+		obj->vmapping = NULL;
+		return;
+	}
+
+	vunmap(obj->vmapping);
+	obj->vmapping = NULL;
+
+	num = obj->base.size >> PAGE_SHIFT;
+	for (i = 0; i < num; ++i)
+		__free_pages(obj->pages[i], 0);
+
+	drm_free_large(obj->pages);
+	obj->pages = NULL;
+}
+
+struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev,
+					      size_t size)
+{
+	struct sdrm_gem_object *obj;
+
+	WARN_ON(!size || (size & ~PAGE_MASK) != 0);
+
+	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+	if (!obj)
+		return NULL;
+
+	drm_gem_private_object_init(ddev, &obj->base, size);
+	return obj;
+}
+
+void sdrm_gem_free_object(struct drm_gem_object *gobj)
+{
+	struct sdrm_gem_object *obj = to_sdrm_bo(gobj);
+	struct drm_device *ddev = gobj->dev;
+
+	if (obj->pages) {
+		/* kill all user-space mappings */
+		drm_vma_node_unmap(&gobj->vma_node,
+				   ddev->anon_inode->i_mapping);
+		sdrm_gem_put_pages(obj);
+	}
+
+	if (gobj->import_attach)
+		drm_prime_gem_destroy(gobj, obj->sg);
+
+	drm_gem_free_mmap_offset(gobj);
+	drm_gem_object_release(gobj);
+	kfree(obj);
+}
+
+int sdrm_dumb_create(struct drm_file *dfile, struct drm_device *ddev,
+		     struct drm_mode_create_dumb *args)
+{
+	struct sdrm_gem_object *obj;
+	int r;
+
+	if (args->flags)
+		return -EINVAL;
+
+	/* overflow checks are done by DRM core */
+	args->pitch = (args->bpp + 7) / 8 * args->width;
+	args->size = PAGE_ALIGN(args->pitch * args->height);
+
+	obj = sdrm_gem_alloc_object(ddev, args->size);
+	if (!obj)
+		return -ENOMEM;
+
+	r = drm_gem_handle_create(dfile, &obj->base, &args->handle);
+	if (r) {
+		drm_gem_object_unreference_unlocked(&obj->base);
+		return r;
+	}
+
+	/* handle owns a reference */
+	drm_gem_object_unreference_unlocked(&obj->base);
+	return 0;
+}
+
+int sdrm_dumb_destroy(struct drm_file *dfile, struct drm_device *ddev,
+		      uint32_t handle)
+{
+	return drm_gem_handle_delete(dfile, handle);
+}
+
+int sdrm_dumb_map_offset(struct drm_file *dfile, struct drm_device *ddev,
+			 uint32_t handle, uint64_t *offset)
+{
+	struct drm_gem_object *gobj;
+	int r;
+
+	mutex_lock(&ddev->struct_mutex);
+
+	gobj = drm_gem_object_lookup(dfile, handle);
+	if (!gobj) {
+		r = -ENOENT;
+		goto out_unlock;
+	}
+
+	r = drm_gem_create_mmap_offset(gobj);
+	if (r)
+		goto out_unref;
+
+	*offset = drm_vma_node_offset_addr(&gobj->vma_node);
+
+out_unref:
+	drm_gem_object_unreference(gobj);
+out_unlock:
+	mutex_unlock(&ddev->struct_mutex);
+	return r;
+}
+
+int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct drm_file *priv = filp->private_data;
+	struct drm_device *dev = priv->minor->dev;
+	struct drm_vma_offset_node *node;
+	struct drm_gem_object *gobj;
+	struct sdrm_gem_object *obj;
+	size_t size, i, num;
+	int r;
+
+	if (drm_device_is_unplugged(dev))
+		return -ENODEV;
+
+	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));
+	drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
+
+	if (!node)
+		return drm_legacy_mmap(filp, vma);
+	else if (!drm_vma_node_is_allowed(node, filp))
+		return -EACCES;
+
+	gobj = container_of(node, struct drm_gem_object, vma_node);
+	obj = to_sdrm_bo(gobj);
+	size = drm_vma_node_size(node) << PAGE_SHIFT;
+	if (size < vma->vm_end - vma->vm_start)
+		return r;
+
+	r = sdrm_gem_get_pages(obj);
+	if (r < 0)
+		return r;
+
+	/* prevent dmabuf-imported mmap to user-space */
+	if (!obj->pages)
+		return -EACCES;
+
+	vma->vm_flags |= VM_DONTEXPAND;
+	vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
+
+	num = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+	for (i = 0; i < num; ++i) {
+		r = vm_insert_page(vma, vma->vm_start + i * PAGE_SIZE,
+				   obj->pages[i]);
+		if (r < 0) {
+			if (i > 0)
+				zap_vma_ptes(vma, vma->vm_start, i * PAGE_SIZE);
+			return r;
+		}
+	}
+
+	return 0;
+}
+
+struct drm_gem_object *sdrm_gem_prime_import(struct drm_device *ddev,
+					     struct dma_buf *dma_buf)
+{
+	struct dma_buf_attachment *attach;
+	struct sdrm_gem_object *obj;
+	struct sg_table *sg;
+	int ret;
+
+	/* need to attach */
+	attach = dma_buf_attach(dma_buf, ddev->dev);
+	if (IS_ERR(attach))
+		return ERR_CAST(attach);
+
+	get_dma_buf(dma_buf);
+
+	sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+	if (IS_ERR(sg)) {
+		ret = PTR_ERR(sg);
+		goto fail_detach;
+	}
+
+	/*
+	 * dma_buf_vmap() gives us a page-aligned mapping, so lets bump the
+	 * size of the dma-buf to the next page-boundary
+	 */
+	obj = sdrm_gem_alloc_object(ddev, PAGE_ALIGN(dma_buf->size));
+	if (!obj) {
+		ret = -ENOMEM;
+		goto fail_unmap;
+	}
+
+	obj->sg = sg;
+	obj->base.import_attach = attach;
+
+	return &obj->base;
+
+fail_unmap:
+	dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL);
+fail_detach:
+	dma_buf_detach(dma_buf, attach);
+	dma_buf_put(dma_buf);
+	return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
new file mode 100644
index 0000000..6295a9f
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
@@ -0,0 +1,276 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.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->fb_width, sdrm->fb_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 inline struct sdrm_device *
+pipe_to_sdrm(struct drm_simple_display_pipe *pipe)
+{
+	return container_of(pipe, struct sdrm_device, pipe);
+}
+
+static int sdrm_display_pipe_check(struct drm_simple_display_pipe *pipe,
+				   struct drm_plane_state *plane_state,
+				   struct drm_crtc_state *crtc_state)
+{
+	struct drm_display_mode *mode = &crtc_state->mode;
+	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
+	struct drm_framebuffer *fb = plane_state->fb;
+	u32 x = plane_state->src_x >> 16;
+	u32 y = plane_state->src_y >> 16;
+
+	if (mode->hdisplay != sdrm->fb_width ||
+	    mode->vdisplay != sdrm->fb_height)
+		return -EINVAL;
+	if (fb->width <= x || fb->height <= y ||
+	    fb->width - x < sdrm->fb_width || fb->height - y < sdrm->fb_height)
+		return -EINVAL;
+
+	return 0;
+}
+
+void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
+			      struct drm_plane_state *plane_state)
+{
+	struct drm_framebuffer *fb = pipe->plane.state->fb;
+	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
+
+	if (fb)
+		sdrm_dirty_all_locked(sdrm);
+}
+
+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;
+	}
+}
+
+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 = {
+	.check = sdrm_display_pipe_check,
+	.update = sdrm_display_pipe_update,
+	.enable = sdrm_display_pipe_enable,
+	.disable = sdrm_display_pipe_disable,
+};
+
+static int sdrm_fb_create_handle(struct drm_framebuffer *fb,
+				 struct drm_file *dfile,
+				 unsigned int *handle)
+{
+	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
+
+	return drm_gem_handle_create(dfile, &sfb->obj->base, handle);
+}
+
+static void sdrm_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
+
+	drm_framebuffer_cleanup(fb);
+	drm_gem_object_unreference_unlocked(&sfb->obj->base);
+	kfree(sfb);
+}
+
+static const struct drm_framebuffer_funcs sdrm_fb_ops = {
+	.create_handle = sdrm_fb_create_handle,
+	.dirty = sdrm_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 sdrm_framebuffer *fb;
+	struct drm_gem_object *gobj;
+	u32 bpp, size;
+	int ret;
+	void *err;
+
+	if (cmd->flags)
+		return ERR_PTR(-EINVAL);
+
+	gobj = drm_gem_object_lookup(dfile, cmd->handles[0]);
+	if (!gobj)
+		return ERR_PTR(-EINVAL);
+
+	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+	if (!fb) {
+		err = ERR_PTR(-ENOMEM);
+		goto err_unref;
+	}
+	fb->obj = to_sdrm_bo(gobj);
+
+	fb->base.pitches[0] = cmd->pitches[0];
+	fb->base.offsets[0] = cmd->offsets[0];
+	fb->base.width = cmd->width;
+	fb->base.height = cmd->height;
+	fb->base.pixel_format = cmd->pixel_format;
+	drm_fb_get_bpp_depth(cmd->pixel_format, &fb->base.depth,
+			     &fb->base.bits_per_pixel);
+
+	/*
+	 * width/height are already clamped into min/max_width/height range,
+	 * so overflows are not possible
+	 */
+
+	bpp = (fb->base.bits_per_pixel + 7) / 8;
+	size = cmd->pitches[0] * cmd->height;
+	if (!bpp ||
+	    bpp > 4 ||
+	    cmd->pitches[0] < bpp * fb->base.width ||
+	    cmd->pitches[0] > 0xffffU ||
+	    size + fb->base.offsets[0] < size ||
+	    size + fb->base.offsets[0] > fb->obj->base.size) {
+		err = ERR_PTR(-EINVAL);
+		goto err_free;
+	}
+
+	ret = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops);
+	if (ret < 0) {
+		err = ERR_PTR(ret);
+		goto err_free;
+	}
+
+	DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id,
+		      drm_get_format_name(fb->base.pixel_format));
+
+	return &fb->base;
+
+err_free:
+	kfree(fb);
+err_unref:
+	drm_gem_object_unreference_unlocked(gobj);
+
+	return err;
+}
+
+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_drm_modeset_init(struct sdrm_device *sdrm)
+{
+	struct drm_connector *conn = &sdrm->conn;
+	struct drm_device *ddev = sdrm->ddev;
+	int ret;
+
+	drm_mode_config_init(ddev);
+	ddev->mode_config.min_width = 1;
+	ddev->mode_config.min_height = 1;
+	ddev->mode_config.max_width = 8192;
+	ddev->mode_config.max_height = 8192;
+	ddev->mode_config.preferred_depth = sdrm->fb_bpp;
+	ddev->mode_config.funcs = &sdrm_mode_config_ops;
+
+	drm_connector_helper_add(conn, &sdrm_conn_hfuncs);
+	ret = drm_connector_init(ddev, conn, &sdrm_conn_ops,
+				 DRM_MODE_CONNECTOR_VIRTUAL);
+	if (ret)
+		goto err_cleanup;
+
+	ret = drm_mode_create_dirty_info_property(ddev);
+	if (ret)
+		goto err_cleanup;
+
+	drm_object_attach_property(&conn->base,
+				   ddev->mode_config.dirty_info_property,
+				   DRM_MODE_DIRTY_ON);
+
+	ret = drm_simple_display_pipe_init(ddev, &sdrm->pipe, &sdrm_pipe_funcs,
+					   sdrm_formats,
+					   ARRAY_SIZE(sdrm_formats), conn);
+	if (ret)
+		goto err_cleanup;
+
+	drm_mode_config_reset(ddev);
+
+	return 0;
+
+err_cleanup:
+	drm_mode_config_cleanup(ddev);
+
+	return ret;
+}
--
2.8.2

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

* [PATCH 1/2] drm: add SimpleDRM driver
@ 2016-08-04 14:03   ` Noralf Trønnes
  0 siblings, 0 replies; 31+ messages in thread
From: Noralf Trønnes @ 2016-08-04 14:03 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-kernel

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.

The driver was originally written by David Herrmann in 2014.
My main contribution is to make use of drm_simple_kms_helper and
rework the probe path to avoid use of the deprecated drm_platform_init()
and drm_driver.{load,unload}().

Cc: dh.herrmann@gmail.com
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---

Changes from previous version:
- Remove FB_SIMPLE=n dependency to avoid kconfig recursive error
- Changed module name to match kconfig help text: sdrm -> simpledrm
- Use drm_simple_display_pipe
- Replace deprecated drm_platform_init()
- sdrm_dumb_create(): drm_gem_object_unreference() -> *_unlocked()
- sdrm_dumb_map_offset(): drm_gem_object_lookup() remove drm_device parameter
- sdrm_drm_mmap() changes:
  Remove struct_mutex locking
  Add drm_vma_offset_{lock,unlock}_lookup()
  drm_mmap() -> drm_legacy_mmap()
- dma_buf_begin_cpu_access() doesn't require start and length anymore
- Use drm_cvt_mode() instead of open coding a mode
- Fix format conversion. In the intermediate step, store the 8/6/5 bit color
  value in the upper part of the 16-bit color variable, not the lower.
- Support clips == NULL in sdrm_dirty()
- Set mode_config.preferred_depth
- Attach mode_config.dirty_info_property to connector

 drivers/gpu/drm/Kconfig                      |   2 +
 drivers/gpu/drm/Makefile                     |   1 +
 drivers/gpu/drm/simpledrm/Kconfig            |  19 ++
 drivers/gpu/drm/simpledrm/Makefile           |   4 +
 drivers/gpu/drm/simpledrm/simpledrm.h        |  87 ++++++++
 drivers/gpu/drm/simpledrm/simpledrm_damage.c | 314 +++++++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_drv.c    | 295 +++++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_gem.c    | 276 +++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_kms.c    | 276 +++++++++++++++++++++++
 9 files changed, 1274 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

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index fc35731..a54cc8d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -290,3 +290,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 e3dba6f..eba32ad 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-$(CONFIG_DRM_SIMPLEDRM) += 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..f6a62dc
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/Makefile
@@ -0,0 +1,4 @@
+simpledrm-y :=	simpledrm_drv.o simpledrm_kms.o simpledrm_gem.o \
+		simpledrm_damage.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..e841655
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm.h
@@ -0,0 +1,87 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef SDRM_DRV_H
+#define SDRM_DRV_H
+
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_simple_kms_helper.h>
+
+struct sdrm_device;
+struct sdrm_gem_object;
+struct sdrm_framebuffer;
+
+struct sdrm_device {
+	struct drm_device *ddev;
+	struct drm_simple_display_pipe pipe;
+	struct drm_connector conn;
+
+	/* framebuffer information */
+	const struct simplefb_format *fb_sformat;
+	u32 fb_format;
+	u32 fb_width;
+	u32 fb_height;
+	u32 fb_stride;
+	u32 fb_bpp;
+	unsigned long fb_base;
+	unsigned long fb_size;
+	void *fb_map;
+};
+
+int sdrm_drm_modeset_init(struct sdrm_device *sdrm);
+int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma);
+
+int sdrm_dirty(struct drm_framebuffer *fb,
+	       struct drm_file *file,
+	       unsigned int flags, unsigned int color,
+	       struct drm_clip_rect *clips,
+	       unsigned int num_clips);
+int sdrm_dirty_all_locked(struct sdrm_device *sdrm);
+int sdrm_dirty_all_unlocked(struct sdrm_device *sdrm);
+
+struct sdrm_gem_object {
+	struct drm_gem_object base;
+	struct sg_table *sg;
+	struct page **pages;
+	void *vmapping;
+};
+
+#define to_sdrm_bo(x) container_of(x, struct sdrm_gem_object, base)
+
+struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev,
+					      size_t size);
+struct drm_gem_object *sdrm_gem_prime_import(struct drm_device *ddev,
+					     struct dma_buf *dma_buf);
+void sdrm_gem_free_object(struct drm_gem_object *obj);
+int sdrm_gem_get_pages(struct sdrm_gem_object *obj);
+
+int sdrm_dumb_create(struct drm_file *file_priv, struct drm_device *ddev,
+		     struct drm_mode_create_dumb *arg);
+int sdrm_dumb_destroy(struct drm_file *file_priv, struct drm_device *ddev,
+		      uint32_t handle);
+int sdrm_dumb_map_offset(struct drm_file *file_priv, struct drm_device *ddev,
+			 uint32_t handle, uint64_t *offset);
+
+struct sdrm_framebuffer {
+	struct drm_framebuffer base;
+	struct sdrm_gem_object *obj;
+};
+
+#define to_sdrm_fb(x) container_of(x, struct sdrm_framebuffer, base)
+
+#endif /* SDRM_DRV_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..e87dac7
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_damage.c
@@ -0,0 +1,314 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/dma-buf.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.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_framebuffer *sfb, u32 x, u32 y,
+		      u32 width, u32 height)
+{
+	struct drm_framebuffer *fb = &sfb->base;
+	struct drm_device *ddev = fb->dev;
+	struct sdrm_device *sdrm = ddev->dev_private;
+	u32 src_bpp, dst_bpp, xoff, yoff, x2, y2;
+	u8 *src, *dst;
+
+	/* already unmapped; ongoing handover? */
+	if (!sdrm->fb_map)
+		return;
+
+	/* empty dirty-region, nothing to do */
+	if (!width || !height)
+		return;
+	if (x >= fb->width || y >= fb->height)
+		return;
+
+	/* sanity checks */
+	if (x + width < x)
+		width = fb->width - x;
+	if (y + height < y)
+		height = fb->height - y;
+
+	/* get scanout offsets */
+	xoff = 0;
+	yoff = 0;
+	if (sdrm->pipe.plane.fb == fb) {
+		xoff = sdrm->pipe.crtc.x;
+		yoff = sdrm->pipe.crtc.y;
+	}
+
+	/* get intersection of dirty region and scan-out region */
+	x2 = min(x + width, xoff + sdrm->fb_width);
+	y2 = min(y + height, yoff + sdrm->fb_height);
+	x = max(x, xoff);
+	y = max(y, yoff);
+	if (x2 <= x || y2 <= y)
+		return;
+	width = x2 - x;
+	height = y2 - y;
+
+	/* get buffer offsets */
+	src = sfb->obj->vmapping;
+	dst = sdrm->fb_map;
+
+	/* bo is guaranteed to be big enough; size checks not needed */
+	src_bpp = (fb->bits_per_pixel + 7) / 8;
+	src += fb->offsets[0] + y * fb->pitches[0] + x * src_bpp;
+
+	dst_bpp = (sdrm->fb_bpp + 7) / 8;
+	dst += (y - yoff) * sdrm->fb_stride + (x - xoff) * dst_bpp;
+
+	/* if formats are identical, do a line-by-line copy.. */
+	if (fb->pixel_format == sdrm->fb_format) {
+		sdrm_blit_lines(src, fb->pitches[0],
+				dst, sdrm->fb_stride,
+				src_bpp, width, height);
+		return;
+	}
+
+	/* ..otherwise call slow blit-function */
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_ARGB8888:
+		/* fallthrough */
+	case DRM_FORMAT_XRGB8888:
+		sdrm_blit_from_xrgb8888(src, fb->pitches[0], src_bpp,
+					dst, sdrm->fb_stride, dst_bpp,
+					sdrm->fb_format, width, height);
+		break;
+	case DRM_FORMAT_RGB565:
+		sdrm_blit_from_rgb565(src, fb->pitches[0], src_bpp,
+				      dst, sdrm->fb_stride, dst_bpp,
+				      sdrm->fb_format, width, height);
+		break;
+	}
+}
+
+static int sdrm_begin_access(struct sdrm_framebuffer *sfb)
+{
+	int r;
+
+	r = sdrm_gem_get_pages(sfb->obj);
+	if (r)
+		return r;
+
+	if (!sfb->obj->base.import_attach)
+		return 0;
+
+	return dma_buf_begin_cpu_access(sfb->obj->base.import_attach->dmabuf,
+					DMA_FROM_DEVICE);
+}
+
+static void sdrm_end_access(struct sdrm_framebuffer *sfb)
+{
+	if (!sfb->obj->base.import_attach)
+		return;
+
+	dma_buf_end_cpu_access(sfb->obj->base.import_attach->dmabuf,
+			       DMA_FROM_DEVICE);
+}
+
+int sdrm_dirty(struct drm_framebuffer *fb,
+	       struct drm_file *file,
+	       unsigned int flags, unsigned int color,
+	       struct drm_clip_rect *clips,
+	       unsigned int num_clips)
+{
+	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
+	struct drm_device *ddev = fb->dev;
+	struct sdrm_device *sdrm = ddev->dev_private;
+	struct drm_clip_rect full_clip;
+	unsigned int i;
+	int r;
+
+	if (!clips || !num_clips) {
+		full_clip.x1 = 0;
+		full_clip.x2 = fb->width;
+		full_clip.y1 = 0;
+		full_clip.y2 = fb->height;
+		clips = &full_clip;
+		num_clips = 1;
+	}
+
+	drm_modeset_lock_all(ddev);
+
+	if (sdrm->pipe.plane.fb != fb) {
+		r = 0;
+		goto unlock;
+	}
+
+	r = sdrm_begin_access(sfb);
+	if (r)
+		goto unlock;
+
+	for (i = 0; i < num_clips; i++) {
+		if (clips[i].x2 <= clips[i].x1 ||
+		    clips[i].y2 <= clips[i].y1)
+			continue;
+
+		sdrm_blit(sfb, clips[i].x1, clips[i].y1,
+			  clips[i].x2 - clips[i].x1,
+			  clips[i].y2 - clips[i].y1);
+	}
+
+	sdrm_end_access(sfb);
+
+unlock:
+	drm_modeset_unlock_all(ddev);
+	return 0;
+}
+
+int sdrm_dirty_all_locked(struct sdrm_device *sdrm)
+{
+	struct drm_framebuffer *fb;
+	struct sdrm_framebuffer *sfb;
+	int r;
+
+	fb = sdrm->pipe.plane.fb;
+	if (!fb)
+		return 0;
+
+	sfb = to_sdrm_fb(fb);
+	r = sdrm_begin_access(sfb);
+	if (r)
+		return r;
+
+	sdrm_blit(sfb, 0, 0, fb->width, fb->height);
+
+	sdrm_end_access(sfb);
+
+	return 0;
+}
+
+int sdrm_dirty_all_unlocked(struct sdrm_device *sdrm)
+{
+	int r;
+
+	drm_modeset_lock_all(sdrm->ddev);
+	r = sdrm_dirty_all_locked(sdrm);
+	drm_modeset_unlock_all(sdrm->ddev);
+
+	return r;
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
new file mode 100644
index 0000000..26b4ca9
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
@@ -0,0 +1,295 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include "simpledrm.h"
+
+static const struct file_operations sdrm_drm_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.mmap = sdrm_drm_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+	.unlocked_ioctl = drm_ioctl,
+	.release = drm_release,
+#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_PRIME |
+			   DRIVER_ATOMIC,
+	.fops = &sdrm_drm_fops,
+
+	.gem_free_object = sdrm_gem_free_object,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_prime_import = sdrm_gem_prime_import,
+
+	.dumb_create = sdrm_dumb_create,
+	.dumb_map_offset = sdrm_dumb_map_offset,
+	.dumb_destroy = sdrm_dumb_destroy,
+
+	.name = "simpledrm",
+	.desc = "Simple firmware framebuffer DRM driver",
+	.date = "20130601",
+	.major = 0,
+	.minor = 0,
+	.patchlevel = 1,
+};
+
+static int parse_dt(struct platform_device *pdev,
+		    struct simplefb_platform_data *mode)
+{
+	struct device_node *np = pdev->dev.of_node;
+	const char *format;
+	int ret;
+
+	if (!np)
+		return -ENODEV;
+
+	ret = of_property_read_u32(np, "width", &mode->width);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't parse width property\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "height", &mode->height);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't parse height property\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "stride", &mode->stride);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't parse stride property\n");
+		return ret;
+	}
+
+	ret = of_property_read_string(np, "format", &format);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't parse format property\n");
+		return ret;
+	}
+	mode->format = format;
+
+	return 0;
+}
+
+static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS;
+
+int sdrm_pdev_init(struct sdrm_device *sdrm)
+{
+	struct platform_device *pdev = sdrm->ddev->platformdev;
+	struct simplefb_platform_data *mode = pdev->dev.platform_data;
+	struct simplefb_platform_data pmode;
+	struct resource *mem;
+	unsigned int depth;
+	int ret, i, bpp;
+
+	if (!mode) {
+		mode = &pmode;
+		ret = parse_dt(pdev, mode);
+		if (ret)
+			return ret;
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(sdrm->ddev->dev, "No memory resource\n");
+		return -ENODEV;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(simplefb_formats); ++i) {
+		if (strcmp(mode->format, simplefb_formats[i].name))
+			continue;
+
+		sdrm->fb_sformat = &simplefb_formats[i];
+		sdrm->fb_format = simplefb_formats[i].fourcc;
+		sdrm->fb_width = mode->width;
+		sdrm->fb_height = mode->height;
+		sdrm->fb_stride = mode->stride;
+		sdrm->fb_base = mem->start;
+		sdrm->fb_size = resource_size(mem);
+		break;
+	}
+
+	if (i >= ARRAY_SIZE(simplefb_formats)) {
+		dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format);
+		return -ENODEV;
+	}
+
+	switch (sdrm->fb_format) {
+	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:
+		dev_err(sdrm->ddev->dev, "Unsupported format %s\n",
+			mode->format);
+		return -ENODEV;
+	}
+
+	drm_fb_get_bpp_depth(sdrm->fb_format, &depth, &bpp);
+	if (!bpp) {
+		dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format);
+		return -ENODEV;
+	}
+
+	if (sdrm->fb_size < sdrm->fb_stride * sdrm->fb_height) {
+		dev_err(sdrm->ddev->dev, "FB too small\n");
+		return -ENODEV;
+	} else if ((bpp + 7) / 8 * sdrm->fb_width > sdrm->fb_stride) {
+		dev_err(sdrm->ddev->dev, "Invalid stride\n");
+		return -ENODEV;
+	}
+
+	sdrm->fb_bpp = bpp;
+
+	sdrm->fb_map = ioremap_wc(sdrm->fb_base, sdrm->fb_size);
+	if (!sdrm->fb_map) {
+		dev_err(sdrm->ddev->dev, "cannot remap VMEM\n");
+		return -EIO;
+	}
+
+	DRM_DEBUG_KMS("format: %s\n", drm_get_format_name(sdrm->fb_format));
+
+	return 0;
+}
+
+void sdrm_pdev_destroy(struct sdrm_device *sdrm)
+{
+	if (sdrm->fb_map) {
+		iounmap(sdrm->fb_map);
+		sdrm->fb_map = NULL;
+	}
+}
+
+static int sdrm_simplefb_probe(struct platform_device *pdev)
+{
+	struct sdrm_device *sdrm;
+	struct drm_device *ddev;
+	int ret;
+
+	ddev = drm_dev_alloc(&sdrm_drm_driver, &pdev->dev);
+	if (!ddev)
+		return -ENOMEM;
+
+	sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL);
+	if (!sdrm)
+		goto err_free;
+
+	ddev->platformdev = pdev;
+	ddev->dev_private = sdrm;
+	sdrm->ddev = ddev;
+
+	ret = sdrm_pdev_init(sdrm);
+	if (ret)
+		goto err_free;
+
+	ret = sdrm_drm_modeset_init(sdrm);
+	if (ret)
+		goto err_destroy;
+
+	ret = drm_dev_register(ddev, 0);
+	if (ret)
+		goto err_cleanup;
+
+	platform_set_drvdata(pdev, ddev);
+
+	DRM_INFO("Initialized %s on minor %d\n", ddev->driver->name,
+		 ddev->primary->index);
+
+	return 0;
+
+err_cleanup:
+	drm_mode_config_cleanup(ddev);
+err_destroy:
+	sdrm_pdev_destroy(sdrm);
+err_free:
+	drm_dev_unref(ddev);
+	kfree(sdrm);
+
+	return ret;
+}
+
+static int sdrm_simplefb_remove(struct platform_device *pdev)
+{
+	struct drm_device *ddev = platform_get_drvdata(pdev);
+	struct sdrm_device *sdrm = ddev->dev_private;
+
+	drm_dev_unregister(ddev);
+	drm_mode_config_cleanup(ddev);
+
+	/* protect fb_map removal against sdrm_blit() */
+	drm_modeset_lock_all(ddev);
+	sdrm_pdev_destroy(sdrm);
+	drm_modeset_unlock_all(ddev);
+
+	drm_dev_unref(ddev);
+	kfree(sdrm);
+
+	return 0;
+}
+
+static const struct of_device_id simplefb_of_match[] = {
+	{ .compatible = "simple-framebuffer", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, 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 = simplefb_of_match,
+	},
+};
+
+static int __init sdrm_init(void)
+{
+	return platform_driver_register(&sdrm_simplefb_driver);
+}
+
+static void __exit sdrm_exit(void)
+{
+	platform_driver_unregister(&sdrm_simplefb_driver);
+}
+
+module_init(sdrm_init);
+module_exit(sdrm_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
+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..2d59632
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c
@@ -0,0 +1,276 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include <drm/drm_legacy.h>
+#include "simpledrm.h"
+
+int sdrm_gem_get_pages(struct sdrm_gem_object *obj)
+{
+	size_t num, i;
+
+	if (obj->vmapping)
+		return 0;
+
+	if (obj->base.import_attach) {
+		obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf);
+		return !obj->vmapping ? -ENOMEM : 0;
+	}
+
+	num = obj->base.size >> PAGE_SHIFT;
+	obj->pages = drm_malloc_ab(num, sizeof(*obj->pages));
+	if (!obj->pages)
+		return -ENOMEM;
+
+	for (i = 0; i < num; ++i) {
+		obj->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
+		if (!obj->pages[i])
+			goto error;
+	}
+
+	obj->vmapping = vmap(obj->pages, num, 0, PAGE_KERNEL);
+	if (!obj->vmapping)
+		goto error;
+
+	return 0;
+
+error:
+	while (i > 0)
+		__free_pages(obj->pages[--i], 0);
+
+	drm_free_large(obj->pages);
+	obj->pages = NULL;
+	return -ENOMEM;
+}
+
+static void sdrm_gem_put_pages(struct sdrm_gem_object *obj)
+{
+	size_t num, i;
+
+	if (!obj->vmapping)
+		return;
+
+	if (obj->base.import_attach) {
+		dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping);
+		obj->vmapping = NULL;
+		return;
+	}
+
+	vunmap(obj->vmapping);
+	obj->vmapping = NULL;
+
+	num = obj->base.size >> PAGE_SHIFT;
+	for (i = 0; i < num; ++i)
+		__free_pages(obj->pages[i], 0);
+
+	drm_free_large(obj->pages);
+	obj->pages = NULL;
+}
+
+struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev,
+					      size_t size)
+{
+	struct sdrm_gem_object *obj;
+
+	WARN_ON(!size || (size & ~PAGE_MASK) != 0);
+
+	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+	if (!obj)
+		return NULL;
+
+	drm_gem_private_object_init(ddev, &obj->base, size);
+	return obj;
+}
+
+void sdrm_gem_free_object(struct drm_gem_object *gobj)
+{
+	struct sdrm_gem_object *obj = to_sdrm_bo(gobj);
+	struct drm_device *ddev = gobj->dev;
+
+	if (obj->pages) {
+		/* kill all user-space mappings */
+		drm_vma_node_unmap(&gobj->vma_node,
+				   ddev->anon_inode->i_mapping);
+		sdrm_gem_put_pages(obj);
+	}
+
+	if (gobj->import_attach)
+		drm_prime_gem_destroy(gobj, obj->sg);
+
+	drm_gem_free_mmap_offset(gobj);
+	drm_gem_object_release(gobj);
+	kfree(obj);
+}
+
+int sdrm_dumb_create(struct drm_file *dfile, struct drm_device *ddev,
+		     struct drm_mode_create_dumb *args)
+{
+	struct sdrm_gem_object *obj;
+	int r;
+
+	if (args->flags)
+		return -EINVAL;
+
+	/* overflow checks are done by DRM core */
+	args->pitch = (args->bpp + 7) / 8 * args->width;
+	args->size = PAGE_ALIGN(args->pitch * args->height);
+
+	obj = sdrm_gem_alloc_object(ddev, args->size);
+	if (!obj)
+		return -ENOMEM;
+
+	r = drm_gem_handle_create(dfile, &obj->base, &args->handle);
+	if (r) {
+		drm_gem_object_unreference_unlocked(&obj->base);
+		return r;
+	}
+
+	/* handle owns a reference */
+	drm_gem_object_unreference_unlocked(&obj->base);
+	return 0;
+}
+
+int sdrm_dumb_destroy(struct drm_file *dfile, struct drm_device *ddev,
+		      uint32_t handle)
+{
+	return drm_gem_handle_delete(dfile, handle);
+}
+
+int sdrm_dumb_map_offset(struct drm_file *dfile, struct drm_device *ddev,
+			 uint32_t handle, uint64_t *offset)
+{
+	struct drm_gem_object *gobj;
+	int r;
+
+	mutex_lock(&ddev->struct_mutex);
+
+	gobj = drm_gem_object_lookup(dfile, handle);
+	if (!gobj) {
+		r = -ENOENT;
+		goto out_unlock;
+	}
+
+	r = drm_gem_create_mmap_offset(gobj);
+	if (r)
+		goto out_unref;
+
+	*offset = drm_vma_node_offset_addr(&gobj->vma_node);
+
+out_unref:
+	drm_gem_object_unreference(gobj);
+out_unlock:
+	mutex_unlock(&ddev->struct_mutex);
+	return r;
+}
+
+int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct drm_file *priv = filp->private_data;
+	struct drm_device *dev = priv->minor->dev;
+	struct drm_vma_offset_node *node;
+	struct drm_gem_object *gobj;
+	struct sdrm_gem_object *obj;
+	size_t size, i, num;
+	int r;
+
+	if (drm_device_is_unplugged(dev))
+		return -ENODEV;
+
+	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));
+	drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
+
+	if (!node)
+		return drm_legacy_mmap(filp, vma);
+	else if (!drm_vma_node_is_allowed(node, filp))
+		return -EACCES;
+
+	gobj = container_of(node, struct drm_gem_object, vma_node);
+	obj = to_sdrm_bo(gobj);
+	size = drm_vma_node_size(node) << PAGE_SHIFT;
+	if (size < vma->vm_end - vma->vm_start)
+		return r;
+
+	r = sdrm_gem_get_pages(obj);
+	if (r < 0)
+		return r;
+
+	/* prevent dmabuf-imported mmap to user-space */
+	if (!obj->pages)
+		return -EACCES;
+
+	vma->vm_flags |= VM_DONTEXPAND;
+	vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
+
+	num = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+	for (i = 0; i < num; ++i) {
+		r = vm_insert_page(vma, vma->vm_start + i * PAGE_SIZE,
+				   obj->pages[i]);
+		if (r < 0) {
+			if (i > 0)
+				zap_vma_ptes(vma, vma->vm_start, i * PAGE_SIZE);
+			return r;
+		}
+	}
+
+	return 0;
+}
+
+struct drm_gem_object *sdrm_gem_prime_import(struct drm_device *ddev,
+					     struct dma_buf *dma_buf)
+{
+	struct dma_buf_attachment *attach;
+	struct sdrm_gem_object *obj;
+	struct sg_table *sg;
+	int ret;
+
+	/* need to attach */
+	attach = dma_buf_attach(dma_buf, ddev->dev);
+	if (IS_ERR(attach))
+		return ERR_CAST(attach);
+
+	get_dma_buf(dma_buf);
+
+	sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+	if (IS_ERR(sg)) {
+		ret = PTR_ERR(sg);
+		goto fail_detach;
+	}
+
+	/*
+	 * dma_buf_vmap() gives us a page-aligned mapping, so lets bump the
+	 * size of the dma-buf to the next page-boundary
+	 */
+	obj = sdrm_gem_alloc_object(ddev, PAGE_ALIGN(dma_buf->size));
+	if (!obj) {
+		ret = -ENOMEM;
+		goto fail_unmap;
+	}
+
+	obj->sg = sg;
+	obj->base.import_attach = attach;
+
+	return &obj->base;
+
+fail_unmap:
+	dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL);
+fail_detach:
+	dma_buf_detach(dma_buf, attach);
+	dma_buf_put(dma_buf);
+	return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
new file mode 100644
index 0000000..6295a9f
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
@@ -0,0 +1,276 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.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->fb_width, sdrm->fb_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 inline struct sdrm_device *
+pipe_to_sdrm(struct drm_simple_display_pipe *pipe)
+{
+	return container_of(pipe, struct sdrm_device, pipe);
+}
+
+static int sdrm_display_pipe_check(struct drm_simple_display_pipe *pipe,
+				   struct drm_plane_state *plane_state,
+				   struct drm_crtc_state *crtc_state)
+{
+	struct drm_display_mode *mode = &crtc_state->mode;
+	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
+	struct drm_framebuffer *fb = plane_state->fb;
+	u32 x = plane_state->src_x >> 16;
+	u32 y = plane_state->src_y >> 16;
+
+	if (mode->hdisplay != sdrm->fb_width ||
+	    mode->vdisplay != sdrm->fb_height)
+		return -EINVAL;
+	if (fb->width <= x || fb->height <= y ||
+	    fb->width - x < sdrm->fb_width || fb->height - y < sdrm->fb_height)
+		return -EINVAL;
+
+	return 0;
+}
+
+void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
+			      struct drm_plane_state *plane_state)
+{
+	struct drm_framebuffer *fb = pipe->plane.state->fb;
+	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
+
+	if (fb)
+		sdrm_dirty_all_locked(sdrm);
+}
+
+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;
+	}
+}
+
+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 = {
+	.check = sdrm_display_pipe_check,
+	.update = sdrm_display_pipe_update,
+	.enable = sdrm_display_pipe_enable,
+	.disable = sdrm_display_pipe_disable,
+};
+
+static int sdrm_fb_create_handle(struct drm_framebuffer *fb,
+				 struct drm_file *dfile,
+				 unsigned int *handle)
+{
+	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
+
+	return drm_gem_handle_create(dfile, &sfb->obj->base, handle);
+}
+
+static void sdrm_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
+
+	drm_framebuffer_cleanup(fb);
+	drm_gem_object_unreference_unlocked(&sfb->obj->base);
+	kfree(sfb);
+}
+
+static const struct drm_framebuffer_funcs sdrm_fb_ops = {
+	.create_handle = sdrm_fb_create_handle,
+	.dirty = sdrm_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 sdrm_framebuffer *fb;
+	struct drm_gem_object *gobj;
+	u32 bpp, size;
+	int ret;
+	void *err;
+
+	if (cmd->flags)
+		return ERR_PTR(-EINVAL);
+
+	gobj = drm_gem_object_lookup(dfile, cmd->handles[0]);
+	if (!gobj)
+		return ERR_PTR(-EINVAL);
+
+	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+	if (!fb) {
+		err = ERR_PTR(-ENOMEM);
+		goto err_unref;
+	}
+	fb->obj = to_sdrm_bo(gobj);
+
+	fb->base.pitches[0] = cmd->pitches[0];
+	fb->base.offsets[0] = cmd->offsets[0];
+	fb->base.width = cmd->width;
+	fb->base.height = cmd->height;
+	fb->base.pixel_format = cmd->pixel_format;
+	drm_fb_get_bpp_depth(cmd->pixel_format, &fb->base.depth,
+			     &fb->base.bits_per_pixel);
+
+	/*
+	 * width/height are already clamped into min/max_width/height range,
+	 * so overflows are not possible
+	 */
+
+	bpp = (fb->base.bits_per_pixel + 7) / 8;
+	size = cmd->pitches[0] * cmd->height;
+	if (!bpp ||
+	    bpp > 4 ||
+	    cmd->pitches[0] < bpp * fb->base.width ||
+	    cmd->pitches[0] > 0xffffU ||
+	    size + fb->base.offsets[0] < size ||
+	    size + fb->base.offsets[0] > fb->obj->base.size) {
+		err = ERR_PTR(-EINVAL);
+		goto err_free;
+	}
+
+	ret = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops);
+	if (ret < 0) {
+		err = ERR_PTR(ret);
+		goto err_free;
+	}
+
+	DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id,
+		      drm_get_format_name(fb->base.pixel_format));
+
+	return &fb->base;
+
+err_free:
+	kfree(fb);
+err_unref:
+	drm_gem_object_unreference_unlocked(gobj);
+
+	return err;
+}
+
+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_drm_modeset_init(struct sdrm_device *sdrm)
+{
+	struct drm_connector *conn = &sdrm->conn;
+	struct drm_device *ddev = sdrm->ddev;
+	int ret;
+
+	drm_mode_config_init(ddev);
+	ddev->mode_config.min_width = 1;
+	ddev->mode_config.min_height = 1;
+	ddev->mode_config.max_width = 8192;
+	ddev->mode_config.max_height = 8192;
+	ddev->mode_config.preferred_depth = sdrm->fb_bpp;
+	ddev->mode_config.funcs = &sdrm_mode_config_ops;
+
+	drm_connector_helper_add(conn, &sdrm_conn_hfuncs);
+	ret = drm_connector_init(ddev, conn, &sdrm_conn_ops,
+				 DRM_MODE_CONNECTOR_VIRTUAL);
+	if (ret)
+		goto err_cleanup;
+
+	ret = drm_mode_create_dirty_info_property(ddev);
+	if (ret)
+		goto err_cleanup;
+
+	drm_object_attach_property(&conn->base,
+				   ddev->mode_config.dirty_info_property,
+				   DRM_MODE_DIRTY_ON);
+
+	ret = drm_simple_display_pipe_init(ddev, &sdrm->pipe, &sdrm_pipe_funcs,
+					   sdrm_formats,
+					   ARRAY_SIZE(sdrm_formats), conn);
+	if (ret)
+		goto err_cleanup;
+
+	drm_mode_config_reset(ddev);
+
+	return 0;
+
+err_cleanup:
+	drm_mode_config_cleanup(ddev);
+
+	return ret;
+}
--
2.8.2

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

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

* [PATCH 2/2] drm: simpledrm: add fbdev fallback support
  2016-08-04 14:03 ` Noralf Trønnes
@ 2016-08-04 14:03   ` Noralf Trønnes
  -1 siblings, 0 replies; 31+ messages in thread
From: Noralf Trønnes @ 2016-08-04 14:03 UTC (permalink / raw)
  To: dri-devel; +Cc: dh.herrmann, linux-kernel, Noralf Trønnes

Create a simple fbdev device during SimpleDRM setup so legacy user-space
and fbcon can use it.

Original work by David Herrmann.

Cc: dh.herrmann@gmail.com
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---

Changes from previous version:
- Remove the DRM_SIMPLEDRM_FBDEV kconfig option and use DRM_FBDEV_EMULATION
- Suspend fbcon/fbdev when the pipeline is enabled, resume in lastclose
- Add FBINFO_CAN_FORCE_OUTPUT flag so we get oops'es on the console

 drivers/gpu/drm/simpledrm/Kconfig           |   3 +
 drivers/gpu/drm/simpledrm/Makefile          |   1 +
 drivers/gpu/drm/simpledrm/simpledrm.h       |  24 +++++
 drivers/gpu/drm/simpledrm/simpledrm_drv.c   |   3 +
 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c | 160 ++++++++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_kms.c   |  12 +++
 6 files changed, 203 insertions(+)
 create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c

diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig
index f45b25d..9454536 100644
--- a/drivers/gpu/drm/simpledrm/Kconfig
+++ b/drivers/gpu/drm/simpledrm/Kconfig
@@ -13,6 +13,9 @@ config DRM_SIMPLEDRM
 	  SimpleDRM supports "simple-framebuffer" DeviceTree objects and
 	  compatible platform framebuffers.

+	  If fbdev support is enabled, this driver will also provide an fbdev
+	  compatibility layer.
+
 	  If unsure, say Y.

 	  To compile this driver as a module, choose M here: the
diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile
index f6a62dc..7087245 100644
--- a/drivers/gpu/drm/simpledrm/Makefile
+++ b/drivers/gpu/drm/simpledrm/Makefile
@@ -1,4 +1,5 @@
 simpledrm-y :=	simpledrm_drv.o simpledrm_kms.o simpledrm_gem.o \
 		simpledrm_damage.o
+simpledrm-$(CONFIG_DRM_FBDEV_EMULATION) += simpledrm_fbdev.o

 obj-$(CONFIG_DRM_SIMPLEDRM) := simpledrm.o
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h
index e841655..a1ed251 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm.h
+++ b/drivers/gpu/drm/simpledrm/simpledrm.h
@@ -30,6 +30,7 @@ struct sdrm_device {
 	struct drm_device *ddev;
 	struct drm_simple_display_pipe pipe;
 	struct drm_connector conn;
+	struct fb_info *fbdev;

 	/* framebuffer information */
 	const struct simplefb_format *fb_sformat;
@@ -43,6 +44,7 @@ struct sdrm_device {
 	void *fb_map;
 };

+void sdrm_lastclose(struct drm_device *ddev);
 int sdrm_drm_modeset_init(struct sdrm_device *sdrm);
 int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma);

@@ -84,4 +86,26 @@ struct sdrm_framebuffer {

 #define to_sdrm_fb(x) container_of(x, struct sdrm_framebuffer, base)

+#ifdef CONFIG_DRM_FBDEV_EMULATION
+
+void sdrm_fbdev_init(struct sdrm_device *sdrm);
+void sdrm_fbdev_cleanup(struct sdrm_device *sdrm);
+void sdrm_fbdev_set_suspend(struct sdrm_device *sdrm, int state);
+
+#else
+
+static inline void sdrm_fbdev_init(struct sdrm_device *sdrm)
+{
+}
+
+static inline void sdrm_fbdev_cleanup(struct sdrm_device *sdrm)
+{
+}
+
+static inline void sdrm_fbdev_set_suspend(struct sdrm_device *sdrm, int state)
+{
+}
+
+#endif
+
 #endif /* SDRM_DRV_H */
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
index 26b4ca9..5574fec 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm_drv.c
+++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
@@ -36,6 +36,7 @@ static struct drm_driver sdrm_drm_driver = {
 	.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
 			   DRIVER_ATOMIC,
 	.fops = &sdrm_drm_fops,
+	.lastclose = sdrm_lastclose,

 	.gem_free_object = sdrm_gem_free_object,
 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
@@ -223,6 +224,7 @@ static int sdrm_simplefb_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_cleanup;

+	sdrm_fbdev_init(ddev->dev_private);
 	platform_set_drvdata(pdev, ddev);

 	DRM_INFO("Initialized %s on minor %d\n", ddev->driver->name,
@@ -246,6 +248,7 @@ static int sdrm_simplefb_remove(struct platform_device *pdev)
 	struct drm_device *ddev = platform_get_drvdata(pdev);
 	struct sdrm_device *sdrm = ddev->dev_private;

+	sdrm_fbdev_cleanup(sdrm);
 	drm_dev_unregister(ddev);
 	drm_mode_config_cleanup(ddev);

diff --git a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
new file mode 100644
index 0000000..b83646b
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
@@ -0,0 +1,160 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * 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.
+ */
+
+/*
+ * fbdev compatibility layer
+ * We provide a basic fbdev device for the same framebuffer that is used for
+ * the pseudo CRTC.
+ */
+
+#include <linux/console.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include "simpledrm.h"
+
+struct sdrm_fbdev {
+	u32 palette[16];
+};
+
+static int sdrm_fbdev_setcolreg(u_int regno, u_int red, u_int green,
+				u_int blue, u_int transp, struct fb_info *info)
+{
+	u32 *pal = info->pseudo_palette;
+	u32 cr = red >> (16 - info->var.red.length);
+	u32 cg = green >> (16 - info->var.green.length);
+	u32 cb = blue >> (16 - info->var.blue.length);
+	u32 value;
+
+	if (regno >= 16)
+		return -EINVAL;
+
+	value = (cr << info->var.red.offset) |
+		(cg << info->var.green.offset) |
+		(cb << info->var.blue.offset);
+
+	if (info->var.transp.length > 0) {
+		u32 mask = (1 << info->var.transp.length) - 1;
+
+		mask <<= info->var.transp.offset;
+		value |= mask;
+	}
+
+	pal[regno] = value;
+
+	return 0;
+}
+
+static struct fb_ops sdrm_fbdev_ops = {
+	.owner		= THIS_MODULE,
+	.fb_setcolreg	= sdrm_fbdev_setcolreg,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+};
+
+void sdrm_fbdev_init(struct sdrm_device *sdrm)
+{
+	struct sdrm_fbdev *fb;
+	struct fb_info *info;
+	int ret;
+
+	if (fb_get_options("simpledrmfb", NULL))
+		return;
+
+	info = framebuffer_alloc(sizeof(struct sdrm_fbdev), sdrm->ddev->dev);
+	if (!info)
+		goto err_out;
+
+	fb = info->par;
+	info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE |
+		      FBINFO_CAN_FORCE_OUTPUT;
+	info->pseudo_palette = fb->palette;
+	info->fbops = &sdrm_fbdev_ops;
+	info->screen_base = sdrm->fb_map;
+
+	strncpy(info->fix.id, "simpledrmfb", 15);
+	info->fix.type = FB_TYPE_PACKED_PIXELS;
+	info->fix.visual = FB_VISUAL_TRUECOLOR;
+	info->fix.accel = FB_ACCEL_NONE;
+	info->fix.smem_start = (unsigned long)sdrm->fb_base;
+	info->fix.smem_len = sdrm->fb_size;
+	info->fix.line_length = sdrm->fb_stride;
+
+	info->var.activate = FB_ACTIVATE_NOW;
+	info->var.vmode = FB_VMODE_NONINTERLACED;
+	info->var.bits_per_pixel = sdrm->fb_bpp;
+	info->var.height = -1;
+	info->var.width = -1;
+	info->var.xres = sdrm->fb_width;
+	info->var.yres = sdrm->fb_height;
+	info->var.xres_virtual = info->var.xres;
+	info->var.yres_virtual = info->var.yres;
+	info->var.red = sdrm->fb_sformat->red;
+	info->var.green = sdrm->fb_sformat->green;
+	info->var.blue = sdrm->fb_sformat->blue;
+	info->var.transp = sdrm->fb_sformat->transp;
+
+	/* some dummy values for timing to make fbset happy */
+	info->var.pixclock = 10000000 / info->var.xres * 1000 / info->var.yres;
+	info->var.left_margin = (info->var.xres / 8) & 0xf8;
+	info->var.right_margin = 32;
+	info->var.upper_margin = 16;
+	info->var.lower_margin = 4;
+	info->var.hsync_len = (info->var.xres / 8) & 0xf8;
+	info->var.vsync_len = 4;
+
+	ret = register_framebuffer(info);
+	if (ret < 0)
+		goto err_free;
+
+	sdrm->fbdev = info;
+	dev_info(sdrm->ddev->dev, "fbdev frontend %s as fb%d\n",
+		 info->fix.id, info->node);
+
+	return;
+
+err_free:
+	framebuffer_release(info);
+err_out:
+	dev_warn(sdrm->ddev->dev, "cannot create fbdev frontend\n");
+}
+
+void sdrm_fbdev_cleanup(struct sdrm_device *sdrm)
+{
+	struct fb_info *info;
+
+	if (!sdrm->fbdev)
+		return;
+
+	info = sdrm->fbdev;
+	sdrm->fbdev = NULL;
+
+	dev_info(sdrm->ddev->dev, "remove fbdev frontend %s (fb%d)\n",
+		 info->fix.id, info->node);
+
+	if (unregister_framebuffer(info))
+		dev_err(sdrm->ddev->dev, "unregister_framebuffer() failed, leaking fw-fb\n");
+	else
+		framebuffer_release(info);
+}
+
+void sdrm_fbdev_set_suspend(struct sdrm_device *sdrm, int state)
+{
+	if (!sdrm->fbdev)
+		return;
+
+	console_lock();
+	fb_set_suspend(sdrm->fbdev, state);
+	console_unlock();
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
index 6295a9f..dd176f2 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm_kms.c
+++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
@@ -25,6 +25,14 @@ static const uint32_t sdrm_formats[] = {
 	DRM_FORMAT_XRGB8888,
 };

+void sdrm_lastclose(struct drm_device *ddev)
+{
+	struct sdrm_device *sdrm = ddev->dev_private;
+
+	/* resume fbcon/fbdev */
+	sdrm_fbdev_set_suspend(sdrm, 0);
+}
+
 static int sdrm_conn_get_modes(struct drm_connector *conn)
 {
 	struct sdrm_device *sdrm = conn->dev->dev_private;
@@ -118,7 +126,11 @@ static void sdrm_crtc_send_vblank_event(struct drm_crtc *crtc)
 static void sdrm_display_pipe_enable(struct drm_simple_display_pipe *pipe,
 				     struct drm_crtc_state *crtc_state)
 {
+	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
+
 	sdrm_crtc_send_vblank_event(&pipe->crtc);
+	/* suspend fbcon/fbdev */
+	sdrm_fbdev_set_suspend(sdrm, 1);
 }

 static void sdrm_display_pipe_disable(struct drm_simple_display_pipe *pipe)
--
2.8.2

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

* [PATCH 2/2] drm: simpledrm: add fbdev fallback support
@ 2016-08-04 14:03   ` Noralf Trønnes
  0 siblings, 0 replies; 31+ messages in thread
From: Noralf Trønnes @ 2016-08-04 14:03 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-kernel

Create a simple fbdev device during SimpleDRM setup so legacy user-space
and fbcon can use it.

Original work by David Herrmann.

Cc: dh.herrmann@gmail.com
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---

Changes from previous version:
- Remove the DRM_SIMPLEDRM_FBDEV kconfig option and use DRM_FBDEV_EMULATION
- Suspend fbcon/fbdev when the pipeline is enabled, resume in lastclose
- Add FBINFO_CAN_FORCE_OUTPUT flag so we get oops'es on the console

 drivers/gpu/drm/simpledrm/Kconfig           |   3 +
 drivers/gpu/drm/simpledrm/Makefile          |   1 +
 drivers/gpu/drm/simpledrm/simpledrm.h       |  24 +++++
 drivers/gpu/drm/simpledrm/simpledrm_drv.c   |   3 +
 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c | 160 ++++++++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_kms.c   |  12 +++
 6 files changed, 203 insertions(+)
 create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c

diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig
index f45b25d..9454536 100644
--- a/drivers/gpu/drm/simpledrm/Kconfig
+++ b/drivers/gpu/drm/simpledrm/Kconfig
@@ -13,6 +13,9 @@ config DRM_SIMPLEDRM
 	  SimpleDRM supports "simple-framebuffer" DeviceTree objects and
 	  compatible platform framebuffers.

+	  If fbdev support is enabled, this driver will also provide an fbdev
+	  compatibility layer.
+
 	  If unsure, say Y.

 	  To compile this driver as a module, choose M here: the
diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile
index f6a62dc..7087245 100644
--- a/drivers/gpu/drm/simpledrm/Makefile
+++ b/drivers/gpu/drm/simpledrm/Makefile
@@ -1,4 +1,5 @@
 simpledrm-y :=	simpledrm_drv.o simpledrm_kms.o simpledrm_gem.o \
 		simpledrm_damage.o
+simpledrm-$(CONFIG_DRM_FBDEV_EMULATION) += simpledrm_fbdev.o

 obj-$(CONFIG_DRM_SIMPLEDRM) := simpledrm.o
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h
index e841655..a1ed251 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm.h
+++ b/drivers/gpu/drm/simpledrm/simpledrm.h
@@ -30,6 +30,7 @@ struct sdrm_device {
 	struct drm_device *ddev;
 	struct drm_simple_display_pipe pipe;
 	struct drm_connector conn;
+	struct fb_info *fbdev;

 	/* framebuffer information */
 	const struct simplefb_format *fb_sformat;
@@ -43,6 +44,7 @@ struct sdrm_device {
 	void *fb_map;
 };

+void sdrm_lastclose(struct drm_device *ddev);
 int sdrm_drm_modeset_init(struct sdrm_device *sdrm);
 int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma);

@@ -84,4 +86,26 @@ struct sdrm_framebuffer {

 #define to_sdrm_fb(x) container_of(x, struct sdrm_framebuffer, base)

+#ifdef CONFIG_DRM_FBDEV_EMULATION
+
+void sdrm_fbdev_init(struct sdrm_device *sdrm);
+void sdrm_fbdev_cleanup(struct sdrm_device *sdrm);
+void sdrm_fbdev_set_suspend(struct sdrm_device *sdrm, int state);
+
+#else
+
+static inline void sdrm_fbdev_init(struct sdrm_device *sdrm)
+{
+}
+
+static inline void sdrm_fbdev_cleanup(struct sdrm_device *sdrm)
+{
+}
+
+static inline void sdrm_fbdev_set_suspend(struct sdrm_device *sdrm, int state)
+{
+}
+
+#endif
+
 #endif /* SDRM_DRV_H */
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
index 26b4ca9..5574fec 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm_drv.c
+++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
@@ -36,6 +36,7 @@ static struct drm_driver sdrm_drm_driver = {
 	.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
 			   DRIVER_ATOMIC,
 	.fops = &sdrm_drm_fops,
+	.lastclose = sdrm_lastclose,

 	.gem_free_object = sdrm_gem_free_object,
 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
@@ -223,6 +224,7 @@ static int sdrm_simplefb_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_cleanup;

+	sdrm_fbdev_init(ddev->dev_private);
 	platform_set_drvdata(pdev, ddev);

 	DRM_INFO("Initialized %s on minor %d\n", ddev->driver->name,
@@ -246,6 +248,7 @@ static int sdrm_simplefb_remove(struct platform_device *pdev)
 	struct drm_device *ddev = platform_get_drvdata(pdev);
 	struct sdrm_device *sdrm = ddev->dev_private;

+	sdrm_fbdev_cleanup(sdrm);
 	drm_dev_unregister(ddev);
 	drm_mode_config_cleanup(ddev);

diff --git a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
new file mode 100644
index 0000000..b83646b
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
@@ -0,0 +1,160 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * 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.
+ */
+
+/*
+ * fbdev compatibility layer
+ * We provide a basic fbdev device for the same framebuffer that is used for
+ * the pseudo CRTC.
+ */
+
+#include <linux/console.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include "simpledrm.h"
+
+struct sdrm_fbdev {
+	u32 palette[16];
+};
+
+static int sdrm_fbdev_setcolreg(u_int regno, u_int red, u_int green,
+				u_int blue, u_int transp, struct fb_info *info)
+{
+	u32 *pal = info->pseudo_palette;
+	u32 cr = red >> (16 - info->var.red.length);
+	u32 cg = green >> (16 - info->var.green.length);
+	u32 cb = blue >> (16 - info->var.blue.length);
+	u32 value;
+
+	if (regno >= 16)
+		return -EINVAL;
+
+	value = (cr << info->var.red.offset) |
+		(cg << info->var.green.offset) |
+		(cb << info->var.blue.offset);
+
+	if (info->var.transp.length > 0) {
+		u32 mask = (1 << info->var.transp.length) - 1;
+
+		mask <<= info->var.transp.offset;
+		value |= mask;
+	}
+
+	pal[regno] = value;
+
+	return 0;
+}
+
+static struct fb_ops sdrm_fbdev_ops = {
+	.owner		= THIS_MODULE,
+	.fb_setcolreg	= sdrm_fbdev_setcolreg,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+};
+
+void sdrm_fbdev_init(struct sdrm_device *sdrm)
+{
+	struct sdrm_fbdev *fb;
+	struct fb_info *info;
+	int ret;
+
+	if (fb_get_options("simpledrmfb", NULL))
+		return;
+
+	info = framebuffer_alloc(sizeof(struct sdrm_fbdev), sdrm->ddev->dev);
+	if (!info)
+		goto err_out;
+
+	fb = info->par;
+	info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE |
+		      FBINFO_CAN_FORCE_OUTPUT;
+	info->pseudo_palette = fb->palette;
+	info->fbops = &sdrm_fbdev_ops;
+	info->screen_base = sdrm->fb_map;
+
+	strncpy(info->fix.id, "simpledrmfb", 15);
+	info->fix.type = FB_TYPE_PACKED_PIXELS;
+	info->fix.visual = FB_VISUAL_TRUECOLOR;
+	info->fix.accel = FB_ACCEL_NONE;
+	info->fix.smem_start = (unsigned long)sdrm->fb_base;
+	info->fix.smem_len = sdrm->fb_size;
+	info->fix.line_length = sdrm->fb_stride;
+
+	info->var.activate = FB_ACTIVATE_NOW;
+	info->var.vmode = FB_VMODE_NONINTERLACED;
+	info->var.bits_per_pixel = sdrm->fb_bpp;
+	info->var.height = -1;
+	info->var.width = -1;
+	info->var.xres = sdrm->fb_width;
+	info->var.yres = sdrm->fb_height;
+	info->var.xres_virtual = info->var.xres;
+	info->var.yres_virtual = info->var.yres;
+	info->var.red = sdrm->fb_sformat->red;
+	info->var.green = sdrm->fb_sformat->green;
+	info->var.blue = sdrm->fb_sformat->blue;
+	info->var.transp = sdrm->fb_sformat->transp;
+
+	/* some dummy values for timing to make fbset happy */
+	info->var.pixclock = 10000000 / info->var.xres * 1000 / info->var.yres;
+	info->var.left_margin = (info->var.xres / 8) & 0xf8;
+	info->var.right_margin = 32;
+	info->var.upper_margin = 16;
+	info->var.lower_margin = 4;
+	info->var.hsync_len = (info->var.xres / 8) & 0xf8;
+	info->var.vsync_len = 4;
+
+	ret = register_framebuffer(info);
+	if (ret < 0)
+		goto err_free;
+
+	sdrm->fbdev = info;
+	dev_info(sdrm->ddev->dev, "fbdev frontend %s as fb%d\n",
+		 info->fix.id, info->node);
+
+	return;
+
+err_free:
+	framebuffer_release(info);
+err_out:
+	dev_warn(sdrm->ddev->dev, "cannot create fbdev frontend\n");
+}
+
+void sdrm_fbdev_cleanup(struct sdrm_device *sdrm)
+{
+	struct fb_info *info;
+
+	if (!sdrm->fbdev)
+		return;
+
+	info = sdrm->fbdev;
+	sdrm->fbdev = NULL;
+
+	dev_info(sdrm->ddev->dev, "remove fbdev frontend %s (fb%d)\n",
+		 info->fix.id, info->node);
+
+	if (unregister_framebuffer(info))
+		dev_err(sdrm->ddev->dev, "unregister_framebuffer() failed, leaking fw-fb\n");
+	else
+		framebuffer_release(info);
+}
+
+void sdrm_fbdev_set_suspend(struct sdrm_device *sdrm, int state)
+{
+	if (!sdrm->fbdev)
+		return;
+
+	console_lock();
+	fb_set_suspend(sdrm->fbdev, state);
+	console_unlock();
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
index 6295a9f..dd176f2 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm_kms.c
+++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
@@ -25,6 +25,14 @@ static const uint32_t sdrm_formats[] = {
 	DRM_FORMAT_XRGB8888,
 };

+void sdrm_lastclose(struct drm_device *ddev)
+{
+	struct sdrm_device *sdrm = ddev->dev_private;
+
+	/* resume fbcon/fbdev */
+	sdrm_fbdev_set_suspend(sdrm, 0);
+}
+
 static int sdrm_conn_get_modes(struct drm_connector *conn)
 {
 	struct sdrm_device *sdrm = conn->dev->dev_private;
@@ -118,7 +126,11 @@ static void sdrm_crtc_send_vblank_event(struct drm_crtc *crtc)
 static void sdrm_display_pipe_enable(struct drm_simple_display_pipe *pipe,
 				     struct drm_crtc_state *crtc_state)
 {
+	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
+
 	sdrm_crtc_send_vblank_event(&pipe->crtc);
+	/* suspend fbcon/fbdev */
+	sdrm_fbdev_set_suspend(sdrm, 1);
 }

 static void sdrm_display_pipe_disable(struct drm_simple_display_pipe *pipe)
--
2.8.2

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

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 14:03 ` Noralf Trønnes
@ 2016-08-04 14:15   ` Luc Verhaegen
  -1 siblings, 0 replies; 31+ messages in thread
From: Luc Verhaegen @ 2016-08-04 14:15 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: dri-devel, linux-kernel

On Thu, Aug 04, 2016 at 04:03:18PM +0200, Noralf Trønnes wrote:
> 
> I have tested simpledrm on a Raspberry Pi B+ with U-boot setting up the
> framebuffer and producing this node:
> 
>         framebuffer@1e887000 {
>                 compatible = "simple-framebuffer";
>                 reg = <0x1e887000 0x36c600>;
>                 format = "r5g6b5";
>                 width = <1824>;
>                 height = <984>;
>                 stride = <3648>;
>                 status = "okay";
>         };
> 
> I have only tested with fbcon and modetest (XR24,RG16).

Please do not make the same mistake as simplefb in making this purely a 
rpifb. Know that you will need some power and clock management for 
properly free devices that do not depend on a binary only RTOS which 
does _everything_ behind our backs.

Cfr this useless, endless and ridiculous discussion: 
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-August/279071.html

Luc Verhaegen.

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
@ 2016-08-04 14:15   ` Luc Verhaegen
  0 siblings, 0 replies; 31+ messages in thread
From: Luc Verhaegen @ 2016-08-04 14:15 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: linux-kernel, dri-devel

On Thu, Aug 04, 2016 at 04:03:18PM +0200, Noralf Trønnes wrote:
> 
> I have tested simpledrm on a Raspberry Pi B+ with U-boot setting up the
> framebuffer and producing this node:
> 
>         framebuffer@1e887000 {
>                 compatible = "simple-framebuffer";
>                 reg = <0x1e887000 0x36c600>;
>                 format = "r5g6b5";
>                 width = <1824>;
>                 height = <984>;
>                 stride = <3648>;
>                 status = "okay";
>         };
> 
> I have only tested with fbcon and modetest (XR24,RG16).

Please do not make the same mistake as simplefb in making this purely a 
rpifb. Know that you will need some power and clock management for 
properly free devices that do not depend on a binary only RTOS which 
does _everything_ behind our backs.

Cfr this useless, endless and ridiculous discussion: 
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-August/279071.html

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

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 14:03 ` Noralf Trønnes
                   ` (3 preceding siblings ...)
  (?)
@ 2016-08-04 14:36 ` Daniel Vetter
  2016-08-04 17:30   ` Noralf Trønnes
  -1 siblings, 1 reply; 31+ messages in thread
From: Daniel Vetter @ 2016-08-04 14:36 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: dri-devel, linux-kernel

On Thu, Aug 04, 2016 at 04:03:18PM +0200, Noralf Trønnes wrote:
> This patchset adds the simpledrm driver by David Herrmann based on a
> patchset[1] from 2014. That patchset also included patches for kicking
> out simpledrm by real drivers. I have stayed away from that since it
> involves another subsystem and I would probably be unable to answer any
> questions about the implementation.

Need David's input on this, but I think the force-removal of simpledrm
when other drivers take over is required. Otherwise hilarity can ensue.
If we leave this out for the inital merge then I think we need a really
big warning in Kconfig that if people aren't careful it could result in a
kaboom.

> I have done my best to bring simpledrm up to speed. However I was unable to
> loose drm_legacy_mmap() in sdrm_drm_mmap() since I don't understand much of
> the gem code.

You can just nuke drm_legacy_mmap and the if check, plus the drm_legacy.h
include. There should be no reason at all for that.

> I was left with some questions after doing this:
> 
> - Is there any reason why simpledrm can't use drm_gem_cma_helper?
>   One obvious difference is that of contiguous memory allocation:
> 
>   sdrm_gem_get_pages():
> 	obj->pages = drm_malloc_ab(num, sizeof(*obj->pages));
> 	for (i = 0; i < num; ++i) {
> 		obj->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
> 	obj->vmapping = vmap(obj->pages, num, 0, PAGE_KERNEL);
> 
>   drm_gem_cma_create():
>         cma_obj->vaddr = dma_alloc_wc(drm->dev, size, &cma_obj->paddr,
>                                       GFP_KERNEL | __GFP_NOWARN);

cma means contiguous memory allocator, that's pretty much the entire
point. Once you have that there's not much left really. Note that since
sdrm was submitted we gained some helpers for normal (shmem-backed) gem
buffers. I think we could extend those a bit to provide the basics for
dumb gem based buffers and use that in sdrm. Otoh there's probably only
udl which could benefit, so not sure this is worth it much. And mmap
helper, maybe.

> - Could we set this range to the actual width/height of the native framebuffer?
> 
>   sdrm_drm_modeset_init():
> 	ddev->mode_config.min_width = 1;
> 	ddev->mode_config.min_height = 1;
> 	ddev->mode_config.max_width = 8192;
> 	ddev->mode_config.max_height = 8192;

Probably a good idea if we don't support modeset changes with simpledrm.

> - Is there a usecase for offsetting the dumb buffer when blitted onto the
>   native buffer?
> 
>   sdrm_blit():
> 	/* get scanout offsets */
> 	xoff = 0;
> 	yoff = 0;
> 	if (sdrm->pipe.plane.fb == fb) {
> 		xoff = sdrm->pipe.crtc.x;
> 		yoff = sdrm->pipe.crtc.y;
> 	}

I don't think allowing the plane to be positioned is a good idea. And
since it's using the simple pipe helpers now that should be even
impossible.

> I have tested simpledrm on a Raspberry Pi B+ with U-boot setting up the
> framebuffer and producing this node:
> 
>         framebuffer@1e887000 {
>                 compatible = "simple-framebuffer";
>                 reg = <0x1e887000 0x36c600>;
>                 format = "r5g6b5";
>                 width = <1824>;
>                 height = <984>;
>                 stride = <3648>;
>                 status = "okay";
>         };
> 
> I have only tested with fbcon and modetest (XR24,RG16).

If you can, booting this on a vesa/uefi platform would be interesting too,
just to make sure.
-Daniel


> 
> 
> Noralf.
> 
> 
> Changes from previous version[2]:
> - Remove FB_SIMPLE=n dependency to avoid kconfig recursive error
> - Changed module name to match kconfig help text: sdrm -> simpledrm
> - Use drm_simple_display_pipe
> - Replace deprecated drm_platform_init()
> - sdrm_dumb_create(): drm_gem_object_unreference() -> *_unlocked()
> - sdrm_dumb_map_offset(): drm_gem_object_lookup() remove drm_device parameter
> - sdrm_drm_mmap() changes:
>   Remove struct_mutex locking
>   Add drm_vma_offset_{lock,unlock}_lookup()
>   drm_mmap() -> drm_legacy_mmap()
> - dma_buf_begin_cpu_access() doesn't require start and length anymore
> - Use drm_cvt_mode() instead of open coding a mode
> - Fix format conversion. In the intermediate step, store the 8/6/5 bit color
>   value in the upper part of the 16-bit color variable, not the lower.
> - Support clips == NULL in sdrm_dirty()
> - Set mode_config.preferred_depth
> - Attach mode_config.dirty_info_property to connector
> fbdev:
> - Remove the DRM_SIMPLEDRM_FBDEV kconfig option and use DRM_FBDEV_EMULATION
> - Suspend fbcon/fbdev when the pipeline is enabled, resume in lastclose
> - Add FBINFO_CAN_FORCE_OUTPUT flag so we get oops'es on the console
> 
> [1] https://lists.freedesktop.org/archives/dri-devel/2014-January/052584.html
> [2] https://lists.freedesktop.org/archives/dri-devel/2014-January/052594.html
> 
> 
> Further history:
> 
> [PATCH v4 0/6] SimpleDRM Driver
> https://lists.freedesktop.org/archives/dri-devel/2013-September/044638.html
> 
> [PATCH v2 00/14] Platform Framebuffers and SimpleDRM
> https://lists.freedesktop.org/archives/dri-devel/2013-July/041090.html
> 
> [RFC 0/6] SimpleDRM Driver (was: dvbe driver)
> https://lists.freedesktop.org/archives/dri-devel/2013-June/040386.html
> 
> [PATCH 0/9] System Framebuffer Bus (sysfb)
> https://lists.freedesktop.org/archives/dri-devel/2013-February/035013.html
> 
> 
> Noralf Trønnes (2):
>   drm: add SimpleDRM driver
>   drm: simpledrm: add fbdev fallback support
> 
>  drivers/gpu/drm/Kconfig                      |   2 +
>  drivers/gpu/drm/Makefile                     |   1 +
>  drivers/gpu/drm/simpledrm/Kconfig            |  22 ++
>  drivers/gpu/drm/simpledrm/Makefile           |   5 +
>  drivers/gpu/drm/simpledrm/simpledrm.h        | 111 ++++++++++
>  drivers/gpu/drm/simpledrm/simpledrm_damage.c | 314 +++++++++++++++++++++++++++
>  drivers/gpu/drm/simpledrm/simpledrm_drv.c    | 298 +++++++++++++++++++++++++
>  drivers/gpu/drm/simpledrm/simpledrm_fbdev.c  | 160 ++++++++++++++
>  drivers/gpu/drm/simpledrm/simpledrm_gem.c    | 276 +++++++++++++++++++++++
>  drivers/gpu/drm/simpledrm/simpledrm_kms.c    | 288 ++++++++++++++++++++++++
>  10 files changed, 1477 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_fbdev.c
>  create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_gem.c
>  create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_kms.c
> 
> --
> 2.8.2
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

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

* Re: [PATCH 1/2] drm: add SimpleDRM driver
  2016-08-04 14:03   ` Noralf Trønnes
@ 2016-08-04 14:45     ` Daniel Vetter
  -1 siblings, 0 replies; 31+ messages in thread
From: Daniel Vetter @ 2016-08-04 14:45 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: dri-devel, linux-kernel

On Thu, Aug 04, 2016 at 04:03:19PM +0200, Noralf Trønnes wrote:
> 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.
> 
> The driver was originally written by David Herrmann in 2014.
> My main contribution is to make use of drm_simple_kms_helper and
> rework the probe path to avoid use of the deprecated drm_platform_init()
> and drm_driver.{load,unload}().
> 
> Cc: dh.herrmann@gmail.com
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>

Scrolled through it, only spotted the below nit (plus removing the legacy
mmap stuff). But I'd like at least an ack from Dave.

> +static int sdrm_simplefb_probe(struct platform_device *pdev)
> +{
> +	struct sdrm_device *sdrm;
> +	struct drm_device *ddev;
> +	int ret;
> +
> +	ddev = drm_dev_alloc(&sdrm_drm_driver, &pdev->dev);
> +	if (!ddev)
> +		return -ENOMEM;
> +
> +	sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL);
> +	if (!sdrm)
> +		goto err_free;
> +
> +	ddev->platformdev = pdev;
> +	ddev->dev_private = sdrm;
> +	sdrm->ddev = ddev;
> +
> +	ret = sdrm_pdev_init(sdrm);
> +	if (ret)
> +		goto err_free;
> +
> +	ret = sdrm_drm_modeset_init(sdrm);
> +	if (ret)
> +		goto err_destroy;
> +
> +	ret = drm_dev_register(ddev, 0);
> +	if (ret)
> +		goto err_cleanup;

drm_dev_register needs to be last, after setting the drvdata.
-Daniel


> +
> +	platform_set_drvdata(pdev, ddev);
> +
> +	DRM_INFO("Initialized %s on minor %d\n", ddev->driver->name,
> +		 ddev->primary->index);
> +
> +	return 0;
> +
> +err_cleanup:
> +	drm_mode_config_cleanup(ddev);
> +err_destroy:
> +	sdrm_pdev_destroy(sdrm);
> +err_free:
> +	drm_dev_unref(ddev);
> +	kfree(sdrm);
> +
> +	return ret;
> +}
> +
> +static int sdrm_simplefb_remove(struct platform_device *pdev)
> +{
> +	struct drm_device *ddev = platform_get_drvdata(pdev);
> +	struct sdrm_device *sdrm = ddev->dev_private;
> +
> +	drm_dev_unregister(ddev);
> +	drm_mode_config_cleanup(ddev);
> +
> +	/* protect fb_map removal against sdrm_blit() */
> +	drm_modeset_lock_all(ddev);
> +	sdrm_pdev_destroy(sdrm);
> +	drm_modeset_unlock_all(ddev);
> +
> +	drm_dev_unref(ddev);
> +	kfree(sdrm);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id simplefb_of_match[] = {
> +	{ .compatible = "simple-framebuffer", },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, 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 = simplefb_of_match,
> +	},
> +};
> +
> +static int __init sdrm_init(void)
> +{
> +	return platform_driver_register(&sdrm_simplefb_driver);
> +}
> +
> +static void __exit sdrm_exit(void)
> +{
> +	platform_driver_unregister(&sdrm_simplefb_driver);
> +}
> +
> +module_init(sdrm_init);
> +module_exit(sdrm_exit);
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
> +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..2d59632
> --- /dev/null
> +++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c
> @@ -0,0 +1,276 @@
> +/*
> + * SimpleDRM firmware framebuffer driver
> + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the Free
> + * Software Foundation; either version 2 of the License, or (at your option)
> + * any later version.
> + */
> +
> +#include <linux/dma-buf.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/string.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_legacy.h>
> +#include "simpledrm.h"
> +
> +int sdrm_gem_get_pages(struct sdrm_gem_object *obj)
> +{
> +	size_t num, i;
> +
> +	if (obj->vmapping)
> +		return 0;
> +
> +	if (obj->base.import_attach) {
> +		obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf);
> +		return !obj->vmapping ? -ENOMEM : 0;
> +	}
> +
> +	num = obj->base.size >> PAGE_SHIFT;
> +	obj->pages = drm_malloc_ab(num, sizeof(*obj->pages));
> +	if (!obj->pages)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < num; ++i) {
> +		obj->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
> +		if (!obj->pages[i])
> +			goto error;
> +	}
> +
> +	obj->vmapping = vmap(obj->pages, num, 0, PAGE_KERNEL);
> +	if (!obj->vmapping)
> +		goto error;
> +
> +	return 0;
> +
> +error:
> +	while (i > 0)
> +		__free_pages(obj->pages[--i], 0);
> +
> +	drm_free_large(obj->pages);
> +	obj->pages = NULL;
> +	return -ENOMEM;
> +}
> +
> +static void sdrm_gem_put_pages(struct sdrm_gem_object *obj)
> +{
> +	size_t num, i;
> +
> +	if (!obj->vmapping)
> +		return;
> +
> +	if (obj->base.import_attach) {
> +		dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping);
> +		obj->vmapping = NULL;
> +		return;
> +	}
> +
> +	vunmap(obj->vmapping);
> +	obj->vmapping = NULL;
> +
> +	num = obj->base.size >> PAGE_SHIFT;
> +	for (i = 0; i < num; ++i)
> +		__free_pages(obj->pages[i], 0);
> +
> +	drm_free_large(obj->pages);
> +	obj->pages = NULL;
> +}
> +
> +struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev,
> +					      size_t size)
> +{
> +	struct sdrm_gem_object *obj;
> +
> +	WARN_ON(!size || (size & ~PAGE_MASK) != 0);
> +
> +	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
> +	if (!obj)
> +		return NULL;
> +
> +	drm_gem_private_object_init(ddev, &obj->base, size);
> +	return obj;
> +}
> +
> +void sdrm_gem_free_object(struct drm_gem_object *gobj)
> +{
> +	struct sdrm_gem_object *obj = to_sdrm_bo(gobj);
> +	struct drm_device *ddev = gobj->dev;
> +
> +	if (obj->pages) {
> +		/* kill all user-space mappings */
> +		drm_vma_node_unmap(&gobj->vma_node,
> +				   ddev->anon_inode->i_mapping);
> +		sdrm_gem_put_pages(obj);
> +	}
> +
> +	if (gobj->import_attach)
> +		drm_prime_gem_destroy(gobj, obj->sg);
> +
> +	drm_gem_free_mmap_offset(gobj);
> +	drm_gem_object_release(gobj);
> +	kfree(obj);
> +}
> +
> +int sdrm_dumb_create(struct drm_file *dfile, struct drm_device *ddev,
> +		     struct drm_mode_create_dumb *args)
> +{
> +	struct sdrm_gem_object *obj;
> +	int r;
> +
> +	if (args->flags)
> +		return -EINVAL;
> +
> +	/* overflow checks are done by DRM core */
> +	args->pitch = (args->bpp + 7) / 8 * args->width;
> +	args->size = PAGE_ALIGN(args->pitch * args->height);
> +
> +	obj = sdrm_gem_alloc_object(ddev, args->size);
> +	if (!obj)
> +		return -ENOMEM;
> +
> +	r = drm_gem_handle_create(dfile, &obj->base, &args->handle);
> +	if (r) {
> +		drm_gem_object_unreference_unlocked(&obj->base);
> +		return r;
> +	}
> +
> +	/* handle owns a reference */
> +	drm_gem_object_unreference_unlocked(&obj->base);
> +	return 0;
> +}
> +
> +int sdrm_dumb_destroy(struct drm_file *dfile, struct drm_device *ddev,
> +		      uint32_t handle)
> +{
> +	return drm_gem_handle_delete(dfile, handle);
> +}
> +
> +int sdrm_dumb_map_offset(struct drm_file *dfile, struct drm_device *ddev,
> +			 uint32_t handle, uint64_t *offset)
> +{
> +	struct drm_gem_object *gobj;
> +	int r;
> +
> +	mutex_lock(&ddev->struct_mutex);
> +
> +	gobj = drm_gem_object_lookup(dfile, handle);
> +	if (!gobj) {
> +		r = -ENOENT;
> +		goto out_unlock;
> +	}
> +
> +	r = drm_gem_create_mmap_offset(gobj);
> +	if (r)
> +		goto out_unref;
> +
> +	*offset = drm_vma_node_offset_addr(&gobj->vma_node);
> +
> +out_unref:
> +	drm_gem_object_unreference(gobj);
> +out_unlock:
> +	mutex_unlock(&ddev->struct_mutex);
> +	return r;
> +}
> +
> +int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> +	struct drm_file *priv = filp->private_data;
> +	struct drm_device *dev = priv->minor->dev;
> +	struct drm_vma_offset_node *node;
> +	struct drm_gem_object *gobj;
> +	struct sdrm_gem_object *obj;
> +	size_t size, i, num;
> +	int r;
> +
> +	if (drm_device_is_unplugged(dev))
> +		return -ENODEV;
> +
> +	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));
> +	drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
> +
> +	if (!node)
> +		return drm_legacy_mmap(filp, vma);
> +	else if (!drm_vma_node_is_allowed(node, filp))
> +		return -EACCES;
> +
> +	gobj = container_of(node, struct drm_gem_object, vma_node);
> +	obj = to_sdrm_bo(gobj);
> +	size = drm_vma_node_size(node) << PAGE_SHIFT;
> +	if (size < vma->vm_end - vma->vm_start)
> +		return r;
> +
> +	r = sdrm_gem_get_pages(obj);
> +	if (r < 0)
> +		return r;
> +
> +	/* prevent dmabuf-imported mmap to user-space */
> +	if (!obj->pages)
> +		return -EACCES;
> +
> +	vma->vm_flags |= VM_DONTEXPAND;
> +	vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
> +
> +	num = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
> +	for (i = 0; i < num; ++i) {
> +		r = vm_insert_page(vma, vma->vm_start + i * PAGE_SIZE,
> +				   obj->pages[i]);
> +		if (r < 0) {
> +			if (i > 0)
> +				zap_vma_ptes(vma, vma->vm_start, i * PAGE_SIZE);
> +			return r;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +struct drm_gem_object *sdrm_gem_prime_import(struct drm_device *ddev,
> +					     struct dma_buf *dma_buf)
> +{
> +	struct dma_buf_attachment *attach;
> +	struct sdrm_gem_object *obj;
> +	struct sg_table *sg;
> +	int ret;
> +
> +	/* need to attach */
> +	attach = dma_buf_attach(dma_buf, ddev->dev);
> +	if (IS_ERR(attach))
> +		return ERR_CAST(attach);
> +
> +	get_dma_buf(dma_buf);
> +
> +	sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
> +	if (IS_ERR(sg)) {
> +		ret = PTR_ERR(sg);
> +		goto fail_detach;
> +	}
> +
> +	/*
> +	 * dma_buf_vmap() gives us a page-aligned mapping, so lets bump the
> +	 * size of the dma-buf to the next page-boundary
> +	 */
> +	obj = sdrm_gem_alloc_object(ddev, PAGE_ALIGN(dma_buf->size));
> +	if (!obj) {
> +		ret = -ENOMEM;
> +		goto fail_unmap;
> +	}
> +
> +	obj->sg = sg;
> +	obj->base.import_attach = attach;
> +
> +	return &obj->base;
> +
> +fail_unmap:
> +	dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL);
> +fail_detach:
> +	dma_buf_detach(dma_buf, attach);
> +	dma_buf_put(dma_buf);
> +	return ERR_PTR(ret);
> +}
> diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
> new file mode 100644
> index 0000000..6295a9f
> --- /dev/null
> +++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
> @@ -0,0 +1,276 @@
> +/*
> + * SimpleDRM firmware framebuffer driver
> + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the Free
> + * Software Foundation; either version 2 of the License, or (at your option)
> + * any later version.
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/string.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.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->fb_width, sdrm->fb_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 inline struct sdrm_device *
> +pipe_to_sdrm(struct drm_simple_display_pipe *pipe)
> +{
> +	return container_of(pipe, struct sdrm_device, pipe);
> +}
> +
> +static int sdrm_display_pipe_check(struct drm_simple_display_pipe *pipe,
> +				   struct drm_plane_state *plane_state,
> +				   struct drm_crtc_state *crtc_state)
> +{
> +	struct drm_display_mode *mode = &crtc_state->mode;
> +	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
> +	struct drm_framebuffer *fb = plane_state->fb;
> +	u32 x = plane_state->src_x >> 16;
> +	u32 y = plane_state->src_y >> 16;
> +
> +	if (mode->hdisplay != sdrm->fb_width ||
> +	    mode->vdisplay != sdrm->fb_height)
> +		return -EINVAL;
> +	if (fb->width <= x || fb->height <= y ||
> +	    fb->width - x < sdrm->fb_width || fb->height - y < sdrm->fb_height)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
> +			      struct drm_plane_state *plane_state)
> +{
> +	struct drm_framebuffer *fb = pipe->plane.state->fb;
> +	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
> +
> +	if (fb)
> +		sdrm_dirty_all_locked(sdrm);
> +}
> +
> +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;
> +	}
> +}
> +
> +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 = {
> +	.check = sdrm_display_pipe_check,
> +	.update = sdrm_display_pipe_update,
> +	.enable = sdrm_display_pipe_enable,
> +	.disable = sdrm_display_pipe_disable,
> +};
> +
> +static int sdrm_fb_create_handle(struct drm_framebuffer *fb,
> +				 struct drm_file *dfile,
> +				 unsigned int *handle)
> +{
> +	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
> +
> +	return drm_gem_handle_create(dfile, &sfb->obj->base, handle);
> +}
> +
> +static void sdrm_fb_destroy(struct drm_framebuffer *fb)
> +{
> +	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
> +
> +	drm_framebuffer_cleanup(fb);
> +	drm_gem_object_unreference_unlocked(&sfb->obj->base);
> +	kfree(sfb);
> +}
> +
> +static const struct drm_framebuffer_funcs sdrm_fb_ops = {
> +	.create_handle = sdrm_fb_create_handle,
> +	.dirty = sdrm_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 sdrm_framebuffer *fb;
> +	struct drm_gem_object *gobj;
> +	u32 bpp, size;
> +	int ret;
> +	void *err;
> +
> +	if (cmd->flags)
> +		return ERR_PTR(-EINVAL);
> +
> +	gobj = drm_gem_object_lookup(dfile, cmd->handles[0]);
> +	if (!gobj)
> +		return ERR_PTR(-EINVAL);
> +
> +	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
> +	if (!fb) {
> +		err = ERR_PTR(-ENOMEM);
> +		goto err_unref;
> +	}
> +	fb->obj = to_sdrm_bo(gobj);
> +
> +	fb->base.pitches[0] = cmd->pitches[0];
> +	fb->base.offsets[0] = cmd->offsets[0];
> +	fb->base.width = cmd->width;
> +	fb->base.height = cmd->height;
> +	fb->base.pixel_format = cmd->pixel_format;
> +	drm_fb_get_bpp_depth(cmd->pixel_format, &fb->base.depth,
> +			     &fb->base.bits_per_pixel);
> +
> +	/*
> +	 * width/height are already clamped into min/max_width/height range,
> +	 * so overflows are not possible
> +	 */
> +
> +	bpp = (fb->base.bits_per_pixel + 7) / 8;
> +	size = cmd->pitches[0] * cmd->height;
> +	if (!bpp ||
> +	    bpp > 4 ||
> +	    cmd->pitches[0] < bpp * fb->base.width ||
> +	    cmd->pitches[0] > 0xffffU ||
> +	    size + fb->base.offsets[0] < size ||
> +	    size + fb->base.offsets[0] > fb->obj->base.size) {
> +		err = ERR_PTR(-EINVAL);
> +		goto err_free;
> +	}
> +
> +	ret = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops);
> +	if (ret < 0) {
> +		err = ERR_PTR(ret);
> +		goto err_free;
> +	}
> +
> +	DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id,
> +		      drm_get_format_name(fb->base.pixel_format));
> +
> +	return &fb->base;
> +
> +err_free:
> +	kfree(fb);
> +err_unref:
> +	drm_gem_object_unreference_unlocked(gobj);
> +
> +	return err;
> +}
> +
> +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_drm_modeset_init(struct sdrm_device *sdrm)
> +{
> +	struct drm_connector *conn = &sdrm->conn;
> +	struct drm_device *ddev = sdrm->ddev;
> +	int ret;
> +
> +	drm_mode_config_init(ddev);
> +	ddev->mode_config.min_width = 1;
> +	ddev->mode_config.min_height = 1;
> +	ddev->mode_config.max_width = 8192;
> +	ddev->mode_config.max_height = 8192;
> +	ddev->mode_config.preferred_depth = sdrm->fb_bpp;
> +	ddev->mode_config.funcs = &sdrm_mode_config_ops;
> +
> +	drm_connector_helper_add(conn, &sdrm_conn_hfuncs);
> +	ret = drm_connector_init(ddev, conn, &sdrm_conn_ops,
> +				 DRM_MODE_CONNECTOR_VIRTUAL);
> +	if (ret)
> +		goto err_cleanup;
> +
> +	ret = drm_mode_create_dirty_info_property(ddev);
> +	if (ret)
> +		goto err_cleanup;
> +
> +	drm_object_attach_property(&conn->base,
> +				   ddev->mode_config.dirty_info_property,
> +				   DRM_MODE_DIRTY_ON);
> +
> +	ret = drm_simple_display_pipe_init(ddev, &sdrm->pipe, &sdrm_pipe_funcs,
> +					   sdrm_formats,
> +					   ARRAY_SIZE(sdrm_formats), conn);
> +	if (ret)
> +		goto err_cleanup;
> +
> +	drm_mode_config_reset(ddev);
> +
> +	return 0;
> +
> +err_cleanup:
> +	drm_mode_config_cleanup(ddev);
> +
> +	return ret;
> +}
> --
> 2.8.2
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

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

* Re: [PATCH 1/2] drm: add SimpleDRM driver
@ 2016-08-04 14:45     ` Daniel Vetter
  0 siblings, 0 replies; 31+ messages in thread
From: Daniel Vetter @ 2016-08-04 14:45 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: linux-kernel, dri-devel

On Thu, Aug 04, 2016 at 04:03:19PM +0200, Noralf Trønnes wrote:
> 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.
> 
> The driver was originally written by David Herrmann in 2014.
> My main contribution is to make use of drm_simple_kms_helper and
> rework the probe path to avoid use of the deprecated drm_platform_init()
> and drm_driver.{load,unload}().
> 
> Cc: dh.herrmann@gmail.com
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>

Scrolled through it, only spotted the below nit (plus removing the legacy
mmap stuff). But I'd like at least an ack from Dave.

> +static int sdrm_simplefb_probe(struct platform_device *pdev)
> +{
> +	struct sdrm_device *sdrm;
> +	struct drm_device *ddev;
> +	int ret;
> +
> +	ddev = drm_dev_alloc(&sdrm_drm_driver, &pdev->dev);
> +	if (!ddev)
> +		return -ENOMEM;
> +
> +	sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL);
> +	if (!sdrm)
> +		goto err_free;
> +
> +	ddev->platformdev = pdev;
> +	ddev->dev_private = sdrm;
> +	sdrm->ddev = ddev;
> +
> +	ret = sdrm_pdev_init(sdrm);
> +	if (ret)
> +		goto err_free;
> +
> +	ret = sdrm_drm_modeset_init(sdrm);
> +	if (ret)
> +		goto err_destroy;
> +
> +	ret = drm_dev_register(ddev, 0);
> +	if (ret)
> +		goto err_cleanup;

drm_dev_register needs to be last, after setting the drvdata.
-Daniel


> +
> +	platform_set_drvdata(pdev, ddev);
> +
> +	DRM_INFO("Initialized %s on minor %d\n", ddev->driver->name,
> +		 ddev->primary->index);
> +
> +	return 0;
> +
> +err_cleanup:
> +	drm_mode_config_cleanup(ddev);
> +err_destroy:
> +	sdrm_pdev_destroy(sdrm);
> +err_free:
> +	drm_dev_unref(ddev);
> +	kfree(sdrm);
> +
> +	return ret;
> +}
> +
> +static int sdrm_simplefb_remove(struct platform_device *pdev)
> +{
> +	struct drm_device *ddev = platform_get_drvdata(pdev);
> +	struct sdrm_device *sdrm = ddev->dev_private;
> +
> +	drm_dev_unregister(ddev);
> +	drm_mode_config_cleanup(ddev);
> +
> +	/* protect fb_map removal against sdrm_blit() */
> +	drm_modeset_lock_all(ddev);
> +	sdrm_pdev_destroy(sdrm);
> +	drm_modeset_unlock_all(ddev);
> +
> +	drm_dev_unref(ddev);
> +	kfree(sdrm);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id simplefb_of_match[] = {
> +	{ .compatible = "simple-framebuffer", },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, 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 = simplefb_of_match,
> +	},
> +};
> +
> +static int __init sdrm_init(void)
> +{
> +	return platform_driver_register(&sdrm_simplefb_driver);
> +}
> +
> +static void __exit sdrm_exit(void)
> +{
> +	platform_driver_unregister(&sdrm_simplefb_driver);
> +}
> +
> +module_init(sdrm_init);
> +module_exit(sdrm_exit);
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
> +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..2d59632
> --- /dev/null
> +++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c
> @@ -0,0 +1,276 @@
> +/*
> + * SimpleDRM firmware framebuffer driver
> + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the Free
> + * Software Foundation; either version 2 of the License, or (at your option)
> + * any later version.
> + */
> +
> +#include <linux/dma-buf.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/string.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_legacy.h>
> +#include "simpledrm.h"
> +
> +int sdrm_gem_get_pages(struct sdrm_gem_object *obj)
> +{
> +	size_t num, i;
> +
> +	if (obj->vmapping)
> +		return 0;
> +
> +	if (obj->base.import_attach) {
> +		obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf);
> +		return !obj->vmapping ? -ENOMEM : 0;
> +	}
> +
> +	num = obj->base.size >> PAGE_SHIFT;
> +	obj->pages = drm_malloc_ab(num, sizeof(*obj->pages));
> +	if (!obj->pages)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < num; ++i) {
> +		obj->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
> +		if (!obj->pages[i])
> +			goto error;
> +	}
> +
> +	obj->vmapping = vmap(obj->pages, num, 0, PAGE_KERNEL);
> +	if (!obj->vmapping)
> +		goto error;
> +
> +	return 0;
> +
> +error:
> +	while (i > 0)
> +		__free_pages(obj->pages[--i], 0);
> +
> +	drm_free_large(obj->pages);
> +	obj->pages = NULL;
> +	return -ENOMEM;
> +}
> +
> +static void sdrm_gem_put_pages(struct sdrm_gem_object *obj)
> +{
> +	size_t num, i;
> +
> +	if (!obj->vmapping)
> +		return;
> +
> +	if (obj->base.import_attach) {
> +		dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping);
> +		obj->vmapping = NULL;
> +		return;
> +	}
> +
> +	vunmap(obj->vmapping);
> +	obj->vmapping = NULL;
> +
> +	num = obj->base.size >> PAGE_SHIFT;
> +	for (i = 0; i < num; ++i)
> +		__free_pages(obj->pages[i], 0);
> +
> +	drm_free_large(obj->pages);
> +	obj->pages = NULL;
> +}
> +
> +struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev,
> +					      size_t size)
> +{
> +	struct sdrm_gem_object *obj;
> +
> +	WARN_ON(!size || (size & ~PAGE_MASK) != 0);
> +
> +	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
> +	if (!obj)
> +		return NULL;
> +
> +	drm_gem_private_object_init(ddev, &obj->base, size);
> +	return obj;
> +}
> +
> +void sdrm_gem_free_object(struct drm_gem_object *gobj)
> +{
> +	struct sdrm_gem_object *obj = to_sdrm_bo(gobj);
> +	struct drm_device *ddev = gobj->dev;
> +
> +	if (obj->pages) {
> +		/* kill all user-space mappings */
> +		drm_vma_node_unmap(&gobj->vma_node,
> +				   ddev->anon_inode->i_mapping);
> +		sdrm_gem_put_pages(obj);
> +	}
> +
> +	if (gobj->import_attach)
> +		drm_prime_gem_destroy(gobj, obj->sg);
> +
> +	drm_gem_free_mmap_offset(gobj);
> +	drm_gem_object_release(gobj);
> +	kfree(obj);
> +}
> +
> +int sdrm_dumb_create(struct drm_file *dfile, struct drm_device *ddev,
> +		     struct drm_mode_create_dumb *args)
> +{
> +	struct sdrm_gem_object *obj;
> +	int r;
> +
> +	if (args->flags)
> +		return -EINVAL;
> +
> +	/* overflow checks are done by DRM core */
> +	args->pitch = (args->bpp + 7) / 8 * args->width;
> +	args->size = PAGE_ALIGN(args->pitch * args->height);
> +
> +	obj = sdrm_gem_alloc_object(ddev, args->size);
> +	if (!obj)
> +		return -ENOMEM;
> +
> +	r = drm_gem_handle_create(dfile, &obj->base, &args->handle);
> +	if (r) {
> +		drm_gem_object_unreference_unlocked(&obj->base);
> +		return r;
> +	}
> +
> +	/* handle owns a reference */
> +	drm_gem_object_unreference_unlocked(&obj->base);
> +	return 0;
> +}
> +
> +int sdrm_dumb_destroy(struct drm_file *dfile, struct drm_device *ddev,
> +		      uint32_t handle)
> +{
> +	return drm_gem_handle_delete(dfile, handle);
> +}
> +
> +int sdrm_dumb_map_offset(struct drm_file *dfile, struct drm_device *ddev,
> +			 uint32_t handle, uint64_t *offset)
> +{
> +	struct drm_gem_object *gobj;
> +	int r;
> +
> +	mutex_lock(&ddev->struct_mutex);
> +
> +	gobj = drm_gem_object_lookup(dfile, handle);
> +	if (!gobj) {
> +		r = -ENOENT;
> +		goto out_unlock;
> +	}
> +
> +	r = drm_gem_create_mmap_offset(gobj);
> +	if (r)
> +		goto out_unref;
> +
> +	*offset = drm_vma_node_offset_addr(&gobj->vma_node);
> +
> +out_unref:
> +	drm_gem_object_unreference(gobj);
> +out_unlock:
> +	mutex_unlock(&ddev->struct_mutex);
> +	return r;
> +}
> +
> +int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> +	struct drm_file *priv = filp->private_data;
> +	struct drm_device *dev = priv->minor->dev;
> +	struct drm_vma_offset_node *node;
> +	struct drm_gem_object *gobj;
> +	struct sdrm_gem_object *obj;
> +	size_t size, i, num;
> +	int r;
> +
> +	if (drm_device_is_unplugged(dev))
> +		return -ENODEV;
> +
> +	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));
> +	drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
> +
> +	if (!node)
> +		return drm_legacy_mmap(filp, vma);
> +	else if (!drm_vma_node_is_allowed(node, filp))
> +		return -EACCES;
> +
> +	gobj = container_of(node, struct drm_gem_object, vma_node);
> +	obj = to_sdrm_bo(gobj);
> +	size = drm_vma_node_size(node) << PAGE_SHIFT;
> +	if (size < vma->vm_end - vma->vm_start)
> +		return r;
> +
> +	r = sdrm_gem_get_pages(obj);
> +	if (r < 0)
> +		return r;
> +
> +	/* prevent dmabuf-imported mmap to user-space */
> +	if (!obj->pages)
> +		return -EACCES;
> +
> +	vma->vm_flags |= VM_DONTEXPAND;
> +	vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
> +
> +	num = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
> +	for (i = 0; i < num; ++i) {
> +		r = vm_insert_page(vma, vma->vm_start + i * PAGE_SIZE,
> +				   obj->pages[i]);
> +		if (r < 0) {
> +			if (i > 0)
> +				zap_vma_ptes(vma, vma->vm_start, i * PAGE_SIZE);
> +			return r;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +struct drm_gem_object *sdrm_gem_prime_import(struct drm_device *ddev,
> +					     struct dma_buf *dma_buf)
> +{
> +	struct dma_buf_attachment *attach;
> +	struct sdrm_gem_object *obj;
> +	struct sg_table *sg;
> +	int ret;
> +
> +	/* need to attach */
> +	attach = dma_buf_attach(dma_buf, ddev->dev);
> +	if (IS_ERR(attach))
> +		return ERR_CAST(attach);
> +
> +	get_dma_buf(dma_buf);
> +
> +	sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
> +	if (IS_ERR(sg)) {
> +		ret = PTR_ERR(sg);
> +		goto fail_detach;
> +	}
> +
> +	/*
> +	 * dma_buf_vmap() gives us a page-aligned mapping, so lets bump the
> +	 * size of the dma-buf to the next page-boundary
> +	 */
> +	obj = sdrm_gem_alloc_object(ddev, PAGE_ALIGN(dma_buf->size));
> +	if (!obj) {
> +		ret = -ENOMEM;
> +		goto fail_unmap;
> +	}
> +
> +	obj->sg = sg;
> +	obj->base.import_attach = attach;
> +
> +	return &obj->base;
> +
> +fail_unmap:
> +	dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL);
> +fail_detach:
> +	dma_buf_detach(dma_buf, attach);
> +	dma_buf_put(dma_buf);
> +	return ERR_PTR(ret);
> +}
> diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
> new file mode 100644
> index 0000000..6295a9f
> --- /dev/null
> +++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
> @@ -0,0 +1,276 @@
> +/*
> + * SimpleDRM firmware framebuffer driver
> + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the Free
> + * Software Foundation; either version 2 of the License, or (at your option)
> + * any later version.
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/string.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.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->fb_width, sdrm->fb_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 inline struct sdrm_device *
> +pipe_to_sdrm(struct drm_simple_display_pipe *pipe)
> +{
> +	return container_of(pipe, struct sdrm_device, pipe);
> +}
> +
> +static int sdrm_display_pipe_check(struct drm_simple_display_pipe *pipe,
> +				   struct drm_plane_state *plane_state,
> +				   struct drm_crtc_state *crtc_state)
> +{
> +	struct drm_display_mode *mode = &crtc_state->mode;
> +	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
> +	struct drm_framebuffer *fb = plane_state->fb;
> +	u32 x = plane_state->src_x >> 16;
> +	u32 y = plane_state->src_y >> 16;
> +
> +	if (mode->hdisplay != sdrm->fb_width ||
> +	    mode->vdisplay != sdrm->fb_height)
> +		return -EINVAL;
> +	if (fb->width <= x || fb->height <= y ||
> +	    fb->width - x < sdrm->fb_width || fb->height - y < sdrm->fb_height)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
> +			      struct drm_plane_state *plane_state)
> +{
> +	struct drm_framebuffer *fb = pipe->plane.state->fb;
> +	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
> +
> +	if (fb)
> +		sdrm_dirty_all_locked(sdrm);
> +}
> +
> +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;
> +	}
> +}
> +
> +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 = {
> +	.check = sdrm_display_pipe_check,
> +	.update = sdrm_display_pipe_update,
> +	.enable = sdrm_display_pipe_enable,
> +	.disable = sdrm_display_pipe_disable,
> +};
> +
> +static int sdrm_fb_create_handle(struct drm_framebuffer *fb,
> +				 struct drm_file *dfile,
> +				 unsigned int *handle)
> +{
> +	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
> +
> +	return drm_gem_handle_create(dfile, &sfb->obj->base, handle);
> +}
> +
> +static void sdrm_fb_destroy(struct drm_framebuffer *fb)
> +{
> +	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
> +
> +	drm_framebuffer_cleanup(fb);
> +	drm_gem_object_unreference_unlocked(&sfb->obj->base);
> +	kfree(sfb);
> +}
> +
> +static const struct drm_framebuffer_funcs sdrm_fb_ops = {
> +	.create_handle = sdrm_fb_create_handle,
> +	.dirty = sdrm_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 sdrm_framebuffer *fb;
> +	struct drm_gem_object *gobj;
> +	u32 bpp, size;
> +	int ret;
> +	void *err;
> +
> +	if (cmd->flags)
> +		return ERR_PTR(-EINVAL);
> +
> +	gobj = drm_gem_object_lookup(dfile, cmd->handles[0]);
> +	if (!gobj)
> +		return ERR_PTR(-EINVAL);
> +
> +	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
> +	if (!fb) {
> +		err = ERR_PTR(-ENOMEM);
> +		goto err_unref;
> +	}
> +	fb->obj = to_sdrm_bo(gobj);
> +
> +	fb->base.pitches[0] = cmd->pitches[0];
> +	fb->base.offsets[0] = cmd->offsets[0];
> +	fb->base.width = cmd->width;
> +	fb->base.height = cmd->height;
> +	fb->base.pixel_format = cmd->pixel_format;
> +	drm_fb_get_bpp_depth(cmd->pixel_format, &fb->base.depth,
> +			     &fb->base.bits_per_pixel);
> +
> +	/*
> +	 * width/height are already clamped into min/max_width/height range,
> +	 * so overflows are not possible
> +	 */
> +
> +	bpp = (fb->base.bits_per_pixel + 7) / 8;
> +	size = cmd->pitches[0] * cmd->height;
> +	if (!bpp ||
> +	    bpp > 4 ||
> +	    cmd->pitches[0] < bpp * fb->base.width ||
> +	    cmd->pitches[0] > 0xffffU ||
> +	    size + fb->base.offsets[0] < size ||
> +	    size + fb->base.offsets[0] > fb->obj->base.size) {
> +		err = ERR_PTR(-EINVAL);
> +		goto err_free;
> +	}
> +
> +	ret = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops);
> +	if (ret < 0) {
> +		err = ERR_PTR(ret);
> +		goto err_free;
> +	}
> +
> +	DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id,
> +		      drm_get_format_name(fb->base.pixel_format));
> +
> +	return &fb->base;
> +
> +err_free:
> +	kfree(fb);
> +err_unref:
> +	drm_gem_object_unreference_unlocked(gobj);
> +
> +	return err;
> +}
> +
> +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_drm_modeset_init(struct sdrm_device *sdrm)
> +{
> +	struct drm_connector *conn = &sdrm->conn;
> +	struct drm_device *ddev = sdrm->ddev;
> +	int ret;
> +
> +	drm_mode_config_init(ddev);
> +	ddev->mode_config.min_width = 1;
> +	ddev->mode_config.min_height = 1;
> +	ddev->mode_config.max_width = 8192;
> +	ddev->mode_config.max_height = 8192;
> +	ddev->mode_config.preferred_depth = sdrm->fb_bpp;
> +	ddev->mode_config.funcs = &sdrm_mode_config_ops;
> +
> +	drm_connector_helper_add(conn, &sdrm_conn_hfuncs);
> +	ret = drm_connector_init(ddev, conn, &sdrm_conn_ops,
> +				 DRM_MODE_CONNECTOR_VIRTUAL);
> +	if (ret)
> +		goto err_cleanup;
> +
> +	ret = drm_mode_create_dirty_info_property(ddev);
> +	if (ret)
> +		goto err_cleanup;
> +
> +	drm_object_attach_property(&conn->base,
> +				   ddev->mode_config.dirty_info_property,
> +				   DRM_MODE_DIRTY_ON);
> +
> +	ret = drm_simple_display_pipe_init(ddev, &sdrm->pipe, &sdrm_pipe_funcs,
> +					   sdrm_formats,
> +					   ARRAY_SIZE(sdrm_formats), conn);
> +	if (ret)
> +		goto err_cleanup;
> +
> +	drm_mode_config_reset(ddev);
> +
> +	return 0;
> +
> +err_cleanup:
> +	drm_mode_config_cleanup(ddev);
> +
> +	return ret;
> +}
> --
> 2.8.2
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 14:15   ` Luc Verhaegen
  (?)
@ 2016-08-04 15:08   ` Daniel Vetter
  2016-08-04 15:34     ` Luc Verhaegen
  2016-08-04 18:08       ` One Thousand Gnomes
  -1 siblings, 2 replies; 31+ messages in thread
From: Daniel Vetter @ 2016-08-04 15:08 UTC (permalink / raw)
  To: Luc Verhaegen; +Cc: Noralf Trønnes, linux-kernel, dri-devel

On Thu, Aug 04, 2016 at 04:15:25PM +0200, Luc Verhaegen wrote:
> On Thu, Aug 04, 2016 at 04:03:18PM +0200, Noralf Trønnes wrote:
> > 
> > I have tested simpledrm on a Raspberry Pi B+ with U-boot setting up the
> > framebuffer and producing this node:
> > 
> >         framebuffer@1e887000 {
> >                 compatible = "simple-framebuffer";
> >                 reg = <0x1e887000 0x36c600>;
> >                 format = "r5g6b5";
> >                 width = <1824>;
> >                 height = <984>;
> >                 stride = <3648>;
> >                 status = "okay";
> >         };
> > 
> > I have only tested with fbcon and modetest (XR24,RG16).
> 
> Please do not make the same mistake as simplefb in making this purely a 
> rpifb. Know that you will need some power and clock management for 
> properly free devices that do not depend on a binary only RTOS which 
> does _everything_ behind our backs.
> 
> Cfr this useless, endless and ridiculous discussion: 
> http://lists.infradead.org/pipermail/linux-arm-kernel/2014-August/279071.html

simpledrm isn't a real driver, but only meant to be used to drive the
firmware framebuffer in early boot until a real driver takes over. It's a
replacement really for all the various uefi/vesa/whatever fbdev drivers.
Full reliance on the firmware very much intended.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 15:08   ` Daniel Vetter
@ 2016-08-04 15:34     ` Luc Verhaegen
  2016-08-04 15:44         ` David Herrmann
  2016-08-04 18:08       ` One Thousand Gnomes
  1 sibling, 1 reply; 31+ messages in thread
From: Luc Verhaegen @ 2016-08-04 15:34 UTC (permalink / raw)
  To: Noralf Trønnes, linux-kernel, dri-devel

On Thu, Aug 04, 2016 at 05:08:43PM +0200, Daniel Vetter wrote:
> On Thu, Aug 04, 2016 at 04:15:25PM +0200, Luc Verhaegen wrote:
> > On Thu, Aug 04, 2016 at 04:03:18PM +0200, Noralf Trønnes wrote:
> > > 
> > > I have tested simpledrm on a Raspberry Pi B+ with U-boot setting up the
> > > framebuffer and producing this node:
> > > 
> > >         framebuffer@1e887000 {
> > >                 compatible = "simple-framebuffer";
> > >                 reg = <0x1e887000 0x36c600>;
> > >                 format = "r5g6b5";
> > >                 width = <1824>;
> > >                 height = <984>;
> > >                 stride = <3648>;
> > >                 status = "okay";
> > >         };
> > > 
> > > I have only tested with fbcon and modetest (XR24,RG16).
> > 
> > Please do not make the same mistake as simplefb in making this purely a 
> > rpifb. Know that you will need some power and clock management for 
> > properly free devices that do not depend on a binary only RTOS which 
> > does _everything_ behind our backs.
> > 
> > Cfr this useless, endless and ridiculous discussion: 
> > http://lists.infradead.org/pipermail/linux-arm-kernel/2014-August/279071.html
> 
> simpledrm isn't a real driver, but only meant to be used to drive the
> firmware framebuffer in early boot until a real driver takes over. It's a
> replacement really for all the various uefi/vesa/whatever fbdev drivers.
> Full reliance on the firmware very much intended.
> -Daniel

In the sunxi case, that firmware was u-boot, and the clocks were 
properly declared and then also properly disabled, which meant the 
display block got disabled.

Is simpledrm only an intermediate solution until the real driver is 
loaded? What stops it from providing a rudimentary display driver for 
the whole uptime of the machine? What stops the kernel from disabling 
the clocks while the supposed real driver is not fully loaded yet?

Do we really want to recreate a 400+ email thread again, or are we 
capable of learning from the past?

Luc Verhaegen.

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 15:34     ` Luc Verhaegen
@ 2016-08-04 15:44         ` David Herrmann
  0 siblings, 0 replies; 31+ messages in thread
From: David Herrmann @ 2016-08-04 15:44 UTC (permalink / raw)
  To: Luc Verhaegen; +Cc: Noralf Trønnes, linux-kernel, dri-devel

Hi

On Thu, Aug 4, 2016 at 5:34 PM, Luc Verhaegen <libv@skynet.be> wrote:
> Do we really want to recreate a 400+ email thread again, or are we
> capable of learning from the past?

No we don't. And no-one intends to. I am fully aware of the discussion
that introduced the clock-dependencies to simplefb, and I gladly
accept patches that add such support to SimpleDRM. Did anyone say
otherwise? This series adds initial support for the devices _we_ know
and can test (which is x86 and RPi, in my case). If someone wants
support for more devices, please send patches. Why does this have to
be included (or even discussed) as part of this submission?

Thanks
David

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
@ 2016-08-04 15:44         ` David Herrmann
  0 siblings, 0 replies; 31+ messages in thread
From: David Herrmann @ 2016-08-04 15:44 UTC (permalink / raw)
  To: Luc Verhaegen; +Cc: linux-kernel, dri-devel

Hi

On Thu, Aug 4, 2016 at 5:34 PM, Luc Verhaegen <libv@skynet.be> wrote:
> Do we really want to recreate a 400+ email thread again, or are we
> capable of learning from the past?

No we don't. And no-one intends to. I am fully aware of the discussion
that introduced the clock-dependencies to simplefb, and I gladly
accept patches that add such support to SimpleDRM. Did anyone say
otherwise? This series adds initial support for the devices _we_ know
and can test (which is x86 and RPi, in my case). If someone wants
support for more devices, please send patches. Why does this have to
be included (or even discussed) as part of this submission?

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

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 15:44         ` David Herrmann
  (?)
@ 2016-08-04 15:59         ` Luc Verhaegen
  2016-08-04 17:10           ` Daniel Vetter
  -1 siblings, 1 reply; 31+ messages in thread
From: Luc Verhaegen @ 2016-08-04 15:59 UTC (permalink / raw)
  To: David Herrmann; +Cc: Noralf Trønnes, linux-kernel, dri-devel

On Thu, Aug 04, 2016 at 05:44:23PM +0200, David Herrmann wrote:
> Hi
> 
> On Thu, Aug 4, 2016 at 5:34 PM, Luc Verhaegen <libv@skynet.be> wrote:
> > Do we really want to recreate a 400+ email thread again, or are we
> > capable of learning from the past?
> 
> No we don't. And no-one intends to. I am fully aware of the discussion
> that introduced the clock-dependencies to simplefb, and I gladly
> accept patches that add such support to SimpleDRM. Did anyone say
> otherwise? This series adds initial support for the devices _we_ know
> and can test (which is x86 and RPi, in my case). If someone wants
> support for more devices, please send patches. Why does this have to
> be included (or even discussed) as part of this submission?

You're right, it does not have to be included until there is a usecase.

But on the otherhand i have become pretty allergic to this as that other 
discussion was completely useless and pointless and stemmed only from 
the fact that people had been kidding themselves all along that simplefb 
was going to be simple and not would need anything extra.

If we can avoid fooling ourselves to the same extent as we did before, 
then putting this off until there is a real usecase is perfectly fine by 
me.

If not, just add this trivial generic addition straight from an existing 
example. 

Luc Verhaegen.

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 14:15   ` Luc Verhaegen
@ 2016-08-04 16:58     ` Noralf Trønnes
  -1 siblings, 0 replies; 31+ messages in thread
From: Noralf Trønnes @ 2016-08-04 16:58 UTC (permalink / raw)
  To: Luc Verhaegen; +Cc: dri-devel, linux-kernel


Den 04.08.2016 16:15, skrev Luc Verhaegen:
> On Thu, Aug 04, 2016 at 04:03:18PM +0200, Noralf Trønnes wrote:
>> I have tested simpledrm on a Raspberry Pi B+ with U-boot setting up the
>> framebuffer and producing this node:
>>
>>          framebuffer@1e887000 {
>>                  compatible = "simple-framebuffer";
>>                  reg = <0x1e887000 0x36c600>;
>>                  format = "r5g6b5";
>>                  width = <1824>;
>>                  height = <984>;
>>                  stride = <3648>;
>>                  status = "okay";
>>          };
>>
>> I have only tested with fbcon and modetest (XR24,RG16).
> Please do not make the same mistake as simplefb in making this purely a
> rpifb. Know that you will need some power and clock management for
> properly free devices that do not depend on a binary only RTOS which
> does _everything_ behind our backs.

I didn't read the binding document[1], which I should have done.
If simpledrm claims to be compatible with simple-framebuffer I assume it
should support the entire binding doc which includes clocks, regulators
and having the node under /chosen.
I will lift the necessary code from simplefb.c and put it in the next
version.

The binding doc also mentions an optional display phandle property, but I
can't find any reference to this in simplefb.c.


Noralf.

[1] Documentation/devicetree/bindings/display/simple-framebuffer.txt

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
@ 2016-08-04 16:58     ` Noralf Trønnes
  0 siblings, 0 replies; 31+ messages in thread
From: Noralf Trønnes @ 2016-08-04 16:58 UTC (permalink / raw)
  To: Luc Verhaegen; +Cc: linux-kernel, dri-devel


Den 04.08.2016 16:15, skrev Luc Verhaegen:
> On Thu, Aug 04, 2016 at 04:03:18PM +0200, Noralf Trønnes wrote:
>> I have tested simpledrm on a Raspberry Pi B+ with U-boot setting up the
>> framebuffer and producing this node:
>>
>>          framebuffer@1e887000 {
>>                  compatible = "simple-framebuffer";
>>                  reg = <0x1e887000 0x36c600>;
>>                  format = "r5g6b5";
>>                  width = <1824>;
>>                  height = <984>;
>>                  stride = <3648>;
>>                  status = "okay";
>>          };
>>
>> I have only tested with fbcon and modetest (XR24,RG16).
> Please do not make the same mistake as simplefb in making this purely a
> rpifb. Know that you will need some power and clock management for
> properly free devices that do not depend on a binary only RTOS which
> does _everything_ behind our backs.

I didn't read the binding document[1], which I should have done.
If simpledrm claims to be compatible with simple-framebuffer I assume it
should support the entire binding doc which includes clocks, regulators
and having the node under /chosen.
I will lift the necessary code from simplefb.c and put it in the next
version.

The binding doc also mentions an optional display phandle property, but I
can't find any reference to this in simplefb.c.


Noralf.

[1] Documentation/devicetree/bindings/display/simple-framebuffer.txt

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

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

* Re: [PATCH 1/2] drm: add SimpleDRM driver
  2016-08-04 14:03   ` Noralf Trønnes
  (?)
  (?)
@ 2016-08-04 17:05   ` Noralf Trønnes
  2016-08-04 17:12     ` Daniel Vetter
  -1 siblings, 1 reply; 31+ messages in thread
From: Noralf Trønnes @ 2016-08-04 17:05 UTC (permalink / raw)
  To: dri-devel; +Cc: dh.herrmann, linux-kernel


Den 04.08.2016 16:03, skrev Noralf Trønnes:
> 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.
>
> The driver was originally written by David Herrmann in 2014.
> My main contribution is to make use of drm_simple_kms_helper and
> rework the probe path to avoid use of the deprecated drm_platform_init()
> and drm_driver.{load,unload}().
>
> Cc: dh.herrmann@gmail.com
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---

[snip]

> 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"

FB_SIMPLE is bool, should we do the same here?

Another thing is that simplefb.c is registred at fs_initcall().
Should we do that?

Here's the commit message introducing it:

simplefb: Change simplefb_init from module_init to fs_initcall
One of the reasons for having the simplefb nodes in /chosen, and doing
explicit enumeration of the nodes there, is too allow enumerating them 
sooner,
so that we get a console earlier on.

Doing this earlier then fs_initcall is not useful, since the fb only turns
into a console when fbcon intializes, which is a fs_initcall too.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>


Noralf.

> +	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..f6a62dc
> --- /dev/null
> +++ b/drivers/gpu/drm/simpledrm/Makefile
> @@ -0,0 +1,4 @@
> +simpledrm-y :=	simpledrm_drv.o simpledrm_kms.o simpledrm_gem.o \
> +		simpledrm_damage.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..e841655
> --- /dev/null
> +++ b/drivers/gpu/drm/simpledrm/simpledrm.h
> @@ -0,0 +1,87 @@
> +/*
> + * SimpleDRM firmware framebuffer driver
> + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> + *
> + * 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.
> + */
> +
> +#ifndef SDRM_DRV_H
> +#define SDRM_DRV_H
> +
> +#include <linux/errno.h>
> +#include <linux/fb.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/platform_data/simplefb.h>
> +#include <linux/string.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_gem.h>
> +#include <drm/drm_simple_kms_helper.h>
> +
> +struct sdrm_device;
> +struct sdrm_gem_object;
> +struct sdrm_framebuffer;
> +
> +struct sdrm_device {
> +	struct drm_device *ddev;
> +	struct drm_simple_display_pipe pipe;
> +	struct drm_connector conn;
> +
> +	/* framebuffer information */
> +	const struct simplefb_format *fb_sformat;
> +	u32 fb_format;
> +	u32 fb_width;
> +	u32 fb_height;
> +	u32 fb_stride;
> +	u32 fb_bpp;
> +	unsigned long fb_base;
> +	unsigned long fb_size;
> +	void *fb_map;
> +};
> +
> +int sdrm_drm_modeset_init(struct sdrm_device *sdrm);
> +int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma);
> +
> +int sdrm_dirty(struct drm_framebuffer *fb,
> +	       struct drm_file *file,
> +	       unsigned int flags, unsigned int color,
> +	       struct drm_clip_rect *clips,
> +	       unsigned int num_clips);
> +int sdrm_dirty_all_locked(struct sdrm_device *sdrm);
> +int sdrm_dirty_all_unlocked(struct sdrm_device *sdrm);
> +
> +struct sdrm_gem_object {
> +	struct drm_gem_object base;
> +	struct sg_table *sg;
> +	struct page **pages;
> +	void *vmapping;
> +};
> +
> +#define to_sdrm_bo(x) container_of(x, struct sdrm_gem_object, base)
> +
> +struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev,
> +					      size_t size);
> +struct drm_gem_object *sdrm_gem_prime_import(struct drm_device *ddev,
> +					     struct dma_buf *dma_buf);
> +void sdrm_gem_free_object(struct drm_gem_object *obj);
> +int sdrm_gem_get_pages(struct sdrm_gem_object *obj);
> +
> +int sdrm_dumb_create(struct drm_file *file_priv, struct drm_device *ddev,
> +		     struct drm_mode_create_dumb *arg);
> +int sdrm_dumb_destroy(struct drm_file *file_priv, struct drm_device *ddev,
> +		      uint32_t handle);
> +int sdrm_dumb_map_offset(struct drm_file *file_priv, struct drm_device *ddev,
> +			 uint32_t handle, uint64_t *offset);
> +
> +struct sdrm_framebuffer {
> +	struct drm_framebuffer base;
> +	struct sdrm_gem_object *obj;
> +};
> +
> +#define to_sdrm_fb(x) container_of(x, struct sdrm_framebuffer, base)
> +
> +#endif /* SDRM_DRV_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..e87dac7
> --- /dev/null
> +++ b/drivers/gpu/drm/simpledrm/simpledrm_damage.c
> @@ -0,0 +1,314 @@
> +/*
> + * SimpleDRM firmware framebuffer driver
> + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the Free
> + * Software Foundation; either version 2 of the License, or (at your option)
> + * any later version.
> + */
> +
> +#include <asm/unaligned.h>
> +#include <linux/dma-buf.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/string.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.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_framebuffer *sfb, u32 x, u32 y,
> +		      u32 width, u32 height)
> +{
> +	struct drm_framebuffer *fb = &sfb->base;
> +	struct drm_device *ddev = fb->dev;
> +	struct sdrm_device *sdrm = ddev->dev_private;
> +	u32 src_bpp, dst_bpp, xoff, yoff, x2, y2;
> +	u8 *src, *dst;
> +
> +	/* already unmapped; ongoing handover? */
> +	if (!sdrm->fb_map)
> +		return;
> +
> +	/* empty dirty-region, nothing to do */
> +	if (!width || !height)
> +		return;
> +	if (x >= fb->width || y >= fb->height)
> +		return;
> +
> +	/* sanity checks */
> +	if (x + width < x)
> +		width = fb->width - x;
> +	if (y + height < y)
> +		height = fb->height - y;
> +
> +	/* get scanout offsets */
> +	xoff = 0;
> +	yoff = 0;
> +	if (sdrm->pipe.plane.fb == fb) {
> +		xoff = sdrm->pipe.crtc.x;
> +		yoff = sdrm->pipe.crtc.y;
> +	}
> +
> +	/* get intersection of dirty region and scan-out region */
> +	x2 = min(x + width, xoff + sdrm->fb_width);
> +	y2 = min(y + height, yoff + sdrm->fb_height);
> +	x = max(x, xoff);
> +	y = max(y, yoff);
> +	if (x2 <= x || y2 <= y)
> +		return;
> +	width = x2 - x;
> +	height = y2 - y;
> +
> +	/* get buffer offsets */
> +	src = sfb->obj->vmapping;
> +	dst = sdrm->fb_map;
> +
> +	/* bo is guaranteed to be big enough; size checks not needed */
> +	src_bpp = (fb->bits_per_pixel + 7) / 8;
> +	src += fb->offsets[0] + y * fb->pitches[0] + x * src_bpp;
> +
> +	dst_bpp = (sdrm->fb_bpp + 7) / 8;
> +	dst += (y - yoff) * sdrm->fb_stride + (x - xoff) * dst_bpp;
> +
> +	/* if formats are identical, do a line-by-line copy.. */
> +	if (fb->pixel_format == sdrm->fb_format) {
> +		sdrm_blit_lines(src, fb->pitches[0],
> +				dst, sdrm->fb_stride,
> +				src_bpp, width, height);
> +		return;
> +	}
> +
> +	/* ..otherwise call slow blit-function */
> +	switch (fb->pixel_format) {
> +	case DRM_FORMAT_ARGB8888:
> +		/* fallthrough */
> +	case DRM_FORMAT_XRGB8888:
> +		sdrm_blit_from_xrgb8888(src, fb->pitches[0], src_bpp,
> +					dst, sdrm->fb_stride, dst_bpp,
> +					sdrm->fb_format, width, height);
> +		break;
> +	case DRM_FORMAT_RGB565:
> +		sdrm_blit_from_rgb565(src, fb->pitches[0], src_bpp,
> +				      dst, sdrm->fb_stride, dst_bpp,
> +				      sdrm->fb_format, width, height);
> +		break;
> +	}
> +}
> +
> +static int sdrm_begin_access(struct sdrm_framebuffer *sfb)
> +{
> +	int r;
> +
> +	r = sdrm_gem_get_pages(sfb->obj);
> +	if (r)
> +		return r;
> +
> +	if (!sfb->obj->base.import_attach)
> +		return 0;
> +
> +	return dma_buf_begin_cpu_access(sfb->obj->base.import_attach->dmabuf,
> +					DMA_FROM_DEVICE);
> +}
> +
> +static void sdrm_end_access(struct sdrm_framebuffer *sfb)
> +{
> +	if (!sfb->obj->base.import_attach)
> +		return;
> +
> +	dma_buf_end_cpu_access(sfb->obj->base.import_attach->dmabuf,
> +			       DMA_FROM_DEVICE);
> +}
> +
> +int sdrm_dirty(struct drm_framebuffer *fb,
> +	       struct drm_file *file,
> +	       unsigned int flags, unsigned int color,
> +	       struct drm_clip_rect *clips,
> +	       unsigned int num_clips)
> +{
> +	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
> +	struct drm_device *ddev = fb->dev;
> +	struct sdrm_device *sdrm = ddev->dev_private;
> +	struct drm_clip_rect full_clip;
> +	unsigned int i;
> +	int r;
> +
> +	if (!clips || !num_clips) {
> +		full_clip.x1 = 0;
> +		full_clip.x2 = fb->width;
> +		full_clip.y1 = 0;
> +		full_clip.y2 = fb->height;
> +		clips = &full_clip;
> +		num_clips = 1;
> +	}
> +
> +	drm_modeset_lock_all(ddev);
> +
> +	if (sdrm->pipe.plane.fb != fb) {
> +		r = 0;
> +		goto unlock;
> +	}
> +
> +	r = sdrm_begin_access(sfb);
> +	if (r)
> +		goto unlock;
> +
> +	for (i = 0; i < num_clips; i++) {
> +		if (clips[i].x2 <= clips[i].x1 ||
> +		    clips[i].y2 <= clips[i].y1)
> +			continue;
> +
> +		sdrm_blit(sfb, clips[i].x1, clips[i].y1,
> +			  clips[i].x2 - clips[i].x1,
> +			  clips[i].y2 - clips[i].y1);
> +	}
> +
> +	sdrm_end_access(sfb);
> +
> +unlock:
> +	drm_modeset_unlock_all(ddev);
> +	return 0;
> +}
> +
> +int sdrm_dirty_all_locked(struct sdrm_device *sdrm)
> +{
> +	struct drm_framebuffer *fb;
> +	struct sdrm_framebuffer *sfb;
> +	int r;
> +
> +	fb = sdrm->pipe.plane.fb;
> +	if (!fb)
> +		return 0;
> +
> +	sfb = to_sdrm_fb(fb);
> +	r = sdrm_begin_access(sfb);
> +	if (r)
> +		return r;
> +
> +	sdrm_blit(sfb, 0, 0, fb->width, fb->height);
> +
> +	sdrm_end_access(sfb);
> +
> +	return 0;
> +}
> +
> +int sdrm_dirty_all_unlocked(struct sdrm_device *sdrm)
> +{
> +	int r;
> +
> +	drm_modeset_lock_all(sdrm->ddev);
> +	r = sdrm_dirty_all_locked(sdrm);
> +	drm_modeset_unlock_all(sdrm->ddev);
> +
> +	return r;
> +}
> diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
> new file mode 100644
> index 0000000..26b4ca9
> --- /dev/null
> +++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
> @@ -0,0 +1,295 @@
> +/*
> + * SimpleDRM firmware framebuffer driver
> + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the Free
> + * Software Foundation; either version 2 of the License, or (at your option)
> + * any later version.
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/platform_data/simplefb.h>
> +#include <linux/string.h>
> +#include <drm/drmP.h>
> +#include "simpledrm.h"
> +
> +static const struct file_operations sdrm_drm_fops = {
> +	.owner = THIS_MODULE,
> +	.open = drm_open,
> +	.mmap = sdrm_drm_mmap,
> +	.poll = drm_poll,
> +	.read = drm_read,
> +	.unlocked_ioctl = drm_ioctl,
> +	.release = drm_release,
> +#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_PRIME |
> +			   DRIVER_ATOMIC,
> +	.fops = &sdrm_drm_fops,
> +
> +	.gem_free_object = sdrm_gem_free_object,
> +	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> +	.gem_prime_import = sdrm_gem_prime_import,
> +
> +	.dumb_create = sdrm_dumb_create,
> +	.dumb_map_offset = sdrm_dumb_map_offset,
> +	.dumb_destroy = sdrm_dumb_destroy,
> +
> +	.name = "simpledrm",
> +	.desc = "Simple firmware framebuffer DRM driver",
> +	.date = "20130601",
> +	.major = 0,
> +	.minor = 0,
> +	.patchlevel = 1,
> +};
> +
> +static int parse_dt(struct platform_device *pdev,
> +		    struct simplefb_platform_data *mode)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	const char *format;
> +	int ret;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	ret = of_property_read_u32(np, "width", &mode->width);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Can't parse width property\n");
> +		return ret;
> +	}
> +
> +	ret = of_property_read_u32(np, "height", &mode->height);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Can't parse height property\n");
> +		return ret;
> +	}
> +
> +	ret = of_property_read_u32(np, "stride", &mode->stride);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Can't parse stride property\n");
> +		return ret;
> +	}
> +
> +	ret = of_property_read_string(np, "format", &format);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Can't parse format property\n");
> +		return ret;
> +	}
> +	mode->format = format;
> +
> +	return 0;
> +}
> +
> +static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS;
> +
> +int sdrm_pdev_init(struct sdrm_device *sdrm)
> +{
> +	struct platform_device *pdev = sdrm->ddev->platformdev;
> +	struct simplefb_platform_data *mode = pdev->dev.platform_data;
> +	struct simplefb_platform_data pmode;
> +	struct resource *mem;
> +	unsigned int depth;
> +	int ret, i, bpp;
> +
> +	if (!mode) {
> +		mode = &pmode;
> +		ret = parse_dt(pdev, mode);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!mem) {
> +		dev_err(sdrm->ddev->dev, "No memory resource\n");
> +		return -ENODEV;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(simplefb_formats); ++i) {
> +		if (strcmp(mode->format, simplefb_formats[i].name))
> +			continue;
> +
> +		sdrm->fb_sformat = &simplefb_formats[i];
> +		sdrm->fb_format = simplefb_formats[i].fourcc;
> +		sdrm->fb_width = mode->width;
> +		sdrm->fb_height = mode->height;
> +		sdrm->fb_stride = mode->stride;
> +		sdrm->fb_base = mem->start;
> +		sdrm->fb_size = resource_size(mem);
> +		break;
> +	}
> +
> +	if (i >= ARRAY_SIZE(simplefb_formats)) {
> +		dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format);
> +		return -ENODEV;
> +	}
> +
> +	switch (sdrm->fb_format) {
> +	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:
> +		dev_err(sdrm->ddev->dev, "Unsupported format %s\n",
> +			mode->format);
> +		return -ENODEV;
> +	}
> +
> +	drm_fb_get_bpp_depth(sdrm->fb_format, &depth, &bpp);
> +	if (!bpp) {
> +		dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format);
> +		return -ENODEV;
> +	}
> +
> +	if (sdrm->fb_size < sdrm->fb_stride * sdrm->fb_height) {
> +		dev_err(sdrm->ddev->dev, "FB too small\n");
> +		return -ENODEV;
> +	} else if ((bpp + 7) / 8 * sdrm->fb_width > sdrm->fb_stride) {
> +		dev_err(sdrm->ddev->dev, "Invalid stride\n");
> +		return -ENODEV;
> +	}
> +
> +	sdrm->fb_bpp = bpp;
> +
> +	sdrm->fb_map = ioremap_wc(sdrm->fb_base, sdrm->fb_size);
> +	if (!sdrm->fb_map) {
> +		dev_err(sdrm->ddev->dev, "cannot remap VMEM\n");
> +		return -EIO;
> +	}
> +
> +	DRM_DEBUG_KMS("format: %s\n", drm_get_format_name(sdrm->fb_format));
> +
> +	return 0;
> +}
> +
> +void sdrm_pdev_destroy(struct sdrm_device *sdrm)
> +{
> +	if (sdrm->fb_map) {
> +		iounmap(sdrm->fb_map);
> +		sdrm->fb_map = NULL;
> +	}
> +}
> +
> +static int sdrm_simplefb_probe(struct platform_device *pdev)
> +{
> +	struct sdrm_device *sdrm;
> +	struct drm_device *ddev;
> +	int ret;
> +
> +	ddev = drm_dev_alloc(&sdrm_drm_driver, &pdev->dev);
> +	if (!ddev)
> +		return -ENOMEM;
> +
> +	sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL);
> +	if (!sdrm)
> +		goto err_free;
> +
> +	ddev->platformdev = pdev;
> +	ddev->dev_private = sdrm;
> +	sdrm->ddev = ddev;
> +
> +	ret = sdrm_pdev_init(sdrm);
> +	if (ret)
> +		goto err_free;
> +
> +	ret = sdrm_drm_modeset_init(sdrm);
> +	if (ret)
> +		goto err_destroy;
> +
> +	ret = drm_dev_register(ddev, 0);
> +	if (ret)
> +		goto err_cleanup;
> +
> +	platform_set_drvdata(pdev, ddev);
> +
> +	DRM_INFO("Initialized %s on minor %d\n", ddev->driver->name,
> +		 ddev->primary->index);
> +
> +	return 0;
> +
> +err_cleanup:
> +	drm_mode_config_cleanup(ddev);
> +err_destroy:
> +	sdrm_pdev_destroy(sdrm);
> +err_free:
> +	drm_dev_unref(ddev);
> +	kfree(sdrm);
> +
> +	return ret;
> +}
> +
> +static int sdrm_simplefb_remove(struct platform_device *pdev)
> +{
> +	struct drm_device *ddev = platform_get_drvdata(pdev);
> +	struct sdrm_device *sdrm = ddev->dev_private;
> +
> +	drm_dev_unregister(ddev);
> +	drm_mode_config_cleanup(ddev);
> +
> +	/* protect fb_map removal against sdrm_blit() */
> +	drm_modeset_lock_all(ddev);
> +	sdrm_pdev_destroy(sdrm);
> +	drm_modeset_unlock_all(ddev);
> +
> +	drm_dev_unref(ddev);
> +	kfree(sdrm);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id simplefb_of_match[] = {
> +	{ .compatible = "simple-framebuffer", },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, 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 = simplefb_of_match,
> +	},
> +};
> +
> +static int __init sdrm_init(void)
> +{
> +	return platform_driver_register(&sdrm_simplefb_driver);
> +}
> +
> +static void __exit sdrm_exit(void)
> +{
> +	platform_driver_unregister(&sdrm_simplefb_driver);
> +}
> +
> +module_init(sdrm_init);
> +module_exit(sdrm_exit);
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
> +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..2d59632
> --- /dev/null
> +++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c
> @@ -0,0 +1,276 @@
> +/*
> + * SimpleDRM firmware framebuffer driver
> + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the Free
> + * Software Foundation; either version 2 of the License, or (at your option)
> + * any later version.
> + */
> +
> +#include <linux/dma-buf.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/string.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_legacy.h>
> +#include "simpledrm.h"
> +
> +int sdrm_gem_get_pages(struct sdrm_gem_object *obj)
> +{
> +	size_t num, i;
> +
> +	if (obj->vmapping)
> +		return 0;
> +
> +	if (obj->base.import_attach) {
> +		obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf);
> +		return !obj->vmapping ? -ENOMEM : 0;
> +	}
> +
> +	num = obj->base.size >> PAGE_SHIFT;
> +	obj->pages = drm_malloc_ab(num, sizeof(*obj->pages));
> +	if (!obj->pages)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < num; ++i) {
> +		obj->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
> +		if (!obj->pages[i])
> +			goto error;
> +	}
> +
> +	obj->vmapping = vmap(obj->pages, num, 0, PAGE_KERNEL);
> +	if (!obj->vmapping)
> +		goto error;
> +
> +	return 0;
> +
> +error:
> +	while (i > 0)
> +		__free_pages(obj->pages[--i], 0);
> +
> +	drm_free_large(obj->pages);
> +	obj->pages = NULL;
> +	return -ENOMEM;
> +}
> +
> +static void sdrm_gem_put_pages(struct sdrm_gem_object *obj)
> +{
> +	size_t num, i;
> +
> +	if (!obj->vmapping)
> +		return;
> +
> +	if (obj->base.import_attach) {
> +		dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping);
> +		obj->vmapping = NULL;
> +		return;
> +	}
> +
> +	vunmap(obj->vmapping);
> +	obj->vmapping = NULL;
> +
> +	num = obj->base.size >> PAGE_SHIFT;
> +	for (i = 0; i < num; ++i)
> +		__free_pages(obj->pages[i], 0);
> +
> +	drm_free_large(obj->pages);
> +	obj->pages = NULL;
> +}
> +
> +struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev,
> +					      size_t size)
> +{
> +	struct sdrm_gem_object *obj;
> +
> +	WARN_ON(!size || (size & ~PAGE_MASK) != 0);
> +
> +	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
> +	if (!obj)
> +		return NULL;
> +
> +	drm_gem_private_object_init(ddev, &obj->base, size);
> +	return obj;
> +}
> +
> +void sdrm_gem_free_object(struct drm_gem_object *gobj)
> +{
> +	struct sdrm_gem_object *obj = to_sdrm_bo(gobj);
> +	struct drm_device *ddev = gobj->dev;
> +
> +	if (obj->pages) {
> +		/* kill all user-space mappings */
> +		drm_vma_node_unmap(&gobj->vma_node,
> +				   ddev->anon_inode->i_mapping);
> +		sdrm_gem_put_pages(obj);
> +	}
> +
> +	if (gobj->import_attach)
> +		drm_prime_gem_destroy(gobj, obj->sg);
> +
> +	drm_gem_free_mmap_offset(gobj);
> +	drm_gem_object_release(gobj);
> +	kfree(obj);
> +}
> +
> +int sdrm_dumb_create(struct drm_file *dfile, struct drm_device *ddev,
> +		     struct drm_mode_create_dumb *args)
> +{
> +	struct sdrm_gem_object *obj;
> +	int r;
> +
> +	if (args->flags)
> +		return -EINVAL;
> +
> +	/* overflow checks are done by DRM core */
> +	args->pitch = (args->bpp + 7) / 8 * args->width;
> +	args->size = PAGE_ALIGN(args->pitch * args->height);
> +
> +	obj = sdrm_gem_alloc_object(ddev, args->size);
> +	if (!obj)
> +		return -ENOMEM;
> +
> +	r = drm_gem_handle_create(dfile, &obj->base, &args->handle);
> +	if (r) {
> +		drm_gem_object_unreference_unlocked(&obj->base);
> +		return r;
> +	}
> +
> +	/* handle owns a reference */
> +	drm_gem_object_unreference_unlocked(&obj->base);
> +	return 0;
> +}
> +
> +int sdrm_dumb_destroy(struct drm_file *dfile, struct drm_device *ddev,
> +		      uint32_t handle)
> +{
> +	return drm_gem_handle_delete(dfile, handle);
> +}
> +
> +int sdrm_dumb_map_offset(struct drm_file *dfile, struct drm_device *ddev,
> +			 uint32_t handle, uint64_t *offset)
> +{
> +	struct drm_gem_object *gobj;
> +	int r;
> +
> +	mutex_lock(&ddev->struct_mutex);
> +
> +	gobj = drm_gem_object_lookup(dfile, handle);
> +	if (!gobj) {
> +		r = -ENOENT;
> +		goto out_unlock;
> +	}
> +
> +	r = drm_gem_create_mmap_offset(gobj);
> +	if (r)
> +		goto out_unref;
> +
> +	*offset = drm_vma_node_offset_addr(&gobj->vma_node);
> +
> +out_unref:
> +	drm_gem_object_unreference(gobj);
> +out_unlock:
> +	mutex_unlock(&ddev->struct_mutex);
> +	return r;
> +}
> +
> +int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> +	struct drm_file *priv = filp->private_data;
> +	struct drm_device *dev = priv->minor->dev;
> +	struct drm_vma_offset_node *node;
> +	struct drm_gem_object *gobj;
> +	struct sdrm_gem_object *obj;
> +	size_t size, i, num;
> +	int r;
> +
> +	if (drm_device_is_unplugged(dev))
> +		return -ENODEV;
> +
> +	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));
> +	drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
> +
> +	if (!node)
> +		return drm_legacy_mmap(filp, vma);
> +	else if (!drm_vma_node_is_allowed(node, filp))
> +		return -EACCES;
> +
> +	gobj = container_of(node, struct drm_gem_object, vma_node);
> +	obj = to_sdrm_bo(gobj);
> +	size = drm_vma_node_size(node) << PAGE_SHIFT;
> +	if (size < vma->vm_end - vma->vm_start)
> +		return r;
> +
> +	r = sdrm_gem_get_pages(obj);
> +	if (r < 0)
> +		return r;
> +
> +	/* prevent dmabuf-imported mmap to user-space */
> +	if (!obj->pages)
> +		return -EACCES;
> +
> +	vma->vm_flags |= VM_DONTEXPAND;
> +	vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
> +
> +	num = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
> +	for (i = 0; i < num; ++i) {
> +		r = vm_insert_page(vma, vma->vm_start + i * PAGE_SIZE,
> +				   obj->pages[i]);
> +		if (r < 0) {
> +			if (i > 0)
> +				zap_vma_ptes(vma, vma->vm_start, i * PAGE_SIZE);
> +			return r;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +struct drm_gem_object *sdrm_gem_prime_import(struct drm_device *ddev,
> +					     struct dma_buf *dma_buf)
> +{
> +	struct dma_buf_attachment *attach;
> +	struct sdrm_gem_object *obj;
> +	struct sg_table *sg;
> +	int ret;
> +
> +	/* need to attach */
> +	attach = dma_buf_attach(dma_buf, ddev->dev);
> +	if (IS_ERR(attach))
> +		return ERR_CAST(attach);
> +
> +	get_dma_buf(dma_buf);
> +
> +	sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
> +	if (IS_ERR(sg)) {
> +		ret = PTR_ERR(sg);
> +		goto fail_detach;
> +	}
> +
> +	/*
> +	 * dma_buf_vmap() gives us a page-aligned mapping, so lets bump the
> +	 * size of the dma-buf to the next page-boundary
> +	 */
> +	obj = sdrm_gem_alloc_object(ddev, PAGE_ALIGN(dma_buf->size));
> +	if (!obj) {
> +		ret = -ENOMEM;
> +		goto fail_unmap;
> +	}
> +
> +	obj->sg = sg;
> +	obj->base.import_attach = attach;
> +
> +	return &obj->base;
> +
> +fail_unmap:
> +	dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL);
> +fail_detach:
> +	dma_buf_detach(dma_buf, attach);
> +	dma_buf_put(dma_buf);
> +	return ERR_PTR(ret);
> +}
> diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
> new file mode 100644
> index 0000000..6295a9f
> --- /dev/null
> +++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
> @@ -0,0 +1,276 @@
> +/*
> + * SimpleDRM firmware framebuffer driver
> + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the Free
> + * Software Foundation; either version 2 of the License, or (at your option)
> + * any later version.
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/string.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.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->fb_width, sdrm->fb_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 inline struct sdrm_device *
> +pipe_to_sdrm(struct drm_simple_display_pipe *pipe)
> +{
> +	return container_of(pipe, struct sdrm_device, pipe);
> +}
> +
> +static int sdrm_display_pipe_check(struct drm_simple_display_pipe *pipe,
> +				   struct drm_plane_state *plane_state,
> +				   struct drm_crtc_state *crtc_state)
> +{
> +	struct drm_display_mode *mode = &crtc_state->mode;
> +	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
> +	struct drm_framebuffer *fb = plane_state->fb;
> +	u32 x = plane_state->src_x >> 16;
> +	u32 y = plane_state->src_y >> 16;
> +
> +	if (mode->hdisplay != sdrm->fb_width ||
> +	    mode->vdisplay != sdrm->fb_height)
> +		return -EINVAL;
> +	if (fb->width <= x || fb->height <= y ||
> +	    fb->width - x < sdrm->fb_width || fb->height - y < sdrm->fb_height)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
> +			      struct drm_plane_state *plane_state)
> +{
> +	struct drm_framebuffer *fb = pipe->plane.state->fb;
> +	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
> +
> +	if (fb)
> +		sdrm_dirty_all_locked(sdrm);
> +}
> +
> +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;
> +	}
> +}
> +
> +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 = {
> +	.check = sdrm_display_pipe_check,
> +	.update = sdrm_display_pipe_update,
> +	.enable = sdrm_display_pipe_enable,
> +	.disable = sdrm_display_pipe_disable,
> +};
> +
> +static int sdrm_fb_create_handle(struct drm_framebuffer *fb,
> +				 struct drm_file *dfile,
> +				 unsigned int *handle)
> +{
> +	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
> +
> +	return drm_gem_handle_create(dfile, &sfb->obj->base, handle);
> +}
> +
> +static void sdrm_fb_destroy(struct drm_framebuffer *fb)
> +{
> +	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
> +
> +	drm_framebuffer_cleanup(fb);
> +	drm_gem_object_unreference_unlocked(&sfb->obj->base);
> +	kfree(sfb);
> +}
> +
> +static const struct drm_framebuffer_funcs sdrm_fb_ops = {
> +	.create_handle = sdrm_fb_create_handle,
> +	.dirty = sdrm_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 sdrm_framebuffer *fb;
> +	struct drm_gem_object *gobj;
> +	u32 bpp, size;
> +	int ret;
> +	void *err;
> +
> +	if (cmd->flags)
> +		return ERR_PTR(-EINVAL);
> +
> +	gobj = drm_gem_object_lookup(dfile, cmd->handles[0]);
> +	if (!gobj)
> +		return ERR_PTR(-EINVAL);
> +
> +	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
> +	if (!fb) {
> +		err = ERR_PTR(-ENOMEM);
> +		goto err_unref;
> +	}
> +	fb->obj = to_sdrm_bo(gobj);
> +
> +	fb->base.pitches[0] = cmd->pitches[0];
> +	fb->base.offsets[0] = cmd->offsets[0];
> +	fb->base.width = cmd->width;
> +	fb->base.height = cmd->height;
> +	fb->base.pixel_format = cmd->pixel_format;
> +	drm_fb_get_bpp_depth(cmd->pixel_format, &fb->base.depth,
> +			     &fb->base.bits_per_pixel);
> +
> +	/*
> +	 * width/height are already clamped into min/max_width/height range,
> +	 * so overflows are not possible
> +	 */
> +
> +	bpp = (fb->base.bits_per_pixel + 7) / 8;
> +	size = cmd->pitches[0] * cmd->height;
> +	if (!bpp ||
> +	    bpp > 4 ||
> +	    cmd->pitches[0] < bpp * fb->base.width ||
> +	    cmd->pitches[0] > 0xffffU ||
> +	    size + fb->base.offsets[0] < size ||
> +	    size + fb->base.offsets[0] > fb->obj->base.size) {
> +		err = ERR_PTR(-EINVAL);
> +		goto err_free;
> +	}
> +
> +	ret = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops);
> +	if (ret < 0) {
> +		err = ERR_PTR(ret);
> +		goto err_free;
> +	}
> +
> +	DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id,
> +		      drm_get_format_name(fb->base.pixel_format));
> +
> +	return &fb->base;
> +
> +err_free:
> +	kfree(fb);
> +err_unref:
> +	drm_gem_object_unreference_unlocked(gobj);
> +
> +	return err;
> +}
> +
> +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_drm_modeset_init(struct sdrm_device *sdrm)
> +{
> +	struct drm_connector *conn = &sdrm->conn;
> +	struct drm_device *ddev = sdrm->ddev;
> +	int ret;
> +
> +	drm_mode_config_init(ddev);
> +	ddev->mode_config.min_width = 1;
> +	ddev->mode_config.min_height = 1;
> +	ddev->mode_config.max_width = 8192;
> +	ddev->mode_config.max_height = 8192;
> +	ddev->mode_config.preferred_depth = sdrm->fb_bpp;
> +	ddev->mode_config.funcs = &sdrm_mode_config_ops;
> +
> +	drm_connector_helper_add(conn, &sdrm_conn_hfuncs);
> +	ret = drm_connector_init(ddev, conn, &sdrm_conn_ops,
> +				 DRM_MODE_CONNECTOR_VIRTUAL);
> +	if (ret)
> +		goto err_cleanup;
> +
> +	ret = drm_mode_create_dirty_info_property(ddev);
> +	if (ret)
> +		goto err_cleanup;
> +
> +	drm_object_attach_property(&conn->base,
> +				   ddev->mode_config.dirty_info_property,
> +				   DRM_MODE_DIRTY_ON);
> +
> +	ret = drm_simple_display_pipe_init(ddev, &sdrm->pipe, &sdrm_pipe_funcs,
> +					   sdrm_formats,
> +					   ARRAY_SIZE(sdrm_formats), conn);
> +	if (ret)
> +		goto err_cleanup;
> +
> +	drm_mode_config_reset(ddev);
> +
> +	return 0;
> +
> +err_cleanup:
> +	drm_mode_config_cleanup(ddev);
> +
> +	return ret;
> +}
> --
> 2.8.2
>

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 15:59         ` Luc Verhaegen
@ 2016-08-04 17:10           ` Daniel Vetter
  0 siblings, 0 replies; 31+ messages in thread
From: Daniel Vetter @ 2016-08-04 17:10 UTC (permalink / raw)
  To: Luc Verhaegen; +Cc: David Herrmann, linux-kernel, dri-devel

On Thu, Aug 04, 2016 at 05:59:42PM +0200, Luc Verhaegen wrote:
> On Thu, Aug 04, 2016 at 05:44:23PM +0200, David Herrmann wrote:
> > On Thu, Aug 4, 2016 at 5:34 PM, Luc Verhaegen <libv@skynet.be> wrote:
> > > Do we really want to recreate a 400+ email thread again, or are we
> > > capable of learning from the past?
> > 
> > No we don't. And no-one intends to. I am fully aware of the discussion
> > that introduced the clock-dependencies to simplefb, and I gladly
> > accept patches that add such support to SimpleDRM. Did anyone say
> > otherwise? This series adds initial support for the devices _we_ know
> > and can test (which is x86 and RPi, in my case). If someone wants
> > support for more devices, please send patches. Why does this have to
> > be included (or even discussed) as part of this submission?
> 
> You're right, it does not have to be included until there is a usecase.
> 
> But on the otherhand i have become pretty allergic to this as that other 
> discussion was completely useless and pointless and stemmed only from 
> the fact that people had been kidding themselves all along that simplefb 
> was going to be simple and not would need anything extra.
> 
> If we can avoid fooling ourselves to the same extent as we did before, 
> then putting this off until there is a real usecase is perfectly fine by 
> me.
> 
> If not, just add this trivial generic addition straight from an existing 
> example. 

I stand corrected on this, seems indeed we need this to avoid issues. I
guess on x86 we don't need this since a pci device is a mostly
well-defined entity, and as long as we stop vesa/uefi for simpledrm before
the real driver loads it'll be all fine.

But on arm it's indeed different with clocks (and I guess sooner or later
some power domains too, or whatever else really). I think as long as it
doesn't go beyond "grab resource on load, drop it again on unload" I'm
prefectly fine with adding all that's needed to simpledrmfb. And totally
up to the folks enabling this whether it should be there upfront or only
once needed for a given platform.
-Daniel

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

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

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

* Re: [PATCH 1/2] drm: add SimpleDRM driver
  2016-08-04 17:05   ` Noralf Trønnes
@ 2016-08-04 17:12     ` Daniel Vetter
  0 siblings, 0 replies; 31+ messages in thread
From: Daniel Vetter @ 2016-08-04 17:12 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: dri-devel, linux-kernel

On Thu, Aug 04, 2016 at 07:05:19PM +0200, Noralf Trønnes wrote:
> 
> Den 04.08.2016 16:03, skrev Noralf Trønnes:
> > 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.
> > 
> > The driver was originally written by David Herrmann in 2014.
> > My main contribution is to make use of drm_simple_kms_helper and
> > rework the probe path to avoid use of the deprecated drm_platform_init()
> > and drm_driver.{load,unload}().
> > 
> > Cc: dh.herrmann@gmail.com
> > Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> > ---
> 
> [snip]
> 
> > 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"
> 
> FB_SIMPLE is bool, should we do the same here?
> 
> Another thing is that simplefb.c is registred at fs_initcall().
> Should we do that?

We could, but seems like an unecessary restriction. Module reloading is
awefull useful for development imo.

> Here's the commit message introducing it:
> 
> simplefb: Change simplefb_init from module_init to fs_initcall
> One of the reasons for having the simplefb nodes in /chosen, and doing
> explicit enumeration of the nodes there, is too allow enumerating them
> sooner,
> so that we get a console earlier on.
> 
> Doing this earlier then fs_initcall is not useful, since the fb only turns
> into a console when fbcon intializes, which is a fs_initcall too.
> 
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>

Once we have the drm emergency console we might be able to load even
earlier than that, not sure. Up to you what you want to do here, or
whether you want to do this in a follow-up patch.
-Daniel

> 
> 
> Noralf.
> 
> > +	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..f6a62dc
> > --- /dev/null
> > +++ b/drivers/gpu/drm/simpledrm/Makefile
> > @@ -0,0 +1,4 @@
> > +simpledrm-y :=	simpledrm_drv.o simpledrm_kms.o simpledrm_gem.o \
> > +		simpledrm_damage.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..e841655
> > --- /dev/null
> > +++ b/drivers/gpu/drm/simpledrm/simpledrm.h
> > @@ -0,0 +1,87 @@
> > +/*
> > + * SimpleDRM firmware framebuffer driver
> > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> > + *
> > + * 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.
> > + */
> > +
> > +#ifndef SDRM_DRV_H
> > +#define SDRM_DRV_H
> > +
> > +#include <linux/errno.h>
> > +#include <linux/fb.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mm.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_data/simplefb.h>
> > +#include <linux/string.h>
> > +#include <drm/drmP.h>
> > +#include <drm/drm_gem.h>
> > +#include <drm/drm_simple_kms_helper.h>
> > +
> > +struct sdrm_device;
> > +struct sdrm_gem_object;
> > +struct sdrm_framebuffer;
> > +
> > +struct sdrm_device {
> > +	struct drm_device *ddev;
> > +	struct drm_simple_display_pipe pipe;
> > +	struct drm_connector conn;
> > +
> > +	/* framebuffer information */
> > +	const struct simplefb_format *fb_sformat;
> > +	u32 fb_format;
> > +	u32 fb_width;
> > +	u32 fb_height;
> > +	u32 fb_stride;
> > +	u32 fb_bpp;
> > +	unsigned long fb_base;
> > +	unsigned long fb_size;
> > +	void *fb_map;
> > +};
> > +
> > +int sdrm_drm_modeset_init(struct sdrm_device *sdrm);
> > +int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma);
> > +
> > +int sdrm_dirty(struct drm_framebuffer *fb,
> > +	       struct drm_file *file,
> > +	       unsigned int flags, unsigned int color,
> > +	       struct drm_clip_rect *clips,
> > +	       unsigned int num_clips);
> > +int sdrm_dirty_all_locked(struct sdrm_device *sdrm);
> > +int sdrm_dirty_all_unlocked(struct sdrm_device *sdrm);
> > +
> > +struct sdrm_gem_object {
> > +	struct drm_gem_object base;
> > +	struct sg_table *sg;
> > +	struct page **pages;
> > +	void *vmapping;
> > +};
> > +
> > +#define to_sdrm_bo(x) container_of(x, struct sdrm_gem_object, base)
> > +
> > +struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev,
> > +					      size_t size);
> > +struct drm_gem_object *sdrm_gem_prime_import(struct drm_device *ddev,
> > +					     struct dma_buf *dma_buf);
> > +void sdrm_gem_free_object(struct drm_gem_object *obj);
> > +int sdrm_gem_get_pages(struct sdrm_gem_object *obj);
> > +
> > +int sdrm_dumb_create(struct drm_file *file_priv, struct drm_device *ddev,
> > +		     struct drm_mode_create_dumb *arg);
> > +int sdrm_dumb_destroy(struct drm_file *file_priv, struct drm_device *ddev,
> > +		      uint32_t handle);
> > +int sdrm_dumb_map_offset(struct drm_file *file_priv, struct drm_device *ddev,
> > +			 uint32_t handle, uint64_t *offset);
> > +
> > +struct sdrm_framebuffer {
> > +	struct drm_framebuffer base;
> > +	struct sdrm_gem_object *obj;
> > +};
> > +
> > +#define to_sdrm_fb(x) container_of(x, struct sdrm_framebuffer, base)
> > +
> > +#endif /* SDRM_DRV_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..e87dac7
> > --- /dev/null
> > +++ b/drivers/gpu/drm/simpledrm/simpledrm_damage.c
> > @@ -0,0 +1,314 @@
> > +/*
> > + * SimpleDRM firmware framebuffer driver
> > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License as published by the Free
> > + * Software Foundation; either version 2 of the License, or (at your option)
> > + * any later version.
> > + */
> > +
> > +#include <asm/unaligned.h>
> > +#include <linux/dma-buf.h>
> > +#include <linux/errno.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mm.h>
> > +#include <linux/module.h>
> > +#include <linux/string.h>
> > +#include <drm/drmP.h>
> > +#include <drm/drm_crtc.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_framebuffer *sfb, u32 x, u32 y,
> > +		      u32 width, u32 height)
> > +{
> > +	struct drm_framebuffer *fb = &sfb->base;
> > +	struct drm_device *ddev = fb->dev;
> > +	struct sdrm_device *sdrm = ddev->dev_private;
> > +	u32 src_bpp, dst_bpp, xoff, yoff, x2, y2;
> > +	u8 *src, *dst;
> > +
> > +	/* already unmapped; ongoing handover? */
> > +	if (!sdrm->fb_map)
> > +		return;
> > +
> > +	/* empty dirty-region, nothing to do */
> > +	if (!width || !height)
> > +		return;
> > +	if (x >= fb->width || y >= fb->height)
> > +		return;
> > +
> > +	/* sanity checks */
> > +	if (x + width < x)
> > +		width = fb->width - x;
> > +	if (y + height < y)
> > +		height = fb->height - y;
> > +
> > +	/* get scanout offsets */
> > +	xoff = 0;
> > +	yoff = 0;
> > +	if (sdrm->pipe.plane.fb == fb) {
> > +		xoff = sdrm->pipe.crtc.x;
> > +		yoff = sdrm->pipe.crtc.y;
> > +	}
> > +
> > +	/* get intersection of dirty region and scan-out region */
> > +	x2 = min(x + width, xoff + sdrm->fb_width);
> > +	y2 = min(y + height, yoff + sdrm->fb_height);
> > +	x = max(x, xoff);
> > +	y = max(y, yoff);
> > +	if (x2 <= x || y2 <= y)
> > +		return;
> > +	width = x2 - x;
> > +	height = y2 - y;
> > +
> > +	/* get buffer offsets */
> > +	src = sfb->obj->vmapping;
> > +	dst = sdrm->fb_map;
> > +
> > +	/* bo is guaranteed to be big enough; size checks not needed */
> > +	src_bpp = (fb->bits_per_pixel + 7) / 8;
> > +	src += fb->offsets[0] + y * fb->pitches[0] + x * src_bpp;
> > +
> > +	dst_bpp = (sdrm->fb_bpp + 7) / 8;
> > +	dst += (y - yoff) * sdrm->fb_stride + (x - xoff) * dst_bpp;
> > +
> > +	/* if formats are identical, do a line-by-line copy.. */
> > +	if (fb->pixel_format == sdrm->fb_format) {
> > +		sdrm_blit_lines(src, fb->pitches[0],
> > +				dst, sdrm->fb_stride,
> > +				src_bpp, width, height);
> > +		return;
> > +	}
> > +
> > +	/* ..otherwise call slow blit-function */
> > +	switch (fb->pixel_format) {
> > +	case DRM_FORMAT_ARGB8888:
> > +		/* fallthrough */
> > +	case DRM_FORMAT_XRGB8888:
> > +		sdrm_blit_from_xrgb8888(src, fb->pitches[0], src_bpp,
> > +					dst, sdrm->fb_stride, dst_bpp,
> > +					sdrm->fb_format, width, height);
> > +		break;
> > +	case DRM_FORMAT_RGB565:
> > +		sdrm_blit_from_rgb565(src, fb->pitches[0], src_bpp,
> > +				      dst, sdrm->fb_stride, dst_bpp,
> > +				      sdrm->fb_format, width, height);
> > +		break;
> > +	}
> > +}
> > +
> > +static int sdrm_begin_access(struct sdrm_framebuffer *sfb)
> > +{
> > +	int r;
> > +
> > +	r = sdrm_gem_get_pages(sfb->obj);
> > +	if (r)
> > +		return r;
> > +
> > +	if (!sfb->obj->base.import_attach)
> > +		return 0;
> > +
> > +	return dma_buf_begin_cpu_access(sfb->obj->base.import_attach->dmabuf,
> > +					DMA_FROM_DEVICE);
> > +}
> > +
> > +static void sdrm_end_access(struct sdrm_framebuffer *sfb)
> > +{
> > +	if (!sfb->obj->base.import_attach)
> > +		return;
> > +
> > +	dma_buf_end_cpu_access(sfb->obj->base.import_attach->dmabuf,
> > +			       DMA_FROM_DEVICE);
> > +}
> > +
> > +int sdrm_dirty(struct drm_framebuffer *fb,
> > +	       struct drm_file *file,
> > +	       unsigned int flags, unsigned int color,
> > +	       struct drm_clip_rect *clips,
> > +	       unsigned int num_clips)
> > +{
> > +	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
> > +	struct drm_device *ddev = fb->dev;
> > +	struct sdrm_device *sdrm = ddev->dev_private;
> > +	struct drm_clip_rect full_clip;
> > +	unsigned int i;
> > +	int r;
> > +
> > +	if (!clips || !num_clips) {
> > +		full_clip.x1 = 0;
> > +		full_clip.x2 = fb->width;
> > +		full_clip.y1 = 0;
> > +		full_clip.y2 = fb->height;
> > +		clips = &full_clip;
> > +		num_clips = 1;
> > +	}
> > +
> > +	drm_modeset_lock_all(ddev);
> > +
> > +	if (sdrm->pipe.plane.fb != fb) {
> > +		r = 0;
> > +		goto unlock;
> > +	}
> > +
> > +	r = sdrm_begin_access(sfb);
> > +	if (r)
> > +		goto unlock;
> > +
> > +	for (i = 0; i < num_clips; i++) {
> > +		if (clips[i].x2 <= clips[i].x1 ||
> > +		    clips[i].y2 <= clips[i].y1)
> > +			continue;
> > +
> > +		sdrm_blit(sfb, clips[i].x1, clips[i].y1,
> > +			  clips[i].x2 - clips[i].x1,
> > +			  clips[i].y2 - clips[i].y1);
> > +	}
> > +
> > +	sdrm_end_access(sfb);
> > +
> > +unlock:
> > +	drm_modeset_unlock_all(ddev);
> > +	return 0;
> > +}
> > +
> > +int sdrm_dirty_all_locked(struct sdrm_device *sdrm)
> > +{
> > +	struct drm_framebuffer *fb;
> > +	struct sdrm_framebuffer *sfb;
> > +	int r;
> > +
> > +	fb = sdrm->pipe.plane.fb;
> > +	if (!fb)
> > +		return 0;
> > +
> > +	sfb = to_sdrm_fb(fb);
> > +	r = sdrm_begin_access(sfb);
> > +	if (r)
> > +		return r;
> > +
> > +	sdrm_blit(sfb, 0, 0, fb->width, fb->height);
> > +
> > +	sdrm_end_access(sfb);
> > +
> > +	return 0;
> > +}
> > +
> > +int sdrm_dirty_all_unlocked(struct sdrm_device *sdrm)
> > +{
> > +	int r;
> > +
> > +	drm_modeset_lock_all(sdrm->ddev);
> > +	r = sdrm_dirty_all_locked(sdrm);
> > +	drm_modeset_unlock_all(sdrm->ddev);
> > +
> > +	return r;
> > +}
> > diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
> > new file mode 100644
> > index 0000000..26b4ca9
> > --- /dev/null
> > +++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
> > @@ -0,0 +1,295 @@
> > +/*
> > + * SimpleDRM firmware framebuffer driver
> > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License as published by the Free
> > + * Software Foundation; either version 2 of the License, or (at your option)
> > + * any later version.
> > + */
> > +
> > +#include <linux/errno.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mm.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_data/simplefb.h>
> > +#include <linux/string.h>
> > +#include <drm/drmP.h>
> > +#include "simpledrm.h"
> > +
> > +static const struct file_operations sdrm_drm_fops = {
> > +	.owner = THIS_MODULE,
> > +	.open = drm_open,
> > +	.mmap = sdrm_drm_mmap,
> > +	.poll = drm_poll,
> > +	.read = drm_read,
> > +	.unlocked_ioctl = drm_ioctl,
> > +	.release = drm_release,
> > +#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_PRIME |
> > +			   DRIVER_ATOMIC,
> > +	.fops = &sdrm_drm_fops,
> > +
> > +	.gem_free_object = sdrm_gem_free_object,
> > +	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> > +	.gem_prime_import = sdrm_gem_prime_import,
> > +
> > +	.dumb_create = sdrm_dumb_create,
> > +	.dumb_map_offset = sdrm_dumb_map_offset,
> > +	.dumb_destroy = sdrm_dumb_destroy,
> > +
> > +	.name = "simpledrm",
> > +	.desc = "Simple firmware framebuffer DRM driver",
> > +	.date = "20130601",
> > +	.major = 0,
> > +	.minor = 0,
> > +	.patchlevel = 1,
> > +};
> > +
> > +static int parse_dt(struct platform_device *pdev,
> > +		    struct simplefb_platform_data *mode)
> > +{
> > +	struct device_node *np = pdev->dev.of_node;
> > +	const char *format;
> > +	int ret;
> > +
> > +	if (!np)
> > +		return -ENODEV;
> > +
> > +	ret = of_property_read_u32(np, "width", &mode->width);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "Can't parse width property\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = of_property_read_u32(np, "height", &mode->height);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "Can't parse height property\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = of_property_read_u32(np, "stride", &mode->stride);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "Can't parse stride property\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = of_property_read_string(np, "format", &format);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "Can't parse format property\n");
> > +		return ret;
> > +	}
> > +	mode->format = format;
> > +
> > +	return 0;
> > +}
> > +
> > +static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS;
> > +
> > +int sdrm_pdev_init(struct sdrm_device *sdrm)
> > +{
> > +	struct platform_device *pdev = sdrm->ddev->platformdev;
> > +	struct simplefb_platform_data *mode = pdev->dev.platform_data;
> > +	struct simplefb_platform_data pmode;
> > +	struct resource *mem;
> > +	unsigned int depth;
> > +	int ret, i, bpp;
> > +
> > +	if (!mode) {
> > +		mode = &pmode;
> > +		ret = parse_dt(pdev, mode);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	if (!mem) {
> > +		dev_err(sdrm->ddev->dev, "No memory resource\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	for (i = 0; i < ARRAY_SIZE(simplefb_formats); ++i) {
> > +		if (strcmp(mode->format, simplefb_formats[i].name))
> > +			continue;
> > +
> > +		sdrm->fb_sformat = &simplefb_formats[i];
> > +		sdrm->fb_format = simplefb_formats[i].fourcc;
> > +		sdrm->fb_width = mode->width;
> > +		sdrm->fb_height = mode->height;
> > +		sdrm->fb_stride = mode->stride;
> > +		sdrm->fb_base = mem->start;
> > +		sdrm->fb_size = resource_size(mem);
> > +		break;
> > +	}
> > +
> > +	if (i >= ARRAY_SIZE(simplefb_formats)) {
> > +		dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format);
> > +		return -ENODEV;
> > +	}
> > +
> > +	switch (sdrm->fb_format) {
> > +	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:
> > +		dev_err(sdrm->ddev->dev, "Unsupported format %s\n",
> > +			mode->format);
> > +		return -ENODEV;
> > +	}
> > +
> > +	drm_fb_get_bpp_depth(sdrm->fb_format, &depth, &bpp);
> > +	if (!bpp) {
> > +		dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format);
> > +		return -ENODEV;
> > +	}
> > +
> > +	if (sdrm->fb_size < sdrm->fb_stride * sdrm->fb_height) {
> > +		dev_err(sdrm->ddev->dev, "FB too small\n");
> > +		return -ENODEV;
> > +	} else if ((bpp + 7) / 8 * sdrm->fb_width > sdrm->fb_stride) {
> > +		dev_err(sdrm->ddev->dev, "Invalid stride\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	sdrm->fb_bpp = bpp;
> > +
> > +	sdrm->fb_map = ioremap_wc(sdrm->fb_base, sdrm->fb_size);
> > +	if (!sdrm->fb_map) {
> > +		dev_err(sdrm->ddev->dev, "cannot remap VMEM\n");
> > +		return -EIO;
> > +	}
> > +
> > +	DRM_DEBUG_KMS("format: %s\n", drm_get_format_name(sdrm->fb_format));
> > +
> > +	return 0;
> > +}
> > +
> > +void sdrm_pdev_destroy(struct sdrm_device *sdrm)
> > +{
> > +	if (sdrm->fb_map) {
> > +		iounmap(sdrm->fb_map);
> > +		sdrm->fb_map = NULL;
> > +	}
> > +}
> > +
> > +static int sdrm_simplefb_probe(struct platform_device *pdev)
> > +{
> > +	struct sdrm_device *sdrm;
> > +	struct drm_device *ddev;
> > +	int ret;
> > +
> > +	ddev = drm_dev_alloc(&sdrm_drm_driver, &pdev->dev);
> > +	if (!ddev)
> > +		return -ENOMEM;
> > +
> > +	sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL);
> > +	if (!sdrm)
> > +		goto err_free;
> > +
> > +	ddev->platformdev = pdev;
> > +	ddev->dev_private = sdrm;
> > +	sdrm->ddev = ddev;
> > +
> > +	ret = sdrm_pdev_init(sdrm);
> > +	if (ret)
> > +		goto err_free;
> > +
> > +	ret = sdrm_drm_modeset_init(sdrm);
> > +	if (ret)
> > +		goto err_destroy;
> > +
> > +	ret = drm_dev_register(ddev, 0);
> > +	if (ret)
> > +		goto err_cleanup;
> > +
> > +	platform_set_drvdata(pdev, ddev);
> > +
> > +	DRM_INFO("Initialized %s on minor %d\n", ddev->driver->name,
> > +		 ddev->primary->index);
> > +
> > +	return 0;
> > +
> > +err_cleanup:
> > +	drm_mode_config_cleanup(ddev);
> > +err_destroy:
> > +	sdrm_pdev_destroy(sdrm);
> > +err_free:
> > +	drm_dev_unref(ddev);
> > +	kfree(sdrm);
> > +
> > +	return ret;
> > +}
> > +
> > +static int sdrm_simplefb_remove(struct platform_device *pdev)
> > +{
> > +	struct drm_device *ddev = platform_get_drvdata(pdev);
> > +	struct sdrm_device *sdrm = ddev->dev_private;
> > +
> > +	drm_dev_unregister(ddev);
> > +	drm_mode_config_cleanup(ddev);
> > +
> > +	/* protect fb_map removal against sdrm_blit() */
> > +	drm_modeset_lock_all(ddev);
> > +	sdrm_pdev_destroy(sdrm);
> > +	drm_modeset_unlock_all(ddev);
> > +
> > +	drm_dev_unref(ddev);
> > +	kfree(sdrm);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id simplefb_of_match[] = {
> > +	{ .compatible = "simple-framebuffer", },
> > +	{ },
> > +};
> > +MODULE_DEVICE_TABLE(of, 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 = simplefb_of_match,
> > +	},
> > +};
> > +
> > +static int __init sdrm_init(void)
> > +{
> > +	return platform_driver_register(&sdrm_simplefb_driver);
> > +}
> > +
> > +static void __exit sdrm_exit(void)
> > +{
> > +	platform_driver_unregister(&sdrm_simplefb_driver);
> > +}
> > +
> > +module_init(sdrm_init);
> > +module_exit(sdrm_exit);
> > +MODULE_LICENSE("GPL");
> > +MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
> > +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..2d59632
> > --- /dev/null
> > +++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c
> > @@ -0,0 +1,276 @@
> > +/*
> > + * SimpleDRM firmware framebuffer driver
> > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License as published by the Free
> > + * Software Foundation; either version 2 of the License, or (at your option)
> > + * any later version.
> > + */
> > +
> > +#include <linux/dma-buf.h>
> > +#include <linux/errno.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mm.h>
> > +#include <linux/module.h>
> > +#include <linux/string.h>
> > +#include <drm/drmP.h>
> > +#include <drm/drm_legacy.h>
> > +#include "simpledrm.h"
> > +
> > +int sdrm_gem_get_pages(struct sdrm_gem_object *obj)
> > +{
> > +	size_t num, i;
> > +
> > +	if (obj->vmapping)
> > +		return 0;
> > +
> > +	if (obj->base.import_attach) {
> > +		obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf);
> > +		return !obj->vmapping ? -ENOMEM : 0;
> > +	}
> > +
> > +	num = obj->base.size >> PAGE_SHIFT;
> > +	obj->pages = drm_malloc_ab(num, sizeof(*obj->pages));
> > +	if (!obj->pages)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < num; ++i) {
> > +		obj->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
> > +		if (!obj->pages[i])
> > +			goto error;
> > +	}
> > +
> > +	obj->vmapping = vmap(obj->pages, num, 0, PAGE_KERNEL);
> > +	if (!obj->vmapping)
> > +		goto error;
> > +
> > +	return 0;
> > +
> > +error:
> > +	while (i > 0)
> > +		__free_pages(obj->pages[--i], 0);
> > +
> > +	drm_free_large(obj->pages);
> > +	obj->pages = NULL;
> > +	return -ENOMEM;
> > +}
> > +
> > +static void sdrm_gem_put_pages(struct sdrm_gem_object *obj)
> > +{
> > +	size_t num, i;
> > +
> > +	if (!obj->vmapping)
> > +		return;
> > +
> > +	if (obj->base.import_attach) {
> > +		dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping);
> > +		obj->vmapping = NULL;
> > +		return;
> > +	}
> > +
> > +	vunmap(obj->vmapping);
> > +	obj->vmapping = NULL;
> > +
> > +	num = obj->base.size >> PAGE_SHIFT;
> > +	for (i = 0; i < num; ++i)
> > +		__free_pages(obj->pages[i], 0);
> > +
> > +	drm_free_large(obj->pages);
> > +	obj->pages = NULL;
> > +}
> > +
> > +struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev,
> > +					      size_t size)
> > +{
> > +	struct sdrm_gem_object *obj;
> > +
> > +	WARN_ON(!size || (size & ~PAGE_MASK) != 0);
> > +
> > +	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
> > +	if (!obj)
> > +		return NULL;
> > +
> > +	drm_gem_private_object_init(ddev, &obj->base, size);
> > +	return obj;
> > +}
> > +
> > +void sdrm_gem_free_object(struct drm_gem_object *gobj)
> > +{
> > +	struct sdrm_gem_object *obj = to_sdrm_bo(gobj);
> > +	struct drm_device *ddev = gobj->dev;
> > +
> > +	if (obj->pages) {
> > +		/* kill all user-space mappings */
> > +		drm_vma_node_unmap(&gobj->vma_node,
> > +				   ddev->anon_inode->i_mapping);
> > +		sdrm_gem_put_pages(obj);
> > +	}
> > +
> > +	if (gobj->import_attach)
> > +		drm_prime_gem_destroy(gobj, obj->sg);
> > +
> > +	drm_gem_free_mmap_offset(gobj);
> > +	drm_gem_object_release(gobj);
> > +	kfree(obj);
> > +}
> > +
> > +int sdrm_dumb_create(struct drm_file *dfile, struct drm_device *ddev,
> > +		     struct drm_mode_create_dumb *args)
> > +{
> > +	struct sdrm_gem_object *obj;
> > +	int r;
> > +
> > +	if (args->flags)
> > +		return -EINVAL;
> > +
> > +	/* overflow checks are done by DRM core */
> > +	args->pitch = (args->bpp + 7) / 8 * args->width;
> > +	args->size = PAGE_ALIGN(args->pitch * args->height);
> > +
> > +	obj = sdrm_gem_alloc_object(ddev, args->size);
> > +	if (!obj)
> > +		return -ENOMEM;
> > +
> > +	r = drm_gem_handle_create(dfile, &obj->base, &args->handle);
> > +	if (r) {
> > +		drm_gem_object_unreference_unlocked(&obj->base);
> > +		return r;
> > +	}
> > +
> > +	/* handle owns a reference */
> > +	drm_gem_object_unreference_unlocked(&obj->base);
> > +	return 0;
> > +}
> > +
> > +int sdrm_dumb_destroy(struct drm_file *dfile, struct drm_device *ddev,
> > +		      uint32_t handle)
> > +{
> > +	return drm_gem_handle_delete(dfile, handle);
> > +}
> > +
> > +int sdrm_dumb_map_offset(struct drm_file *dfile, struct drm_device *ddev,
> > +			 uint32_t handle, uint64_t *offset)
> > +{
> > +	struct drm_gem_object *gobj;
> > +	int r;
> > +
> > +	mutex_lock(&ddev->struct_mutex);
> > +
> > +	gobj = drm_gem_object_lookup(dfile, handle);
> > +	if (!gobj) {
> > +		r = -ENOENT;
> > +		goto out_unlock;
> > +	}
> > +
> > +	r = drm_gem_create_mmap_offset(gobj);
> > +	if (r)
> > +		goto out_unref;
> > +
> > +	*offset = drm_vma_node_offset_addr(&gobj->vma_node);
> > +
> > +out_unref:
> > +	drm_gem_object_unreference(gobj);
> > +out_unlock:
> > +	mutex_unlock(&ddev->struct_mutex);
> > +	return r;
> > +}
> > +
> > +int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma)
> > +{
> > +	struct drm_file *priv = filp->private_data;
> > +	struct drm_device *dev = priv->minor->dev;
> > +	struct drm_vma_offset_node *node;
> > +	struct drm_gem_object *gobj;
> > +	struct sdrm_gem_object *obj;
> > +	size_t size, i, num;
> > +	int r;
> > +
> > +	if (drm_device_is_unplugged(dev))
> > +		return -ENODEV;
> > +
> > +	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));
> > +	drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
> > +
> > +	if (!node)
> > +		return drm_legacy_mmap(filp, vma);
> > +	else if (!drm_vma_node_is_allowed(node, filp))
> > +		return -EACCES;
> > +
> > +	gobj = container_of(node, struct drm_gem_object, vma_node);
> > +	obj = to_sdrm_bo(gobj);
> > +	size = drm_vma_node_size(node) << PAGE_SHIFT;
> > +	if (size < vma->vm_end - vma->vm_start)
> > +		return r;
> > +
> > +	r = sdrm_gem_get_pages(obj);
> > +	if (r < 0)
> > +		return r;
> > +
> > +	/* prevent dmabuf-imported mmap to user-space */
> > +	if (!obj->pages)
> > +		return -EACCES;
> > +
> > +	vma->vm_flags |= VM_DONTEXPAND;
> > +	vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
> > +
> > +	num = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
> > +	for (i = 0; i < num; ++i) {
> > +		r = vm_insert_page(vma, vma->vm_start + i * PAGE_SIZE,
> > +				   obj->pages[i]);
> > +		if (r < 0) {
> > +			if (i > 0)
> > +				zap_vma_ptes(vma, vma->vm_start, i * PAGE_SIZE);
> > +			return r;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +struct drm_gem_object *sdrm_gem_prime_import(struct drm_device *ddev,
> > +					     struct dma_buf *dma_buf)
> > +{
> > +	struct dma_buf_attachment *attach;
> > +	struct sdrm_gem_object *obj;
> > +	struct sg_table *sg;
> > +	int ret;
> > +
> > +	/* need to attach */
> > +	attach = dma_buf_attach(dma_buf, ddev->dev);
> > +	if (IS_ERR(attach))
> > +		return ERR_CAST(attach);
> > +
> > +	get_dma_buf(dma_buf);
> > +
> > +	sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
> > +	if (IS_ERR(sg)) {
> > +		ret = PTR_ERR(sg);
> > +		goto fail_detach;
> > +	}
> > +
> > +	/*
> > +	 * dma_buf_vmap() gives us a page-aligned mapping, so lets bump the
> > +	 * size of the dma-buf to the next page-boundary
> > +	 */
> > +	obj = sdrm_gem_alloc_object(ddev, PAGE_ALIGN(dma_buf->size));
> > +	if (!obj) {
> > +		ret = -ENOMEM;
> > +		goto fail_unmap;
> > +	}
> > +
> > +	obj->sg = sg;
> > +	obj->base.import_attach = attach;
> > +
> > +	return &obj->base;
> > +
> > +fail_unmap:
> > +	dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL);
> > +fail_detach:
> > +	dma_buf_detach(dma_buf, attach);
> > +	dma_buf_put(dma_buf);
> > +	return ERR_PTR(ret);
> > +}
> > diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
> > new file mode 100644
> > index 0000000..6295a9f
> > --- /dev/null
> > +++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
> > @@ -0,0 +1,276 @@
> > +/*
> > + * SimpleDRM firmware framebuffer driver
> > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License as published by the Free
> > + * Software Foundation; either version 2 of the License, or (at your option)
> > + * any later version.
> > + */
> > +
> > +#include <linux/errno.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mm.h>
> > +#include <linux/module.h>
> > +#include <linux/string.h>
> > +#include <drm/drmP.h>
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_crtc_helper.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->fb_width, sdrm->fb_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 inline struct sdrm_device *
> > +pipe_to_sdrm(struct drm_simple_display_pipe *pipe)
> > +{
> > +	return container_of(pipe, struct sdrm_device, pipe);
> > +}
> > +
> > +static int sdrm_display_pipe_check(struct drm_simple_display_pipe *pipe,
> > +				   struct drm_plane_state *plane_state,
> > +				   struct drm_crtc_state *crtc_state)
> > +{
> > +	struct drm_display_mode *mode = &crtc_state->mode;
> > +	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
> > +	struct drm_framebuffer *fb = plane_state->fb;
> > +	u32 x = plane_state->src_x >> 16;
> > +	u32 y = plane_state->src_y >> 16;
> > +
> > +	if (mode->hdisplay != sdrm->fb_width ||
> > +	    mode->vdisplay != sdrm->fb_height)
> > +		return -EINVAL;
> > +	if (fb->width <= x || fb->height <= y ||
> > +	    fb->width - x < sdrm->fb_width || fb->height - y < sdrm->fb_height)
> > +		return -EINVAL;
> > +
> > +	return 0;
> > +}
> > +
> > +void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
> > +			      struct drm_plane_state *plane_state)
> > +{
> > +	struct drm_framebuffer *fb = pipe->plane.state->fb;
> > +	struct sdrm_device *sdrm = pipe_to_sdrm(pipe);
> > +
> > +	if (fb)
> > +		sdrm_dirty_all_locked(sdrm);
> > +}
> > +
> > +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;
> > +	}
> > +}
> > +
> > +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 = {
> > +	.check = sdrm_display_pipe_check,
> > +	.update = sdrm_display_pipe_update,
> > +	.enable = sdrm_display_pipe_enable,
> > +	.disable = sdrm_display_pipe_disable,
> > +};
> > +
> > +static int sdrm_fb_create_handle(struct drm_framebuffer *fb,
> > +				 struct drm_file *dfile,
> > +				 unsigned int *handle)
> > +{
> > +	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
> > +
> > +	return drm_gem_handle_create(dfile, &sfb->obj->base, handle);
> > +}
> > +
> > +static void sdrm_fb_destroy(struct drm_framebuffer *fb)
> > +{
> > +	struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
> > +
> > +	drm_framebuffer_cleanup(fb);
> > +	drm_gem_object_unreference_unlocked(&sfb->obj->base);
> > +	kfree(sfb);
> > +}
> > +
> > +static const struct drm_framebuffer_funcs sdrm_fb_ops = {
> > +	.create_handle = sdrm_fb_create_handle,
> > +	.dirty = sdrm_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 sdrm_framebuffer *fb;
> > +	struct drm_gem_object *gobj;
> > +	u32 bpp, size;
> > +	int ret;
> > +	void *err;
> > +
> > +	if (cmd->flags)
> > +		return ERR_PTR(-EINVAL);
> > +
> > +	gobj = drm_gem_object_lookup(dfile, cmd->handles[0]);
> > +	if (!gobj)
> > +		return ERR_PTR(-EINVAL);
> > +
> > +	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
> > +	if (!fb) {
> > +		err = ERR_PTR(-ENOMEM);
> > +		goto err_unref;
> > +	}
> > +	fb->obj = to_sdrm_bo(gobj);
> > +
> > +	fb->base.pitches[0] = cmd->pitches[0];
> > +	fb->base.offsets[0] = cmd->offsets[0];
> > +	fb->base.width = cmd->width;
> > +	fb->base.height = cmd->height;
> > +	fb->base.pixel_format = cmd->pixel_format;
> > +	drm_fb_get_bpp_depth(cmd->pixel_format, &fb->base.depth,
> > +			     &fb->base.bits_per_pixel);
> > +
> > +	/*
> > +	 * width/height are already clamped into min/max_width/height range,
> > +	 * so overflows are not possible
> > +	 */
> > +
> > +	bpp = (fb->base.bits_per_pixel + 7) / 8;
> > +	size = cmd->pitches[0] * cmd->height;
> > +	if (!bpp ||
> > +	    bpp > 4 ||
> > +	    cmd->pitches[0] < bpp * fb->base.width ||
> > +	    cmd->pitches[0] > 0xffffU ||
> > +	    size + fb->base.offsets[0] < size ||
> > +	    size + fb->base.offsets[0] > fb->obj->base.size) {
> > +		err = ERR_PTR(-EINVAL);
> > +		goto err_free;
> > +	}
> > +
> > +	ret = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops);
> > +	if (ret < 0) {
> > +		err = ERR_PTR(ret);
> > +		goto err_free;
> > +	}
> > +
> > +	DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id,
> > +		      drm_get_format_name(fb->base.pixel_format));
> > +
> > +	return &fb->base;
> > +
> > +err_free:
> > +	kfree(fb);
> > +err_unref:
> > +	drm_gem_object_unreference_unlocked(gobj);
> > +
> > +	return err;
> > +}
> > +
> > +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_drm_modeset_init(struct sdrm_device *sdrm)
> > +{
> > +	struct drm_connector *conn = &sdrm->conn;
> > +	struct drm_device *ddev = sdrm->ddev;
> > +	int ret;
> > +
> > +	drm_mode_config_init(ddev);
> > +	ddev->mode_config.min_width = 1;
> > +	ddev->mode_config.min_height = 1;
> > +	ddev->mode_config.max_width = 8192;
> > +	ddev->mode_config.max_height = 8192;
> > +	ddev->mode_config.preferred_depth = sdrm->fb_bpp;
> > +	ddev->mode_config.funcs = &sdrm_mode_config_ops;
> > +
> > +	drm_connector_helper_add(conn, &sdrm_conn_hfuncs);
> > +	ret = drm_connector_init(ddev, conn, &sdrm_conn_ops,
> > +				 DRM_MODE_CONNECTOR_VIRTUAL);
> > +	if (ret)
> > +		goto err_cleanup;
> > +
> > +	ret = drm_mode_create_dirty_info_property(ddev);
> > +	if (ret)
> > +		goto err_cleanup;
> > +
> > +	drm_object_attach_property(&conn->base,
> > +				   ddev->mode_config.dirty_info_property,
> > +				   DRM_MODE_DIRTY_ON);
> > +
> > +	ret = drm_simple_display_pipe_init(ddev, &sdrm->pipe, &sdrm_pipe_funcs,
> > +					   sdrm_formats,
> > +					   ARRAY_SIZE(sdrm_formats), conn);
> > +	if (ret)
> > +		goto err_cleanup;
> > +
> > +	drm_mode_config_reset(ddev);
> > +
> > +	return 0;
> > +
> > +err_cleanup:
> > +	drm_mode_config_cleanup(ddev);
> > +
> > +	return ret;
> > +}
> > --
> > 2.8.2
> > 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 14:36 ` Daniel Vetter
@ 2016-08-04 17:30   ` Noralf Trønnes
  0 siblings, 0 replies; 31+ messages in thread
From: Noralf Trønnes @ 2016-08-04 17:30 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel, linux-kernel, David Herrmann


Den 04.08.2016 16:36, skrev Daniel Vetter:
> On Thu, Aug 04, 2016 at 04:03:18PM +0200, Noralf Trønnes wrote:
>> This patchset adds the simpledrm driver by David Herrmann based on a
>> patchset[1] from 2014. That patchset also included patches for kicking
>> out simpledrm by real drivers. I have stayed away from that since it
>> involves another subsystem and I would probably be unable to answer any
>> questions about the implementation.
> Need David's input on this, but I think the force-removal of simpledrm
> when other drivers take over is required. Otherwise hilarity can ensue.
> If we leave this out for the inital merge then I think we need a really
> big warning in Kconfig that if people aren't careful it could result in a
> kaboom.

I understand. I anticipated this, but I was hoping that if I moved this 
part forward,
which I was capable of doing, maybe someone else could do the rest :-)
Anyways, if no one chimes in to do this, lets add a warning.

> If you can, booting this on a vesa/uefi platform would be interesting too,
> just to make sure.

I'm a bit limited here I'm afraid. I only have a Raspberry Pi to test with.
My other Linux installations are running on a VMware ESXi server.


Noralf.

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 15:08   ` Daniel Vetter
@ 2016-08-04 18:08       ` One Thousand Gnomes
  2016-08-04 18:08       ` One Thousand Gnomes
  1 sibling, 0 replies; 31+ messages in thread
From: One Thousand Gnomes @ 2016-08-04 18:08 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Luc Verhaegen, Noralf Trønnes, linux-kernel, dri-devel

> firmware framebuffer in early boot until a real driver takes over. It's a
> replacement really for all the various uefi/vesa/whatever fbdev drivers.
> Full reliance on the firmware very much intended.

Most of those have firmware interfaces for things like colour setting and
hardware scrolling. It's a replacement for far less than might first
appear but still useful.

Alan

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
@ 2016-08-04 18:08       ` One Thousand Gnomes
  0 siblings, 0 replies; 31+ messages in thread
From: One Thousand Gnomes @ 2016-08-04 18:08 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: linux-kernel, dri-devel

> firmware framebuffer in early boot until a real driver takes over. It's a
> replacement really for all the various uefi/vesa/whatever fbdev drivers.
> Full reliance on the firmware very much intended.

Most of those have firmware interfaces for things like colour setting and
hardware scrolling. It's a replacement for far less than might first
appear but still useful.

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

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 16:58     ` Noralf Trønnes
  (?)
@ 2016-08-04 18:12     ` Luc Verhaegen
  2016-08-05  7:18       ` Hans de Goede
  -1 siblings, 1 reply; 31+ messages in thread
From: Luc Verhaegen @ 2016-08-04 18:12 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: dri-devel, linux-kernel, Hans de Goede

On Thu, Aug 04, 2016 at 06:58:55PM +0200, Noralf Trønnes wrote:
> 
> I didn't read the binding document[1], which I should have done.
> If simpledrm claims to be compatible with simple-framebuffer I assume it
> should support the entire binding doc which includes clocks, regulators
> and having the node under /chosen.
> I will lift the necessary code from simplefb.c and put it in the next
> version.

Smashing, repeat of a massive pain avoided, thanks :)

> The binding doc also mentions an optional display phandle property, but I
> can't find any reference to this in simplefb.c.

By that time i had long, long given up on this, i think Hans de Goede, 
who is a few orders of magnitude more patient than me, should know. He 
valiantly whipped this thing through back then.

Luc Verhaegen.

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 18:12     ` Luc Verhaegen
@ 2016-08-05  7:18       ` Hans de Goede
  0 siblings, 0 replies; 31+ messages in thread
From: Hans de Goede @ 2016-08-05  7:18 UTC (permalink / raw)
  To: Luc Verhaegen, Noralf Trønnes; +Cc: dri-devel, linux-kernel

Hi,

On 04-08-16 20:12, Luc Verhaegen wrote:
> On Thu, Aug 04, 2016 at 06:58:55PM +0200, Noralf Trønnes wrote:
>>
>> I didn't read the binding document[1], which I should have done.
>> If simpledrm claims to be compatible with simple-framebuffer I assume it
>> should support the entire binding doc which includes clocks, regulators
>> and having the node under /chosen.
>> I will lift the necessary code from simplefb.c and put it in the next
>> version.
>
> Smashing, repeat of a massive pain avoided, thanks :)
>
>> The binding doc also mentions an optional display phandle property, but I
>> can't find any reference to this in simplefb.c.

Ah yes, the display phandle, so the idea behind this is that the
simplefb node would have a display phandle pointing to a node
describing the "primary" node describing the actual display-pipe hardware.

The primary language is there because a display pipeline typically
consists of multiple blocks and thus has multiple nodes describing it.

This way the hardware driver would be able to figure out which simplefb
to disable if there is more then 1.

In practice the remove_conflicting_framebuffers kernel API is used for this and
that takes a framebuffer address, so that bit of the bindings is essentially
unused. Either way that bit is only relevant to the actual display hardware driver
(so that it can disable sumplefb when it takes over the display) and for
simpledrm you can simply ignore it.

Regards,

Hans

p.s.

Noralf, I recognize your name from the ft6236 touchscreen driver, I've mailed
you about this in the past because it is a duplicate driver, the edt-ft5x06
driver already speaks the same protocol. I see now that I made a copy and paste
error in your email address, so you never got my mails on this. I'll resend
my latest mail (a kernel patch removing the duplicate driver!) with a fixed
email address.

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 20:07   ` David Herrmann
  2016-08-04 23:34     ` Ken Phillis Jr
@ 2016-08-05  8:28     ` Jani Nikula
  1 sibling, 0 replies; 31+ messages in thread
From: Jani Nikula @ 2016-08-05  8:28 UTC (permalink / raw)
  To: David Herrmann, Daniel Vetter; +Cc: dri-devel, Ken Phillis Jr

On Thu, 04 Aug 2016, David Herrmann <dh.herrmann@gmail.com> wrote:
> Hi
>
> On Thu, Aug 4, 2016 at 10:01 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> On Thu, Aug 04, 2016 at 02:50:37PM -0500, Ken Phillis Jr wrote:
>>> I believe this driver is extremely useful, and I see possible issues with
>>> the fact that the driver is GPL Only. This driver is critical for devices
>>> that lack a proper DRM driver, and locking it into GPL Only could lead to
>>> issues with porting the system over to platforms like FreeBSD,
>>> DragonFlyBSD, etc.  Is there any chance the code placed in the
>>> "drivers/gpu/drm/simpledrm" folder be placed under a more permissive
>>> license setup. I would suggest using MIT[1], BSD 2-Clause[2], or BSD
>>> 3-clause[3]. Also as a quick note it is possible to enable this under a
>>> dual-license setup with GPLv2/BSD-3 Clause. An example a dual licensed
>>> driver is the Intel iwlwifi driver ( see:
>>> drivers/net/wireless/intel/iwlwifi/iwl-8000.c )
>>
>> tbh, most if not all arm drivers are gpl only, and due to code sharing and
>> refactoring I'd say a lot of that has leaked all over drm. IANAL and all
>> that, but personally I believe that the entire idea of drm being MIT is
>> walking on ever thinner ice. And personally I'm not going to extend effort
>> to slow this down or prevent it outright, since I think all that code
>> sharing with arm folks is extremely beneficial for everyone. At least here
>> on Linux.
>> -Daniel
>
> On top of that: Feel free to copy SimpleDRM in any way possible.
> Consider my original implementation public domain.

N.b. the improvements by others on top of that, at least once included
in the kernel tree, will not be public domain. IANAL etc.

BR,
Jani.


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

-- 
Jani Nikula, Intel Open Source Technology Center
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 20:07   ` David Herrmann
@ 2016-08-04 23:34     ` Ken Phillis Jr
  2016-08-05  8:28     ` Jani Nikula
  1 sibling, 0 replies; 31+ messages in thread
From: Ken Phillis Jr @ 2016-08-04 23:34 UTC (permalink / raw)
  To: David Herrmann, Daniel Vetter; +Cc: dri-devel


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

On Thu, Aug 4, 2016 at 3:01 PM, Daniel Vetter <daniel@ffwll.ch> wrote:

> tbh, most if not all arm drivers are gpl only, and due to code sharing and
> refactoring I'd say a lot of that has leaked all over drm. IANAL and all
> that, but personally I believe that the entire idea of drm being MIT is
> walking on ever thinner ice. And personally I'm not going to extend effort
> to slow this down or prevent it outright, since I think all that code
> sharing with arm folks is extremely beneficial for everyone. At least here
> on Linux.
> -Daniel
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
>

I generally do not have a issue with GPLv2 license. I believe that most
developers
are under the impression that for a driver (and code) to get mainlined into
the
Linux kernel requires the code to be placed under GPLv2 only. This is just a
generalized check to make sure that there is no problem placing this code
under a more permissive license.

On Thu, Aug 4, 2016 at 3:07 PM, David Herrmann <dh.herrmann@gmail.com>
wrote:

> Hi
>
> On top of that: Feel free to copy SimpleDRM in any way possible.
> Consider my original implementation public domain.
>
> Thanks
> David
>

Thank you for the quick response, and I'll make a note of this.

[-- Attachment #1.2: Type: text/html, Size: 2197 bytes --]

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

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

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 20:01 ` Daniel Vetter
@ 2016-08-04 20:07   ` David Herrmann
  2016-08-04 23:34     ` Ken Phillis Jr
  2016-08-05  8:28     ` Jani Nikula
  0 siblings, 2 replies; 31+ messages in thread
From: David Herrmann @ 2016-08-04 20:07 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel, Ken Phillis Jr

Hi

On Thu, Aug 4, 2016 at 10:01 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Thu, Aug 04, 2016 at 02:50:37PM -0500, Ken Phillis Jr wrote:
>> I believe this driver is extremely useful, and I see possible issues with
>> the fact that the driver is GPL Only. This driver is critical for devices
>> that lack a proper DRM driver, and locking it into GPL Only could lead to
>> issues with porting the system over to platforms like FreeBSD,
>> DragonFlyBSD, etc.  Is there any chance the code placed in the
>> "drivers/gpu/drm/simpledrm" folder be placed under a more permissive
>> license setup. I would suggest using MIT[1], BSD 2-Clause[2], or BSD
>> 3-clause[3]. Also as a quick note it is possible to enable this under a
>> dual-license setup with GPLv2/BSD-3 Clause. An example a dual licensed
>> driver is the Intel iwlwifi driver ( see:
>> drivers/net/wireless/intel/iwlwifi/iwl-8000.c )
>
> tbh, most if not all arm drivers are gpl only, and due to code sharing and
> refactoring I'd say a lot of that has leaked all over drm. IANAL and all
> that, but personally I believe that the entire idea of drm being MIT is
> walking on ever thinner ice. And personally I'm not going to extend effort
> to slow this down or prevent it outright, since I think all that code
> sharing with arm folks is extremely beneficial for everyone. At least here
> on Linux.
> -Daniel

On top of that: Feel free to copy SimpleDRM in any way possible.
Consider my original implementation public domain.

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

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

* Re: [PATCH 0/2] drm: add SimpleDRM driver
  2016-08-04 19:50 Ken Phillis Jr
@ 2016-08-04 20:01 ` Daniel Vetter
  2016-08-04 20:07   ` David Herrmann
  0 siblings, 1 reply; 31+ messages in thread
From: Daniel Vetter @ 2016-08-04 20:01 UTC (permalink / raw)
  To: Ken Phillis Jr; +Cc: dri-devel

On Thu, Aug 04, 2016 at 02:50:37PM -0500, Ken Phillis Jr wrote:
> I believe this driver is extremely useful, and I see possible issues with
> the fact that the driver is GPL Only. This driver is critical for devices
> that lack a proper DRM driver, and locking it into GPL Only could lead to
> issues with porting the system over to platforms like FreeBSD,
> DragonFlyBSD, etc.  Is there any chance the code placed in the
> "drivers/gpu/drm/simpledrm" folder be placed under a more permissive
> license setup. I would suggest using MIT[1], BSD 2-Clause[2], or BSD
> 3-clause[3]. Also as a quick note it is possible to enable this under a
> dual-license setup with GPLv2/BSD-3 Clause. An example a dual licensed
> driver is the Intel iwlwifi driver ( see:
> drivers/net/wireless/intel/iwlwifi/iwl-8000.c )

tbh, most if not all arm drivers are gpl only, and due to code sharing and
refactoring I'd say a lot of that has leaked all over drm. IANAL and all
that, but personally I believe that the entire idea of drm being MIT is
walking on ever thinner ice. And personally I'm not going to extend effort
to slow this down or prevent it outright, since I think all that code
sharing with arm folks is extremely beneficial for everyone. At least here
on Linux.
-Daniel

> 
> 
> [1]https://opensource.org/licenses/mit-license.html
> [2]https://opensource.org/licenses/BSD-2-Clause
> [3]https://opensource.org/licenses/BSD-3-Clause

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


-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* re: [PATCH 0/2] drm: add SimpleDRM driver
@ 2016-08-04 19:50 Ken Phillis Jr
  2016-08-04 20:01 ` Daniel Vetter
  0 siblings, 1 reply; 31+ messages in thread
From: Ken Phillis Jr @ 2016-08-04 19:50 UTC (permalink / raw)
  To: dri-devel


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

I believe this driver is extremely useful, and I see possible issues with
the fact that the driver is GPL Only. This driver is critical for devices
that lack a proper DRM driver, and locking it into GPL Only could lead to
issues with porting the system over to platforms like FreeBSD,
DragonFlyBSD, etc.  Is there any chance the code placed in the
"drivers/gpu/drm/simpledrm" folder be placed under a more permissive
license setup. I would suggest using MIT[1], BSD 2-Clause[2], or BSD
3-clause[3]. Also as a quick note it is possible to enable this under a
dual-license setup with GPLv2/BSD-3 Clause. An example a dual licensed
driver is the Intel iwlwifi driver ( see:
drivers/net/wireless/intel/iwlwifi/iwl-8000.c )


[1]https://opensource.org/licenses/mit-license.html
[2]https://opensource.org/licenses/BSD-2-Clause
[3]https://opensource.org/licenses/BSD-3-Clause

[-- Attachment #1.2: Type: text/html, Size: 1102 bytes --]

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

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

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

end of thread, other threads:[~2016-08-05  8:28 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-04 14:03 [PATCH 0/2] drm: add SimpleDRM driver Noralf Trønnes
2016-08-04 14:03 ` Noralf Trønnes
2016-08-04 14:03 ` [PATCH 1/2] " Noralf Trønnes
2016-08-04 14:03   ` Noralf Trønnes
2016-08-04 14:45   ` Daniel Vetter
2016-08-04 14:45     ` Daniel Vetter
2016-08-04 17:05   ` Noralf Trønnes
2016-08-04 17:12     ` Daniel Vetter
2016-08-04 14:03 ` [PATCH 2/2] drm: simpledrm: add fbdev fallback support Noralf Trønnes
2016-08-04 14:03   ` Noralf Trønnes
2016-08-04 14:15 ` [PATCH 0/2] drm: add SimpleDRM driver Luc Verhaegen
2016-08-04 14:15   ` Luc Verhaegen
2016-08-04 15:08   ` Daniel Vetter
2016-08-04 15:34     ` Luc Verhaegen
2016-08-04 15:44       ` David Herrmann
2016-08-04 15:44         ` David Herrmann
2016-08-04 15:59         ` Luc Verhaegen
2016-08-04 17:10           ` Daniel Vetter
2016-08-04 18:08     ` One Thousand Gnomes
2016-08-04 18:08       ` One Thousand Gnomes
2016-08-04 16:58   ` Noralf Trønnes
2016-08-04 16:58     ` Noralf Trønnes
2016-08-04 18:12     ` Luc Verhaegen
2016-08-05  7:18       ` Hans de Goede
2016-08-04 14:36 ` Daniel Vetter
2016-08-04 17:30   ` Noralf Trønnes
2016-08-04 19:50 Ken Phillis Jr
2016-08-04 20:01 ` Daniel Vetter
2016-08-04 20:07   ` David Herrmann
2016-08-04 23:34     ` Ken Phillis Jr
2016-08-05  8:28     ` Jani Nikula

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.