2014-01-23 14:15:51

by David Herrmann

[permalink] [raw]
Subject: [PATCH 00/11] SimpleDRM & Sysfb

Hi

Another round of SimpleDRM patches. I somehow lost track of the last ones and as
this is a major rewrite, I'll just start at v1 again.

Some comments up-front:

- @Ingo: Patch #1 and #2 are unchanged from the previous ML discussions. I
included them in this series as the other patches depend on them. Could you
pick them up for the x86 tree? The other 9 patches won't make it in 3.14 so
no reason to put them through the DRM tree.
All mentioned issues should be addressed. If there's still sth missing,
please let me know.

- The DRM patches depend on my "DRM Anonymous Inode" patches. But it should be
trivial to apply them on drm-next (I think only one line needs to be changed:
i_mapping => dev_mapping).

- I tested the SimpleDRM fbdev fallback with linux-console+Xorg and it works
fine. The DRM backend is only tested with some DRM tests I have locally. I
have no idea how to make Xorg pick up a specific /dev/dri/card0 card. It
always tells me "no screens found" (as the underlying device is not marked as
boot_vga..). If someone knows how to tell Xorg to use card0, I'd gladly test
this. But I'm no longer used to writing xorg.confs..


This series introduces two new concepts: sysfb and SimpleDRM
Sysfb is just a generalization of the x86-sysfb concept. It allows to register
firmware-framebuffers with the system as platform-devices. This way, drivers can
properly bind to these devices and we prevent multiple drivers from accessing
the same firmware-framebuffer.
Sysfb also provides hooks to get a safe handover to real hw-drivers (like i915).
Please see the "video: sysfb: add generic firmware-fb interface" patch for a
thorough description of the API. This patch also adds a rather verbose
documentation of all known firmware-fb facilities.

As second part, this series introduces SimpleDRM. It's a very basic DRM driver
that can replace efifb, vesafb, simplefb and friends. It's 100% compatible to
the "udl" DRM driver, so user-space like xf86-video-modesetting can pick them up
just fine. User-space that cannot deal with drmModeDirtyFB() (like weston and
friends) currently cannot use SimpleDRM. However, that's also true for all other
DRM drivers which provide shadow framebuffers. We could provide something like
FB-DEFIO, but that's just useless overhead to paper of lazy user-space.

I have tested this with all hardware that I have at home, with a lot hand-over
combinations (with/without SYSFB, with efifb/vesafb/simplefb, with SimpleDRM,
...) and all worked great so far.

Comments welcome!
David

David Herrmann (11):
x86: sysfb: fool-proof CONFIG_X86_SYSFB
x86: sysfb: remove sysfb when probing real hw
fbdev: efifb: add dev->remove() callback
fbdev: vesafb: add dev->remove() callback
x86: sysfb: store apertures in simplefb platform-data
video: sysfb: add generic firmware-fb interface
drm: mgag200: remove redundant fbdev removal
drm/i915: remove sysfbs early
drm: add SimpleDRM driver
drm: simpledrm: add fbdev fallback support
x86/sysfb: allow sysfb+simpledrm combination

Documentation/firmware-fbs.txt | 236 +++++++++++++++++
MAINTAINERS | 8 +
arch/x86/Kconfig | 2 +
arch/x86/include/asm/sysfb.h | 6 +-
arch/x86/kernel/sysfb.c | 3 +-
arch/x86/kernel/sysfb_simplefb.c | 97 ++++---
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/i915/i915_drv.c | 6 +
drivers/gpu/drm/mgag200/mgag200_main.c | 9 -
drivers/gpu/drm/simpledrm/Kconfig | 29 +++
drivers/gpu/drm/simpledrm/Makefile | 4 +
drivers/gpu/drm/simpledrm/simpledrm.c | 263 +++++++++++++++++++
drivers/gpu/drm/simpledrm/simpledrm.h | 122 +++++++++
drivers/gpu/drm/simpledrm/simpledrm_damage.c | 306 ++++++++++++++++++++++
drivers/gpu/drm/simpledrm/simpledrm_fbdev.c | 148 +++++++++++
drivers/gpu/drm/simpledrm/simpledrm_gem.c | 282 +++++++++++++++++++++
drivers/gpu/drm/simpledrm/simpledrm_kms.c | 365 +++++++++++++++++++++++++++
drivers/video/Kconfig | 3 +
drivers/video/Makefile | 1 +
drivers/video/efifb.c | 13 +-
drivers/video/fbmem.c | 17 +-
drivers/video/simplefb.c | 8 -
drivers/video/sysfb.c | 348 +++++++++++++++++++++++++
drivers/video/vesafb.c | 13 +-
include/linux/fb.h | 9 +-
include/linux/platform_data/simplefb.h | 2 +
include/linux/sysfb.h | 62 +++++
28 files changed, 2299 insertions(+), 66 deletions(-)
create mode 100644 Documentation/firmware-fbs.txt
create mode 100644 drivers/gpu/drm/simpledrm/Kconfig
create mode 100644 drivers/gpu/drm/simpledrm/Makefile
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm.c
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_fbdev.c
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_gem.c
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_kms.c
create mode 100644 drivers/video/sysfb.c
create mode 100644 include/linux/sysfb.h

--
1.8.5.3


2014-01-23 14:15:55

by David Herrmann

[permalink] [raw]
Subject: [PATCH 03/11] fbdev: efifb: add dev->remove() callback

If x86-sysfb platform-devices are removed from a system, we should
properly unload efifb. Otherwise, we end up releasing the parent while our
efi framebuffer is still running. This currently works just fine, but will
cause problems on handover to real hw. So add the ->remove() callback and
unregister efifb.

Signed-off-by: David Herrmann <[email protected]>
---
drivers/video/efifb.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c
index cd7c0df..ae9618f 100644
--- a/drivers/video/efifb.c
+++ b/drivers/video/efifb.c
@@ -73,7 +73,6 @@ static void efifb_destroy(struct fb_info *info)
release_mem_region(info->apertures->ranges[0].base,
info->apertures->ranges[0].size);
fb_dealloc_cmap(&info->cmap);
- framebuffer_release(info);
}

static struct fb_ops efifb_ops = {
@@ -244,6 +243,7 @@ static int efifb_probe(struct platform_device *dev)
err = -ENOMEM;
goto err_release_mem;
}
+ platform_set_drvdata(dev, info);
info->pseudo_palette = info->par;
info->par = NULL;

@@ -337,12 +337,23 @@ err_release_mem:
return err;
}

+static int efifb_remove(struct platform_device *pdev)
+{
+ struct fb_info *info = platform_get_drvdata(pdev);
+
+ unregister_framebuffer(info);
+ framebuffer_release(info);
+
+ return 0;
+}
+
static struct platform_driver efifb_driver = {
.driver = {
.name = "efi-framebuffer",
.owner = THIS_MODULE,
},
.probe = efifb_probe,
+ .remove = efifb_remove,
};

module_platform_driver(efifb_driver);
--
1.8.5.3

2014-01-23 14:16:01

by David Herrmann

[permalink] [raw]
Subject: [PATCH 05/11] x86: sysfb: store apertures in simplefb platform-data

To get a generic remove_conflicting_framebuffers() for
firmware-framebuffers, we need to store the apertures in the platform-data
of each framebuffer. So make x86-sysfb do that for simple-framebuffer
devices.

Unfortunately, "struct apertures_struct" contains a VLA so we cannot
easily embed it. Thus, we have to use a "apert_buf" buffer with enough
room for one "struct aperture".

Signed-off-by: David Herrmann <[email protected]>
---
arch/x86/include/asm/sysfb.h | 6 +--
arch/x86/kernel/sysfb.c | 2 +-
arch/x86/kernel/sysfb_simplefb.c | 68 ++++++++++++++++++++--------------
include/linux/platform_data/simplefb.h | 1 +
4 files changed, 44 insertions(+), 33 deletions(-)

diff --git a/arch/x86/include/asm/sysfb.h b/arch/x86/include/asm/sysfb.h
index 6f95b8d..4f9fda2 100644
--- a/arch/x86/include/asm/sysfb.h
+++ b/arch/x86/include/asm/sysfb.h
@@ -81,8 +81,7 @@ static inline void sysfb_apply_efi_quirks(void)

bool parse_mode(const struct screen_info *si,
struct simplefb_platform_data *mode);
-int create_simplefb(const struct screen_info *si,
- const struct simplefb_platform_data *mode);
+int create_simplefb(const struct simplefb_platform_data *mode);

#else /* CONFIG_X86_SYSFB */

@@ -92,8 +91,7 @@ static inline bool parse_mode(const struct screen_info *si,
return false;
}

