* [RFC 1/6] fbdev: simplefb: add init through platform_data
2013-06-24 22:27 [RFC 0/6] SimpleDRM Driver (was: dvbe driver) David Herrmann
@ 2013-06-24 22:27 ` David Herrmann
2013-06-26 20:39 ` Stephen Warren
2013-06-24 22:27 ` [RFC 2/6] x86: provide platform-devices for boot-framebuffers David Herrmann
` (5 subsequent siblings)
6 siblings, 1 reply; 20+ messages in thread
From: David Herrmann @ 2013-06-24 22:27 UTC (permalink / raw)
To: dri-devel
Cc: linux-kernel, Dave Airlie, linux-fbdev, Stephen Warren,
Olof Johansson, David Herrmann
If we create proper platform-devices in x86 boot-code, we can use simplefb
for VBE or EFI framebuffers, too. However, there is normally no OF support
so we introduce a platform_data object so x86 boot-code can pass the
paramaters via plain old platform-data.
This also removes the OF dependency as it is not needed. The headers
provide proper dummies for the case OF is disabled.
Furthermore, we move the FORMAT-definitions to the common platform header
so initialization code can use it to transform "struct screen_info" to
the right format-name.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
drivers/video/Kconfig | 5 ++--
drivers/video/simplefb.c | 45 +++++++++++++++++++++++++---------
include/linux/platform_data/simplefb.h | 40 ++++++++++++++++++++++++++++++
3 files changed, 76 insertions(+), 14 deletions(-)
create mode 100644 include/linux/platform_data/simplefb.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 2e937bd..22586ee 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2455,7 +2455,7 @@ config FB_HYPERV
config FB_SIMPLE
bool "Simple framebuffer support"
- depends on (FB = y) && OF
+ depends on (FB = y)
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@@ -2467,8 +2467,7 @@ config FB_SIMPLE
pre-allocated frame buffer surface.
Configuration re: surface address, size, and format must be provided
- through device tree, or potentially plain old platform data in the
- future.
+ through device tree, or plain old platform data.
source "drivers/video/omap/Kconfig"
source "drivers/video/omap2/Kconfig"
diff --git a/drivers/video/simplefb.c b/drivers/video/simplefb.c
index e2e9e3e..35e36c5 100644
--- a/drivers/video/simplefb.c
+++ b/drivers/video/simplefb.c
@@ -24,6 +24,7 @@
#include <linux/fb.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/platform_data/simplefb.h>
#include <linux/platform_device.h>
static struct fb_fix_screeninfo simplefb_fix = {
@@ -73,17 +74,8 @@ static struct fb_ops simplefb_ops = {
.fb_imageblit = cfb_imageblit,
};
-struct simplefb_format {
- const char *name;
- u32 bits_per_pixel;
- struct fb_bitfield red;
- struct fb_bitfield green;
- struct fb_bitfield blue;
- struct fb_bitfield transp;
-};
-
static struct simplefb_format simplefb_formats[] = {
- { "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0} },
+ SIMPLEFB_FORMATS
};
struct simplefb_params {
@@ -139,6 +131,32 @@ static int simplefb_parse_dt(struct platform_device *pdev,
return 0;
}
+static int simplefb_parse_pd(struct platform_device *pdev,
+ struct simplefb_params *params)
+{
+ struct simplefb_platform_data *pd = pdev->dev.platform_data;
+ int i;
+
+ params->width = pd->width;
+ params->height = pd->height;
+ params->stride = pd->stride;
+
+ for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) {
+ if (strcmp(pd->format, simplefb_formats[i].name))
+ continue;
+
+ params->format = &simplefb_formats[i];
+ break;
+ }
+
+ if (!params->format) {
+ dev_err(&pdev->dev, "Invalid format value\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int simplefb_probe(struct platform_device *pdev)
{
int ret;
@@ -149,7 +167,12 @@ static int simplefb_probe(struct platform_device *pdev)
if (fb_get_options("simplefb", NULL))
return -ENODEV;
- ret = simplefb_parse_dt(pdev, ¶ms);
+ ret = -ENODEV;
+ if (pdev->dev.platform_data)
+ ret = simplefb_parse_pd(pdev, ¶ms);
+ else if (pdev->dev.of_node)
+ ret = simplefb_parse_dt(pdev, ¶ms);
+
if (ret)
return ret;
diff --git a/include/linux/platform_data/simplefb.h b/include/linux/platform_data/simplefb.h
new file mode 100644
index 0000000..a18353d
--- /dev/null
+++ b/include/linux/platform_data/simplefb.h
@@ -0,0 +1,40 @@
+/*
+ * simplefb.h - Simple Framebuffer Device
+ *
+ * Copyright (C) 2013 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __PLATFORM_DATA_SIMPLEFB_H__
+#define __PLATFORM_DATA_SIMPLEFB_H__
+
+#include <drm/drm_fourcc.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+
+#define SIMPLEFB_FORMATS \
+ { "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0}, DRM_FORMAT_RGB565 }
+
+struct simplefb_format {
+ const char *name;
+ u32 bits_per_pixel;
+ struct fb_bitfield red;
+ struct fb_bitfield green;
+ struct fb_bitfield blue;
+ struct fb_bitfield transp;
+ u32 fourcc;
+};
+
+/* the framebuffer size and location is available as IORESOURCE_MEM */
+struct simplefb_platform_data {
+ u32 width;
+ u32 height;
+ u32 stride;
+ char format[64];
+};
+
+#endif /* __PLATFORM_DATA_SIMPLEFB_H__ */
--
1.8.3.1
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [RFC 1/6] fbdev: simplefb: add init through platform_data
2013-06-24 22:27 ` [RFC 1/6] fbdev: simplefb: add init through platform_data David Herrmann
@ 2013-06-26 20:39 ` Stephen Warren
2013-06-28 10:03 ` David Herrmann
0 siblings, 1 reply; 20+ messages in thread
From: Stephen Warren @ 2013-06-26 20:39 UTC (permalink / raw)
To: David Herrmann
Cc: dri-devel, linux-kernel, Dave Airlie, linux-fbdev,
Stephen Warren, Olof Johansson
On 06/24/2013 04:27 PM, David Herrmann wrote:
> If we create proper platform-devices in x86 boot-code, we can use simplefb
> for VBE or EFI framebuffers, too. However, there is normally no OF support
> so we introduce a platform_data object so x86 boot-code can pass the
> paramaters via plain old platform-data.
>
> This also removes the OF dependency as it is not needed. The headers
> provide proper dummies for the case OF is disabled.
>
> Furthermore, we move the FORMAT-definitions to the common platform header
> so initialization code can use it to transform "struct screen_info" to
> the right format-name.
> diff --git a/include/linux/platform_data/simplefb.h b/include/linux/platform_data/simplefb.h
> +/* the framebuffer size and location is available as IORESOURCE_MEM */
> +struct simplefb_platform_data {
> + u32 width;
> + u32 height;
> + u32 stride;
> + char format[64];
> +};
Any reason not to make format:
const char *format;
You should be able to initialize that just as easily in platform code,
either as static data or at runtime, I think.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC 1/6] fbdev: simplefb: add init through platform_data
2013-06-26 20:39 ` Stephen Warren
@ 2013-06-28 10:03 ` David Herrmann
0 siblings, 0 replies; 20+ messages in thread
From: David Herrmann @ 2013-06-28 10:03 UTC (permalink / raw)
To: Stephen Warren
Cc: dri-devel, linux-kernel, Dave Airlie, linux-fbdev,
Stephen Warren, Olof Johansson
Hi
On Wed, Jun 26, 2013 at 10:39 PM, Stephen Warren <swarren@wwwdotorg.org> wrote:
> On 06/24/2013 04:27 PM, David Herrmann wrote:
>> If we create proper platform-devices in x86 boot-code, we can use simplefb
>> for VBE or EFI framebuffers, too. However, there is normally no OF support
>> so we introduce a platform_data object so x86 boot-code can pass the
>> paramaters via plain old platform-data.
>>
>> This also removes the OF dependency as it is not needed. The headers
>> provide proper dummies for the case OF is disabled.
>>
>> Furthermore, we move the FORMAT-definitions to the common platform header
>> so initialization code can use it to transform "struct screen_info" to
>> the right format-name.
>
>> diff --git a/include/linux/platform_data/simplefb.h b/include/linux/platform_data/simplefb.h
>
>> +/* the framebuffer size and location is available as IORESOURCE_MEM */
>> +struct simplefb_platform_data {
>> + u32 width;
>> + u32 height;
>> + u32 stride;
>> + char format[64];
>> +};
>
> Any reason not to make format:
>
> const char *format;
>
> You should be able to initialize that just as easily in platform code,
> either as static data or at runtime, I think.
That makes sense. I fixed it up.
Thanks
David
^ permalink raw reply [flat|nested] 20+ messages in thread
* [RFC 2/6] x86: provide platform-devices for boot-framebuffers
2013-06-24 22:27 [RFC 0/6] SimpleDRM Driver (was: dvbe driver) David Herrmann
2013-06-24 22:27 ` [RFC 1/6] fbdev: simplefb: add init through platform_data David Herrmann
@ 2013-06-24 22:27 ` David Herrmann
2013-06-26 20:49 ` Stephen Warren
2013-06-24 22:27 ` [RFC 3/6] drm: add SimpleDRM driver David Herrmann
` (4 subsequent siblings)
6 siblings, 1 reply; 20+ messages in thread
From: David Herrmann @ 2013-06-24 22:27 UTC (permalink / raw)
To: dri-devel
Cc: linux-kernel, Dave Airlie, linux-fbdev, Stephen Warren,
Olof Johansson, David Herrmann
The current situation regarding boot-framebuffers (VGA, VESA/VBE, EFI) on
x86 causes troubles when loading multiple fbdev drivers. The global
"struct screen_info" does not provide any state-tracking about which
drivers use the FBs. request_mem_region() theoretically works, but
unfortunately vesafb/efifb ignore it due to quirks for broken boards.
Avoid this by creating a "platform-framebuffer" device with a pointer
to the "struct screen_info" as platform-data. Drivers can now create
platform-drivers and the driver-core will refuse multiple drivers being
active simultaneously.
We keep the screen_info available for backwards-compatibility. Drivers
can be converted in follow-up patches.
Apart from "platform-framebuffer" devices, this also introduces a
compatibility option for "simple-framebuffer" drivers which recently got
introduced for OF based systems. If CONFIG_X86_SYSFB is selected, we
try to match the screen_info against a simple-framebuffer supported
format. If we succeed, we create a "simple-framebuffer" device instead
of a platform-framebuffer.
This allows to reuse the simplefb.c driver across architectures and also
to introduce a SimpleDRM driver. There is no need to have vesafb.c,
efifb.c, simplefb.c and more just to have architecture specific quirks
in their setup-routines.
Instead, we now move the architecture specific quirks into x86-setup and
provide a generic simple-framebuffer. For backwards-compatibility (if
strange formats are used), we still allow vesafb/efifb to be loaded
simultaneously and pick up all remaining devices.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
arch/x86/Kconfig | 18 ++++++
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/sysfb.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 176 insertions(+)
create mode 100644 arch/x86/kernel/sysfb.c
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index fe120da..8eb06b5 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2255,6 +2255,24 @@ config RAPIDIO
source "drivers/rapidio/Kconfig"
+config X86_SYSFB
+ bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
+ help
+ Firmwares often provide initial graphics framebuffers so the BIOS,
+ bootloader or kernel can show basic video-output during boot for
+ user-guidance and debugging. Historically, x86 used the VESA BIOS
+ Extensions and EFI-framebuffers for this, which are mostly limited
+ to x86. However, a generic system-framebuffer initialization emerged
+ recently on some non-x86 architectures.
+ This option, if enabled, marks VGA/VBE/EFI framebuffers as generic
+ framebuffers so the new generic system-framebuffer drivers can be
+ used on x86.
+
+ This breaks any x86-only driver like efifb, vesafb, uvesafb, which
+ will not work if this is selected.
+
+ If unsure, say N.
+
endmenu
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 7bd3bd3..1e1005a 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -100,6 +100,7 @@ obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o
obj-$(CONFIG_SWIOTLB) += pci-swiotlb.o
obj-$(CONFIG_OF) += devicetree.o
obj-$(CONFIG_UPROBES) += uprobes.o
+obj-y += sysfb.o
obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
diff --git a/arch/x86/kernel/sysfb.c b/arch/x86/kernel/sysfb.c
new file mode 100644
index 0000000..8272958
--- /dev/null
+++ b/arch/x86/kernel/sysfb.c
@@ -0,0 +1,157 @@
+/*
+ * Generic System Framebuffers on x86
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+/*
+ * Simple-Framebuffer support for x86 systems
+ * Create a platform-device for any available boot framebuffer. The
+ * simple-framebuffer platform device is already available on DT systems, so
+ * this module parses the global "screen_info" object and creates a suitable
+ * platform device compatible with the "simple-framebuffer" DT object. If
+ * the framebuffer is incompatible, we instead create a "platform-framebuffer"
+ * device and pass the screen_info as platform_data. This allows legacy drivers
+ * to pick these devices up without messing with simple-framebuffer drivers.
+ *
+ * If CONFIG_X86_SYSFB is not selected, we never register "simple-framebuffer"
+ * platform devices, but only use "platform-framebuffer" devices for
+ * backwards compatibility.
+ *
+ * TODO: We set the dev_id field of all platform-devices to 0. This allows
+ * other x86 OF/DT parsers to create such devices, too. However, they must
+ * start at offset 1 for this to work.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/platform_device.h>
+#include <linux/screen_info.h>
+
+#ifdef CONFIG_X86_SYSFB
+
+static const char simplefb_resname[] = "BOOTFB";
+
+static const struct simplefb_format formats[] = {
+ SIMPLEFB_FORMATS
+};
+
+static bool parse_mode(const struct screen_info *si,
+ struct simplefb_platform_data *mode)
+{
+ const struct simplefb_format *f;
+ __u8 type;
+ unsigned int i;
+
+ /* TODO: apply quirks from efifb.c here before probing the devices */
+
+ type = si->orig_video_isVGA;
+ if (type != VIDEO_TYPE_VLFB && type != VIDEO_TYPE_EFI)
+ return false;
+
+ 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) {
+ strlcpy(mode->format, f->name, sizeof(mode->format));
+ mode->width = si->lfb_width;
+ mode->height = si->lfb_height;
+ mode->stride = si->lfb_linelength;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int create_simplefb(const struct screen_info *si,
+ const struct simplefb_platform_data *mode)
+{
+ struct platform_device *pd;
+ struct resource memres;
+ 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 > si->lfb_size << 16) {
+ printk(KERN_WARNING "sysfb: VRAM smaller than advertised\n");
+ return -EINVAL;
+ }
+
+ /* setup IORESOURCE_MEM as framebuffer memory */
+ memset(&memres, 0, sizeof(memres));
+ memres.flags = IORESOURCE_MEM;
+ memres.name = simplefb_resname;
+ memres.start = si->lfb_base;
+ memres.end = si->lfb_base + len - 1;
+ if (memres.end <= memres.start)
+ return -EINVAL;
+
+ pd = platform_device_register_resndata(NULL,
+ "simple-framebuffer", 0,
+ &memres, 1,
+ mode, sizeof(*mode));
+ if (IS_ERR(pd))
+ return PTR_ERR(pd);
+
+ return 0;
+}
+
+#else /* CONFIG_X86_SYSFB */
+
+static bool parse_mode(const struct screen_info *si,
+ struct simplefb_platform_data *mode)
+{
+ return false;
+}
+
+static int create_simplefb(const struct screen_info *si,
+ const struct simplefb_platform_data *mode)
+{
+ return -EINVAL;
+}
+
+#endif /* CONFIG_X86_SYSFB */
+
+static __init int add_sysfb(void)
+{
+ const struct screen_info *si = &screen_info;
+ struct simplefb_platform_data mode;
+ struct platform_device *pd;
+ bool compatible = false;
+ int ret;
+
+ compatible = parse_mode(si, &mode);
+
+ ret = -ENODEV;
+ if (compatible)
+ ret = create_simplefb(si, &mode);
+
+ if (ret) {
+ pd = platform_device_register_resndata(NULL,
+ "platform-framebuffer", 0,
+ NULL, 0, si, sizeof(*si));
+ ret = IS_ERR(pd) ? PTR_ERR(pd) : 0;
+ }
+
+ return ret;
+}
+device_initcall(add_sysfb);
--
1.8.3.1
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [RFC 2/6] x86: provide platform-devices for boot-framebuffers
2013-06-24 22:27 ` [RFC 2/6] x86: provide platform-devices for boot-framebuffers David Herrmann
@ 2013-06-26 20:49 ` Stephen Warren
2013-06-28 10:11 ` David Herrmann
0 siblings, 1 reply; 20+ messages in thread
From: Stephen Warren @ 2013-06-26 20:49 UTC (permalink / raw)
To: David Herrmann
Cc: dri-devel, linux-kernel, Dave Airlie, linux-fbdev,
Stephen Warren, Olof Johansson
On 06/24/2013 04:27 PM, David Herrmann wrote:
> The current situation regarding boot-framebuffers (VGA, VESA/VBE, EFI) on
> x86 causes troubles when loading multiple fbdev drivers. The global
> "struct screen_info" does not provide any state-tracking about which
> drivers use the FBs. request_mem_region() theoretically works, but
> unfortunately vesafb/efifb ignore it due to quirks for broken boards.
>
> Avoid this by creating a "platform-framebuffer" device with a pointer
> to the "struct screen_info" as platform-data. Drivers can now create
> platform-drivers and the driver-core will refuse multiple drivers being
> active simultaneously.
>
> We keep the screen_info available for backwards-compatibility. Drivers
> can be converted in follow-up patches.
>
> Apart from "platform-framebuffer" devices, this also introduces a
> compatibility option for "simple-framebuffer" drivers which recently got
> introduced for OF based systems. If CONFIG_X86_SYSFB is selected, we
> try to match the screen_info against a simple-framebuffer supported
> format. If we succeed, we create a "simple-framebuffer" device instead
> of a platform-framebuffer.
> This allows to reuse the simplefb.c driver across architectures and also
> to introduce a SimpleDRM driver. There is no need to have vesafb.c,
> efifb.c, simplefb.c and more just to have architecture specific quirks
> in their setup-routines.
>
> Instead, we now move the architecture specific quirks into x86-setup and
> provide a generic simple-framebuffer. For backwards-compatibility (if
> strange formats are used), we still allow vesafb/efifb to be loaded
> simultaneously and pick up all remaining devices.
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> +config X86_SYSFB
> + bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
> + help
> + Firmwares often provide initial graphics framebuffers so the BIOS,
> + bootloader or kernel can show basic video-output during boot for
> + user-guidance and debugging. Historically, x86 used the VESA BIOS
> + Extensions and EFI-framebuffers for this, which are mostly limited
> + to x86. However, a generic system-framebuffer initialization emerged
> + recently on some non-x86 architectures.
After this patch has been in the kernel a while, that very last won't
really be true; simplefb won't have been introduced recently. Perhaps
just delete that one sentence?
> + This option, if enabled, marks VGA/VBE/EFI framebuffers as generic
> + framebuffers so the new generic system-framebuffer drivers can be
> + used on x86.
> +
> + This breaks any x86-only driver like efifb, vesafb, uvesafb, which
> + will not work if this is selected.
Doesn't that imply that some form of conflicts or depends ! statement
should be added here?
> diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
> +obj-y += sysfb.o
I suspect that should be obj-$(CONFIG_X86_SYSFB) += sysfb.o.
> diff --git a/arch/x86/kernel/sysfb.c b/arch/x86/kernel/sysfb.c
> +#ifdef CONFIG_X86_SYSFB
Rather than ifdef'ing the body of this file, why not create a dummy
static inline version of add_sysfb() and put that into a header file
that users include. There should be a header file to prototype the
function anyway. That way, you avoid all of the ifdefs and static inline
functions in this file.
> +static bool parse_mode(const struct screen_info *si,
> + struct simplefb_platform_data *mode)
> + strlcpy(mode->format, f->name, sizeof(mode->format));
Per my comments about the type of mode->format, I think that could just be:
mode->format = f->name;
... since formats[] (i.e. f) isn't initdata.
> +#else /* CONFIG_X86_SYSFB */
> +
> +static bool parse_mode(const struct screen_info *si,
> + struct simplefb_platform_data *mode)
> +{
> + return false;
> +}
> +
> +static int create_simplefb(const struct screen_info *si,
> + const struct simplefb_platform_data *mode)
> +{
> + return -EINVAL;
> +}
> +
> +#endif /* CONFIG_X86_SYSFB */
Following on from my ifdef comment above, I believe those versions of
those functions will always cause add_sysfb() to return -ENODEV, so you
may as well provide a static inline for add_sysfb() instead.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC 2/6] x86: provide platform-devices for boot-framebuffers
2013-06-26 20:49 ` Stephen Warren
@ 2013-06-28 10:11 ` David Herrmann
2013-07-01 15:48 ` Stephen Warren
0 siblings, 1 reply; 20+ messages in thread
From: David Herrmann @ 2013-06-28 10:11 UTC (permalink / raw)
To: Stephen Warren
Cc: dri-devel, linux-kernel, Dave Airlie, linux-fbdev,
Stephen Warren, Olof Johansson
Hi
On Wed, Jun 26, 2013 at 10:49 PM, Stephen Warren <swarren@wwwdotorg.org> wrote:
> On 06/24/2013 04:27 PM, David Herrmann wrote:
>> The current situation regarding boot-framebuffers (VGA, VESA/VBE, EFI) on
>> x86 causes troubles when loading multiple fbdev drivers. The global
>> "struct screen_info" does not provide any state-tracking about which
>> drivers use the FBs. request_mem_region() theoretically works, but
>> unfortunately vesafb/efifb ignore it due to quirks for broken boards.
>>
>> Avoid this by creating a "platform-framebuffer" device with a pointer
>> to the "struct screen_info" as platform-data. Drivers can now create
>> platform-drivers and the driver-core will refuse multiple drivers being
>> active simultaneously.
>>
>> We keep the screen_info available for backwards-compatibility. Drivers
>> can be converted in follow-up patches.
>>
>> Apart from "platform-framebuffer" devices, this also introduces a
>> compatibility option for "simple-framebuffer" drivers which recently got
>> introduced for OF based systems. If CONFIG_X86_SYSFB is selected, we
>> try to match the screen_info against a simple-framebuffer supported
>> format. If we succeed, we create a "simple-framebuffer" device instead
>> of a platform-framebuffer.
>> This allows to reuse the simplefb.c driver across architectures and also
>> to introduce a SimpleDRM driver. There is no need to have vesafb.c,
>> efifb.c, simplefb.c and more just to have architecture specific quirks
>> in their setup-routines.
>>
>> Instead, we now move the architecture specific quirks into x86-setup and
>> provide a generic simple-framebuffer. For backwards-compatibility (if
>> strange formats are used), we still allow vesafb/efifb to be loaded
>> simultaneously and pick up all remaining devices.
>
>> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
>
>> +config X86_SYSFB
>> + bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
>> + help
>> + Firmwares often provide initial graphics framebuffers so the BIOS,
>> + bootloader or kernel can show basic video-output during boot for
>> + user-guidance and debugging. Historically, x86 used the VESA BIOS
>> + Extensions and EFI-framebuffers for this, which are mostly limited
>> + to x86. However, a generic system-framebuffer initialization emerged
>> + recently on some non-x86 architectures.
>
> After this patch has been in the kernel a while, that very last won't
> really be true; simplefb won't have been introduced recently. Perhaps
> just delete that one sentence?
It rather belongs in the commit message, right. I will rephrase that.
>> + This option, if enabled, marks VGA/VBE/EFI framebuffers as generic
>> + framebuffers so the new generic system-framebuffer drivers can be
>> + used on x86.
>> +
>> + This breaks any x86-only driver like efifb, vesafb, uvesafb, which
>> + will not work if this is selected.
>
> Doesn't that imply that some form of conflicts or depends ! statement
> should be added here?
There is no real conflict here. You still can use vesafb/... with this
option, but they will not pick up the device. I intend to fix these up
to use "platform-framebuffer" devices instead of globally binding to
"struct screen_info". This way, framebuffers either end up as
simple-framebuffers or platform-framebuffers. This option selects
which device they end up as.
As all non-compatible framebuffers (with incompatible pixel-formats)
always end up as "platform-framebuffer", it still makes sense to use
vesafb as fallback. Hence, I'd not introduce any "conflicts"
dependency here.
Maybe I should rephrase the warning to something that makes clear that
if this option is selected, you need simplefb.c or simpledrm to make
use of these devices.
>> diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
>
>> +obj-y += sysfb.o
>
> I suspect that should be obj-$(CONFIG_X86_SYSFB) += sysfb.o.
No. This patch tries to solve two things: First of all, every
system-framebuffer now gets a "platform-framebuffer" platform-device.
Iff X86_SYSFB is selected, it additionally tries to parse the
framebuffer information as "simple-framebuffer". If it succeeds, it
created a "simple-framebuffer" object, if it doesn't, a fallback
"platform-framebuffer" is provided.
This series is missing vesafb/efifb/.. patches, which should now bind
to "platform-framebuffer" devices instead of using "struct
screen_info" directly. I intend to add these in the next round.
>> diff --git a/arch/x86/kernel/sysfb.c b/arch/x86/kernel/sysfb.c
>
>> +#ifdef CONFIG_X86_SYSFB
>
> Rather than ifdef'ing the body of this file, why not create a dummy
> static inline version of add_sysfb() and put that into a header file
> that users include. There should be a header file to prototype the
> function anyway. That way, you avoid all of the ifdefs and static inline
> functions in this file.
>
>> +static bool parse_mode(const struct screen_info *si,
>> + struct simplefb_platform_data *mode)
>
>> + strlcpy(mode->format, f->name, sizeof(mode->format));
>
> Per my comments about the type of mode->format, I think that could just be:
>
> mode->format = f->name;
>
> ... since formats[] (i.e. f) isn't initdata.
>
>> +#else /* CONFIG_X86_SYSFB */
>> +
>> +static bool parse_mode(const struct screen_info *si,
>> + struct simplefb_platform_data *mode)
>> +{
>> + return false;
>> +}
>> +
>> +static int create_simplefb(const struct screen_info *si,
>> + const struct simplefb_platform_data *mode)
>> +{
>> + return -EINVAL;
>> +}
>> +
>> +#endif /* CONFIG_X86_SYSFB */
>
> Following on from my ifdef comment above, I believe those versions of
> those functions will always cause add_sysfb() to return -ENODEV, so you
> may as well provide a static inline for add_sysfb() instead.
No. add_sysfb() is supposed to always succeed. However, if
parse_mode/create_simplefb fail, it creates a "platform-framebuffer"
as fallback. I don't see any way to avoid these ifdefs. Considering
the explanation above, could you elaborate how you think this should
work?
Thanks
David
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC 2/6] x86: provide platform-devices for boot-framebuffers
2013-06-28 10:11 ` David Herrmann
@ 2013-07-01 15:48 ` Stephen Warren
0 siblings, 0 replies; 20+ messages in thread
From: Stephen Warren @ 2013-07-01 15:48 UTC (permalink / raw)
To: David Herrmann
Cc: dri-devel, linux-kernel, Dave Airlie, linux-fbdev,
Stephen Warren, Olof Johansson
On 06/28/2013 04:11 AM, David Herrmann wrote:
> Hi
>
> On Wed, Jun 26, 2013 at 10:49 PM, Stephen Warren <swarren@wwwdotorg.org> wrote:
>> On 06/24/2013 04:27 PM, David Herrmann wrote:
>>> The current situation regarding boot-framebuffers (VGA, VESA/VBE, EFI) on
>>> x86 causes troubles when loading multiple fbdev drivers. The global
>>> "struct screen_info" does not provide any state-tracking about which
>>> drivers use the FBs. request_mem_region() theoretically works, but
>>> unfortunately vesafb/efifb ignore it due to quirks for broken boards.
>>>
>>> Avoid this by creating a "platform-framebuffer" device with a pointer
>>> to the "struct screen_info" as platform-data. Drivers can now create
>>> platform-drivers and the driver-core will refuse multiple drivers being
>>> active simultaneously.
>>>
>>> We keep the screen_info available for backwards-compatibility. Drivers
>>> can be converted in follow-up patches.
>>>
>>> Apart from "platform-framebuffer" devices, this also introduces a
>>> compatibility option for "simple-framebuffer" drivers which recently got
>>> introduced for OF based systems. If CONFIG_X86_SYSFB is selected, we
>>> try to match the screen_info against a simple-framebuffer supported
>>> format. If we succeed, we create a "simple-framebuffer" device instead
>>> of a platform-framebuffer.
...
>>> diff --git a/arch/x86/kernel/sysfb.c b/arch/x86/kernel/sysfb.c
...
>>> +#else /* CONFIG_X86_SYSFB */
>>> +
>>> +static bool parse_mode(const struct screen_info *si,
>>> + struct simplefb_platform_data *mode)
>>> +{
>>> + return false;
>>> +}
>>> +
>>> +static int create_simplefb(const struct screen_info *si,
>>> + const struct simplefb_platform_data *mode)
>>> +{
>>> + return -EINVAL;
>>> +}
>>> +
>>> +#endif /* CONFIG_X86_SYSFB */
>>
>> Following on from my ifdef comment above, I believe those versions of
>> those functions will always cause add_sysfb() to return -ENODEV, so you
>> may as well provide a static inline for add_sysfb() instead.
>
> No. add_sysfb() is supposed to always succeed. However, if
> parse_mode/create_simplefb fail, it creates a "platform-framebuffer"
> as fallback. I don't see any way to avoid these ifdefs. Considering
> the explanation above, could you elaborate how you think this should
> work?
Ah, I wasn't getting the fallback mechanism; that if creating a simplefb
wasn't possible or didn't succeed, a platformfb device would be created
instead.
Perhaps the following might be slightly clearer; there are certainly
fewer nesting levels:
static __init int add_sysfb(void)
{
const struct screen_info *si = &screen_info;
struct simplefb_platform_data mode;
struct platform_device *pd;
bool compatible = false;
int ret;
compatible = parse_mode(si, &mode);
if (compatible) {
ret = create_simplefb(si, &mode);
if (!ret)
return 0;
}
pd = platform_device_register_resndata(NULL,
"platform-framebuffer", 0,
NULL, 0, si, sizeof(*si));
ret = IS_ERR(pd) ? PTR_ERR(pd) : 0;
return ret;
}
^ permalink raw reply [flat|nested] 20+ messages in thread
* [RFC 3/6] drm: add SimpleDRM driver
2013-06-24 22:27 [RFC 0/6] SimpleDRM Driver (was: dvbe driver) David Herrmann
2013-06-24 22:27 ` [RFC 1/6] fbdev: simplefb: add init through platform_data David Herrmann
2013-06-24 22:27 ` [RFC 2/6] x86: provide platform-devices for boot-framebuffers David Herrmann
@ 2013-06-24 22:27 ` David Herrmann
2013-06-25 1:05 ` Andy Lutomirski
2013-06-26 20:58 ` Stephen Warren
2013-06-24 22:27 ` [RFC 4/6] drm: simpledrm: add fbdev fallback support David Herrmann
` (3 subsequent siblings)
6 siblings, 2 replies; 20+ messages in thread
From: David Herrmann @ 2013-06-24 22:27 UTC (permalink / raw)
To: dri-devel
Cc: linux-kernel, Dave Airlie, linux-fbdev, Stephen Warren,
Olof Johansson, David Herrmann
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 one dumb-buffer and attach it to the CRTC. Only if
the buffer is destroyed, a new buffer can be created. The buffer is
directly mapped into user-space, so we have only resources for a single
buffer. Otherwise, shadow buffers plus damage-request would be needed.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
MAINTAINERS | 8 +
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/simpledrm/Kconfig | 18 ++
drivers/gpu/drm/simpledrm/Makefile | 5 +
drivers/gpu/drm/simpledrm/simpledrm.h | 91 ++++++++
drivers/gpu/drm/simpledrm/simpledrm_drv.c | 230 ++++++++++++++++++++
drivers/gpu/drm/simpledrm/simpledrm_main.c | 330 +++++++++++++++++++++++++++++
drivers/gpu/drm/simpledrm/simpledrm_mem.c | 254 ++++++++++++++++++++++
9 files changed, 939 insertions(+)
create mode 100644 drivers/gpu/drm/simpledrm/Kconfig
create mode 100644 drivers/gpu/drm/simpledrm/Makefile
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm.h
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_drv.c
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_main.c
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_mem.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 5be702c..0f651845 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7305,6 +7305,14 @@ S: Odd Fixes
F: drivers/media/platform/sh_vou.c
F: include/media/sh_vou.h
+SIMPLE DRM DRIVER
+M: David Herrmann <dh.herrmann@gmail.com>
+L: dri-devel@lists.freedesktop.org
+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 <lenb@kernel.org>
L: sfi-devel@simplefirmware.org
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index b16c50e..b3ccef6 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -220,3 +220,5 @@ source "drivers/gpu/drm/omapdrm/Kconfig"
source "drivers/gpu/drm/tilcdc/Kconfig"
source "drivers/gpu/drm/qxl/Kconfig"
+
+source "drivers/gpu/drm/simpledrm/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 1c9f2439..59c424d 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -52,4 +52,5 @@ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
obj-$(CONFIG_DRM_OMAP) += omapdrm/
obj-$(CONFIG_DRM_TILCDC) += tilcdc/
obj-$(CONFIG_DRM_QXL) += qxl/
+obj-$(CONFIG_DRM_SIMPLEDRM) += simpledrm/
obj-y += i2c/
diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig
new file mode 100644
index 0000000..1d4f38e
--- /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
+ 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, x86 VESA
+ BIOS Extensions (VBE), EFI 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..2d474a5
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/Makefile
@@ -0,0 +1,5 @@
+ccflags-y := -Iinclude/drm
+
+simpledrm-y := simpledrm_drv.o simpledrm_main.o simpledrm_mem.o
+
+obj-$(CONFIG_DRM_SIMPLEDRM) := simpledrm.o
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h
new file mode 100644
index 0000000..279d847
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm.h
@@ -0,0 +1,91 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#ifndef SDRM_DRV_H
+#define SDRM_DRV_H
+
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+
+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 sdrm_gem_object *fb_obj;
+ 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);
+
+/* simpledrm gem objects */
+
+struct sdrm_gem_object {
+ struct drm_gem_object base;
+ unsigned long fb_base;
+ unsigned long fb_size;
+};
+
+#define to_sdrm_bo(x) container_of(x, struct sdrm_gem_object, base)
+
+int sdrm_gem_init_object(struct drm_gem_object *obj);
+void sdrm_gem_free_object(struct drm_gem_object *obj);
+void sdrm_gem_unmap_object(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_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
new file mode 100644
index 0000000..282752c
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
@@ -0,0 +1,230 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include "simpledrm.h"
+
+static const struct file_operations sdrm_drm_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .mmap = sdrm_drm_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .unlocked_ioctl = drm_ioctl,
+ .release = drm_release,
+ .fasync = drm_fasync,
+#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_init_object = sdrm_gem_init_object,
+ .gem_free_object = sdrm_gem_free_object,
+
+ .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;
+ }
+ strlcpy(mode->format, format, sizeof(mode->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;
+ }
+
+ 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;
+
+ if (!request_mem_region(sdrm->fb_base, sdrm->fb_size,
+ "simple-framebuffer")) {
+ dev_err(sdrm->ddev->dev, "cannot reserve VMEM\n");
+ return -EIO;
+ }
+
+ sdrm->fb_map = ioremap(sdrm->fb_base, sdrm->fb_size);
+ if (!sdrm->fb_map) {
+ dev_err(sdrm->ddev->dev, "cannot remap VMEM\n");
+ ret = -EIO;
+ goto err_region;
+ }
+
+ return 0;
+
+err_region:
+ release_mem_region(sdrm->fb_base, sdrm->fb_size);
+ return ret;
+}
+
+void sdrm_pdev_destroy(struct sdrm_device *sdrm)
+{
+ if (sdrm->fb_map) {
+ iounmap(sdrm->fb_map);
+ release_mem_region(sdrm->fb_base, sdrm->fb_size);
+ 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)
+{
+ drm_platform_exit(&sdrm_drm_driver, pdev);
+
+ return 0;
+}
+
+static const struct of_device_id simplefb_of_match[] = {
+ { .compatible = "simple-framebuffer", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, simplefb_of_match);
+
+static struct platform_driver sdrm_simplefb_driver = {
+ .probe = sdrm_simplefb_probe,
+ .remove = sdrm_simplefb_remove,
+ .driver = {
+ .name = "simple-framebuffer",
+ .mod_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .of_match_table = simplefb_of_match,
+ },
+};
+
+static int __init sdrm_init(void)
+{
+ return platform_driver_register(&sdrm_simplefb_driver);
+}
+
+static void __exit sdrm_exit(void)
+{
+ platform_driver_unregister(&sdrm_simplefb_driver);
+}
+
+module_init(sdrm_init);
+module_exit(sdrm_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
+MODULE_DESCRIPTION("Simple firmware framebuffer DRM driver");
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_main.c b/drivers/gpu/drm/simpledrm/simpledrm_main.c
new file mode 100644
index 0000000..dcc3d0a
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_main.c
@@ -0,0 +1,330 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include <drm/drm_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->x || set->y)
+ return -EINVAL;
+ if (set->mode->hdisplay != sdrm->fb_width ||
+ set->mode->vdisplay != 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 = 0;
+ sdrm->crtc.y = 0;
+
+ drm_calc_timestamping_constants(&sdrm->crtc);
+ 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 ret;
+
+ 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_gtf_mode(sdrm->ddev, sdrm->fb_width, sdrm->fb_height,
+ 60, 0, 0);
+ if (mode) {
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(conn, mode);
+ sdrm->mode = mode;
+ drm_mode_connector_list_update(conn);
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+
+ 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 ret;
+}
+
+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. */
+ if (device_is_registered(&conn->kdev))
+ 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,
+ .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_device *sdrm = ddev->dev_private;
+ struct sdrm_framebuffer *fb;
+ struct drm_gem_object *gobj;
+ int ret, i;
+ void *err;
+
+ if (cmd->flags || cmd->pixel_format != sdrm->fb_format)
+ return ERR_PTR(-EINVAL);
+ if (cmd->height != sdrm->fb_height || cmd->width != sdrm->fb_width)
+ return ERR_PTR(-EINVAL);
+ if (cmd->offsets[0] || cmd->pitches[0] != sdrm->fb_stride)
+ 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];
+ for (i = 1; i < 4; i++) {
+ fb->base.pitches[i] = 0;
+ fb->base.offsets[i] = 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);
+
+ 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;
+
+ 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 = 0;
+ ddev->mode_config.min_height = 0;
+ 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_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;
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_mem.c b/drivers/gpu/drm/simpledrm/simpledrm_mem.c
new file mode 100644
index 0000000..afadcf5
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_mem.c
@@ -0,0 +1,254 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include "simpledrm.h"
+
+/*
+ * Create GEM Object
+ * Allocates a new GEM object to manage the physical memory at @fb_base with
+ * size @fb_size. Both parameters must be page-aligned and point to the
+ * physical memory of the framebuffer to manage. They must not have a
+ * "struct page" and have to be reserved before.
+ * It is the callers responsibility to create only one object per framebuffer.
+ */
+static struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev,
+ unsigned long fb_base,
+ unsigned long fb_size)
+{
+ struct sdrm_gem_object *obj;
+
+ WARN_ON((fb_base & ~PAGE_MASK) != 0);
+ WARN_ON((fb_size & ~PAGE_MASK) != 0);
+
+ /* align to page-size */
+ fb_size = fb_size + (fb_base & ~PAGE_MASK);
+ fb_base = fb_base & PAGE_MASK;
+ fb_size = PAGE_ALIGN(fb_size);
+
+ if (fb_base + fb_size < fb_base)
+ return NULL;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return NULL;
+ obj->fb_base = fb_base;
+ obj->fb_size = fb_size;
+
+ if (drm_gem_private_object_init(ddev, &obj->base, fb_size)) {
+ kfree(obj);
+ return NULL;
+ }
+
+ return obj;
+}
+
+/* drm_gem_object_alloc() is not supported */
+int sdrm_gem_init_object(struct drm_gem_object *gobj)
+{
+ return -EINVAL;
+}
+
+/*
+ * Unmap GEM Object
+ * Destroy any memory-mappings that user-space created on this object. Note
+ * that this will cause SIGBUS errors if user-space continues writing to it.
+ * There is no way to remap the pages in fault-handlers as this is not what
+ * we want. You should destroy the mappings only when destroying the object
+ * so no remapping will be needed.
+ * It's the callers responsibility to prevent any further mappings. This only
+ * destroys all current mappings.
+ */
+void sdrm_gem_unmap_object(struct sdrm_gem_object *obj)
+{
+ struct drm_device *ddev = obj->base.dev;
+ loff_t off;
+
+ off = obj->base.map_list.hash.key << PAGE_SHIFT;
+ unmap_mapping_range(ddev->dev_mapping, off, obj->fb_size, 1);
+}
+
+/*
+ * Free GEM Object
+ * Frees the given GEM object. It does not release the framebuffer memory that
+ * was passed during allocation, but destroys all user-space mappings.
+ */
+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;
+ struct sdrm_device *sdrm = ddev->dev_private;
+
+ if (sdrm->fb_obj == obj)
+ sdrm->fb_obj = NULL;
+
+ sdrm_gem_unmap_object(obj);
+
+ if (gobj->map_list.map)
+ drm_gem_free_mmap_offset(gobj);
+ drm_gem_object_release(gobj);
+ kfree(obj);
+}
+
+/*
+ * Create Dumb Buffer
+ * IOCTL backend for dumb-buffers. We only support one framebuffer per
+ * simple-DRM device so this function fails if there is already a framebuffer
+ * allocated. If not, an initial GEM-object plus framebuffer is created and
+ * forwarded to the caller.
+ *
+ * We could try to kill off the previous framebuffer and create a new one for
+ * the caller. However, user-space often allocates two buffers in a row to
+ * allow double-buffering. If we kill the previous buffer, user-space would
+ * have no chance to notice that only one buffer is available.
+ *
+ * So user-space must make sure they either destroy their buffer when dropping
+ * DRM-Master or leave the CRTC intact and let others share the buffer via
+ * drmModeGetFB().
+ *
+ * The buffer parameters must be the same as from the default-mode of the CRTC.
+ * No other sizes can be supported!
+ */
+int sdrm_dumb_create(struct drm_file *dfile, struct drm_device *ddev,
+ struct drm_mode_create_dumb *args)
+{
+ struct drm_device *dev = dfile->minor->dev;
+ struct sdrm_device *sdrm = dev->dev_private;
+ struct sdrm_gem_object *obj;
+ int ret;
+
+ /* only allow one framebuffer at a time */
+ if (sdrm->fb_obj)
+ return -ENOMEM;
+
+ if (args->width != sdrm->fb_width ||
+ args->height != sdrm->fb_height ||
+ args->bpp != sdrm->fb_bpp ||
+ args->flags)
+ return -EINVAL;
+
+ args->pitch = sdrm->fb_stride;
+ args->size = sdrm->fb_size;
+ obj = sdrm_gem_alloc_object(ddev, sdrm->fb_base, sdrm->fb_size);
+ if (!obj)
+ return -ENOMEM;
+
+ ret = drm_gem_handle_create(dfile, &obj->base, &args->handle);
+ if (ret) {
+ drm_gem_object_unreference(&obj->base);
+ return ret;
+ }
+
+ /* fb_obj is cleared by sdrm_gem_free_object() */
+ sdrm->fb_obj = obj;
+ 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 ret;
+
+ mutex_lock(&ddev->struct_mutex);
+
+ gobj = drm_gem_object_lookup(ddev, dfile, handle);
+ if (!gobj) {
+ ret = -ENOENT;
+ goto out_unlock;
+ }
+
+ if (!gobj->map_list.map) {
+ ret = drm_gem_create_mmap_offset(gobj);
+ if (ret)
+ goto out_unref;
+ }
+
+ *offset = gobj->map_list.hash.key << PAGE_SHIFT;
+
+out_unref:
+ drm_gem_object_unreference(gobj);
+out_unlock:
+ mutex_unlock(&ddev->struct_mutex);
+ return ret;
+}
+
+/*
+ * mmap ioctl
+ * We simply map the physical range of the FB into user-space as requested. We
+ * perform few sanity-checks and then let io_remap_pfn_range() do all the work.
+ * No vma_ops are needed this way as pages are either cleared or present.
+ */
+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_gem_mm *mm = dev->mm_private;
+ struct drm_local_map *map;
+ struct drm_hash_item *item;
+ struct drm_gem_object *gobj;
+ struct sdrm_gem_object *obj;
+ unsigned long len;
+ int ret;
+
+ mutex_lock(&dev->struct_mutex);
+
+ if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &item)) {
+ mutex_unlock(&dev->struct_mutex);
+ return drm_mmap(filp, vma);
+ }
+
+ map = drm_hash_entry(item, struct drm_map_list, hash)->map;
+ if (!map ||
+ ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) {
+ ret = -EPERM;
+ goto out_unlock;
+ }
+
+ gobj = map->handle;
+ obj = to_sdrm_bo(gobj);
+
+ vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ /* FIXME: do we need fb_pgprotect() here? */
+
+ /* verify mapping size */
+ len = vma->vm_end - vma->vm_start;
+ if (len > obj->fb_size) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ /* This object is _not_ referenced here. Therefore, we _must_ destroy
+ * the mapping before destroying the bo! */
+
+ ret = io_remap_pfn_range(vma, vma->vm_start, obj->fb_base >> PAGE_SHIFT,
+ obj->fb_size, vma->vm_page_prot);
+
+out_unlock:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
--
1.8.3.1
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [RFC 3/6] drm: add SimpleDRM driver
2013-06-24 22:27 ` [RFC 3/6] drm: add SimpleDRM driver David Herrmann
@ 2013-06-25 1:05 ` Andy Lutomirski
2013-06-28 9:59 ` David Herrmann
2013-06-26 20:58 ` Stephen Warren
1 sibling, 1 reply; 20+ messages in thread
From: Andy Lutomirski @ 2013-06-25 1:05 UTC (permalink / raw)
To: David Herrmann
Cc: dri-devel, linux-kernel, Dave Airlie, linux-fbdev,
Stephen Warren, Olof Johansson
On 06/24/2013 03:27 PM, David Herrmann wrote:
> + sdrm->fb_map = ioremap(sdrm->fb_base, sdrm->fb_size);
This should probably be ioremap_wc. Otherwise it will be *really* slow
if used in legacy mode and it may cause conflicts with the
pgprot_writecombine mode for mmap.
(Watching boot messages go by on fbcon on efifb was like using an old
2400 baud modem before I made the corresponding change to efifb.)
--Andy
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC 3/6] drm: add SimpleDRM driver
2013-06-25 1:05 ` Andy Lutomirski
@ 2013-06-28 9:59 ` David Herrmann
0 siblings, 0 replies; 20+ messages in thread
From: David Herrmann @ 2013-06-28 9:59 UTC (permalink / raw)
To: Andy Lutomirski
Cc: dri-devel, linux-kernel, Dave Airlie, linux-fbdev,
Stephen Warren, Olof Johansson
Hi
On Tue, Jun 25, 2013 at 3:05 AM, Andy Lutomirski <luto@amacapital.net> wrote:
> On 06/24/2013 03:27 PM, David Herrmann wrote:
>> + sdrm->fb_map = ioremap(sdrm->fb_base, sdrm->fb_size);
>
> This should probably be ioremap_wc. Otherwise it will be *really* slow
> if used in legacy mode and it may cause conflicts with the
> pgprot_writecombine mode for mmap.
Whoops, yepp, I fixed that.
Thanks
David
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC 3/6] drm: add SimpleDRM driver
2013-06-24 22:27 ` [RFC 3/6] drm: add SimpleDRM driver David Herrmann
2013-06-25 1:05 ` Andy Lutomirski
@ 2013-06-26 20:58 ` Stephen Warren
2013-06-28 10:01 ` David Herrmann
1 sibling, 1 reply; 20+ messages in thread
From: Stephen Warren @ 2013-06-26 20:58 UTC (permalink / raw)
To: David Herrmann
Cc: dri-devel, linux-kernel, Dave Airlie, linux-fbdev,
Stephen Warren, Olof Johansson
On 06/24/2013 04:27 PM, David Herrmann wrote:
> The SimpleDRM driver binds to simple-framebuffer devices and provides a
> DRM/KMS API. It provides only a single CRTC+encoder+connector combination
> plus one initial mode.
>
> Userspace can create one dumb-buffer and attach it to the CRTC. Only if
> the buffer is destroyed, a new buffer can be created. The buffer is
> directly mapped into user-space, so we have only resources for a single
> buffer. Otherwise, shadow buffers plus damage-request would be needed.
> diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig
> +config DRM_SIMPLEDRM
> + tristate "Simple firmware framebuffer DRM driver"
> + depends on DRM && !FB_SIMPLE
> + 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, x86 VESA
> + BIOS Extensions (VBE), EFI framebuffers
DT objects, yes. I'm not sure it's quite true to say it actually
directly supports VBE or EFI FBs; it's more the code in patch 2/6 that
supports those. I guess this is a bit nit-picky of a distinction though.
> diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
> +static int parse_dt(struct platform_device *pdev,
> + struct simplefb_platform_data *mode)
> + strlcpy(mode->format, format, sizeof(mode->format));
Even here, I believe the DT data sticks around so just copying the
pointer should be safe. It'd be worth validating that for sure though.
I didn't review the DRM stuff here since I'm not at all familiar with DRM.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC 3/6] drm: add SimpleDRM driver
2013-06-26 20:58 ` Stephen Warren
@ 2013-06-28 10:01 ` David Herrmann
0 siblings, 0 replies; 20+ messages in thread
From: David Herrmann @ 2013-06-28 10:01 UTC (permalink / raw)
To: Stephen Warren
Cc: dri-devel, linux-kernel, Dave Airlie, linux-fbdev,
Stephen Warren, Olof Johansson
Hi
On Wed, Jun 26, 2013 at 10:58 PM, Stephen Warren <swarren@wwwdotorg.org> wrote:
> On 06/24/2013 04:27 PM, David Herrmann wrote:
>> The SimpleDRM driver binds to simple-framebuffer devices and provides a
>> DRM/KMS API. It provides only a single CRTC+encoder+connector combination
>> plus one initial mode.
>>
>> Userspace can create one dumb-buffer and attach it to the CRTC. Only if
>> the buffer is destroyed, a new buffer can be created. The buffer is
>> directly mapped into user-space, so we have only resources for a single
>> buffer. Otherwise, shadow buffers plus damage-request would be needed.
>
>> diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig
>
>> +config DRM_SIMPLEDRM
>> + tristate "Simple firmware framebuffer DRM driver"
>> + depends on DRM && !FB_SIMPLE
>> + 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, x86 VESA
>> + BIOS Extensions (VBE), EFI framebuffers
>
> DT objects, yes. I'm not sure it's quite true to say it actually
> directly supports VBE or EFI FBs; it's more the code in patch 2/6 that
> supports those. I guess this is a bit nit-picky of a distinction though.
I initially intended to add support for "platform-framebuffer"
objects, too. This would make vesafb/... obsolete but I dropped that
idea for now. I will fix the Kconfig description.
>> diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
>
>> +static int parse_dt(struct platform_device *pdev,
>> + struct simplefb_platform_data *mode)
>
>> + strlcpy(mode->format, format, sizeof(mode->format));
>
> Even here, I believe the DT data sticks around so just copying the
> pointer should be safe. It'd be worth validating that for sure though.
Yep, indeed, I fixed that.
Thanks!
David
> I didn't review the DRM stuff here since I'm not at all familiar with DRM.
^ permalink raw reply [flat|nested] 20+ messages in thread
* [RFC 4/6] drm: simpledrm: add fbdev fallback support
2013-06-24 22:27 [RFC 0/6] SimpleDRM Driver (was: dvbe driver) David Herrmann
` (2 preceding siblings ...)
2013-06-24 22:27 ` [RFC 3/6] drm: add SimpleDRM driver David Herrmann
@ 2013-06-24 22:27 ` David Herrmann
2013-06-26 20:59 ` Stephen Warren
2013-06-24 22:27 ` [RFC 5/6] drm: add helpers to kick out firmware drivers David Herrmann
` (2 subsequent siblings)
6 siblings, 1 reply; 20+ messages in thread
From: David Herrmann @ 2013-06-24 22:27 UTC (permalink / raw)
To: dri-devel
Cc: linux-kernel, Dave Airlie, linux-fbdev, Stephen Warren,
Olof Johansson, David Herrmann
Create a simple fbdev device during SimpleDRM setup so legacy user-space
and fbcon can use it.
fbdev deletion is quite buggy. A unregister_framebuffer() call followed by
a printk() causes NULL-derefs in hide_cursor() and other places in the VT
layer. Hence, we leak the fbdev device currently to make the VT layer
happy. This needs to be fixed soon! Otherwise, we need a "depends !VT"
line for SimpleDRM.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
drivers/gpu/drm/simpledrm/Kconfig | 11 ++
drivers/gpu/drm/simpledrm/Makefile | 4 +
drivers/gpu/drm/simpledrm/simpledrm.h | 22 ++++
drivers/gpu/drm/simpledrm/simpledrm_fbdev.c | 180 ++++++++++++++++++++++++++++
drivers/gpu/drm/simpledrm/simpledrm_main.c | 2 +
5 files changed, 219 insertions(+)
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig
index 1d4f38e..7936211 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, x86 VESA
BIOS Extensions (VBE), EFI 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 2d474a5..e77bd9b 100644
--- a/drivers/gpu/drm/simpledrm/Makefile
+++ b/drivers/gpu/drm/simpledrm/Makefile
@@ -2,4 +2,8 @@ ccflags-y := -Iinclude/drm
simpledrm-y := simpledrm_drv.o simpledrm_main.o simpledrm_mem.o
+ifdef CONFIG_DRM_SIMPLEDRM_FBDEV
+ simpledrm-y += simpledrm_fbdev.o
+endif
+
obj-$(CONFIG_DRM_SIMPLEDRM) := simpledrm.o
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h
index 279d847..cfd99f9 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm.h
+++ b/drivers/gpu/drm/simpledrm/simpledrm.h
@@ -48,6 +48,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);
@@ -88,4 +91,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..40a2696
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
@@ -0,0 +1,180 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+/*
+ * fbdev compatibility layer
+ * We provide a basic fbdev device for the same framebuffer that is used for
+ * the pseudo CRTC.
+ */
+
+#include <linux/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[256];
+};
+
+static int sdrm_fbdev_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ /*
+ * Set a single color register. The values supplied are
+ * already rounded down to the hardware's capabilities
+ * (according to the entries in the `var' structure). Return != 0 for
+ * invalid regno.
+ */
+
+ if (regno >= info->cmap.len)
+ return 1;
+ if (info->var.bits_per_pixel == 8)
+ return -EINVAL;
+ if (regno >= 16)
+ return 0;
+
+ switch (info->var.bits_per_pixel) {
+ case 16:
+ if (info->var.red.offset == 10) {
+ /* 1:5:5:5 */
+ ((u32*) (info->pseudo_palette))[regno] =
+ ((red & 0xf800) >> 1) |
+ ((green & 0xf800) >> 6) |
+ ((blue & 0xf800) >> 11);
+ } else {
+ /* 0:5:6:5 */
+ ((u32*) (info->pseudo_palette))[regno] =
+ ((red & 0xf800) ) |
+ ((green & 0xfc00) >> 5) |
+ ((blue & 0xf800) >> 11);
+ }
+ break;
+ case 24:
+ case 32:
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+ ((u32*) (info->pseudo_palette))[regno] =
+ (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset);
+ break;
+ }
+
+ 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;
+
+ info = framebuffer_alloc(sizeof(struct sdrm_fbdev), sdrm->ddev->dev);
+ if (!info)
+ goto err_out;
+
+ fb = info->par;
+ info->flags = FBINFO_FLAG_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;
+
+ info->apertures = alloc_apertures(1);
+ if (!info->apertures)
+ goto err_free;
+
+ info->apertures->ranges[0].base = (unsigned long)sdrm->fb_base;
+ info->apertures->ranges[0].size = sdrm->fb_size;
+
+ 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);
+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->info)
+ return;
+
+ dev_info(sdrm->ddev->dev, "fbdev cleanup\n");
+ info = sdrm->fbdev;
+ sdrm->fbdev = NULL;
+
+ unregister_framebuffer(info);
+
+ /*
+ * FIXME: unregister_framebuffer() may silently fail, which is odd
+ * because there is no sane way for us to detect when to release the
+ * framebuffer.
+ * If we fix unbind_console() to not fail during framebuffer
+ * unregistration, we end up with NULL-derefs in the VT layer
+ * afterwards. So lets just leak the FB here for the VT layer's sake.
+ */
+ /* framebuffer_release(info); */
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_main.c b/drivers/gpu/drm/simpledrm/simpledrm_main.c
index dcc3d0a..ffa1abb 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm_main.c
+++ b/drivers/gpu/drm/simpledrm/simpledrm_main.c
@@ -305,6 +305,7 @@ int sdrm_drm_load(struct drm_device *ddev, unsigned long flags)
if (ret)
goto err_cleanup;
+ sdrm_fbdev_init(sdrm);
return 0;
err_cleanup:
@@ -322,6 +323,7 @@ int sdrm_drm_unload(struct drm_device *ddev)
{
struct sdrm_device *sdrm = ddev->dev_private;
+ sdrm_fbdev_cleanup(sdrm);
drm_mode_config_cleanup(ddev);
sdrm_pdev_destroy(sdrm);
kfree(sdrm);
--
1.8.3.1
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [RFC 4/6] drm: simpledrm: add fbdev fallback support
2013-06-24 22:27 ` [RFC 4/6] drm: simpledrm: add fbdev fallback support David Herrmann
@ 2013-06-26 20:59 ` Stephen Warren
2013-06-28 10:14 ` David Herrmann
0 siblings, 1 reply; 20+ messages in thread
From: Stephen Warren @ 2013-06-26 20:59 UTC (permalink / raw)
To: David Herrmann
Cc: dri-devel, linux-kernel, Dave Airlie, linux-fbdev,
Stephen Warren, Olof Johansson
On 06/24/2013 04:27 PM, David Herrmann wrote:
> Create a simple fbdev device during SimpleDRM setup so legacy user-space
> and fbcon can use it.
>
> fbdev deletion is quite buggy. A unregister_framebuffer() call followed by
> a printk() causes NULL-derefs in hide_cursor() and other places in the VT
> layer. Hence, we leak the fbdev device currently to make the VT layer
> happy. This needs to be fixed soon! Otherwise, we need a "depends !VT"
> line for SimpleDRM.
> diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile
> simpledrm-y := simpledrm_drv.o simpledrm_main.o simpledrm_mem.o
>
> +ifdef CONFIG_DRM_SIMPLEDRM_FBDEV
> + simpledrm-y += simpledrm_fbdev.o
> +endif
I think that's:
+ simpledrm-$(CONFIG_DRM_SIMPLEDRM_FBDEV) += simpledrm_fbdev.o
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC 4/6] drm: simpledrm: add fbdev fallback support
2013-06-26 20:59 ` Stephen Warren
@ 2013-06-28 10:14 ` David Herrmann
0 siblings, 0 replies; 20+ messages in thread
From: David Herrmann @ 2013-06-28 10:14 UTC (permalink / raw)
To: Stephen Warren
Cc: dri-devel, linux-kernel, Dave Airlie, linux-fbdev,
Stephen Warren, Olof Johansson
Hi
On Wed, Jun 26, 2013 at 10:59 PM, Stephen Warren <swarren@wwwdotorg.org> wrote:
> On 06/24/2013 04:27 PM, David Herrmann wrote:
>> Create a simple fbdev device during SimpleDRM setup so legacy user-space
>> and fbcon can use it.
>>
>> fbdev deletion is quite buggy. A unregister_framebuffer() call followed by
>> a printk() causes NULL-derefs in hide_cursor() and other places in the VT
>> layer. Hence, we leak the fbdev device currently to make the VT layer
>> happy. This needs to be fixed soon! Otherwise, we need a "depends !VT"
>> line for SimpleDRM.
>
>> diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile
>
>> simpledrm-y := simpledrm_drv.o simpledrm_main.o simpledrm_mem.o
>>
>> +ifdef CONFIG_DRM_SIMPLEDRM_FBDEV
>> + simpledrm-y += simpledrm_fbdev.o
>> +endif
>
> I think that's:
>
> + simpledrm-$(CONFIG_DRM_SIMPLEDRM_FBDEV) += simpledrm_fbdev.o
Ugh, I got errors trying that because SIMPLEDRM_FBDEV is a boolean but
SIMPLEDRM is a tristate. But I guess I tried:
obj-$(CONFIG_DRM_SIMPLEDRM_FBDEV) += simpledrm_fbdev.o
which obviously fails if SIMPLEDRM=m and SIMPLEDRM_FBDEV=y. I will try
to fix that.
Thanks
David
^ permalink raw reply [flat|nested] 20+ messages in thread
* [RFC 5/6] drm: add helpers to kick out firmware drivers
2013-06-24 22:27 [RFC 0/6] SimpleDRM Driver (was: dvbe driver) David Herrmann
` (3 preceding siblings ...)
2013-06-24 22:27 ` [RFC 4/6] drm: simpledrm: add fbdev fallback support David Herrmann
@ 2013-06-24 22:27 ` David Herrmann
2013-06-24 22:27 ` [RFC 6/6] drm: nouveau: kick out firmware drivers during probe David Herrmann
2013-06-26 21:30 ` [RFC 0/6] SimpleDRM Driver (was: dvbe driver) Stephen Warren
6 siblings, 0 replies; 20+ messages in thread
From: David Herrmann @ 2013-06-24 22:27 UTC (permalink / raw)
To: dri-devel
Cc: linux-kernel, Dave Airlie, linux-fbdev, Stephen Warren,
Olof Johansson, David Herrmann
If we load a real hardware DRM driver, we want all firmware drivers to be
unloaded. Historically, this was done via
remove_conflicting_framebuffers(), but for DRM drivers (like SimpleDRM) we
need a new way.
This patch introduces DRIVER_FIRMWARE and DRM apertures to provide a quite
similar way to kick out firmware DRM drivers. Additionally, unlike the
fbdev equivalent, DRM firmware drivers can now query the system whether a
real hardware driver is already loaded and prevent loading themselves.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
drivers/gpu/drm/drm_pci.c | 1 +
drivers/gpu/drm/drm_platform.c | 1 +
drivers/gpu/drm/drm_stub.c | 107 +++++++++++++++++++++++++++++
drivers/gpu/drm/drm_usb.c | 1 +
drivers/gpu/drm/simpledrm/simpledrm.h | 1 +
drivers/gpu/drm/simpledrm/simpledrm_drv.c | 3 +-
drivers/gpu/drm/simpledrm/simpledrm_main.c | 34 +++++++++
include/drm/drmP.h | 26 +++++++
8 files changed, 173 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c
index 14194b6..4dcb2a4 100644
--- a/drivers/gpu/drm/drm_pci.c
+++ b/drivers/gpu/drm/drm_pci.c
@@ -366,6 +366,7 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
}
list_add_tail(&dev->driver_item, &driver->device_list);
+ list_add_tail(&dev->global_item, &drm_devlist);
DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n",
driver->name, driver->major, driver->minor, driver->patchlevel,
diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c
index b8a282e..94923c8 100644
--- a/drivers/gpu/drm/drm_platform.c
+++ b/drivers/gpu/drm/drm_platform.c
@@ -88,6 +88,7 @@ int drm_get_platform_dev(struct platform_device *platdev,
}
list_add_tail(&dev->driver_item, &driver->device_list);
+ list_add_tail(&dev->global_item, &drm_devlist);
mutex_unlock(&drm_global_mutex);
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
index 16f3ec5..a433ab0 100644
--- a/drivers/gpu/drm/drm_stub.c
+++ b/drivers/gpu/drm/drm_stub.c
@@ -46,6 +46,9 @@ EXPORT_SYMBOL(drm_vblank_offdelay);
unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */
EXPORT_SYMBOL(drm_timestamp_precision);
+LIST_HEAD(drm_devlist); /* device list; protected by global lock */
+EXPORT_SYMBOL(drm_devlist);
+
/*
* Default to use monotonic timestamps for wait-for-vblank and page-flip
* complete events.
@@ -484,7 +487,9 @@ void drm_put_dev(struct drm_device *dev)
drm_put_minor(&dev->primary);
+ list_del(&dev->global_item);
list_del(&dev->driver_item);
+ kfree(dev->apertures);
kfree(dev->devname);
kfree(dev);
}
@@ -507,3 +512,105 @@ void drm_unplug_dev(struct drm_device *dev)
mutex_unlock(&drm_global_mutex);
}
EXPORT_SYMBOL(drm_unplug_dev);
+
+void drm_unplug_dev_locked(struct drm_device *dev)
+{
+ /* for a USB device */
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ drm_unplug_minor(dev->control);
+ drm_unplug_minor(dev->primary);
+
+ drm_device_set_unplugged(dev);
+
+ /* TODO: schedule drm_put_dev if open_count == 0 */
+}
+EXPORT_SYMBOL(drm_unplug_dev_locked);
+
+#define VGA_FB_PHYS 0xa0000
+
+static bool apertures_overlap(struct drm_device *dev,
+ struct apertures_struct *ap,
+ bool boot)
+{
+ unsigned int i, j;
+ struct aperture *a, *b;
+
+ if (!dev->apertures)
+ return false;
+
+ for (i = 0; i < dev->apertures->count; ++i) {
+ a = &dev->apertures->ranges[i];
+
+ if (boot && a->base == VGA_FB_PHYS)
+ return true;
+
+ for (j = 0; ap && j < ap->count; ++j) {
+ b = &ap->ranges[j];
+ if (a->base <= b->base && a->base + a->size > b->base)
+ return true;
+ if (b->base <= a->base && b->base + b->size > a->base)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Kick out firmware
+ *
+ * Virtually unplug any firmware graphics devices which overlap the given
+ * region. This must be called with the global-drm-mutex locked.
+ * This calls the kick_out_firmware() callback on any firmware DRM driver and
+ * after that remove_conflicting_framebuffers() to remove legacy fbdev
+ * framebuffers.
+ */
+void drm_kick_out_firmware(struct apertures_struct *ap, bool boot)
+{
+ struct drm_device *dev;
+
+ list_for_each_entry(dev, &drm_devlist, global_item) {
+ if (!drm_core_check_feature(dev, DRIVER_FIRMWARE))
+ continue;
+ if (apertures_overlap(dev, ap, boot)) {
+ DRM_INFO("kicking out firmware %s\n",
+ dev->devname);
+ dev->driver->kick_out_firmware(dev);
+ }
+ }
+
+ remove_conflicting_framebuffers(ap, "DRM", boot);
+}
+EXPORT_SYMBOL(drm_kick_out_firmware);
+
+/**
+ * Verify that no driver uses firmware FBs
+ *
+ * Whenever you register a firmware framebuffer driver, you should store the
+ * apertures in @ap and test whether any other registered driver already
+ * claimed this area. Hence, if this function returns true, you should _not_
+ * register your driver!
+ */
+bool drm_is_firmware_used(struct apertures_struct *ap)
+{
+ struct drm_device *dev;
+ unsigned int i;
+ bool boot = false;
+
+ for (i = 0; ap && i < ap->count; ++i) {
+ if (ap->ranges[i].base == VGA_FB_PHYS) {
+ boot = true;
+ break;
+ }
+ }
+
+ list_for_each_entry(dev, &drm_devlist, global_item) {
+ if (dev->apert_boot && boot)
+ return true;
+ if (apertures_overlap(dev, ap, false))
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL(drm_is_firmware_used);
diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c
index 34a156f..5ad8dba 100644
--- a/drivers/gpu/drm/drm_usb.c
+++ b/drivers/gpu/drm/drm_usb.c
@@ -50,6 +50,7 @@ int drm_get_usb_dev(struct usb_interface *interface,
goto err_g3;
list_add_tail(&dev->driver_item, &driver->device_list);
+ list_add_tail(&dev->global_item, &drm_devlist);
mutex_unlock(&drm_global_mutex);
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h
index cfd99f9..03373fe 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm.h
+++ b/drivers/gpu/drm/simpledrm/simpledrm.h
@@ -55,6 +55,7 @@ struct sdrm_device {
int sdrm_drm_load(struct drm_device *ddev, unsigned long flags);
int sdrm_drm_unload(struct drm_device *ddev);
+void sdrm_drm_kick_out_firmware(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);
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
index 282752c..e5d0ce0 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm_drv.c
+++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
@@ -36,9 +36,10 @@ static const struct file_operations sdrm_drm_fops = {
};
static struct drm_driver sdrm_drm_driver = {
- .driver_features = DRIVER_MODESET | DRIVER_GEM,
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_FIRMWARE,
.load = sdrm_drm_load,
.unload = sdrm_drm_unload,
+ .kick_out_firmware = sdrm_drm_kick_out_firmware,
.fops = &sdrm_drm_fops,
.gem_init_object = sdrm_gem_init_object,
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_main.c b/drivers/gpu/drm/simpledrm/simpledrm_main.c
index ffa1abb..6b7696e 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm_main.c
+++ b/drivers/gpu/drm/simpledrm/simpledrm_main.c
@@ -269,6 +269,21 @@ int sdrm_drm_load(struct drm_device *ddev, unsigned long flags)
if (ret)
goto err_name;
+ ddev->apertures = alloc_apertures(1);
+ if (!ddev->apertures) {
+ ret = -ENOMEM;
+ goto err_pdev;
+ }
+
+ ddev->apertures->ranges[0].base = sdrm->fb_base;
+ ddev->apertures->ranges[0].size = sdrm->fb_size;
+
+ if (drm_is_firmware_used(ddev->apertures)) {
+ dev_info(ddev->dev, "firmware framebuffer is already in use\n");
+ ret = -EBUSY;
+ goto err_apert;
+ }
+
drm_mode_config_init(ddev);
ddev->mode_config.min_width = 0;
ddev->mode_config.min_height = 0;
@@ -310,6 +325,9 @@ int sdrm_drm_load(struct drm_device *ddev, unsigned long flags)
err_cleanup:
drm_mode_config_cleanup(ddev);
+err_apert:
+ kfree(ddev->apertures);
+err_pdev:
sdrm_pdev_destroy(sdrm);
err_name:
kfree(ddev->devname);
@@ -326,7 +344,23 @@ int sdrm_drm_unload(struct drm_device *ddev)
sdrm_fbdev_cleanup(sdrm);
drm_mode_config_cleanup(ddev);
sdrm_pdev_destroy(sdrm);
+ kfree(ddev->apertures);
kfree(sdrm);
return 0;
}
+
+void sdrm_drm_kick_out_firmware(struct drm_device *ddev)
+{
+ struct sdrm_device *sdrm = ddev->dev_private;
+
+ mutex_lock(&ddev->struct_mutex);
+
+ sdrm_fbdev_cleanup(sdrm);
+ drm_unplug_dev_locked(ddev);
+ if (sdrm->fb_obj)
+ sdrm_gem_unmap_object(sdrm->fb_obj);
+ sdrm_pdev_destroy(sdrm);
+
+ mutex_unlock(&ddev->struct_mutex);
+}
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 63d17ee..a19e710 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -47,6 +47,7 @@
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
+#include <linux/fb.h>
#include <linux/file.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
@@ -156,6 +157,7 @@ int drm_err(const char *func, const char *format, ...);
#define DRIVER_GEM 0x1000
#define DRIVER_MODESET 0x2000
#define DRIVER_PRIME 0x4000
+#define DRIVER_FIRMWARE 0x8000
#define DRIVER_BUS_PCI 0x1
#define DRIVER_BUS_PLATFORM 0x2
@@ -954,6 +956,23 @@ struct drm_driver {
struct drm_device *dev,
uint32_t handle);
+ /**
+ * kick_out_firmware - kick out firmware driver
+ * @dev: DRM device
+ *
+ * Iff this driver has DRIVER_FIRMWARE set, this function is called
+ * when a real hw-driver is loaded and claims the framebuffer memory
+ * that is mapped by the firmware driver. This is called with the
+ * global drm mutex held.
+ * Drivers should unmap any memory-mappings, disable the device and
+ * schedule a driver removal.
+ *
+ * Note that a driver setting DRIVER_FIRMWARE is supposed to also set
+ * the "apertures" information on @dev. It is used to match the memory
+ * regions that are used by firmware drivers.
+ */
+ void (*kick_out_firmware) (struct drm_device *dev);
+
/* Driver private ops for this object */
const struct vm_operations_struct *gem_vm_ops;
@@ -1077,6 +1096,7 @@ struct drm_pending_vblank_event {
*/
struct drm_device {
struct list_head driver_item; /**< list of devices per driver */
+ struct list_head global_item; /**< global list of devices */
char *devname; /**< For /proc/interrupts */
int if_version; /**< Highest interface version set */
@@ -1218,6 +1238,8 @@ struct drm_device {
int switch_power_state;
atomic_t unplugged; /* device has been unplugged or gone away */
+ struct apertures_struct *apertures; /**< fbmem apertures */
+ bool apert_boot; /**< true if mapped as boot fb */
};
#define DRM_SWITCH_POWER_ON 0
@@ -1532,6 +1554,10 @@ extern void drm_master_put(struct drm_master **master);
extern void drm_put_dev(struct drm_device *dev);
extern int drm_put_minor(struct drm_minor **minor);
extern void drm_unplug_dev(struct drm_device *dev);
+extern void drm_unplug_dev_locked(struct drm_device *dev);
+extern void drm_kick_out_firmware(struct apertures_struct *ap, bool boot);
+extern bool drm_is_firmware_used(struct apertures_struct *ap);
+extern struct list_head drm_devlist;
extern unsigned int drm_debug;
extern unsigned int drm_vblank_offdelay;
--
1.8.3.1
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [RFC 6/6] drm: nouveau: kick out firmware drivers during probe
2013-06-24 22:27 [RFC 0/6] SimpleDRM Driver (was: dvbe driver) David Herrmann
` (4 preceding siblings ...)
2013-06-24 22:27 ` [RFC 5/6] drm: add helpers to kick out firmware drivers David Herrmann
@ 2013-06-24 22:27 ` David Herrmann
2013-06-26 21:30 ` [RFC 0/6] SimpleDRM Driver (was: dvbe driver) Stephen Warren
6 siblings, 0 replies; 20+ messages in thread
From: David Herrmann @ 2013-06-24 22:27 UTC (permalink / raw)
To: dri-devel
Cc: linux-kernel, Dave Airlie, linux-fbdev, Stephen Warren,
Olof Johansson, David Herrmann
Use the new DRM infrastructure to kick out firmware drivers before probing
the real driver.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
drivers/gpu/drm/nouveau/nouveau_drm.c | 29 ++++++++++++++++++++++-------
1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 383f4e6..418867f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -233,13 +233,11 @@ nouveau_accel_init(struct nouveau_drm *drm)
nouveau_bo_move_init(drm);
}
-static int nouveau_drm_probe(struct pci_dev *pdev,
- const struct pci_device_id *pent)
+static int nouveau_kick_out_firmware(struct drm_device *dev)
{
- struct nouveau_device *device;
struct apertures_struct *aper;
bool boot = false;
- int ret;
+ struct pci_dev *pdev = dev->pdev;
/* remove conflicting drivers (vesafb, efifb etc) */
aper = alloc_apertures(3);
@@ -265,8 +263,18 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
#ifdef CONFIG_X86
boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
#endif
- remove_conflicting_framebuffers(aper, "nouveaufb", boot);
- kfree(aper);
+
+ drm_kick_out_firmware(aper, boot);
+ dev->apertures = aper;
+ dev->apert_boot = boot;
+ return 0;
+}
+
+static int nouveau_drm_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pent)
+{
+ struct nouveau_device *device;
+ int ret;
ret = nouveau_device_create(pdev, nouveau_name(pdev), pci_name(pdev),
nouveau_config, nouveau_debug, &device);
@@ -294,9 +302,13 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
struct nouveau_drm *drm;
int ret;
- ret = nouveau_cli_create(pdev, "DRM", sizeof(*drm), (void**)&drm);
+ ret = nouveau_kick_out_firmware(dev);
if (ret)
return ret;
+
+ ret = nouveau_cli_create(pdev, "DRM", sizeof(*drm), (void**)&drm);
+ if (ret)
+ goto fail_apert;
lockdep_set_class(&drm->client.mutex, &drm_client_lock_class_key);
dev->dev_private = drm;
@@ -392,6 +404,9 @@ fail_ttm:
nouveau_vga_fini(drm);
fail_device:
nouveau_cli_destroy(&drm->client);
+fail_apert:
+ kfree(dev->apertures);
+ dev->apertures = NULL;
return ret;
}
--
1.8.3.1
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [RFC 0/6] SimpleDRM Driver (was: dvbe driver)
2013-06-24 22:27 [RFC 0/6] SimpleDRM Driver (was: dvbe driver) David Herrmann
` (5 preceding siblings ...)
2013-06-24 22:27 ` [RFC 6/6] drm: nouveau: kick out firmware drivers during probe David Herrmann
@ 2013-06-26 21:30 ` Stephen Warren
2013-06-28 10:43 ` David Herrmann
6 siblings, 1 reply; 20+ messages in thread
From: Stephen Warren @ 2013-06-26 21:30 UTC (permalink / raw)
To: David Herrmann
Cc: dri-devel, linux-kernel, Dave Airlie, linux-fbdev,
Stephen Warren, Olof Johansson
On 06/24/2013 04:27 PM, David Herrmann wrote:
> Hi
>
> This is my second revision of the dvbe driver. I renamed it to SimpleDRM to
> show the resemblence with the recently introduced simplefb.c fbdev driver. The
> driver is supposed to be the most basic DRM driver similar to efifb.c, vesafb.c,
> offb.c, simplefb.c, ...
> It provides a single virtual CRTC+encoder+connector and allows user-space to
> create one dumb-buffer at a time and attach it.
>
> The setup changed slightly. It no longer uses shadow buffers but instead maps
> the framebuffer directly into userspace. Furthermore, a new infrastructure is
> used to unload firmware drivers during real hardware drivers probe cycles. Only
> nouveau was changed to use it, yet.
>
> I still have an odd problem when unloading DRM drivers (not just SimpleDRM) with
> an fbdev fallback. If I call printk() directly after unregister_framebufer(), I
> get a NULL-deref somewhere in the VT layer (most times hide_cursor()). I haven't
> figured out exactly where that happens, but I am also very reluctant to spend
> more time debugging the VT layer.
I tested this on a Tegra ARM system, and it basically worked.
I have one question: With the simplefb driver, and console=tty1 on the
kernel command-line, I see both the penguins logo and Linux's boot
messages on the LCD panel that's hooked up through simplefb. However,
with simpledrm, I only see the penguins logo, but no boot messages. Is
that expected? How would I solve that if so?
Note: I needed to apply the following patch to get it to compile:
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
index 40a2696..39885c8 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
+++ b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
@@ -159,7 +159,7 @@ void sdrm_fbdev_cleanup(struct sdrm_device *sdrm)
{
struct fb_info *info;
- if (!sdrm->info)
+ if (!sdrm->fbdev)
return;
dev_info(sdrm->ddev->dev, "fbdev cleanup\n");
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [RFC 0/6] SimpleDRM Driver (was: dvbe driver)
2013-06-26 21:30 ` [RFC 0/6] SimpleDRM Driver (was: dvbe driver) Stephen Warren
@ 2013-06-28 10:43 ` David Herrmann
0 siblings, 0 replies; 20+ messages in thread
From: David Herrmann @ 2013-06-28 10:43 UTC (permalink / raw)
To: Stephen Warren
Cc: dri-devel, linux-kernel, Dave Airlie, linux-fbdev,
Stephen Warren, Olof Johansson
Hi
On Wed, Jun 26, 2013 at 11:30 PM, Stephen Warren <swarren@wwwdotorg.org> wrote:
> On 06/24/2013 04:27 PM, David Herrmann wrote:
>> Hi
>>
>> This is my second revision of the dvbe driver. I renamed it to SimpleDRM to
>> show the resemblence with the recently introduced simplefb.c fbdev driver. The
>> driver is supposed to be the most basic DRM driver similar to efifb.c, vesafb.c,
>> offb.c, simplefb.c, ...
>> It provides a single virtual CRTC+encoder+connector and allows user-space to
>> create one dumb-buffer at a time and attach it.
>>
>> The setup changed slightly. It no longer uses shadow buffers but instead maps
>> the framebuffer directly into userspace. Furthermore, a new infrastructure is
>> used to unload firmware drivers during real hardware drivers probe cycles. Only
>> nouveau was changed to use it, yet.
>>
>> I still have an odd problem when unloading DRM drivers (not just SimpleDRM) with
>> an fbdev fallback. If I call printk() directly after unregister_framebufer(), I
>> get a NULL-deref somewhere in the VT layer (most times hide_cursor()). I haven't
>> figured out exactly where that happens, but I am also very reluctant to spend
>> more time debugging the VT layer.
>
> I tested this on a Tegra ARM system, and it basically worked.
Thanks a lot for the feedback!
> I have one question: With the simplefb driver, and console=tty1 on the
> kernel command-line, I see both the penguins logo and Linux's boot
> messages on the LCD panel that's hooked up through simplefb. However,
> with simpledrm, I only see the penguins logo, but no boot messages. Is
> that expected? How would I solve that if so?
No idea what is going wrong there. Somehow the simpledrm-fbdev device
is not picked up as primary device. I only got a black-cursor on
black-background (visible if painting something else on the fb0
device). And I get NULL-derefs in cursor_hide() during unregistration.
I digged through fbcon.c to find out what's going wrong but without
any success. I will see what I can do.
However, X-server or other apps work perfectly fine with it.
> Note: I needed to apply the following patch to get it to compile:
>
> diff --git a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
> b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
> index 40a2696..39885c8 100644
> --- a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
> +++ b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
> @@ -159,7 +159,7 @@ void sdrm_fbdev_cleanup(struct sdrm_device *sdrm)
> {
> struct fb_info *info;
>
> - if (!sdrm->info)
> + if (!sdrm->fbdev)
> return;
Ugh, embarrassing, sorry. I fixed it up. It was a late fix to a avoid
fbcon from panicking.
Thanks!
David
>
> dev_info(sdrm->ddev->dev, "fbdev cleanup\n");
>
>
>
^ permalink raw reply [flat|nested] 20+ messages in thread