-static inline int create_simplefb(const struct screen_info *si,
- const struct simplefb_platform_data *mode)
+static inline int create_simplefb(const struct simplefb_platform_data *mode)
{
return -EINVAL;
}
diff --git a/arch/x86/kernel/sysfb.c b/arch/x86/kernel/sysfb.c
index ba9ff26..fd07b09 100644
--- a/arch/x86/kernel/sysfb.c
+++ b/arch/x86/kernel/sysfb.c
@@ -117,7 +117,7 @@ static __init int sysfb_init(void)
/* try to create a simple-framebuffer device */
compatible = parse_mode(si, &mode);
if (compatible) {
- ret = create_simplefb(si, &mode);
+ ret = create_simplefb(&mode);
if (!ret)
return 0;
}
diff --git a/arch/x86/kernel/sysfb_simplefb.c b/arch/x86/kernel/sysfb_simplefb.c
index a760d47..9338427 100644
--- a/arch/x86/kernel/sysfb_simplefb.c
+++ b/arch/x86/kernel/sysfb_simplefb.c
@@ -31,9 +31,12 @@ static const struct simplefb_format formats[] = SIMPLEFB_FORMATS;
__init bool parse_mode(const struct screen_info *si,
struct simplefb_platform_data *mode)
{
+ struct apertures_struct *apert = (void*)mode->apert_buf;
const struct simplefb_format *f;
+ unsigned long len;
__u8 type;
unsigned int i;
+ u64 size;

type = si->orig_video_isVGA;
if (type != VIDEO_TYPE_VLFB && type != VIDEO_TYPE_EFI)
@@ -41,47 +44,56 @@ __init bool parse_mode(const struct screen_info *si,

for (i = 0; i < ARRAY_SIZE(formats); ++i) {
f = &formats[i];
- if (si->lfb_depth == f->bits_per_pixel &&
- si->red_size == f->red.length &&
- si->red_pos == f->red.offset &&
- si->green_size == f->green.length &&
- si->green_pos == f->green.offset &&
- si->blue_size == f->blue.length &&
- si->blue_pos == f->blue.offset &&
- si->rsvd_size == f->transp.length &&
- si->rsvd_pos == f->transp.offset) {
- mode->format = f->name;
- mode->width = si->lfb_width;
- mode->height = si->lfb_height;
- mode->stride = si->lfb_linelength;
- return true;
+ if (si->lfb_depth != f->bits_per_pixel ||
+ si->red_size != f->red.length ||
+ si->red_pos != f->red.offset ||
+ si->green_size != f->green.length ||
+ si->green_pos != f->green.offset ||
+ si->blue_size != f->blue.length ||
+ si->blue_pos != f->blue.offset ||
+ si->rsvd_size != f->transp.length ||
+ si->rsvd_pos != f->transp.offset)
+ continue;
+
+ /* lfb_size is in bytes except for VLFB it's in 64kb blocks */
+ size = si->lfb_size;
+ if (type == VIDEO_TYPE_VLFB)
+ size <<= 16;
+
+ /* don't use lfb_size as it may contain the whole VMEM instead
+ * of only the part that is occupied by the framebuffer */
+ len = si->lfb_height * (unsigned long)si->lfb_linelength;
+ if (len > size) {
+ printk(KERN_WARNING "sysfb: VRAM smaller than advertised framebuffer (%llu > %llu)\n",
+ (unsigned long long)len,
+ (unsigned long long)size);
+ return false;
}
+
+ mode->format = f->name;
+ mode->width = si->lfb_width;
+ mode->height = si->lfb_height;
+ mode->stride = si->lfb_linelength;
+ apert->count = 1;
+ apert->ranges[0].base = si->lfb_base;
+ apert->ranges[0].size = len;
+ return true;
}

return false;
}

-__init int create_simplefb(const struct screen_info *si,
- const struct simplefb_platform_data *mode)
+__init int create_simplefb(const struct simplefb_platform_data *mode)
{
+ const struct apertures_struct *apert = (void*)mode->apert_buf;
struct resource res;
- unsigned long len;
-
- /* don't use lfb_size as it may contain the whole VMEM instead of only
- * the part that is occupied by the framebuffer */
- len = mode->height * mode->stride;
- len = PAGE_ALIGN(len);
- if (len > (u64)si->lfb_size << 16) {
- printk(KERN_WARNING "sysfb: VRAM smaller than advertised\n");
- return -EINVAL;
- }

/* setup IORESOURCE_MEM as framebuffer memory */
memset(&res, 0, sizeof(res));
res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
res.name = simplefb_resname;
- res.start = si->lfb_base;
- res.end = si->lfb_base + len - 1;
+ res.start = apert->ranges[0].base;
+ res.end = res.start + apert->ranges[0].size - 1;
if (res.end <= res.start)
return -EINVAL;

diff --git a/include/linux/platform_data/simplefb.h b/include/linux/platform_data/simplefb.h
index 077303c..21983cc 100644
--- a/include/linux/platform_data/simplefb.h
+++ b/include/linux/platform_data/simplefb.h
@@ -59,6 +59,7 @@ struct simplefb_platform_data {
u32 height;
u32 stride;
const char *format;
+ u8 apert_buf[sizeof(struct apertures_struct) + sizeof(struct aperture)];
};

#endif /* __PLATFORM_DATA_SIMPLEFB_H__ */
--
1.8.5.3

2014-01-23 14:16:16

by David Herrmann

[permalink] [raw]
Subject: [PATCH 11/11] x86/sysfb: allow sysfb+simpledrm combination

We used to protect X86_SYSFB by depending on FB_SIMPLE so users don't
accidentally end up without a kernel console. Now that DRM_SIMPLEDRM also
provides a simple-framebuffer driver, we can whitelist this driver, too.
---
arch/x86/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 93df439..f3eae6a 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2299,7 +2299,7 @@ source "drivers/rapidio/Kconfig"

config X86_SYSFB
bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
- depends on FB_SIMPLE
+ depends on FB_SIMPLE || DRM_SIMPLEDRM
select SYSFB
help
Firmwares often provide initial graphics framebuffers so the BIOS,
--
1.8.5.3

2014-01-23 14:16:12

by David Herrmann

[permalink] [raw]
Subject: [PATCH 08/11] drm/i915: remove sysfbs early

Once we allow DRM drivers for system-framebuffers, we need to evict such
devices *before* probing the real driver. A simple call to sysfb_claim()
does this and remove_conflicting_framebuffers() implicitly calls this.
However, it causes the sysfb device to be unloaded and thus locks
drm_global_mutex. remove_conflicting_framebuffers() must be called from
outside any ->load() callback to avoid a dead-lock.

All other DRM drivers call this right before probing the pci-device, which
is fine. For i915 we need to figure out the apertures before we can evict
fw-framebuffers, though. This turns out to be not as easy as you might
think, so lets just evict all sysfbs for now before loading i915.

A proper fix would be to make DRM code allow parallel device probing.
That's not going to happen soon, so be safe and make i915 evict all
sysfbs.

Signed-off-by: David Herrmann <[email protected]>
---
drivers/gpu/drm/i915/i915_drv.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 43245b3..ceb875a 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -838,6 +838,12 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)

driver.driver_features &= ~(DRIVER_USE_AGP);

+ /* We cannot call remove_conflicting_framebuffers() here as we cannot
+ * easily figure out the apertures here. So lets just remove all
+ * system-framebuffers early so we don't deadlock later when calling it
+ * with drm_global_mutex held. */
+ sysfb_claim(NULL, SYSFB_CLAIM_ALL);
+
return drm_get_pci_dev(pdev, ent, &driver);
}

--
1.8.5.3

2014-01-23 14:16:44

by David Herrmann

[permalink] [raw]
Subject: [PATCH 10/11] drm: simpledrm: add fbdev fallback support

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

Signed-off-by: David Herrmann <[email protected]>
---
drivers/gpu/drm/simpledrm/Kconfig | 11 +++
drivers/gpu/drm/simpledrm/Makefile | 1 +
drivers/gpu/drm/simpledrm/simpledrm.c | 13 ++-
drivers/gpu/drm/simpledrm/simpledrm.h | 22 +++++
drivers/gpu/drm/simpledrm/simpledrm_fbdev.c | 148 ++++++++++++++++++++++++++++
5 files changed, 194 insertions(+), 1 deletion(-)
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 35bcce8..eef2a36 100644
--- a/drivers/gpu/drm/simpledrm/Kconfig
+++ b/drivers/gpu/drm/simpledrm/Kconfig
@@ -12,7 +12,18 @@ 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
module will be called simpledrm.
+
+config DRM_SIMPLEDRM_FBDEV
+ bool
+ depends on DRM_SIMPLEDRM && FB
+ default y
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile
index 35f73ea..363b17d 100644
--- a/drivers/gpu/drm/simpledrm/Makefile
+++ b/drivers/gpu/drm/simpledrm/Makefile
@@ -1,3 +1,4 @@
sdrm-y := simpledrm.o simpledrm_kms.o simpledrm_gem.o simpledrm_damage.o
+sdrm-$(CONFIG_DRM_SIMPLEDRM_FBDEV) += simpledrm_fbdev.o

obj-$(CONFIG_DRM_SIMPLEDRM) := sdrm.o
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.c b/drivers/gpu/drm/simpledrm/simpledrm.c
index 98273f5..4346bcd 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm.c
+++ b/drivers/gpu/drm/simpledrm/simpledrm.c
@@ -192,7 +192,17 @@ void sdrm_pdev_destroy(struct sdrm_device *sdrm)

static int sdrm_simplefb_probe(struct platform_device *pdev)
{
- return drm_platform_init(&sdrm_drm_driver, pdev);
+ struct drm_device *ddev;
+ int r;
+
+ r = drm_platform_init(&sdrm_drm_driver, pdev);
+ if (r < 0)
+ return r;
+
+ ddev = platform_get_drvdata(pdev);
+ sdrm_fbdev_init(ddev->dev_private);
+
+ return 0;
}

static int sdrm_simplefb_remove(struct platform_device *pdev)
@@ -205,6 +215,7 @@ static int sdrm_simplefb_remove(struct platform_device *pdev)
* CPU is currently doing some GEM-bo access in parallel to us. But, eh,
* what can we do.. Fixes for that is being worked on. */

+ sdrm_fbdev_cleanup(sdrm);
drm_connector_unplug_all(ddev);

/* protect fb_map removal against sdrm_blit() */
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h
index 4ece7f5..7050594 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm.h
+++ b/drivers/gpu/drm/simpledrm/simpledrm.h
@@ -45,6 +45,9 @@ struct sdrm_device {
struct drm_encoder enc;
struct drm_connector conn;
struct drm_display_mode *mode;
+
+ /* fbdev */
+ struct fb_info *fbdev;
};

int sdrm_drm_load(struct drm_device *ddev, unsigned long flags);
@@ -97,4 +100,23 @@ struct sdrm_framebuffer {

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

+/* simpledrm fbdev helpers */
+
+#ifdef CONFIG_DRM_SIMPLEDRM_FBDEV
+
+void sdrm_fbdev_init(struct sdrm_device *sdrm);
+void sdrm_fbdev_cleanup(struct sdrm_device *sdrm);
+
+#else /* CONFIG_DRM_SIMPLEDRM_FBDEV */
+
+static inline void sdrm_fbdev_init(struct sdrm_device *sdrm)
+{
+}
+
+static inline void sdrm_fbdev_cleanup(struct sdrm_device *sdrm)
+{
+}
+
+#endif /* CONFIG_DRM_SIMPLEDRM_FBDEV */
+
#endif /* SDRM_DRV_H */
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
new file mode 100644
index 0000000..5df1482
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
@@ -0,0 +1,148 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <[email protected]>
+ *
+ * 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/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;
+ 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;
+
+ sdrm->fbdev = info;
+ ret = register_framebuffer(info);
+ if (ret < 0)
+ goto err_free;
+
+ dev_info(sdrm->ddev->dev, "fbdev frontend %s as fb%d\n",
+ info->fix.id, info->node);
+
+ return;
+
+err_free:
+ framebuffer_release(info);
+ sdrm->fbdev = NULL;
+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);
+}
--
1.8.5.3

2014-01-23 14:16:09

by David Herrmann

[permalink] [raw]
Subject: [PATCH 07/11] drm: mgag200: remove redundant fbdev removal

We already call remove_conflicting_framebuffers() on PCI BAR0 during
pci-probe, no need to do that again during device loading.

This avoids calling into remove_conflicting_framebuffers() from within DRM
->load() callback, which might deadlock, once we make this call remove
DRM-backed sysfb-devices, too.

Signed-off-by: David Herrmann <[email protected]>
---
drivers/gpu/drm/mgag200/mgag200_main.c | 9 ---------
1 file changed, 9 deletions(-)

diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c
index 26868e5..0ee093c 100644
--- a/drivers/gpu/drm/mgag200/mgag200_main.c
+++ b/drivers/gpu/drm/mgag200/mgag200_main.c
@@ -117,20 +117,11 @@ static int mga_probe_vram(struct mga_device *mdev, void __iomem *mem)
static int mga_vram_init(struct mga_device *mdev)
{
void __iomem *mem;
- struct apertures_struct *aper = alloc_apertures(1);
- if (!aper)
- return -ENOMEM;

/* BAR 0 is VRAM */
mdev->mc.vram_base = pci_resource_start(mdev->dev->pdev, 0);
mdev->mc.vram_window = pci_resource_len(mdev->dev->pdev, 0);

- aper->ranges[0].base = mdev->mc.vram_base;
- aper->ranges[0].size = mdev->mc.vram_window;
-
- remove_conflicting_framebuffers(aper, "mgafb", true);
- kfree(aper);
-
if (!devm_request_mem_region(mdev->dev->dev, mdev->mc.vram_base, mdev->mc.vram_window,
"mgadrmfb_vram")) {
DRM_ERROR("can't reserve VRAM\n");
--
1.8.5.3

2014-01-23 14:17:06

by David Herrmann

[permalink] [raw]
Subject: [PATCH 09/11] drm: add SimpleDRM driver

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
convertion-helper.

Signed-off-by: David Herrmann <[email protected]>
---
MAINTAINERS | 8 +
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/simpledrm/Kconfig | 18 ++
drivers/gpu/drm/simpledrm/Makefile | 3 +
drivers/gpu/drm/simpledrm/simpledrm.c | 252 ++++++++++++++++++
drivers/gpu/drm/simpledrm/simpledrm.h | 100 ++++++++
drivers/gpu/drm/simpledrm/simpledrm_damage.c | 306 ++++++++++++++++++++++
drivers/gpu/drm/simpledrm/simpledrm_gem.c | 282 +++++++++++++++++++++
drivers/gpu/drm/simpledrm/simpledrm_kms.c | 365 +++++++++++++++++++++++++++
10 files changed, 1337 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.c
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_gem.c
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_kms.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 31a0462..e7adc84 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7725,6 +7725,14 @@ S: Odd Fixes
F: drivers/media/platform/sh_vou.c
F: include/media/sh_vou.h

+SIMPLE DRM DRIVER
+M: David Herrmann <[email protected]>
+L: [email protected]
+T: git git://people.freedesktop.org/~dvdhrm/linux
+S: Maintained
+F: drivers/gpu/drm/simpledrm
+F: include/linux/platform_data/simpledrm.h
+
SIMPLE FIRMWARE INTERFACE (SFI)
M: Len Brown <[email protected]>
L: [email protected]
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 8e7fa4d..f795359 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -199,3 +199,5 @@ source "drivers/gpu/drm/msm/Kconfig"
source "drivers/gpu/drm/tegra/Kconfig"

source "drivers/gpu/drm/panel/Kconfig"
+
+source "drivers/gpu/drm/simpledrm/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 292a79d..7b27e07 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -61,5 +61,6 @@ obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_BOCHS) += bochs/
obj-$(CONFIG_DRM_MSM) += msm/
obj-$(CONFIG_DRM_TEGRA) += tegra/
+obj-$(CONFIG_DRM_SIMPLEDRM) += simpledrm/
obj-y += i2c/
obj-y += panel/
diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig
new file mode 100644
index 0000000..35bcce8
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/Kconfig
@@ -0,0 +1,18 @@
+config DRM_SIMPLEDRM
+ tristate "Simple firmware framebuffer DRM driver"
+ depends on DRM && (FB_SIMPLE = n)
+ 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..35f73ea
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/Makefile
@@ -0,0 +1,3 @@
+sdrm-y := simpledrm.o simpledrm_kms.o simpledrm_gem.o simpledrm_damage.o
+
+obj-$(CONFIG_DRM_SIMPLEDRM) := sdrm.o
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.c b/drivers/gpu/drm/simpledrm/simpledrm.c
new file mode 100644
index 0000000..98273f5
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm.c
@@ -0,0 +1,252 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <[email protected]>
+ *
+ * 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_MODESET | DRIVER_GEM,
+ .load = sdrm_drm_load,
+ .unload = sdrm_drm_unload,
+ .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;
+ }
+
+ 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)
+{
+ return drm_platform_init(&sdrm_drm_driver, pdev);
+}
+
+static int sdrm_simplefb_remove(struct platform_device *pdev)
+{
+ struct drm_device *ddev = platform_get_drvdata(pdev);
+ struct sdrm_device *sdrm = ddev->dev_private;
+
+ /* Oh, god! This is racy, but always has been. Unless we finally get
+ * immediate device removal in DRM, we can never guarantee that no other
+ * CPU is currently doing some GEM-bo access in parallel to us. But, eh,
+ * what can we do.. Fixes for that is being worked on. */
+
+ drm_connector_unplug_all(ddev);
+
+ /* protect fb_map removal against sdrm_blit() */
+ drm_modeset_lock_all(ddev);
+ sdrm_pdev_destroy(sdrm);
+ drm_modeset_unlock_all(ddev);
+
+ drm_unplug_dev(ddev);
+
+ 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 <[email protected]>");
+MODULE_DESCRIPTION("Simple firmware framebuffer DRM driver");
+MODULE_ALIAS("platform:simple-framebuffer");
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h
new file mode 100644
index 0000000..4ece7f5
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm.h
@@ -0,0 +1,100 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <[email protected]>
+ *
+ * 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>
+
+struct sdrm_device;
+struct sdrm_gem_object;
+struct sdrm_framebuffer;
+
+/* simpledrm devices */
+
+struct sdrm_device {
+ struct drm_device *ddev;
+
+ /* 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;
+
+ /* mode-setting objects */
+ struct drm_crtc crtc;
+ struct drm_encoder enc;
+ struct drm_connector conn;
+ struct drm_display_mode *mode;
+};
+
+int sdrm_drm_load(struct drm_device *ddev, unsigned long flags);
+int sdrm_drm_unload(struct drm_device *ddev);
+int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma);
+int sdrm_pdev_init(struct sdrm_device *sdrm);
+void sdrm_pdev_destroy(struct sdrm_device *sdrm);
+
+int sdrm_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips);
+int sdrm_dirty_all_locked(struct sdrm_device *sdrm);
+int sdrm_dirty_all_unlocked(struct sdrm_device *sdrm);
+
+/* simpledrm gem objects */
+
+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);
+
+/* dumb buffers */
+
+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);
+
+/* simpledrm framebuffers */
+
+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..67a2841
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_damage.c
@@ -0,0 +1,306 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <[email protected]>
+ *
+ * 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 &= 0x1f;
+ g &= 0x3f;
+ b &= 0x1f;
+ put_unaligned((u16)((r << 11) | (g << 5) | b), (u16*)dst);
+ break;
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_ARGB1555:
+ r &= 0x1f;
+ g &= 0x1f;
+ b &= 0x1f;
+ put_unaligned((u16)((r << 10) | (g << 5) | b), (u16*)dst);
+ break;
+ case DRM_FORMAT_RGB888:
+ r &= 0xff;
+ g &= 0xff;
+ b &= 0xff;
+#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 &= 0xff;
+ g &= 0xff;
+ b &= 0xff;
+ put_unaligned((u32)((r << 16) | (g << 8) | b), (u32*)dst);
+ break;
+ case DRM_FORMAT_ABGR8888:
+ r &= 0xff;
+ g &= 0xff;
+ b &= 0xff;
+ put_unaligned((u32)((b << 16) | (g << 8) | r), (u32*)dst);
+ break;
+ case DRM_FORMAT_XRGB2101010:
+ case DRM_FORMAT_ARGB2101010:
+ r &= 0x3ff;
+ g &= 0x3ff;
+ b &= 0x3ff;
+ 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) >> 16,
+ (val & 0x0000ff00U) >> 8,
+ (val & 0x000000ffU));
+ }
+
+ 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) >> 11,
+ (val & 0x07e0) >> 5,
+ (val & 0x001f));
+ }
+
+ 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->crtc.fb == fb) {
+ xoff = sdrm->crtc.x;
+ yoff = sdrm->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,
+ 0, sfb->obj->base.size,
+ 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,
+ 0, sfb->obj->base.size,
+ DMA_FROM_DEVICE);
+}
+
+int sdrm_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips)
+{
+ struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
+ struct drm_device *ddev = fb->dev;
+ struct sdrm_device *sdrm = ddev->dev_private;
+ unsigned int i;
+ int r;
+
+ drm_modeset_lock_all(ddev);
+
+ if (sdrm->crtc.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->crtc.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_gem.c b/drivers/gpu/drm/simpledrm/simpledrm_gem.c
new file mode 100644
index 0000000..f12c56c
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c
@@ -0,0 +1,282 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <[email protected]>
+ *
+ * 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 "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(&obj->base);
+ return r;
+ }
+
+ /* handle owns a reference */
+ drm_gem_object_unreference(&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(ddev, 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;
+
+ mutex_lock(&dev->struct_mutex);
+
+ node = drm_vma_offset_exact_lookup(dev->vma_offset_manager,
+ vma->vm_pgoff,
+ vma_pages(vma));
+ if (!node) {
+ mutex_unlock(&dev->struct_mutex);
+ return drm_mmap(filp, vma);
+ } else if (!drm_vma_node_is_allowed(node, filp)) {
+ r = -EACCES;
+ goto out_unlock;
+ }
+
+ 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) {
+ r = -EINVAL;
+ goto out_unlock;
+ }
+
+ r = sdrm_gem_get_pages(obj);
+ if (r < 0)
+ goto out_unlock;
+
+ /* prevent dmabuf-imported mmap to user-space */
+ if (!obj->pages) {
+ r = -EACCES;
+ goto out_unlock;
+ }
+
+ 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);
+ goto out_unlock;
+ }
+ }
+
+ r = 0;
+
+out_unlock:
+ mutex_unlock(&dev->struct_mutex);
+ return r;
+}
+
+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..3e686bb
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
@@ -0,0 +1,365 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <[email protected]>
+ *
+ * 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_crtc.h>
+#include "simpledrm.h"
+
+/* crtcs */
+
+static int sdrm_crtc_set_config(struct drm_mode_set *set)
+{
+ struct drm_device *ddev;
+ struct sdrm_device *sdrm;
+ struct sdrm_framebuffer *fb;
+
+ if (!set || !set->crtc)
+ return -EINVAL;
+
+ ddev = set->crtc->dev;
+ sdrm = ddev->dev_private;
+
+ if (set->crtc != &sdrm->crtc)
+ return -EINVAL;
+
+ if (!set->mode || !set->fb || !set->num_connectors) {
+ sdrm->conn.encoder = NULL;
+ sdrm->conn.dpms = DRM_MODE_DPMS_OFF;
+ sdrm->enc.crtc = NULL;
+ sdrm->crtc.fb = NULL;
+ sdrm->crtc.enabled = false;
+ return 0;
+ }
+
+ fb = to_sdrm_fb(set->fb);
+
+ if (set->num_connectors != 1 || set->connectors[0] != &sdrm->conn)
+ return -EINVAL;
+ if (set->mode->hdisplay != sdrm->fb_width ||
+ set->mode->vdisplay != sdrm->fb_height)
+ return -EINVAL;
+ if (fb->base.width <= set->x ||
+ fb->base.height <= set->y ||
+ fb->base.width - set->x < sdrm->fb_width ||
+ fb->base.height - set->y < sdrm->fb_height)
+ return -EINVAL;
+
+ sdrm->conn.encoder = &sdrm->enc;
+ sdrm->conn.dpms = DRM_MODE_DPMS_ON;
+ sdrm->enc.crtc = &sdrm->crtc;
+ sdrm->crtc.fb = set->fb;
+ sdrm->crtc.enabled = true;
+ sdrm->crtc.mode = *set->mode;
+ sdrm->crtc.hwmode = *set->mode;
+ sdrm->crtc.x = set->x;
+ sdrm->crtc.y = set->y;
+
+ drm_calc_timestamping_constants(&sdrm->crtc, &sdrm->crtc.hwmode);
+ sdrm_dirty_all_locked(sdrm);
+ return 0;
+}
+
+static const struct drm_crtc_funcs sdrm_crtc_ops = {
+ .set_config = sdrm_crtc_set_config,
+ .destroy = drm_crtc_cleanup,
+};
+
+/* encoders */
+
+static const struct drm_encoder_funcs sdrm_enc_ops = {
+ .destroy = drm_encoder_cleanup,
+};
+
+/* connectors */
+
+static void sdrm_conn_dpms(struct drm_connector *conn, int mode)
+{
+ conn->dpms = mode;
+}
+
+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 int sdrm_conn_fill_modes(struct drm_connector *conn, uint32_t max_x,
+ uint32_t max_y)
+{
+ struct sdrm_device *sdrm = conn->dev->dev_private;
+ struct drm_display_mode *mode;
+ int r;
+
+ if (conn->force == DRM_FORCE_ON)
+ conn->status = connector_status_connected;
+ else if (conn->force)
+ conn->status = connector_status_disconnected;
+ else
+ conn->status = connector_status_connected;
+
+ list_for_each_entry(mode, &conn->modes, head)
+ mode->status = MODE_UNVERIFIED;
+
+ mode = drm_mode_create(sdrm->ddev);
+ if (!mode) {
+ r = 0;
+ } else {
+ /* some values with 10% vsync/hsync to make user-space happy */
+ mode->hdisplay = sdrm->fb_width;
+ mode->hsync_start = mode->hdisplay;
+ mode->hsync_end = mode->hdisplay * 11 / 10;
+ mode->htotal = mode->hsync_end;
+
+ mode->vdisplay = sdrm->fb_height;
+ mode->vsync_start = mode->vdisplay;
+ mode->vsync_end = mode->vdisplay * 11 / 10;
+ mode->vtotal = mode->vsync_end;
+
+ mode->vrefresh = 60;
+ mode->clock = mode->htotal * mode->vtotal * mode->vrefresh;
+ mode->clock /= 1000; /* Hz => kHz */
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+ mode->crtc_htotal = mode->htotal;
+ mode->crtc_vtotal = mode->vtotal;
+ mode->crtc_clock = mode->clock;
+
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(conn, mode);
+ drm_mode_connector_list_update(conn);
+ r = 1;
+ }
+
+ if (max_x && max_y)
+ drm_mode_validate_size(conn->dev, &conn->modes,
+ max_x, max_y, 0);
+
+ drm_mode_prune_invalid(conn->dev, &conn->modes, false);
+ if (list_empty(&conn->modes))
+ return 0;
+
+ drm_mode_sort(&conn->modes);
+
+ list_for_each_entry(mode, &conn->modes, head) {
+ mode->vrefresh = drm_mode_vrefresh(mode);
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ }
+
+ return r;
+}
+
+static void sdrm_conn_destroy(struct drm_connector *conn)
+{
+ /* Remove the fake-connector from sysfs and then let the DRM core
+ * clean up all associated resources. */
+ drm_sysfs_connector_remove(conn);
+ drm_connector_cleanup(conn);
+}
+
+static const struct drm_connector_funcs sdrm_conn_ops = {
+ .dpms = sdrm_conn_dpms,
+ .detect = sdrm_conn_detect,
+ .fill_modes = sdrm_conn_fill_modes,
+ .destroy = sdrm_conn_destroy,
+};
+
+/* framebuffers */
+
+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,
+ 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);
+ if (cmd->pixel_format != DRM_FORMAT_ARGB8888 &&
+ cmd->pixel_format != DRM_FORMAT_XRGB8888 &&
+ cmd->pixel_format != DRM_FORMAT_RGB565)
+ return ERR_PTR(-EINVAL);
+
+ gobj = drm_gem_object_lookup(ddev, 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;
+ }
+
+ 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,
+};
+
+/* initialization */
+
+int sdrm_drm_load(struct drm_device *ddev, unsigned long flags)
+{
+ struct sdrm_device *sdrm;
+ int ret;
+
+ sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL);
+ if (!sdrm)
+ return -ENOMEM;
+
+ platform_set_drvdata(ddev->platformdev, ddev);
+ sdrm->ddev = ddev;
+ ddev->dev_private = sdrm;
+
+ ddev->devname = kstrdup("simpledrm", GFP_KERNEL);
+ if (!ddev->devname) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ ret = sdrm_pdev_init(sdrm);
+ if (ret)
+ goto err_name;
+
+ 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.funcs = &sdrm_mode_config_ops;
+
+ ret = drm_crtc_init(ddev, &sdrm->crtc, &sdrm_crtc_ops);
+ if (ret)
+ goto err_cleanup;
+
+ sdrm->enc.possible_crtcs = 1;
+ sdrm->enc.possible_clones = 0;
+ ret = drm_encoder_init(ddev, &sdrm->enc, &sdrm_enc_ops,
+ DRM_MODE_ENCODER_VIRTUAL);
+ if (ret)
+ goto err_cleanup;
+
+ sdrm->conn.display_info.width_mm = 0;
+ sdrm->conn.display_info.height_mm = 0;
+ sdrm->conn.interlace_allowed = false;
+ sdrm->conn.doublescan_allowed = false;
+ sdrm->conn.polled = 0;
+ ret = drm_connector_init(ddev, &sdrm->conn, &sdrm_conn_ops,
+ DRM_MODE_CONNECTOR_VIRTUAL);
+ if (ret)
+ goto err_cleanup;
+
+ ret = drm_mode_connector_attach_encoder(&sdrm->conn, &sdrm->enc);
+ if (ret)
+ goto err_cleanup;
+
+ ret = drm_mode_create_dirty_info_property(ddev);
+ if (ret)
+ goto err_cleanup;
+
+ ret = drm_sysfs_connector_add(&sdrm->conn);
+ if (ret)
+ goto err_cleanup;
+
+ return 0;
+
+err_cleanup:
+ drm_mode_config_cleanup(ddev);
+ sdrm_pdev_destroy(sdrm);
+err_name:
+ kfree(ddev->devname);
+ ddev->devname = NULL;
+err_free:
+ kfree(sdrm);
+ return ret;
+}
+
+int sdrm_drm_unload(struct drm_device *ddev)
+{
+ struct sdrm_device *sdrm = ddev->dev_private;
+
+ drm_mode_config_cleanup(ddev);
+ sdrm_pdev_destroy(sdrm);
+ kfree(sdrm);
+
+ return 0;
+}
--
1.8.5.3

2014-01-23 14:18:02

by David Herrmann

[permalink] [raw]
Subject: [PATCH 06/11] video: sysfb: add generic firmware-fb interface

We supported many different firmware-fbs in linux for a long time. On x86,
we tried to unify the different types into platform-devices so their
lifetime and drivers can be more easily controlled. This patch moves the
x86-specific sysfb_*() helpers into drivers/video/sysfb.c so other
architectures can make use of it, too.

The sysfb API consists of 4 functions:
sysfb_register()
sysfb_register_dyn()
sysfb_unregister()
sysfb_claim()

The first 3 can be used by architecture setup code to register
firmware-framebuffers with the system/sysfb. Once the framebuffers are
registered, matching platform-drivers will pick it up. Via
sysfb_unregister() devices can be manually removed again, in case this is
ever needed (x86 doesn't make use of this).

Real hw-drivers like i915/radeon/nouveau can use sysfb_claim() to evict
any firmware-framebuffer from the system before accessing the hw. This
guarantees that any previous driver (like vesafb/efifb) will be unloaded
before the real hw driver takes over.

The sysfb file contains a thorough API documentation which explains the
corner-cases and how we guarantee firmware-fb drivers can no longer
interfere with real-hw drivers. Additionally, a short documentation of the
different existing firmware-fbs and handover-mechanisms is put into
Documentation/firmware-fbs.txt.

Compared to remove_conflicting_framebuffers(), the sysfb interface is
independent of FBDEV. Thus, we can use it for DRM-only handovers.

Signed-off-by: David Herrmann <[email protected]>
---
Documentation/firmware-fbs.txt | 236 ++++++++++++++++++++++
arch/x86/Kconfig | 1 +
arch/x86/include/asm/sysfb.h | 5 -
arch/x86/kernel/sysfb.c | 64 ------
arch/x86/kernel/sysfb_simplefb.c | 26 ++-
drivers/video/Kconfig | 3 +
drivers/video/Makefile | 1 +
drivers/video/fbmem.c | 24 ++-
drivers/video/sysfb.c | 348 +++++++++++++++++++++++++++++++++
include/linux/fb.h | 9 +-
include/linux/platform_data/simplefb.h | 1 +
include/linux/sysfb.h | 62 ++++++
12 files changed, 692 insertions(+), 88 deletions(-)
create mode 100644 Documentation/firmware-fbs.txt
create mode 100644 drivers/video/sysfb.c
create mode 100644 include/linux/sysfb.h

diff --git a/Documentation/firmware-fbs.txt b/Documentation/firmware-fbs.txt
new file mode 100644
index 0000000..e0276ec
--- /dev/null
+++ b/Documentation/firmware-fbs.txt
@@ -0,0 +1,236 @@
+ Firmware Framebuffers
+----------------------------------------------------------------------------
+
+1. Intro
+~~~~~~~~
+Modern firmware often initializes the graphics hardware before booting the
+kernel. A basic framebuffer is created and used for single-buffered rendering.
+Linux can detect such framebuffers and provide them to user-space. Early
+user-space can use it to draw boot-splashs, disk-encryption prompts and more.
+Once all hardware has been probed, real graphics drivers may take over.
+
+This document describes which firmware framebuffers are currently supported and
+how the handover works.
+
+2. Supported Drivers
+~~~~~~~~~~~~~~~~~~~~
+There are fbdev and DRM drivers which can make use of firmware framebuffers.
+This currently includes:
+
+ fbdev:
+ - vesafb: Uses VBE/VESA graphics mode
+ - efifb: Uses EFI UGA/GOP
+ - simplefb: Binds to custom platform-devices (eg., via DT)
+ - offb: Binds to custom platform-devices via DT
+ - vga16: Uses x86 VGA mode
+ DRM:
+ - SimpleDRM: Binds to custom platform-devices (eg., via DT)
+
+Furthermore, other miscellaneous drivers make use of firmware-framebuffers or
+their properties. Their effect is discussed at the end of this document (this
+includes vgacon and friends).
+
+3. Underlying Devices
+~~~~~~~~~~~~~~~~~~~~~
+Firmware-framebuffer drivers use different techniques to detect devices and bind
+to them. Some of these are compatible, some not. This section describes the
+different device types.
+
+3.1 simple-framebuffer
+~~~~~~~~~~~~~~~~~~~~~~
+The newest and most compatible way to represent firmware-fbs is to create a
+"simple-framebuffer" platform-device. Early boot code in arch/ should create
+such devices from device-tree data or other means of input (BIOS queries or boot
+parameters). Such devices are picked up by simplefb or SimpleDRM and provided to
+user-space as single raw framebuffer.
+
+Currently, the following ways exist to create such fbs:
+ - device-tree:
+ See: Documentation/devicetree/bindings/video/simple-framebuffer.txt
+ An example device-tree binding is:
+ framebuffer {
+ compatible = "simple-framebuffer";
+ reg = <0x1d385000 (1600 * 1200 * 2)>;
+ width = <1600>;
+ height = <1200>;
+ stride = <(1600 * 2)>;
+ format = "r5g6b5";
+ };
+ - platform-data:
+ You can create platform-devices in arch/ setup code and set the platform-data
+ to "struct simplefb_platform_data". It is defined in:
+ include/linux/platform_data/simplefb.h
+ It contains the width, height, stride and format of the framebuffer. Base
+ address and size should be passed as primary IORESOURCE_MEM resource.
+
+Supported pixel-formats are listed in:
+ include/linux/platform_data/simplefb.h
+
+All new code should use either method to advertise firmware-fbs. Other means are
+deprecated and may conflict with simple-framebuffers. If the simple-framebuffer
+method is not suitable, it should be extended to fulfil your needs. If that's
+not possible, you still should register your framebuffer with the device-model
+so it can be properly detected and driver-binding is well defined.
+See below (3.2 platform-devices) for other examples.
+
+3.2 platform-devices
+~~~~~~~~~~~~~~~~~~~~
+There are a bunch of legacy device-types that are incompatible to the
+"simple-framebuffer" platform-device or supported for backwards-compatibility.
+All these devices are represented as a "struct platform_device" similar to
+simple-framebuffers but with a different device-name.
+
+ - "vesa-framebuffer":
+ On x86, a "vesa-framebuffer" platform-device is created if a VESA framebuffer
+ is detected during boot. The platform-data contains a pointer to the
+ "struct screen_info" related to the device.
+ Currently, only the vesafb driver binds to such devices.
+ - "efi-framebuffer":
+ On EFI systems, a "efi-framebuffer" platform-device is created if an EFI
+ framebuffer is provided by the firmware. Similar to vesa-framebuffers, the
+ platform-data contains a pointer to the "struct screen_info" related to the
+ device.
+ Currently, only the efifb driver binds to such devices.
+
+3.3 open-firmware:
+~~~~~~~~~~~~~~~~~~
+There are several open-firmware based framebuffers that are supported by the
+"offb.c" driver. These are all very similar to the simple-framebuffer
+device-tree format and supported for compatibility reasons. See the offb.c
+driver source for more information on the exact format.
+Note that these are specific to the offb.c driver. They are *not* registered as
+platform-device (or any other device) and don't integrate at all with the
+device-model. Instead, only the fbdev device itself is registered as char-dev.
+The underlying un-typed firmware-framebuffer is not represented by a
+"struct device".
+
+3.4 "struct screen_info":
+~~~~~~~~~~~~~~~~~~~~~~~~~
+The legacy mode to register firmware-fbs on x86 was to initialize a global
+instance of type "struct screen_info". Drivers can access this object directly
+via its name "screen_info".
+This structure is defined in include/uapi/linux/screen_info.h and contains
+pixel-mode information, base-address and size of the framebuffer. Drivers simply
+use the information in this structure. There is no synchronization between those
+drivers and no-one prevents multiple of these to bind to the same device.
+
+The screen_info.orig_video_isVGA field defines the hw-mode during bootup. It may
+indicate a graphics or text-mode (see below 3.5 VGA for text-mode). This
+structure is *not* modified if the mode changes during runtime. Thus, such
+drivers will break if hotplugged after another driver was already loaded.
+
+vesafb and efifb have been converted to not use the global "screen_info" object
+but instead bind to platform-devices. All other drivers that use "screen_info"
+are deprecated and may not work well with hw-handover to real graphics drivers.
+Note that some platform-devices contain a "struct screen_info" as platform-data,
+which is fine! The platform-device itself provides synchronization and
+driver-binding. It's only the drivers that rely on the global "screen_info"
+object that will likely break during hw-takeover.
+
+3.5 VGA
+~~~~~~~
+On x86, drivers may use VGA I/O registers directly to test for text-mode or
+basic vga-graphics mode. These drivers usually verify that the system runs in a
+compatible VGA-mode by reading the global "screen_info" object.
+
+VGA drivers suffer from the same problem as screen_info drivers (see 3.4).
+No-one prevents multiple drivers from accessing the same device and there is no
+sane hand-over to real hw-drivers.
+
+VGA drivers should be used with care (best: not used at all!) if hw-handover is
+required. If someone cares for VGA/text-mode and hw-handover, they should add
+"vga-framebuffer" platform-devices and bind to them in the drivers (similar to
+vesa-framebuffer and efi-framebuffer devices). This would allow removing these
+devices on hw-handover and prevent further access from VGA drivers.
+
+4. Hand-over
+~~~~~~~~~~~~
+Many graphics devices support much more features than a single framebuffer.
+Therefore, linux allows real hw-drivers to take over control (eg., radeon-drm
+taking over from efifb).
+
+To support hand-over, we need to unload the previous driver before loading the
+new driver. Furthermore, we must prevent any firmware-driver from loading again
+later. Multiple hand-over helpers exist and are described below.
+
+4.1 sysfb
+~~~~~~~~~
+The sysfb-handover is the newest of all helpers and should be used by new code.
+Currently, only x86 uses it (arch/x86/kernel/sysfb.c). sysfb is quite simple and
+provides a single hand-over layer.
+
+Architecture setup must register all firmware-framebuffers via sysfb_register()
+instead of calling platform_device_register() directly. Currently sysfb supports
+only a single firmware-fb at a time, but could be extended to allow multiple fbs
+(once such systems occur in the wild).
+sysfb_register() remembers the device and calls platform_device_register().
+Generic firmware-fb drivers can now bind to the firmware devices. Once these
+devices are removed from the system, the generic firmware-fb drivers are
+automatically unbound.
+
+Any real hw video-driver that binds to a device *must* call sysfb_claim()
+before using the device. sysfb_claim() will check whether the given resource
+is used by any firmware-fb and remove any conflicting platform-device. Removing
+the platform-device will unbind the related platform-driver. Thus, the real hw
+driver can now be sure that there is no other driver accessing any firmware-fb
+based on its hardware.
+sysfb_claim() also makes sure that no following call to sysfb_register()
+will succeed, thus, preventing any new firmware-fb on the given device.
+
+Currently, remove_conflicting_framebuffers() (see below at 4.2) and DRM drivers
+call sysfb_claim() to remove conflicting firmware-fbs.
+
+See drivers/video/sysfb.c for a thorough API description of sysfb.
+
+4.2 fbdev
+~~~~~~~~~
+The fbdev layer provides a single helper called
+remove_conflicting_framebuffers(). This is implicitly called before any fbdev
+driver is registered and explicitly called by all affected DRM drivers. This
+helper is supposed to remove any existing framebuffer device that conflicts with
+the new device. Note that it does *not* prevent any new device from re-occuring.
+So loading a firmware-fb driver *after* the real hw-driver will break.
+
+Furthermore, this is limited to fbdev. If CONFIG_FB is disabled, it is not
+available.
+
+5. vgacon
+~~~~~~~~~
+The vgacon driver is similar to the vga-fbdev drivers (see above at 3.5). It
+does not register any "struct device" and thus there's no simple way to prevent
+multiple drivers from accessing vga registers simultaneously.
+
+The vgacon driver is created by early-arch-setup and marked as default VT
+console. Once the VT layer is started, it binds vgacon to all VTs. If VTs or
+VGACON are disabled, all this obviously does not apply. Same is true if the
+system is not booted in VGA/text-mode. vgacon only takes over if "screen_info"
+tells it that the system is booted in VGA/text-mode.
+
+vgacon then accesses VGA I/O registers directly to print the VT console. If a
+real-hw driver takes over, it cannot unregister vgacon directly. Instead, it
+needs to register another VT console and call do_take_over_console() (or you may
+call it with dummy_con). Obviously, vgacon must not be registered *after* the
+hw-driver is probed, otherwise, vgacon will take over unconditionally. This,
+however, seems to be no problem as vgacon is only probed by early arch-setup
+code.
+
+Currently, fbdev and DRM drivers register fbdev drivers, which are then picked
+up by fbcon which calls do_take_over_console() and removes vgacon. However, once
+the hw-driver is unloaded, fbcon is unbound, too. Therefore, vgacon will take
+over again (and fail horribly if the driver didn't restore VGA registers!).
+Running a system without fbdev but with vgacon+DRM will break, too. There's
+no-one who unloads vgacon when DRM starts up.
+
+6. Early Consoles
+~~~~~~~~~~~~~~~~~
+Architecture setup-code can register early-consoles. These may include consoles
+that access firmware-framebuffers. Such consoles are automatically removed from
+the system when the first real console-driver is loaded. However, you can
+disable this on the kernel-command line. Therefore, you're highly discouraged to
+enable early-boot consoles by default. You should only enable them for
+debugging. Especially on systems without VTs but DRM enabled, chances are high
+that no real console-driver will be available so no-one will ever remove
+early-boot consoles.
+
+----------------------------------------------------------------------------
+ Written 2013-2014 by David Herrmann <[email protected]>
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 098228e..93df439 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2300,6 +2300,7 @@ source "drivers/rapidio/Kconfig"
config X86_SYSFB
bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
depends on FB_SIMPLE
+ select SYSFB
help
Firmwares often provide initial graphics framebuffers so the BIOS,
bootloader or kernel can show basic video-output during boot for
diff --git a/arch/x86/include/asm/sysfb.h b/arch/x86/include/asm/sysfb.h
index 4f9fda2..f777949 100644
--- a/arch/x86/include/asm/sysfb.h
+++ b/arch/x86/include/asm/sysfb.h
@@ -59,11 +59,6 @@ struct efifb_dmi_info {
int flags;
};

-int __init sysfb_register(const char *name, int id,
- const struct resource *res, unsigned int res_num,
- const void *data, size_t data_size);
-void sysfb_unregister(const struct apertures_struct *apert, bool primary);
-
#ifdef CONFIG_EFI

extern struct efifb_dmi_info efifb_dmi_list[];
diff --git a/arch/x86/kernel/sysfb.c b/arch/x86/kernel/sysfb.c
index fd07b09..96f9289 100644
--- a/arch/x86/kernel/sysfb.c
+++ b/arch/x86/kernel/sysfb.c
@@ -39,70 +39,6 @@
#include <linux/screen_info.h>
#include <asm/sysfb.h>

-static DEFINE_MUTEX(sysfb_lock);
-static struct platform_device *sysfb_dev;
-
-int __init sysfb_register(const char *name, int id,
- const struct resource *res, unsigned int res_num,
- const void *data, size_t data_size)
-{
- struct platform_device *pd;
- int ret = 0;
-
- mutex_lock(&sysfb_lock);
- if (!sysfb_dev) {
- pd = platform_device_register_resndata(NULL, name, id,
- res, res_num,
- data, data_size);
- if (IS_ERR(pd))
- ret = PTR_ERR(pd);
- else
- sysfb_dev = pd;
- }
- mutex_unlock(&sysfb_lock);
-
- return ret;
-}
-
-static bool sysfb_match(const struct apertures_struct *apert)
-{
- struct screen_info *si = &screen_info;
- unsigned int i;
- const struct aperture *a;
-
- for (i = 0; i < apert->count; ++i) {
- a = &apert->ranges[i];
- if (a->base >= si->lfb_base &&
- a->base < si->lfb_base + ((u64)si->lfb_size << 16))
- return true;
- if (si->lfb_base >= a->base &&
- si->lfb_base < a->base + a->size)
- return true;
- }
-
- return false;
-}
-
-/* Remove sysfb and disallow new sysfbs from now on. Can be called from any
- * context except recursively (see also remove_conflicting_framebuffers()). */
-void sysfb_unregister(const struct apertures_struct *apert, bool primary)
-{
- if (!apert)
- return;
-
- mutex_lock(&sysfb_lock);
- if (!IS_ERR(sysfb_dev) && sysfb_dev) {
- if (primary || sysfb_match(apert)) {
- platform_device_unregister(sysfb_dev);
- sysfb_dev = ERR_PTR(-EALREADY);
- }
- } else {
- /* set/overwrite error so no new sysfb is probed later */
- sysfb_dev = ERR_PTR(-EALREADY);
- }
- mutex_unlock(&sysfb_lock);
-}
-
static __init int sysfb_init(void)
{
struct screen_info *si = &screen_info;
diff --git a/arch/x86/kernel/sysfb_simplefb.c b/arch/x86/kernel/sysfb_simplefb.c
index 9338427..97ed702 100644
--- a/arch/x86/kernel/sysfb_simplefb.c
+++ b/arch/x86/kernel/sysfb_simplefb.c
@@ -22,6 +22,7 @@
#include <linux/platform_data/simplefb.h>
#include <linux/platform_device.h>
#include <linux/screen_info.h>
+#include <linux/sysfb.h>
#include <asm/sysfb.h>

static const char simplefb_resname[] = "BOOTFB";
@@ -86,7 +87,9 @@ __init bool parse_mode(const struct screen_info *si,
__init int create_simplefb(const struct simplefb_platform_data *mode)
{
const struct apertures_struct *apert = (void*)mode->apert_buf;
+ struct platform_device *dev;
struct resource res;
+ int ret;

/* setup IORESOURCE_MEM as framebuffer memory */
memset(&res, 0, sizeof(res));
@@ -97,6 +100,25 @@ __init int create_simplefb(const struct simplefb_platform_data *mode)
if (res.end <= res.start)
return -EINVAL;

- return sysfb_register("simple-framebuffer", 0, &res, 1, mode,
- sizeof(*mode));
+ dev = platform_device_alloc("simple-framebuffer", 0);
+ if (!dev)
+ return -ENOMEM;
+
+ ret = platform_device_add_resources(dev, &res, 1);
+ if (ret)
+ goto err;
+
+ ret = platform_device_add_data(dev, mode, sizeof(*mode));
+ if (ret)
+ goto err;
+
+ /* platform_data is a copy of @mode, so adjust pointers */
+ mode = dev->dev.platform_data;
+ apert = (void*)mode->apert_buf;
+
+ sysfb_register(dev, apert);
+
+err:
+ platform_device_put(dev);
+ return ret;
}
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 4f2e1b3..06bbd5f 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -39,6 +39,9 @@ config VIDEOMODE_HELPERS
config HDMI
bool

+config SYSFB
+ bool
+
menuconfig FB
tristate "Support for frame buffer devices"
---help---
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index e8bae8d..384926e 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -6,6 +6,7 @@

obj-$(CONFIG_VGASTATE) += vgastate.o
obj-$(CONFIG_HDMI) += hdmi.o
+obj-$(CONFIG_SYSFB) += sysfb.o
obj-y += fb_notify.o
obj-$(CONFIG_FB) += fb.o
fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 79a47ff..e3bceaa 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -35,10 +35,6 @@

#include <asm/fb.h>

-#ifdef CONFIG_X86_SYSFB
-# include <asm/sysfb.h>
-#endif
-
/*
* Frame buffer device initialization and setup routines
*/
@@ -1749,14 +1745,23 @@ int unlink_framebuffer(struct fb_info *fb_info)
}
EXPORT_SYMBOL(unlink_framebuffer);

+static void remove_conflicting_sysfb(struct apertures_struct *apert,
+ bool primary)
+{
+ /* We must not call into sysfb_claim() from within ->probe() or
+ * ->remove() of a sysfb-device, otherwise we dead-lock. Luckily, these
+ * devices don't have any apertures set (and must never add any), so we
+ * can just skip it then. */
+ if (apert)
+ sysfb_claim(apert, primary ? SYSFB_CLAIM_SHADOW : 0);
+}
+
int remove_conflicting_framebuffers(struct apertures_struct *a,
const char *name, bool primary)
{
int ret;

-#ifdef CONFIG_X86_SYSFB
- sysfb_unregister(a, primary);
-#endif
+ remove_conflicting_sysfb(a, primary);

mutex_lock(&registration_lock);
ret = do_remove_conflicting_framebuffers(a, name, primary);
@@ -1780,9 +1785,8 @@ register_framebuffer(struct fb_info *fb_info)
{
int ret;

-#ifdef CONFIG_X86_SYSFB
- sysfb_unregister(fb_info->apertures, fb_is_primary_device(fb_info));
-#endif
+ remove_conflicting_sysfb(fb_info->apertures,
+ fb_is_primary_device(fb_info));

mutex_lock(&registration_lock);
ret = do_register_framebuffer(fb_info);
diff --git a/drivers/video/sysfb.c b/drivers/video/sysfb.c
new file mode 100644
index 0000000..91e0888
--- /dev/null
+++ b/drivers/video/sysfb.c
@@ -0,0 +1,348 @@
+/*
+ * Generic System Framebuffers
+ * Copyright (c) 2012-2013 David Herrmann <[email protected]>
+ *
+ * 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/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/sysfb.h>
+#include <linux/types.h>
+
+/**
+ * DOC: sysfb
+ *
+ * Firmware might initialize graphics hardware before booting a kernel. Usually,
+ * it sets up a single framebuffer that we can render to (no page-flipping,
+ * double-buffering, vsync, ..). Linux can pick these up to draw early boot
+ * oops/panic screens or allow user-space to render boot-splashs.
+ * However, once all hardware has been detected, we usually want to load real
+ * graphics drivers. But before they take off, we must remove the
+ * firmware-framebuffer first. Otherwise, we will end up with resource conflicts
+ * and invalid memory accesses from a generic firmware-framebuffer driver.
+ *
+ * The sysfb infrastructure allows architecture boot-up code to register
+ * firmware-framebuffers. Real hw-drivers can use sysfb to unload firmware-fbs
+ * before loading the real driver.
+ * A firmware-framebuffer is represented by a platform_device. Other device
+ * types may be supported if required, but currently only platform_devices
+ * make sense. The name and payload of these platform-devices depend on the
+ * firmware-framebuffer type and are outside the scope of sysfb. Known types are
+ * "simple-framebuffer", "vesa-framebuffer", "efi-framebuffer" and more.
+ *
+ * Architecture setup code should allocate a platform-device, set the payload
+ * and then call sysfb_register() to register the platform-device and integrate
+ * it with sysfb. It should then drop any reference to the device and let sysfb
+ * manage it. The architecture code *may* keep a reference and unregister the
+ * firmware-fb at any time via sysfb_unregister() if and only if it has other
+ * means of notification about firmware-framebuffer destruction. Usually, this
+ * is not given.
+ *
+ * Real hw-drivers for the underlying hardware of a firmware-framebuffer must be
+ * probed on separate "struct device" objects! These devices *must* represent
+ * the real hardware instead of the firmware-framebuffer. Once a real hw-driver
+ * is probed, it shall call sysfb_claim() to claim its real resources. This will
+ * evict any conflicting firmware-framebuffers from the system and prevent any
+ * new firmware-framebuffer from being registered (it is assumed that their
+ * underlying resources are invalidated). After that, the real hw-driver can
+ * initialize the device and will have exclusive access to the resources.
+ * sysfb_claim() will unregister any conflicting firmware-framebuffers that have
+ * been registered before. It causes the ->remove() callback of the
+ * platform-devices to be called and generic framebuffers driver will get
+ * removed. It then drops its reference to the platform-devices so they get
+ * destroyed (if no-one else keeps a reference).
+ * sysfb_claim() is synchronous so it may be called in parallel by many
+ * hw-drivers and it always guarantees that it returns *after* all conflicting
+ * framebuffers have been removed.
+ *
+ * After a real hw-driver has claimed resources, sysfb automatically prevents
+ * new firmware-framebuffers from being registered. Architecture setup code
+ * should make sure that firmware-framebuffer platform-devices are registered
+ * *before* real hw-drivers are probed. This is usually implicitly given by most
+ * bus systems.
+ * However, if you unload a real hw-driver, it *may* restore the previous
+ * firmware-fb or create a new one. In that case, the driver explicitly has to
+ * create a new platform-device and register it via sysfb_register_dyn().
+ * Compared to sysfb_register() this helper also allows adding devices after a
+ * real hw-driver has been probed.
+ *
+ * A system may support multiple firmware-framebuffers. For example, firmware
+ * may set up framebuffers for all available connectors on the
+ * display-controller. However, no such system has been seen in the wild and
+ * given that sysfb is usually only used during boot, the implementation is
+ * limited to a single firmware-framebuffer. However, the API doesn't reflect
+ * that so callers *must not* assume that. On the contrary, we may, at any
+ * point, decide to support multiple framebuffers without changing the API. But
+ * as that requires keeping track of *all* previous apertures, we didn't
+ * implement this now. You're highly encouraged to write proper *real*
+ * hw-drivers if you want more sophisticated access to your graphics-hardware.
+ */
+
+static DEFINE_MUTEX(sysfb_lock);
+static const struct apertures_struct *sysfb_apert;
+static struct platform_device *sysfb_dev;
+
+static int __sysfb_register(struct platform_device *dev,
+ const struct apertures_struct *apert)
+{
+ int ret;
+
+ if (!IS_ERR_OR_NULL(sysfb_dev)) {
+ dev_info(&dev->dev,
+ "multiple firmware-framebuffers are not supported\n");
+ return -EALREADY;
+ }
+
+ ret = platform_device_add(dev);
+ if (ret)
+ return ret;
+
+ get_device(&dev->dev);
+ sysfb_apert = apert;
+ sysfb_dev = dev;
+ return 0;
+}
+
+/**
+ * sysfb_register - Register firmware-framebuffer
+ * @dev: Non-registered platform-device for firmware-framebuffer
+ * @apert: Aperture describing the framebuffer location/size or NULL
+ *
+ * This takes an initialized platform-device and registers it with the system
+ * via platform_device_add(). Furthermore, the device is remembered by sysfb so
+ * real-hw drivers can evict the firmware-fb later via sysfb_claim(). The
+ * aperture parameter must be constant and is *not* copied by this helper. You
+ * can usually store it in the platform_data member of the platform-device.
+ * The aperture-object describes the regions of the framebuffer data so it can
+ * be matched against real hw-drivers. Set it to NULL if any hw-driver should
+ * evict this firmware-fb.
+ *
+ * The given platform device must not have been added to the system before this
+ * call! Furthermore, on success, this call takes a reference to the
+ * platform-device so the caller can (and should) drop its own.
+ *
+ * The firmware-framebuffer is unregistered and destroyed if a real hw-driver
+ * calls sysfb_claim() and the firmware-fb matches. You can manually unregister
+ * and destroy the device via sysfb_unregister(), if required. You must keep a
+ * reference to the device then, though.
+ *
+ * If the firmware-fb couldn't be registered, this function will fail. Callers
+ * should *not* try to register the fb themselves. Instead, they must assume a
+ * real hw-driver already took over and the firmware-fb has been invalidated.
+ * Furthermore, if a real-hw driver has been probed before, this call will
+ * always fail and prevent firmware-fbs from getting registered. It is assumed
+ * that the fbs have been invalidated by the real hw. Architecture code should
+ * make sure that firmware-fbs are added *before* any real hw-drivers are
+ * probed, otherwise, the firmware-fbs might not get used.
+ * If you create the firmware-fb on-the-fly, you can use sysfb_register_dyn().
+ *
+ * Currently, we also fail if you try to register multiple firmware-fbs. No such
+ * setup has been seen, yet, and there's no real reason to support multiple
+ * firmware-fbs so we simply drop them. This is an implementation detail and may
+ * change in the future. You can safely ignore it even if you actually have
+ * multiple firmware-fbs.
+ *
+ * sysfb_register() can be called from any context *except* from inside any
+ * callbacks of the platform-device itself. So the ->probe() and ->remove()
+ * callbacks of the driver probed on @dev must not call into sysfb or it will
+ * dead-lock.
+ *
+ * RETURNS:
+ * Returns 0 on success, a negative errno on failure. Callers should usually
+ * ignore the return code and just drop their reference to the platform-device.
+ */
+int sysfb_register(struct platform_device *dev,
+ const struct apertures_struct *apert)
+{
+ int ret = -EALREADY;
+
+ mutex_lock(&sysfb_lock);
+ if (!IS_ERR(sysfb_dev))
+ ret = __sysfb_register(dev, apert);
+ mutex_unlock(&sysfb_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(sysfb_register);
+
+/**
+ * sysfb_register_dyn - Register firmware-framebuffer dynamically
+ * @dev: Non-registered platform-device for firmware-framebuffer
+ * @apert: Aperture describing the framebuffer location/size or NULL
+ *
+ * This is the same as sysfb_register() but also works if a real hw-driver has
+ * already been loaded. This can be used by real hw-drivers on unload. If they
+ * restore a firmware-framebuffer or leave a new one behind, they can setup a
+ * new platform-device and register it. Generic drivers will then be able to
+ * pick it up again and if a conflicting real hw-driver is probed again, it will
+ * evict it.
+ *
+ * Note that this *must* be called from within a safe unload/remove callback.
+ * The real hw-driver must make sure that this returns before it releases the
+ * real hw resources. Otherwise, another real hw-driver might be probed before
+ * this call returns.
+ *
+ * Usually, this helper is only used to allow unloading, recompiling and
+ * reloading the same real hw-driver and get graphics support in between.
+ *
+ * Note that @apert is *not* copied so you should store it in the platform-data
+ * field of @dev (same as for sysfb_register()).
+ *
+ * RETURNS:
+ * Returns 0 on success, a negative errno on failure. Callers should usually
+ * ignore the return code and just drop their reference to the platform-device.
+ */
+int sysfb_register_dyn(struct platform_device *dev,
+ const struct apertures_struct *apert)
+{
+ int ret;
+
+ mutex_lock(&sysfb_lock);
+ ret = __sysfb_register(dev, apert);
+ mutex_unlock(&sysfb_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(sysfb_register_dyn);
+
+/**
+ * sysfb_unregister - Unregister firmware-framebuffer
+ * @dev: Firmware-framebuffer to unregister
+ *
+ * This undoes sysfb_register(). Usually a caller should just drop the
+ * reference to its platform-device and never call this. However, if it has its
+ * own detection when a platform-framebuffer gets invalidated, it can keep a
+ * reference and call this once the fb is invalid.
+ *
+ * If a real hw-driver has already evicted the firmware-fb, this does nothing.
+ * If, and only if the firmware-fb hasn't been evicted, yet, another firmware-fb
+ * can be registered via sysfb_register() afterwards. It is assumed that the
+ * caller of sysfb_unregister() knows what they're doing.
+ *
+ * sysfb_unregister() can be called from any context *except* from inside any
+ * callbacks of the platform-device itself. So the ->probe() and ->remove()
+ * callbacks of the driver probed on @dev must not call into sysfb or it will
+ * dead-lock.
+ */
+void sysfb_unregister(struct platform_device *dev)
+{
+ mutex_lock(&sysfb_lock);
+ if (sysfb_dev == dev) {
+ platform_device_del(dev);
+ put_device(&dev->dev);
+
+ /* allow new firmware-fbs as it has been explicitly removed */
+ sysfb_dev = NULL;
+ sysfb_apert = NULL;
+ }
+ mutex_unlock(&sysfb_lock);
+}
+EXPORT_SYMBOL(sysfb_unregister);
+
+/**
+ * __sysfb_match - Test whether hw conflicts with firmware-fb
+ * @apert: Apertures describing the real hw or NULL
+ * @flags: Matching flags
+ *
+ * This tests whether the apertures given in @apert overlap with any registered
+ * firmware-fb. If @apert is NULL, it is ignored. As we currently support only
+ * a single firmware-fb, the firmware-fb to match against is passed implicitly.
+ *
+ * Several flags are supported:
+ * SYSFB_CLAIM_ALL: Regardless of @apert, this always matches. Should be used
+ * if apertures are unknown.
+ * SYSFB_CLAIM_SHADOW: Additionally to aperture matching, this also matches
+ * against shadow mapped firmware-framebuffers. HW-drivers should use hints
+ * like IORESOURCE_ROM_SHADOW to set/unset this flag.
+ * Shadow mapped firmware-fbs include PCI-BARs mapped into VGA/VESA regions
+ * for backwards-compatibility and alike.
+ *
+ * RETURNS:
+ * Returns true if the given apertures conflict with the registered firmware-fb,
+ * false if not.
+ */
+static bool __sysfb_match(const struct apertures_struct *apert,
+ unsigned int flags)
+{
+ bool claim_shadow = flags & SYSFB_CLAIM_SHADOW;
+ const struct aperture *a, *b;
+ size_t i, j;
+
+ if (flags & SYSFB_CLAIM_ALL || !sysfb_apert)
+ return true;
+
+ for (i = 0; i < sysfb_apert->count; ++i) {
+ a = &sysfb_apert->ranges[i];
+
+ /* VBE/VESA base address is 0xA0000 */
+ if (claim_shadow && a->base == 0xA0000)
+ return true;
+ if (!apert)
+ continue;
+
+ for (j = 0; j < apert->count; ++j) {
+ b = &apert->ranges[j];
+
+ if (a->base >= b->base &&
+ a->base < b->base + b->size)
+ return true;
+ if (b->base >= a->base &&
+ b->base < a->base + a->size)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * sysfb_claim - Claim hw-resources and evict conflicting firmware-fbs
+ * @apert: Apertures describing real hw-resources or NULL
+ * @flags: Matching flags
+ *
+ * This shall be called by real hw-drivers to evict all firmware-fbs that
+ * conflict with the real hardware-driver. @apert describes the apertures of
+ * the real hw and is matched against the registered firmware-framebuffers.
+ * @flags contains some additional flags to control matching behavior. See
+ * __sysfb_match() for a description of the matching behavior.
+ *
+ * Note that after this has been called *once*, no new firmware-fb will be able
+ * to get registered. So even when unloading the real-hw driver, no firmware-fb
+ * will take over again. This is to protect against hw-drivers which don't
+ * restore the firmware fb correctly. See sysfb_register_dyn() for a safe
+ * exception to this rule.
+ *
+ * This must not be called from atomic-contexts. This also does *not* protect
+ * multiple real hw-drivers from each other. Real hw-drivers should use their
+ * underlying bus (pci, usb, platform, ..) to correctly bind to real resources.
+ * The sysfb_claim() helper only evicts pseudo-devices that were registered as
+ * firmware-framebuffers.
+ *
+ * sysfb_claim() can be called from any context *except* from inside any
+ * callbacks of the platform-device itself. So the ->probe() and ->remove()
+ * callbacks of the driver probed on @dev must not call into sysfb or it will
+ * dead-lock.
+ */
+void sysfb_claim(const struct apertures_struct *apert, unsigned int flags)
+{
+ mutex_lock(&sysfb_lock);
+ if (IS_ERR_OR_NULL(sysfb_dev)) {
+ /* set err to prevent new firmware-fbs to be probed later */
+ sysfb_dev = ERR_PTR(-EALREADY);
+ } else if (__sysfb_match(apert, flags)) {
+ platform_device_unregister(sysfb_dev);
+ put_device(&sysfb_dev->dev);
+ sysfb_dev = ERR_PTR(-EALREADY);
+ sysfb_apert = NULL;
+ }
+ mutex_unlock(&sysfb_lock);
+}
+EXPORT_SYMBOL(sysfb_claim);
diff --git a/include/linux/fb.h b/include/linux/fb.h
index fe6ac95..70695fc 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -13,6 +13,7 @@
#include <linux/list.h>
#include <linux/backlight.h>
#include <linux/slab.h>
+#include <linux/sysfb.h>
#include <asm/io.h>

struct vm_area_struct;
@@ -494,13 +495,7 @@ struct fb_info {
/* we need the PCI or similar aperture base/size not
smem_start/size as smem_start may just be an object
allocated inside the aperture so may not actually overlap */
- struct apertures_struct {
- unsigned int count;
- struct aperture {
- resource_size_t base;
- resource_size_t size;
- } ranges[0];
- } *apertures;
+ struct apertures_struct *apertures;

bool skip_vt_switch; /* no VT switch on suspend/resume required */
};
diff --git a/include/linux/platform_data/simplefb.h b/include/linux/platform_data/simplefb.h
index 21983cc..00e3575 100644
--- a/include/linux/platform_data/simplefb.h
+++ b/include/linux/platform_data/simplefb.h
@@ -15,6 +15,7 @@
#include <drm/drm_fourcc.h>
#include <linux/fb.h>
#include <linux/kernel.h>
+#include <linux/sysfb.h>

/* format array, use it to initialize a "struct simplefb_format" array */
#define SIMPLEFB_FORMATS \
diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h
new file mode 100644
index 0000000..f1638ac
--- /dev/null
+++ b/include/linux/sysfb.h
@@ -0,0 +1,62 @@
+#ifndef _LINUX_SYSFB_H
+#define _LINUX_SYSFB_H
+
+/*
+ * Generic System Framebuffers
+ * Copyright (c) 2012-2014 David Herrmann <[email protected]>
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+
+struct platform_device;
+
+struct apertures_struct {
+ unsigned int count;
+ struct aperture {
+ resource_size_t base;
+ resource_size_t size;
+ } ranges[0];
+};
+
+enum sysfb_claim_flags {
+ SYSFB_CLAIM_ALL = 0x01,
+ SYSFB_CLAIM_SHADOW = 0x02,
+};
+
+#ifdef CONFIG_SYSFB
+
+int sysfb_register(struct platform_device *dev,
+ const struct apertures_struct *apert);
+int sysfb_register_dyn(struct platform_device *dev,
+ const struct apertures_struct *apert);
+void sysfb_unregister(struct platform_device *dev);
+void sysfb_claim(const struct apertures_struct *apert, unsigned int flags);
+
+#else /* CONFIG_SYSFB */
+
+static inline int sysfb_register(struct platform_device *dev,
+ const struct apertures_struct *apert)
+{
+ return -ENOSYS;
+}
+
+static inline int sysfb_register_dyn(struct platform_device *dev,
+ const struct apertures_struct *apert)
+{
+ return -ENOSYS;
+}
+
+static inline void sysfb_unregister(struct platform_device *dev) { }
+
+static inline void sysfb_claim(const struct apertures_struct *apert,
+ unsigned int flags) { }
+
+#endif /* CONFIG_SYSFB */
+
+#endif /* _LINUX_SYSFB_H */
--
1.8.5.3

2014-01-23 14:18:36

by David Herrmann

[permalink] [raw]
Subject: [PATCH 04/11] fbdev: vesafb: add dev->remove() callback

If x86-sysfb platform-devices are removed from a system, we should
properly unload vesafb. Otherwise, we end up releasing the parent while
our vesa framebuffer is still running. This currently works just fine, but
will cause problems on handover to real hw. So add the ->remove() callback
and unregister vesafb.

Signed-off-by: David Herrmann <[email protected]>
---
drivers/video/vesafb.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c
index 1c7da3b..6170e7f 100644
--- a/drivers/video/vesafb.c
+++ b/drivers/video/vesafb.c
@@ -179,7 +179,6 @@ static void vesafb_destroy(struct fb_info *info)
if (info->screen_base)
iounmap(info->screen_base);
release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size);
- framebuffer_release(info);
}

static struct fb_ops vesafb_ops = {
@@ -297,6 +296,7 @@ static int vesafb_probe(struct platform_device *dev)
release_mem_region(vesafb_fix.smem_start, size_total);
return -ENOMEM;
}
+ platform_set_drvdata(dev, info);
info->pseudo_palette = info->par;
info->par = NULL;

@@ -499,12 +499,23 @@ err:
return err;
}

+static int vesafb_remove(struct platform_device *pdev)
+{
+ struct fb_info *info = platform_get_drvdata(pdev);
+
+ unregister_framebuffer(info);
+ framebuffer_release(info);
+
+ return 0;
+}
+
static struct platform_driver vesafb_driver = {
.driver = {
.name = "vesa-framebuffer",
.owner = THIS_MODULE,
},
.probe = vesafb_probe,
+ .remove = vesafb_remove,
};

module_platform_driver(vesafb_driver);
--
1.8.5.3

2014-01-23 14:19:11

by David Herrmann

[permalink] [raw]
Subject: [PATCH 02/11] x86: sysfb: remove sysfb when probing real hw

With CONFIG_X86_SYSFB=y, probing real hw-drivers may result in
resource-conflicts and drivers will refuse to load. A call to
request_mem_region() will fail, if the region overlaps with the mem-region
used by simplefb. The common desktop DRM drivers (intel, nouveau, radeon)
are not affected as they don't reserve their resources, but some others
do, including (nvidiafb, cirrus, ..).

The problem is that we add an IORESOURCE_MEM to the simple-framebuffer
platform-device during bootup but never release it. Probing simplefb on
this platform-device is fine, but the handover to real-hw via
remove_conflicting_framebuffers() will only unload the fbdev driver, but
keep the platform-device around. Thus, if the real hw-driver calls
request_mem_region() and friends on the same PCI region, we will get a
resource conflict and most drivers refuse to load. Users will see
errors like:
"nvidiafb: cannot reserve video memory at <xyz>"

vesafb and efifb don't store any resources, so disabling CONFIG_X86_SYSFB
and falling back to those drivers will avoid the bug. With sysfb enabled,
we need to properly unload the simple-framebuffer devices before probing
real hw-drivers.

This patch adds sysfb_unregister() for that purpose. It can be called from
any context (except from the platform-device ->probe and ->remove callback
path), synchronously unloads any global sysfb and prevents new sysfbs from
getting registered. Thus, you can call it even before any sysfb has been
loaded. Note that for now we only do that for simple-framebuffer devices,
as efi/vesa-framebuffer platform-drivers lack ->remove() callbacks.
They're not affected by the resource-conflicts, so we can fix those later.

This also changes remove_conflicting_framebuffer() to call this helper
*before* trying its heuristic to remove conflicting drivers. This way, we
unload sysfbs properly on any conflict. But to avoid dead-locks in
register_framebuffer(), we must not call sysfb_unregister() for
framebuffers probing on sysfb devices. Hence, we simply remove any
aperture from simplefb and we're good to go. simplefb is unregistered by
sysfb_unregister() now, so no reason to keep the apertures (on non-x86,
there currently is no handover from simplefb, so we're fine. If it's
added, they need to provide something like sysfb_unregister() too)

Signed-off-by: David Herrmann <[email protected]>
Reviewed-by: Takashi Iwai <[email protected]>
Tested-by: Takashi Iwai <[email protected]>
---
arch/x86/include/asm/sysfb.h | 5 ++++
arch/x86/kernel/sysfb.c | 65 ++++++++++++++++++++++++++++++++++++++++
arch/x86/kernel/sysfb_simplefb.c | 9 ++----
drivers/video/fbmem.c | 11 +++++++
drivers/video/simplefb.c | 8 -----
5 files changed, 83 insertions(+), 15 deletions(-)

diff --git a/arch/x86/include/asm/sysfb.h b/arch/x86/include/asm/sysfb.h
index 2aeb3e2..6f95b8d 100644
--- a/arch/x86/include/asm/sysfb.h
+++ b/arch/x86/include/asm/sysfb.h
@@ -59,6 +59,11 @@ struct efifb_dmi_info {
int flags;
};

+int __init sysfb_register(const char *name, int id,
+ const struct resource *res, unsigned int res_num,
+ const void *data, size_t data_size);
+void sysfb_unregister(const struct apertures_struct *apert, bool primary);
+
#ifdef CONFIG_EFI

extern struct efifb_dmi_info efifb_dmi_list[];
diff --git a/arch/x86/kernel/sysfb.c b/arch/x86/kernel/sysfb.c
index 193ec2c..ba9ff26 100644
--- a/arch/x86/kernel/sysfb.c
+++ b/arch/x86/kernel/sysfb.c
@@ -33,11 +33,76 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mm.h>
+#include <linux/mutex.h>
#include <linux/platform_data/simplefb.h>
#include <linux/platform_device.h>
#include <linux/screen_info.h>
#include <asm/sysfb.h>

+static DEFINE_MUTEX(sysfb_lock);
+static struct platform_device *sysfb_dev;
+
+int __init sysfb_register(const char *name, int id,
+ const struct resource *res, unsigned int res_num,
+ const void *data, size_t data_size)
+{
+ struct platform_device *pd;
+ int ret = 0;
+
+ mutex_lock(&sysfb_lock);
+ if (!sysfb_dev) {
+ pd = platform_device_register_resndata(NULL, name, id,
+ res, res_num,
+ data, data_size);
+ if (IS_ERR(pd))
+ ret = PTR_ERR(pd);
+ else
+ sysfb_dev = pd;
+ }
+ mutex_unlock(&sysfb_lock);
+
+ return ret;
+}
+
+static bool sysfb_match(const struct apertures_struct *apert)
+{
+ struct screen_info *si = &screen_info;
+ unsigned int i;
+ const struct aperture *a;
+
+ for (i = 0; i < apert->count; ++i) {
+ a = &apert->ranges[i];
+ if (a->base >= si->lfb_base &&
+ a->base < si->lfb_base + ((u64)si->lfb_size << 16))
+ return true;
+ if (si->lfb_base >= a->base &&
+ si->lfb_base < a->base + a->size)
+ return true;
+ }
+
+ return false;
+}
+
+/* Remove sysfb and disallow new sysfbs from now on. Can be called from any
+ * context except recursively (see also remove_conflicting_framebuffers()). */
+void sysfb_unregister(const struct apertures_struct *apert, bool primary)
+{
+ if (!apert)
+ return;
+
+ mutex_lock(&sysfb_lock);
+ if (!IS_ERR(sysfb_dev) && sysfb_dev) {
+ if (primary || sysfb_match(apert)) {
+ platform_device_unregister(sysfb_dev);
+ sysfb_dev = ERR_PTR(-EALREADY);
+ }
+ } else {
+ /* set/overwrite error so no new sysfb is probed later */
+ sysfb_dev = ERR_PTR(-EALREADY);
+ }
+ mutex_unlock(&sysfb_lock);
+}
+
static __init int sysfb_init(void)
{
struct screen_info *si = &screen_info;
diff --git a/arch/x86/kernel/sysfb_simplefb.c b/arch/x86/kernel/sysfb_simplefb.c
index 86179d4..a760d47 100644
--- a/arch/x86/kernel/sysfb_simplefb.c
+++ b/arch/x86/kernel/sysfb_simplefb.c
@@ -64,7 +64,6 @@ __init bool parse_mode(const struct screen_info *si,
__init int create_simplefb(const struct screen_info *si,
const struct simplefb_platform_data *mode)
{
- struct platform_device *pd;
struct resource res;
unsigned long len;

@@ -86,10 +85,6 @@ __init int create_simplefb(const struct screen_info *si,
if (res.end <= res.start)
return -EINVAL;

- pd = platform_device_register_resndata(NULL, "simple-framebuffer", 0,
- &res, 1, mode, sizeof(*mode));
- if (IS_ERR(pd))
- return PTR_ERR(pd);
-
- return 0;
+ return sysfb_register("simple-framebuffer", 0, &res, 1, mode,
+ sizeof(*mode));
}
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index e296967..79a47ff 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -35,6 +35,9 @@

#include <asm/fb.h>

+#ifdef CONFIG_X86_SYSFB
+# include <asm/sysfb.h>
+#endif

/*
* Frame buffer device initialization and setup routines
@@ -1751,6 +1754,10 @@ int remove_conflicting_framebuffers(struct apertures_struct *a,
{
int ret;

+#ifdef CONFIG_X86_SYSFB
+ sysfb_unregister(a, primary);
+#endif
+
mutex_lock(&registration_lock);
ret = do_remove_conflicting_framebuffers(a, name, primary);
mutex_unlock(&registration_lock);
@@ -1773,6 +1780,10 @@ register_framebuffer(struct fb_info *fb_info)
{
int ret;

+#ifdef CONFIG_X86_SYSFB
+ sysfb_unregister(fb_info->apertures, fb_is_primary_device(fb_info));
+#endif
+
mutex_lock(&registration_lock);
ret = do_register_framebuffer(fb_info);
mutex_unlock(&registration_lock);
diff --git a/drivers/video/simplefb.c b/drivers/video/simplefb.c
index 210f3a0..9f4a0cf 100644
--- a/drivers/video/simplefb.c
+++ b/drivers/video/simplefb.c
@@ -209,14 +209,6 @@ static int simplefb_probe(struct platform_device *pdev)
info->var.blue = params.format->blue;
info->var.transp = params.format->transp;

- info->apertures = alloc_apertures(1);
- if (!info->apertures) {
- framebuffer_release(info);
- return -ENOMEM;
- }
- info->apertures->ranges[0].base = info->fix.smem_start;
- info->apertures->ranges[0].size = info->fix.smem_len;
-
info->fbops = &simplefb_ops;
info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE;
info->screen_base = ioremap_wc(info->fix.smem_start,
--
1.8.5.3

2014-01-23 14:15:49

by David Herrmann

[permalink] [raw]
Subject: [PATCH 01/11] x86: sysfb: fool-proof CONFIG_X86_SYSFB

Turns out, people do not read help-texts of new config-options and enable
them nonetheless. So several reports came in with X86_SYSFB=y and
FB_SIMPLE=n, which in almost all situations prevents firmware-fbs from
being probed.

X86_SYSFB clearly states that it turns legacy vesa/efi framebuffers into a
format compatible to simplefb (and does nothing else..). So to avoid
further complaints about missing gfx-support during boot, simply depend on
FB_SIMPLE now.
As FB_SIMPLE is disabled by default and usually only enabled on selected
ARM architectures, x86 users should thus never see the X86_SYSFB
config-option. And if they do, everything is fine as simplefb will be
available.

Note that most of the sysfb code is enabled independently of X86_SYSFB.
The config option only selects a compatibility mode for simplefb. It was
introduced to ease the transition to SimpleDRM and disabling fbdev. As
this is still ongoing, there's no need for non-developers to care for
X86_SYSFB so we can safely hide it behind FB_SIMPLE.

Signed-off-by: David Herrmann <[email protected]>
---
arch/x86/Kconfig | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 0952ecd..098228e 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2299,6 +2299,7 @@ source "drivers/rapidio/Kconfig"

config X86_SYSFB
bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
+ depends on FB_SIMPLE
help
Firmwares often provide initial graphics framebuffers so the BIOS,
bootloader or kernel can show basic video-output during boot for
--
1.8.5.3

2014-01-23 16:51:24

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH 02/11] x86: sysfb: remove sysfb when probing real hw


Just a couple of small nits:

* David Herrmann <[email protected]> wrote:

> --- a/arch/x86/kernel/sysfb.c
> +++ b/arch/x86/kernel/sysfb.c
> @@ -33,11 +33,76 @@
> #include <linux/init.h>
> #include <linux/kernel.h>
> #include <linux/mm.h>
> +#include <linux/mutex.h>
> #include <linux/platform_data/simplefb.h>
> #include <linux/platform_device.h>
> #include <linux/screen_info.h>
> #include <asm/sysfb.h>
>
> +static DEFINE_MUTEX(sysfb_lock);
> +static struct platform_device *sysfb_dev;
> +
> +int __init sysfb_register(const char *name, int id,
> + const struct resource *res, unsigned int res_num,
> + const void *data, size_t data_size)
> +{
> + struct platform_device *pd;
> + int ret = 0;
> +
> + mutex_lock(&sysfb_lock);
> + if (!sysfb_dev) {
> + pd = platform_device_register_resndata(NULL, name, id,
> + res, res_num,
> + data, data_size);
> + if (IS_ERR(pd))
> + ret = PTR_ERR(pd);
> + else
> + sysfb_dev = pd;
> + }
> + mutex_unlock(&sysfb_lock);
> +
> + return ret;
> +}
> +
> +static bool sysfb_match(const struct apertures_struct *apert)
> +{
> + struct screen_info *si = &screen_info;
> + unsigned int i;
> + const struct aperture *a;
> +
> + for (i = 0; i < apert->count; ++i) {
> + a = &apert->ranges[i];
> + if (a->base >= si->lfb_base &&
> + a->base < si->lfb_base + ((u64)si->lfb_size << 16))
> + return true;
> + if (si->lfb_base >= a->base &&
> + si->lfb_base < a->base + a->size)
> + return true;
> + }
> +
> + return false;
> +}
> +
> +/* Remove sysfb and disallow new sysfbs from now on. Can be called from any
> + * context except recursively (see also remove_conflicting_framebuffers()). */
> +void sysfb_unregister(const struct apertures_struct *apert, bool primary)

Please use the customary (multi-line) comment style:

/*
* Comment .....
* ...... goes here.
*/

specified in Documentation/CodingStyle.

> +#ifdef CONFIG_X86_SYSFB
> +# include <asm/sysfb.h>
> +#endif

I guess a single space is sufficient?

Better yet, I'd include sysfb.h unconditionally:

> @@ -1773,6 +1780,10 @@ register_framebuffer(struct fb_info *fb_info)
> {
> int ret;
>
> +#ifdef CONFIG_X86_SYSFB
> + sysfb_unregister(fb_info->apertures, fb_is_primary_device(fb_info));
> +#endif

So, if a dummy sysfb_unregister() inline was defined in the
!CONFIG_X86_SYSFB case then this ugly #ifdef could possibly be
removed? Especially as it's used twice.

Thanks,

Ingo

2014-01-23 17:07:06

by David Herrmann

[permalink] [raw]
Subject: Re: [PATCH 02/11] x86: sysfb: remove sysfb when probing real hw

Hi

On Thu, Jan 23, 2014 at 5:51 PM, Ingo Molnar <[email protected]> wrote:
>
> Just a couple of small nits:
>
> * David Herrmann <[email protected]> wrote:
>
>> --- a/arch/x86/kernel/sysfb.c
>> +++ b/arch/x86/kernel/sysfb.c
>> @@ -33,11 +33,76 @@
>> #include <linux/init.h>
>> #include <linux/kernel.h>
>> #include <linux/mm.h>
>> +#include <linux/mutex.h>
>> #include <linux/platform_data/simplefb.h>
>> #include <linux/platform_device.h>
>> #include <linux/screen_info.h>
>> #include <asm/sysfb.h>
>>
>> +static DEFINE_MUTEX(sysfb_lock);
>> +static struct platform_device *sysfb_dev;
>> +
>> +int __init sysfb_register(const char *name, int id,
>> + const struct resource *res, unsigned int res_num,
>> + const void *data, size_t data_size)
>> +{
>> + struct platform_device *pd;
>> + int ret = 0;
>> +
>> + mutex_lock(&sysfb_lock);
>> + if (!sysfb_dev) {
>> + pd = platform_device_register_resndata(NULL, name, id,
>> + res, res_num,
>> + data, data_size);
>> + if (IS_ERR(pd))
>> + ret = PTR_ERR(pd);
>> + else
>> + sysfb_dev = pd;
>> + }
>> + mutex_unlock(&sysfb_lock);
>> +
>> + return ret;
>> +}
>> +
>> +static bool sysfb_match(const struct apertures_struct *apert)
>> +{
>> + struct screen_info *si = &screen_info;
>> + unsigned int i;
>> + const struct aperture *a;
>> +
>> + for (i = 0; i < apert->count; ++i) {
>> + a = &apert->ranges[i];
>> + if (a->base >= si->lfb_base &&
>> + a->base < si->lfb_base + ((u64)si->lfb_size << 16))
>> + return true;
>> + if (si->lfb_base >= a->base &&
>> + si->lfb_base < a->base + a->size)
>> + return true;
>> + }
>> +
>> + return false;
>> +}
>> +
>> +/* Remove sysfb and disallow new sysfbs from now on. Can be called from any
>> + * context except recursively (see also remove_conflicting_framebuffers()). */
>> +void sysfb_unregister(const struct apertures_struct *apert, bool primary)
>
> Please use the customary (multi-line) comment style:
>
> /*
> * Comment .....
> * ...... goes here.
> */
>
> specified in Documentation/CodingStyle.

Whoops, will fix it up. Still used to that from HID code.

>> +#ifdef CONFIG_X86_SYSFB
>> +# include <asm/sysfb.h>
>> +#endif
>
> I guess a single space is sufficient?
>
> Better yet, I'd include sysfb.h unconditionally:

Unconditionally won't work as only x86 has this header. If there's a
way to place a dummy into asm-generic which is picked if
arch/xy/include/asm/ doesn't have the header, let me know. But if I
include it unconditionally without any fallback, this will fail on
non-x86.
And adding the header to all archs seems overkill.

>> @@ -1773,6 +1780,10 @@ register_framebuffer(struct fb_info *fb_info)
>> {
>> int ret;
>>
>> +#ifdef CONFIG_X86_SYSFB
>> + sysfb_unregister(fb_info->apertures, fb_is_primary_device(fb_info));
>> +#endif
>
> So, if a dummy sysfb_unregister() inline was defined in the
> !CONFIG_X86_SYSFB case then this ugly #ifdef could possibly be
> removed? Especially as it's used twice.

Again, this is fine for x86, but not for other archs. I would still
need the #ifdef x86.
Note that patch #6 introduces linux/sysfb.h and removes all these ugly
#ifdefs again. They're only needed to fix the x86 code *now*. Patch #6
generalizes the x86-sysfb infrastructure and makes it
arch-independent. But Patch #6 introduces new features and thus
shouldn't go to stable or 3.14.

As Patch #1 already fixes nearly all issues with sysfb, let me know if
you want to drop this patch and just wait for the arch-independent
sysfb to get merged. This patch is only needed if people enable
X86_SYSFB *and* FB_SIMPLE *purposely* and want hw-handover. The case
were people enable it accidentally is fixed by Patch #1.
The situation is kind of screwed.. sorry for that.

Thanks
David

2014-01-23 17:14:42

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH 02/11] x86: sysfb: remove sysfb when probing real hw


* David Herrmann <[email protected]> wrote:

> >> +#ifdef CONFIG_X86_SYSFB
> >> +# include <asm/sysfb.h>
> >> +#endif
> >
> > I guess a single space is sufficient?
> >
> > Better yet, I'd include sysfb.h unconditionally:
>
> Unconditionally won't work as only x86 has this header. [...]

Well, in non-x86 code an #ifdef x86 looks ugly as well - but I guess
better than not building.

> [...] If there's a way to place a dummy into asm-generic which is
> picked if arch/xy/include/asm/ doesn't have the header, let me know.

Not that I know of.

> But if I include it unconditionally without any fallback, this will
> fail on non-x86. And adding the header to all archs seems overkill.

So why not drop the x86-ism and rename it to CONFIG_PLATFORM_SYSFB?
Some platforms configure it, some don't. Then the prototypes could
move into include/linux/sysfb.h or so and would be platform agnostic.

Thanks,

Ingo

2014-01-23 19:09:08

by David Herrmann

[permalink] [raw]
Subject: Re: [PATCH 02/11] x86: sysfb: remove sysfb when probing real hw

Hi

On Thu, Jan 23, 2014 at 6:14 PM, Ingo Molnar <[email protected]> wrote:
>
> * David Herrmann <[email protected]> wrote:
>
>> >> +#ifdef CONFIG_X86_SYSFB
>> >> +# include <asm/sysfb.h>
>> >> +#endif
>> >
>> > I guess a single space is sufficient?
>> >
>> > Better yet, I'd include sysfb.h unconditionally:
>>
>> Unconditionally won't work as only x86 has this header. [...]
>
> Well, in non-x86 code an #ifdef x86 looks ugly as well - but I guess
> better than not building.
>
>> [...] If there's a way to place a dummy into asm-generic which is
>> picked if arch/xy/include/asm/ doesn't have the header, let me know.
>
> Not that I know of.
>
>> But if I include it unconditionally without any fallback, this will
>> fail on non-x86. And adding the header to all archs seems overkill.
>
> So why not drop the x86-ism and rename it to CONFIG_PLATFORM_SYSFB?
> Some platforms configure it, some don't. Then the prototypes could
> move into include/linux/sysfb.h or so and would be platform agnostic.

This is almost exactly what patch #6 does. But it also adds ~400 lines
of kernel-doc and ~400 lines of Documentation/. Given your remarks, I
guess I will just split this patch into code and docs, so we can just
pick it up for stable in case patch #1 does not fix all issues.

Thanks
David

2014-01-24 10:17:03

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH 02/11] x86: sysfb: remove sysfb when probing real hw


* David Herrmann <[email protected]> wrote:

> Hi
>
> On Thu, Jan 23, 2014 at 6:14 PM, Ingo Molnar <[email protected]> wrote:
> >
> > * David Herrmann <[email protected]> wrote:
> >
> >> >> +#ifdef CONFIG_X86_SYSFB
> >> >> +# include <asm/sysfb.h>
> >> >> +#endif
> >> >
> >> > I guess a single space is sufficient?
> >> >
> >> > Better yet, I'd include sysfb.h unconditionally:
> >>
> >> Unconditionally won't work as only x86 has this header. [...]
> >
> > Well, in non-x86 code an #ifdef x86 looks ugly as well - but I guess
> > better than not building.
> >
> >> [...] If there's a way to place a dummy into asm-generic which is
> >> picked if arch/xy/include/asm/ doesn't have the header, let me know.
> >
> > Not that I know of.
> >
> >> But if I include it unconditionally without any fallback, this will
> >> fail on non-x86. And adding the header to all archs seems overkill.
> >
> > So why not drop the x86-ism and rename it to CONFIG_PLATFORM_SYSFB?
> > Some platforms configure it, some don't. Then the prototypes could
> > move into include/linux/sysfb.h or so and would be platform agnostic.
>
> This is almost exactly what patch #6 does. [...]

Indeed - I never got so far down into the series.

> [...] But it also adds ~400 lines of kernel-doc and ~400 lines of
> Documentation/. Given your remarks, I guess I will just split this
> patch into code and docs, so we can just pick it up for stable in
> case patch #1 does not fix all issues.

I have no objections to this form if it's fixed in a later patch and
this one is easier to backport. I just missed that aspect.

Thanks,

Ingo

2014-01-27 22:18:54

by David Herrmann

[permalink] [raw]
Subject: Re: [PATCH 00/11] SimpleDRM & Sysfb

Hi

On Thu, Jan 23, 2014 at 3:14 PM, David Herrmann <[email protected]> wrote:
> Hi
>
> Another round of SimpleDRM patches. I somehow lost track of the last ones and as
> this is a major rewrite, I'll just start at v1 again.
>
> Some comments up-front:
>
> - @Ingo: Patch #1 and #2 are unchanged from the previous ML discussions. I
> included them in this series as the other patches depend on them. Could you
> pick them up for the x86 tree? The other 9 patches won't make it in 3.14 so
> no reason to put them through the DRM tree.
> All mentioned issues should be addressed. If there's still sth missing,
> please let me know.
>
> - The DRM patches depend on my "DRM Anonymous Inode" patches. But it should be
> trivial to apply them on drm-next (I think only one line needs to be changed:
> i_mapping => dev_mapping).
>
> - I tested the SimpleDRM fbdev fallback with linux-console+Xorg and it works
> fine. The DRM backend is only tested with some DRM tests I have locally. I
> have no idea how to make Xorg pick up a specific /dev/dri/card0 card. It
> always tells me "no screens found" (as the underlying device is not marked as
> boot_vga..). If someone knows how to tell Xorg to use card0, I'd gladly test
> this. But I'm no longer used to writing xorg.confs..

For completeness, I tested this with Xorg+xf86-video-modesetting and
it works just fine. The xorg.conf I used can be found below. If this
driver gets upstreamed, I will try to make the X11 auto-loader detect
it just like any other platform-device.

Thanks
David


Section "ServerLayout"
Identifier "MyLayout"
Screen 0 "MyScreen" 0 0
EndSection

Section "Device"
Identifier "MyDevice"
Driver "modesetting"
BusID "platform:/sys/devices/platform/simple-framebuffer.0"
Screen 0
EndSection

Section "Screen"
Identifier "MyScreen"
Device "MyDevice"
EndSection

2014-02-21 09:56:14

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH 00/11] SimpleDRM & Sysfb

On Mon, Jan 27, 2014 at 11:18:51PM +0100, David Herrmann wrote:
> Hi
>
> On Thu, Jan 23, 2014 at 3:14 PM, David Herrmann <[email protected]> wrote:
> > Hi
> >
> > Another round of SimpleDRM patches. I somehow lost track of the last ones and as
> > this is a major rewrite, I'll just start at v1 again.
> >
> > Some comments up-front:
> >
> > - @Ingo: Patch #1 and #2 are unchanged from the previous ML discussions. I
> > included them in this series as the other patches depend on them. Could you
> > pick them up for the x86 tree? The other 9 patches won't make it in 3.14 so
> > no reason to put them through the DRM tree.
> > All mentioned issues should be addressed. If there's still sth missing,
> > please let me know.
> >
> > - The DRM patches depend on my "DRM Anonymous Inode" patches. But it should be
> > trivial to apply them on drm-next (I think only one line needs to be changed:
> > i_mapping => dev_mapping).
> >
> > - I tested the SimpleDRM fbdev fallback with linux-console+Xorg and it works
> > fine. The DRM backend is only tested with some DRM tests I have locally. I
> > have no idea how to make Xorg pick up a specific /dev/dri/card0 card. It
> > always tells me "no screens found" (as the underlying device is not marked as
> > boot_vga..). If someone knows how to tell Xorg to use card0, I'd gladly test
> > this. But I'm no longer used to writing xorg.confs..
>
> For completeness, I tested this with Xorg+xf86-video-modesetting and
> it works just fine. The xorg.conf I used can be found below. If this
> driver gets upstreamed, I will try to make the X11 auto-loader detect
> it just like any other platform-device.

I recently posted patches[0] to the xorg-devel mailing list that I think
should solve that issue.

Thierry

[0]: http://lists.x.org/archives/xorg-devel/2014-February/040568.html


Attachments:
(No filename) (1.82 kB)
(No filename) (836.00 B)
Download all attachments