Hey
On request of Noralf, I picked up the patches and prepared v5. Works fine with Xorg, if configured according to: https://lists.freedesktop.org/archives/dri-devel/2014-January/052777.html If anyone knows how to make Xorg pick it up dynamically without such a static configuration, please let me know.
Thanks David
David Herrmann (7): x86/sysfb: add support for 64bit EFI lfb_base x86/sysfb: fix lfb_size calculation of/platform: expose of_platform_device_destroy() video: add generic framebuffer eviction drm: switch to sysfb_evict_conflicts() drm: add SimpleDRM driver drm/simpledrm: add fbdev fallback support
MAINTAINERS | 6 + arch/x86/kernel/sysfb_simplefb.c | 39 ++- drivers/gpu/drm/Kconfig | 3 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 24 +- drivers/gpu/drm/bochs/bochs_drv.c | 19 +- drivers/gpu/drm/i915/i915_drv.c | 73 +--- drivers/gpu/drm/mgag200/mgag200_drv.c | 27 +- drivers/gpu/drm/mgag200/mgag200_main.c | 9 - drivers/gpu/drm/nouveau/nouveau_drm.c | 33 +- drivers/gpu/drm/radeon/radeon_drv.c | 24 +- drivers/gpu/drm/simpledrm/Kconfig | 19 ++ drivers/gpu/drm/simpledrm/Makefile | 9 + drivers/gpu/drm/simpledrm/simpledrm.h | 91 +++++ drivers/gpu/drm/simpledrm/simpledrm_damage.c | 194 +++++++++++ drivers/gpu/drm/simpledrm/simpledrm_drv.c | 477 +++++++++++++++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_fbdev.c | 143 ++++++++ drivers/gpu/drm/simpledrm/simpledrm_gem.c | 109 ++++++ drivers/gpu/drm/simpledrm/simpledrm_kms.c | 270 +++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_of.c | 265 +++++++++++++++ drivers/gpu/drm/sun4i/sun4i_drv.c | 24 +- drivers/gpu/drm/vc4/vc4_drv.c | 25 +- drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 24 +- drivers/of/platform.c | 35 +- drivers/video/Kconfig | 4 + drivers/video/Makefile | 1 + drivers/video/sysfb.c | 327 ++++++++++++++++++ include/linux/of_platform.h | 1 + include/linux/sysfb.h | 34 ++ 29 files changed, 2054 insertions(+), 256 deletions(-) create mode 100644 drivers/gpu/drm/simpledrm/Kconfig create mode 100644 drivers/gpu/drm/simpledrm/Makefile create mode 100644 drivers/gpu/drm/simpledrm/simpledrm.h create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_damage.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_drv.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_gem.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_kms.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_of.c create mode 100644 drivers/video/sysfb.c create mode 100644 include/linux/sysfb.h
The screen_info object was extended to support 64bit lfb_base addresses in:
commit ae2ee627dc87a70910de91b791b3cd0e9c6facdd Author: Matt Fleming matt.fleming@intel.com Date: Tue Aug 25 16:32:55 2015 +0100
efifb: Add support for 64-bit frame buffer addresses
However, the x86 simple-framebuffer setup code never made use of it. Fix it to properly assemble and verify the lfb_base before advertising simple-framebuffer devices.
In particular, this means if VIDEO_CAPABILITY_64BIT_BASE is set, the screen_info->ext_lfb_base field will contain the upper 32bit of the actual lfb_base. Make sure the address is not 0 (i.e., unset), as well as does not overflow the physical address type.
Signed-off-by: David Herrmann dh.herrmann@gmail.com --- arch/x86/kernel/sysfb_simplefb.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/arch/x86/kernel/sysfb_simplefb.c b/arch/x86/kernel/sysfb_simplefb.c index 764a29f..35b8641 100644 --- a/arch/x86/kernel/sysfb_simplefb.c +++ b/arch/x86/kernel/sysfb_simplefb.c @@ -67,6 +67,20 @@ __init int create_simplefb(const struct screen_info *si, struct platform_device *pd; struct resource res; unsigned long len; + u64 base; + + /* + * If the 64BIT_BASE capability is set, ext_lfb_base will contain the + * upper half of the base address. Assemble the address, then make sure + * it is valid and we can actually access it. + */ + base = si->lfb_base; + if (si->capabilities & VIDEO_CAPABILITY_64BIT_BASE) + base |= (u64)si->ext_lfb_base << 32; + if (!base || (u64)(resource_size_t)base != base) { + printk(KERN_DEBUG "sysfb: inaccessible VRAM base\n"); + return -EINVAL; + }
/* don't use lfb_size as it may contain the whole VMEM instead of only * the part that is occupied by the framebuffer */ @@ -81,8 +95,8 @@ __init int create_simplefb(const struct screen_info *si, memset(&res, 0, sizeof(res)); res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; res.name = simplefb_resname; - res.start = si->lfb_base; - res.end = si->lfb_base + len - 1; + res.start = base; + res.end = res.start + len - 1; if (res.end <= res.start) return -EINVAL;
On Fri, Sep 2, 2016 at 10:22 AM, David Herrmann dh.herrmann@gmail.com wrote:
The screen_info object was extended to support 64bit lfb_base addresses in:
commit ae2ee627dc87a70910de91b791b3cd0e9c6facdd Author: Matt Fleming <matt.fleming@intel.com> Date: Tue Aug 25 16:32:55 2015 +0100 efifb: Add support for 64-bit frame buffer addresses
However, the x86 simple-framebuffer setup code never made use of it. Fix it to properly assemble and verify the lfb_base before advertising simple-framebuffer devices.
In particular, this means if VIDEO_CAPABILITY_64BIT_BASE is set, the screen_info->ext_lfb_base field will contain the upper 32bit of the actual lfb_base. Make sure the address is not 0 (i.e., unset), as well as does not overflow the physical address type.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
Reviewed-by: Tom Gundersen teg@jklm.no
arch/x86/kernel/sysfb_simplefb.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/arch/x86/kernel/sysfb_simplefb.c b/arch/x86/kernel/sysfb_simplefb.c index 764a29f..35b8641 100644 --- a/arch/x86/kernel/sysfb_simplefb.c +++ b/arch/x86/kernel/sysfb_simplefb.c @@ -67,6 +67,20 @@ __init int create_simplefb(const struct screen_info *si, struct platform_device *pd; struct resource res; unsigned long len;
u64 base;
/*
* If the 64BIT_BASE capability is set, ext_lfb_base will contain the
* upper half of the base address. Assemble the address, then make sure
* it is valid and we can actually access it.
*/
base = si->lfb_base;
if (si->capabilities & VIDEO_CAPABILITY_64BIT_BASE)
base |= (u64)si->ext_lfb_base << 32;
if (!base || (u64)(resource_size_t)base != base) {
printk(KERN_DEBUG "sysfb: inaccessible VRAM base\n");
return -EINVAL;
} /* don't use lfb_size as it may contain the whole VMEM instead of only * the part that is occupied by the framebuffer */
@@ -81,8 +95,8 @@ __init int create_simplefb(const struct screen_info *si, memset(&res, 0, sizeof(res)); res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; res.name = simplefb_resname;
res.start = si->lfb_base;
res.end = si->lfb_base + len - 1;
res.start = base;
res.end = res.start + len - 1; if (res.end <= res.start) return -EINVAL;
-- 2.9.3
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
The screen_info.lfb_size field is shifted by 16 bits *only* in case of VBE. This has historical reasons since VBE advertised it similarly. However, in case of EFI framebuffers, the size is no longer shifted. Fix the x86 simple-framebuffer setup code to use the correct size in the non-VBE case.
While at it, avoid variable abbreviations and rename 'len' to 'length', and use the correct types matching the screen_info definition.
Signed-off-by: David Herrmann dh.herrmann@gmail.com --- arch/x86/kernel/sysfb_simplefb.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/arch/x86/kernel/sysfb_simplefb.c b/arch/x86/kernel/sysfb_simplefb.c index 35b8641..85195d4 100644 --- a/arch/x86/kernel/sysfb_simplefb.c +++ b/arch/x86/kernel/sysfb_simplefb.c @@ -66,8 +66,8 @@ __init int create_simplefb(const struct screen_info *si, { struct platform_device *pd; struct resource res; - unsigned long len; - u64 base; + u64 base, size; + u32 length;
/* * If the 64BIT_BASE capability is set, ext_lfb_base will contain the @@ -82,11 +82,20 @@ __init int create_simplefb(const struct screen_info *si, return -EINVAL; }
- /* don't use lfb_size as it may contain the whole VMEM instead of only - * the part that is occupied by the framebuffer */ - len = mode->height * mode->stride; - len = PAGE_ALIGN(len); - if (len > (u64)si->lfb_size << 16) { + /* + * Don't use lfb_size as IORESOURCE size, since it may contain the + * entire VMEM, and thus require huge mappings. Use just the part we + * need, that is, the part where the framebuffer is located. But verify + * that it does not exceed the advertised VMEM. + * Note that in case of VBE, the lfb_size is shifted by 16 bits for + * historical reasons. + */ + size = si->lfb_size; + if (si->orig_video_isVGA == VIDEO_TYPE_VLFB) + size <<= 16; + length = mode->height * mode->stride; + length = PAGE_ALIGN(length); + if (length > size) { printk(KERN_WARNING "sysfb: VRAM smaller than advertised\n"); return -EINVAL; } @@ -96,7 +105,7 @@ __init int create_simplefb(const struct screen_info *si, res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; res.name = simplefb_resname; res.start = base; - res.end = res.start + len - 1; + res.end = res.start + length - 1; if (res.end <= res.start) return -EINVAL;
On Fri, Sep 2, 2016 at 10:22 AM, David Herrmann dh.herrmann@gmail.com wrote:
The screen_info.lfb_size field is shifted by 16 bits *only* in case of VBE. This has historical reasons since VBE advertised it similarly. However, in case of EFI framebuffers, the size is no longer shifted. Fix the x86 simple-framebuffer setup code to use the correct size in the non-VBE case.
While at it, avoid variable abbreviations and rename 'len' to 'length', and use the correct types matching the screen_info definition.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
Reviewed-by: Tom Gundersen teg@jklm.no
arch/x86/kernel/sysfb_simplefb.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/arch/x86/kernel/sysfb_simplefb.c b/arch/x86/kernel/sysfb_simplefb.c index 35b8641..85195d4 100644 --- a/arch/x86/kernel/sysfb_simplefb.c +++ b/arch/x86/kernel/sysfb_simplefb.c @@ -66,8 +66,8 @@ __init int create_simplefb(const struct screen_info *si, { struct platform_device *pd; struct resource res;
unsigned long len;
u64 base;
u64 base, size;
u32 length; /* * If the 64BIT_BASE capability is set, ext_lfb_base will contain the
@@ -82,11 +82,20 @@ __init int create_simplefb(const struct screen_info *si, return -EINVAL; }
/* don't use lfb_size as it may contain the whole VMEM instead of only
* the part that is occupied by the framebuffer */
len = mode->height * mode->stride;
len = PAGE_ALIGN(len);
if (len > (u64)si->lfb_size << 16) {
/*
* Don't use lfb_size as IORESOURCE size, since it may contain the
* entire VMEM, and thus require huge mappings. Use just the part we
* need, that is, the part where the framebuffer is located. But verify
* that it does not exceed the advertised VMEM.
* Note that in case of VBE, the lfb_size is shifted by 16 bits for
* historical reasons.
*/
size = si->lfb_size;
if (si->orig_video_isVGA == VIDEO_TYPE_VLFB)
size <<= 16;
length = mode->height * mode->stride;
length = PAGE_ALIGN(length);
if (length > size) { printk(KERN_WARNING "sysfb: VRAM smaller than advertised\n"); return -EINVAL; }
@@ -96,7 +105,7 @@ __init int create_simplefb(const struct screen_info *si, res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; res.name = simplefb_resname; res.start = base;
res.end = res.start + len - 1;
res.end = res.start + length - 1; if (res.end <= res.start) return -EINVAL;
-- 2.9.3
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
We already expose of_platform_device_create(), but give the caller no chance to revert its effect. Make sure we also provide the counterpart of_platform_device_destroy().
This requires a small refactoring, since so far the internal destructor is used as iterator to for_each_device(), but we don't want to expose it with the "void *data" parameter. So provide of_platform_device_depopulate() as new iterator, which calls into of_platform_device_destroy().
While at it, drop the unused 'children_left' argument by of_platform_notify(). It is a left-over that somehow was never removed.
Signed-off-by: David Herrmann dh.herrmann@gmail.com --- drivers/of/platform.c | 35 ++++++++++++++++++++++++++--------- include/linux/of_platform.h | 1 + 2 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c index f39ccd5..f9bb563 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -524,15 +524,18 @@ static int __init of_platform_default_populate_init(void) arch_initcall_sync(of_platform_default_populate_init); #endif
-static int of_platform_device_destroy(struct device *dev, void *data) +/** + * of_platform_device_destroy - unregister an of_device + * @dev: device to unregister + * + * This is the inverse operation of of_platform_device_create(). It unregisters + * the passed device, if registered. + */ +void of_platform_device_destroy(struct device *dev) { /* Do not touch devices not populated from the device tree */ if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) - return 0; - - /* Recurse for any nodes that were treated as busses */ - if (of_node_check_flag(dev->of_node, OF_POPULATED_BUS)) - device_for_each_child(dev, NULL, of_platform_device_destroy); + return;
if (dev->bus == &platform_bus_type) platform_device_unregister(to_platform_device(dev)); @@ -544,6 +547,20 @@ static int of_platform_device_destroy(struct device *dev, void *data) of_dma_deconfigure(dev); of_node_clear_flag(dev->of_node, OF_POPULATED); of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); +} +EXPORT_SYMBOL(of_platform_device_destroy); + +static int of_platform_device_depopulate(struct device *dev, void *data) +{ + /* Do not touch devices not populated from the device tree */ + if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) + return 0; + + /* Recurse for any nodes that were treated as busses */ + if (of_node_check_flag(dev->of_node, OF_POPULATED_BUS)) + device_for_each_child(dev, NULL, of_platform_device_depopulate); + + of_platform_device_destroy(dev); return 0; }
@@ -562,7 +579,8 @@ static int of_platform_device_destroy(struct device *dev, void *data) void of_platform_depopulate(struct device *parent) { if (parent->of_node && of_node_check_flag(parent->of_node, OF_POPULATED_BUS)) { - device_for_each_child(parent, NULL, of_platform_device_destroy); + device_for_each_child(parent, NULL, + of_platform_device_depopulate); of_node_clear_flag(parent->of_node, OF_POPULATED_BUS); } } @@ -574,7 +592,6 @@ static int of_platform_notify(struct notifier_block *nb, { struct of_reconfig_data *rd = arg; struct platform_device *pdev_parent, *pdev; - bool children_left;
switch (of_reconfig_get_state_change(action, rd)) { case OF_RECONFIG_CHANGE_ADD: @@ -612,7 +629,7 @@ static int of_platform_notify(struct notifier_block *nb, return NOTIFY_OK; /* no? not meant for us */
/* unregister takes one ref away */ - of_platform_device_destroy(&pdev->dev, &children_left); + of_platform_device_depopulate(&pdev->dev, NULL);
/* and put the reference of the find */ of_dev_put(pdev); diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index 956a100..a9017d3 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -63,6 +63,7 @@ extern struct platform_device *of_find_device_by_node(struct device_node *np); extern struct platform_device *of_platform_device_create(struct device_node *np, const char *bus_id, struct device *parent); +extern void of_platform_device_destroy(struct device *dev);
extern int of_platform_bus_probe(struct device_node *root, const struct of_device_id *matches,
On Fri, Sep 2, 2016 at 10:22 AM, David Herrmann dh.herrmann@gmail.com wrote:
We already expose of_platform_device_create(), but give the caller no chance to revert its effect. Make sure we also provide the counterpart of_platform_device_destroy().
This requires a small refactoring, since so far the internal destructor is used as iterator to for_each_device(), but we don't want to expose it with the "void *data" parameter. So provide of_platform_device_depopulate() as new iterator, which calls into of_platform_device_destroy().
While at it, drop the unused 'children_left' argument by of_platform_notify(). It is a left-over that somehow was never removed.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
Reviewed-by: Tom Gundersen teg@jklm.no
drivers/of/platform.c | 35 ++++++++++++++++++++++++++--------- include/linux/of_platform.h | 1 + 2 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c index f39ccd5..f9bb563 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -524,15 +524,18 @@ static int __init of_platform_default_populate_init(void) arch_initcall_sync(of_platform_default_populate_init); #endif
-static int of_platform_device_destroy(struct device *dev, void *data) +/**
- of_platform_device_destroy - unregister an of_device
- @dev: device to unregister
- This is the inverse operation of of_platform_device_create(). It unregisters
- the passed device, if registered.
- */
+void of_platform_device_destroy(struct device *dev) { /* Do not touch devices not populated from the device tree */ if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED))
return 0;
/* Recurse for any nodes that were treated as busses */
if (of_node_check_flag(dev->of_node, OF_POPULATED_BUS))
device_for_each_child(dev, NULL, of_platform_device_destroy);
return; if (dev->bus == &platform_bus_type) platform_device_unregister(to_platform_device(dev));
@@ -544,6 +547,20 @@ static int of_platform_device_destroy(struct device *dev, void *data) of_dma_deconfigure(dev); of_node_clear_flag(dev->of_node, OF_POPULATED); of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); +} +EXPORT_SYMBOL(of_platform_device_destroy);
+static int of_platform_device_depopulate(struct device *dev, void *data) +{
/* Do not touch devices not populated from the device tree */
if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED))
return 0;
/* Recurse for any nodes that were treated as busses */
if (of_node_check_flag(dev->of_node, OF_POPULATED_BUS))
device_for_each_child(dev, NULL, of_platform_device_depopulate);
of_platform_device_destroy(dev); return 0;
}
@@ -562,7 +579,8 @@ static int of_platform_device_destroy(struct device *dev, void *data) void of_platform_depopulate(struct device *parent) { if (parent->of_node && of_node_check_flag(parent->of_node, OF_POPULATED_BUS)) {
device_for_each_child(parent, NULL, of_platform_device_destroy);
device_for_each_child(parent, NULL,
of_platform_device_depopulate); of_node_clear_flag(parent->of_node, OF_POPULATED_BUS); }
} @@ -574,7 +592,6 @@ static int of_platform_notify(struct notifier_block *nb, { struct of_reconfig_data *rd = arg; struct platform_device *pdev_parent, *pdev;
bool children_left; switch (of_reconfig_get_state_change(action, rd)) { case OF_RECONFIG_CHANGE_ADD:
@@ -612,7 +629,7 @@ static int of_platform_notify(struct notifier_block *nb, return NOTIFY_OK; /* no? not meant for us */
/* unregister takes one ref away */
of_platform_device_destroy(&pdev->dev, &children_left);
of_platform_device_depopulate(&pdev->dev, NULL); /* and put the reference of the find */ of_dev_put(pdev);
diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index 956a100..a9017d3 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -63,6 +63,7 @@ extern struct platform_device *of_find_device_by_node(struct device_node *np); extern struct platform_device *of_platform_device_create(struct device_node *np, const char *bus_id, struct device *parent); +extern void of_platform_device_destroy(struct device *dev);
extern int of_platform_bus_probe(struct device_node *root, const struct of_device_id *matches, -- 2.9.3
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
There are several situations where we want hardware handover from an early boot GFX driver (e.g., vgacon, vesafb, efifb, simplefb) to a full fletched GFX driver (e.g., most DRM drivers). So far, we relied on remove_conflicting_framebuffers() to do this for us, however, this had a bunch of downsides:
o It only removes conflicting fbdev drivers. It does not drop vgacon, early boot console drivers, conflicting DRM drivers, etc.
o It only unloads the fbdev driver, it does not modify the underlying device or resources. In case of "screen_info" drivers (e.g., efifb) this is fine, since no resources are pinned. However, if the driver binds to a platform-device like "simple-framebuffer", we must make sure to unregister that device as well. Otherwise, pinned resources like IORESOURCE_MEM stay around, triggering WARN_ONs if the following driver requests those resources.
o It is only available if CONFIG_FB is selected.
This commit adds a new infrastructure that manages system-framebuffers (short: sysfb). The initial commit provides conflict-resolution for system-framebuffers. At its core it provides sysfb_evict_conflicts(), which implements conflict detection and removal for all known types of GFX driver hand-overs. So far, this includes platform-device removal, fbdev-firmware-device removal, vgacon removal and VBE detection. To further simplify the callers, it also provides helpers to figure out what hand-over to do, based on the device the new drivers binds to:
o PCI drivers can use sysfb_evict_conflicts_pci(), which will figure out the apertures automatically, and does VGA/VBE detection.
o Generic firmware drivers that might be shadowed at any address in memory can use sysfb_evict_conflicts_firmware(), basically removing *all* firmware framebuffers in effect.
This only adds the generic sysfb helpers. No users are converted, yet.
Signed-off-by: David Herrmann dh.herrmann@gmail.com --- drivers/video/Kconfig | 4 + drivers/video/Makefile | 1 + drivers/video/sysfb.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysfb.h | 34 +++++ 4 files changed, 366 insertions(+) create mode 100644 drivers/video/sysfb.c create mode 100644 include/linux/sysfb.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3c20af9..56a8294 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -36,6 +36,10 @@ config VIDEOMODE_HELPERS config HDMI bool
+config SYSFB + bool + select DUMMY_CONSOLE if VT + if VT source "drivers/video/console/Kconfig" endif diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9ad3c17..df7bd75 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_VGASTATE) += vgastate.o obj-$(CONFIG_HDMI) += hdmi.o +obj-$(CONFIG_SYSFB) += sysfb.o
obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ diff --git a/drivers/video/sysfb.c b/drivers/video/sysfb.c new file mode 100644 index 0000000..00585c9 --- /dev/null +++ b/drivers/video/sysfb.c @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2013-2016 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) "sysfb: " fmt +#include <linux/console.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/fb.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sysfb.h> +#include <linux/vt.h> + +static bool sysfb_evict_match_resource(struct sysfb_evict_ctx *ctx, + struct resource *mem) +{ + struct aperture *g; + unsigned int i; + + for (i = 0; i < ctx->ap->count; ++i) { + g = &ctx->ap->ranges[i]; + + if (mem->start == g->base) + return true; + if (mem->start >= g->base && mem->end < g->base + g->size) + return true; + if ((ctx->flags & SYSFB_EVICT_VBE) && mem->start == 0xA0000) + return true; + } + + return false; +} + +static int sysfb_evict_platform_device(struct device *dev, void *userdata) +{ + struct sysfb_evict_ctx *ctx = userdata; + struct platform_device *pdev = to_platform_device(dev); + struct resource *mem; + + if (!pdev->name) + return 0; + + if (!strcmp(pdev->name, "simple-framebuffer")) { + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) + return 0; + if (!sysfb_evict_match_resource(ctx, mem)) + return 0; + +#ifdef CONFIG_OF_ADDRESS + if (dev->of_node) + of_platform_device_destroy(dev); + else +#endif + if (dev_get_platdata(&pdev->dev)) + platform_device_del(pdev); + } + + return 0; +} + +static int sysfb_evict_platform(struct sysfb_evict_ctx *ctx) +{ + /* + * Early-boot architecture setup and boot-loarders sometimes create + * preliminary platform devices with a generic framebuffer setup. This + * allows graphics access during boot-up, without a real graphics + * driver loaded. However, once a real graphics driver takes over, we + * have to destroy those platform devices. In the legacy fbdev case, we + * just used to unload the fbdev driver. However, to make sure any kind + * of driver is unloaded, the platform-eviction code here simply + * removes any conflicting platform device directly. This causes any + * bound driver to be detached, and then removes the device entirely so + * it cannot be bound to later on. + * + * Please note that any such platform device must be registered by + * early architecture setup code. If they are registered after regular + * GFX drivers, this will fail horribly. + */ + + static DEFINE_MUTEX(lock); + int ret; + + /* + * In case of static platform-devices, we must iterate the bus and + * remove them manually. We know that we're the only code that might + * remove them, so a simple static lock serializes all calls here. + */ + mutex_lock(&lock); + ret = bus_for_each_dev(&platform_bus_type, NULL, ctx, + sysfb_evict_platform_device); + mutex_unlock(&lock); + return ret; +} + +static int sysfb_evict_fbdev(struct sysfb_evict_ctx *ctx) +{ + /* + * Usually, evicting platform devices should be enough to also trigger + * fbdev unloading. However, some legacy devices (e.g., uvesafb) have + * no platform devices that can be evicted, so we still fall back to + * the legacy fbdev removal code. Note that this only removes fbdev + * devices marked as FBINFO_MISC_FIRMWARE. Anything else is left + * untouched. + * + * As usual, this only works if the fbdev device is probed early, + * before any real GFX driver wants to take over. + */ + + int ret = 0; + +#ifdef CONFIG_FB + ret = remove_conflicting_framebuffers(ctx->ap, "sysfb", + ctx->flags & SYSFB_EVICT_VBE); +#endif + + return ret; +} + +static int sysfb_evict_vgacon(struct sysfb_evict_ctx *ctx) +{ + /* + * The VGACON console driver pokes at VGA registers randomly. If a GFX + * driver cannot keep the VGA support alive, it better makes sure to + * unload VGACON before probing. + * + * Unloading VGACON requires us to first force dummycon to take over + * from vgacon (but only if vgacon is really in use), followed by a + * deregistration of vgacon. Note that this prevents vgacon from being + * used again after the GFX driver is unloaded. But that is usually + * fine, since VGA state is rarely restored on driver-unload, anyway. + * + * Note that we rely on VGACON to be probed in early boot (actually + * done by ARCH setup code). If it is probed after GFX drivers, this + * will fail horribly. You better make sure VGACON is probed early and + * GFX drivers are probed as normal modules. + */ + + int ret = 0; + +#ifdef CONFIG_VGA_CONSOLE + console_lock(); + if (con_is_bound(&vga_con)) + ret = do_take_over_console(&dummy_con, 0, + MAX_NR_CONSOLES - 1, 1); + if (ret == 0) { + ret = do_unregister_con_driver(&vga_con); + if (ret == -ENODEV) /* ignore "already unregistered" */ + ret = 0; + } + console_unlock(); +#endif + + return ret; +} + +/** + * sysfb_evict_conflicts - remove any conflicting system-framebuffers + * @ctx: eviction context + * + * This function evicts any conflicting system-framebuffers and their bound + * drivers, according to the data given in @ctx. + * + * Depending on @ctx->flags, the following operations are performed: + * + * SYSFB_EVICT_PLATFORM: Firmware framebuffer platform devices (eg., + * 'simple-framebuffer') that overlap @ctx are removed + * from the system, causing drivers to be unbound. + * If SYSFB_EVICT_VBE is given, this also affects + * devices that own the VGA region. + * + * SYSFB_EVICT_FBDEV: Any firmware fbdev drivers that overlap @ctx are + * unloaded. + * Furthermore, if SYSFB_EVICT_VBE is given as well, any + * fbdev driver that maps the VGA region is unloaded. + * + * SYSFB_EVICT_VGACON: The vgacon console driver is unbound and unregistered. + * + * This might call into fbdev driver unregistration, or even device_del() on + * some buses. Hence, make sure you call this from your top-level + * probe-callbacks, rather than with any gfx-subsystem locks held. + * + * RETURNS: + * 0 on success, negative error code on failure. + */ +int sysfb_evict_conflicts(struct sysfb_evict_ctx *ctx) +{ + int ret; + + if (WARN_ON(!ctx || !ctx->ap)) + return -EINVAL; + + pr_info("removing conflicts (sysfb%s%s%s%s)\n", + (ctx->flags & SYSFB_EVICT_PLATFORM) ? ", platform" : "", + (ctx->flags & SYSFB_EVICT_FBDEV) ? ", fbdev" : "", + (ctx->flags & SYSFB_EVICT_VGACON) ? ", vgacon" : "", + (ctx->flags & SYSFB_EVICT_VBE) ? ", vbe" : ""); + + if (ctx->flags & SYSFB_EVICT_PLATFORM) { + ret = sysfb_evict_platform(ctx); + if (ret < 0) + return ret; + } + + if (ctx->flags & SYSFB_EVICT_FBDEV) { + ret = sysfb_evict_fbdev(ctx); + if (ret < 0) + return ret; + } + + if (ctx->flags & SYSFB_EVICT_VGACON) { + ret = sysfb_evict_vgacon(ctx); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(sysfb_evict_conflicts); + +/** + * sysfb_evict_conflicts_firmware() - remove all firmware framebuffers + * + * This is similar to sysfb_evict_conflicts() but uses a fake aperture spanning + * the entire address-space. This is suitable for any GFX driver that just + * wants to get rid of all available firmware framebuffers. + * + * RETURNS: + * 0 on success, negative error code on failure. + */ +int sysfb_evict_conflicts_firmware(void) +{ + struct sysfb_evict_ctx ctx = {}; + int ret; + + ctx.ap = alloc_apertures(1); + if (!ctx.ap) + return -ENOMEM; + + ctx.ap->ranges[0].base = 0; + ctx.ap->ranges[0].size = ~0; + + ctx.flags |= SYSFB_EVICT_FBDEV | SYSFB_EVICT_PLATFORM; + ret = sysfb_evict_conflicts(&ctx); + + kfree(ctx.ap); + return ret; +} +EXPORT_SYMBOL(sysfb_evict_conflicts_firmware); + +#ifdef CONFIG_PCI +/** + * sysfb_evict_conflicts_pci() - remove all system framebuffers conflicting + * with the given pci device + * @pdev: pci device + * + * This is similar to sysfb_evict_conflicts() but generates the eviction + * context based on the given pci device @pdev. + * + * RETURNS: + * 0 on success, negative error code on failure. + */ +int sysfb_evict_conflicts_pci(struct pci_dev *pdev) +{ + struct sysfb_evict_ctx ctx = {}; + size_t i, n, offset; + int ret; + + /* + * If this device is used as primary VGA device, it is shadowed at the + * VBE base address, so make sure to include it in the apertures. + */ + if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) + ctx.flags |= SYSFB_EVICT_VBE; + + /* + * If a device is a VGA device, make sure to kick out vgacon. We cannot + * rely on the IORESOURCE_ROM_SHADOW, since vgacon can switch between + * vga devices at runtime. So kick out vgacon anyway. + */ + if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) + ctx.flags |= SYSFB_EVICT_VGACON; + + /* + * Allocate apertures for all standard PCI resources. Skip them in case + * they are empty. + */ + ctx.ap = alloc_apertures(PCI_STD_RESOURCE_END - PCI_STD_RESOURCES + 1); + if (!ctx.ap) + return -ENOMEM; + + offset = PCI_STD_RESOURCES; + for (n = 0, i = 0; i < ctx.ap->count; ++i) { + if (pci_resource_len(pdev, offset + i) < 1) + continue; + + ctx.ap->ranges[n].base = pci_resource_start(pdev, offset + i); + ctx.ap->ranges[n].size = pci_resource_len(pdev, offset + i); + ++n; + } + ctx.ap->count = n; + + /* + * Evict all matching fbdev devices, VBE devices if they shadow this + * device, vgacon if this is a vga device, and platform devices if they + * match. + */ + ctx.flags |= SYSFB_EVICT_FBDEV | SYSFB_EVICT_PLATFORM; + ret = sysfb_evict_conflicts(&ctx); + + kfree(ctx.ap); + return ret; +} +EXPORT_SYMBOL(sysfb_evict_conflicts_pci); +#endif /* CONFIG_PCI */ diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h new file mode 100644 index 0000000..b67c74a --- /dev/null +++ b/include/linux/sysfb.h @@ -0,0 +1,34 @@ +#ifndef __LINUX_SYSFB_H +#define __LINUX_SYSFB_H + +/* + * Copyright (C) 2013-2016 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + */ + +#include <linux/kernel.h> + +struct apertures_struct; +struct pci_dev; + +enum { + SYSFB_EVICT_PLATFORM = (1U << 0), + SYSFB_EVICT_FBDEV = (1U << 1), + SYSFB_EVICT_VGACON = (1U << 2), + SYSFB_EVICT_VBE = (1U << 3), +}; + +struct sysfb_evict_ctx { + struct apertures_struct *ap; + unsigned int flags; +}; + +int sysfb_evict_conflicts(struct sysfb_evict_ctx *ctx); +int sysfb_evict_conflicts_firmware(void); +int sysfb_evict_conflicts_pci(struct pci_dev *pdev); + +#endif /* __LINUX_SYSFB_H */
On Fri, Sep 2, 2016 at 10:22 AM, David Herrmann dh.herrmann@gmail.com wrote:
There are several situations where we want hardware handover from an early boot GFX driver (e.g., vgacon, vesafb, efifb, simplefb) to a full fletched GFX driver (e.g., most DRM drivers). So far, we relied on remove_conflicting_framebuffers() to do this for us, however, this had a bunch of downsides:
o It only removes conflicting fbdev drivers. It does not drop vgacon, early boot console drivers, conflicting DRM drivers, etc.
o It only unloads the fbdev driver, it does not modify the underlying device or resources. In case of "screen_info" drivers (e.g., efifb) this is fine, since no resources are pinned. However, if the driver binds to a platform-device like "simple-framebuffer", we must make sure to unregister that device as well. Otherwise, pinned resources like IORESOURCE_MEM stay around, triggering WARN_ONs if the following driver requests those resources.
o It is only available if CONFIG_FB is selected.
This commit adds a new infrastructure that manages system-framebuffers (short: sysfb). The initial commit provides conflict-resolution for system-framebuffers. At its core it provides sysfb_evict_conflicts(), which implements conflict detection and removal for all known types of GFX driver hand-overs. So far, this includes platform-device removal, fbdev-firmware-device removal, vgacon removal and VBE detection. To further simplify the callers, it also provides helpers to figure out what hand-over to do, based on the device the new drivers binds to:
o PCI drivers can use sysfb_evict_conflicts_pci(), which will figure out the apertures automatically, and does VGA/VBE detection.
o Generic firmware drivers that might be shadowed at any address in memory can use sysfb_evict_conflicts_firmware(), basically removing *all* firmware framebuffers in effect.
This only adds the generic sysfb helpers. No users are converted, yet.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
Reviewed-by: Tom Gundersen teg@jklm.no
drivers/video/Kconfig | 4 + drivers/video/Makefile | 1 + drivers/video/sysfb.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysfb.h | 34 +++++ 4 files changed, 366 insertions(+) create mode 100644 drivers/video/sysfb.c create mode 100644 include/linux/sysfb.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3c20af9..56a8294 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -36,6 +36,10 @@ config VIDEOMODE_HELPERS config HDMI bool
+config SYSFB
bool
select DUMMY_CONSOLE if VT
if VT source "drivers/video/console/Kconfig" endif diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9ad3c17..df7bd75 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_VGASTATE) += vgastate.o obj-$(CONFIG_HDMI) += hdmi.o +obj-$(CONFIG_SYSFB) += sysfb.o
obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ diff --git a/drivers/video/sysfb.c b/drivers/video/sysfb.c new file mode 100644 index 0000000..00585c9 --- /dev/null +++ b/drivers/video/sysfb.c @@ -0,0 +1,327 @@ +/*
- Copyright (C) 2013-2016 Red Hat, Inc.
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2.1 of the License, or (at your
- option) any later version.
- */
+#define pr_fmt(fmt) "sysfb: " fmt +#include <linux/console.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/fb.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sysfb.h> +#include <linux/vt.h>
+static bool sysfb_evict_match_resource(struct sysfb_evict_ctx *ctx,
struct resource *mem)
+{
struct aperture *g;
unsigned int i;
for (i = 0; i < ctx->ap->count; ++i) {
g = &ctx->ap->ranges[i];
if (mem->start == g->base)
return true;
if (mem->start >= g->base && mem->end < g->base + g->size)
return true;
if ((ctx->flags & SYSFB_EVICT_VBE) && mem->start == 0xA0000)
return true;
}
return false;
+}
+static int sysfb_evict_platform_device(struct device *dev, void *userdata) +{
struct sysfb_evict_ctx *ctx = userdata;
struct platform_device *pdev = to_platform_device(dev);
struct resource *mem;
if (!pdev->name)
return 0;
if (!strcmp(pdev->name, "simple-framebuffer")) {
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem)
return 0;
if (!sysfb_evict_match_resource(ctx, mem))
return 0;
+#ifdef CONFIG_OF_ADDRESS
if (dev->of_node)
of_platform_device_destroy(dev);
else
+#endif
if (dev_get_platdata(&pdev->dev))
platform_device_del(pdev);
}
return 0;
+}
+static int sysfb_evict_platform(struct sysfb_evict_ctx *ctx) +{
/*
* Early-boot architecture setup and boot-loarders sometimes create
* preliminary platform devices with a generic framebuffer setup. This
* allows graphics access during boot-up, without a real graphics
* driver loaded. However, once a real graphics driver takes over, we
* have to destroy those platform devices. In the legacy fbdev case, we
* just used to unload the fbdev driver. However, to make sure any kind
* of driver is unloaded, the platform-eviction code here simply
* removes any conflicting platform device directly. This causes any
* bound driver to be detached, and then removes the device entirely so
* it cannot be bound to later on.
*
* Please note that any such platform device must be registered by
* early architecture setup code. If they are registered after regular
* GFX drivers, this will fail horribly.
*/
static DEFINE_MUTEX(lock);
int ret;
/*
* In case of static platform-devices, we must iterate the bus and
* remove them manually. We know that we're the only code that might
* remove them, so a simple static lock serializes all calls here.
*/
mutex_lock(&lock);
ret = bus_for_each_dev(&platform_bus_type, NULL, ctx,
sysfb_evict_platform_device);
mutex_unlock(&lock);
return ret;
+}
+static int sysfb_evict_fbdev(struct sysfb_evict_ctx *ctx) +{
/*
* Usually, evicting platform devices should be enough to also trigger
* fbdev unloading. However, some legacy devices (e.g., uvesafb) have
* no platform devices that can be evicted, so we still fall back to
* the legacy fbdev removal code. Note that this only removes fbdev
* devices marked as FBINFO_MISC_FIRMWARE. Anything else is left
* untouched.
*
* As usual, this only works if the fbdev device is probed early,
* before any real GFX driver wants to take over.
*/
int ret = 0;
+#ifdef CONFIG_FB
ret = remove_conflicting_framebuffers(ctx->ap, "sysfb",
ctx->flags & SYSFB_EVICT_VBE);
+#endif
return ret;
+}
+static int sysfb_evict_vgacon(struct sysfb_evict_ctx *ctx) +{
/*
* The VGACON console driver pokes at VGA registers randomly. If a GFX
* driver cannot keep the VGA support alive, it better makes sure to
* unload VGACON before probing.
*
* Unloading VGACON requires us to first force dummycon to take over
* from vgacon (but only if vgacon is really in use), followed by a
* deregistration of vgacon. Note that this prevents vgacon from being
* used again after the GFX driver is unloaded. But that is usually
* fine, since VGA state is rarely restored on driver-unload, anyway.
*
* Note that we rely on VGACON to be probed in early boot (actually
* done by ARCH setup code). If it is probed after GFX drivers, this
* will fail horribly. You better make sure VGACON is probed early and
* GFX drivers are probed as normal modules.
*/
int ret = 0;
+#ifdef CONFIG_VGA_CONSOLE
console_lock();
if (con_is_bound(&vga_con))
ret = do_take_over_console(&dummy_con, 0,
MAX_NR_CONSOLES - 1, 1);
if (ret == 0) {
ret = do_unregister_con_driver(&vga_con);
if (ret == -ENODEV) /* ignore "already unregistered" */
ret = 0;
}
console_unlock();
+#endif
return ret;
+}
+/**
- sysfb_evict_conflicts - remove any conflicting system-framebuffers
- @ctx: eviction context
- This function evicts any conflicting system-framebuffers and their bound
- drivers, according to the data given in @ctx.
- Depending on @ctx->flags, the following operations are performed:
- SYSFB_EVICT_PLATFORM: Firmware framebuffer platform devices (eg.,
'simple-framebuffer') that overlap @ctx are removed
from the system, causing drivers to be unbound.
If SYSFB_EVICT_VBE is given, this also affects
devices that own the VGA region.
- SYSFB_EVICT_FBDEV: Any firmware fbdev drivers that overlap @ctx are
unloaded.
Furthermore, if SYSFB_EVICT_VBE is given as well, any
fbdev driver that maps the VGA region is unloaded.
- SYSFB_EVICT_VGACON: The vgacon console driver is unbound and unregistered.
- This might call into fbdev driver unregistration, or even device_del() on
- some buses. Hence, make sure you call this from your top-level
- probe-callbacks, rather than with any gfx-subsystem locks held.
- RETURNS:
- 0 on success, negative error code on failure.
- */
+int sysfb_evict_conflicts(struct sysfb_evict_ctx *ctx) +{
int ret;
if (WARN_ON(!ctx || !ctx->ap))
return -EINVAL;
pr_info("removing conflicts (sysfb%s%s%s%s)\n",
(ctx->flags & SYSFB_EVICT_PLATFORM) ? ", platform" : "",
(ctx->flags & SYSFB_EVICT_FBDEV) ? ", fbdev" : "",
(ctx->flags & SYSFB_EVICT_VGACON) ? ", vgacon" : "",
(ctx->flags & SYSFB_EVICT_VBE) ? ", vbe" : "");
if (ctx->flags & SYSFB_EVICT_PLATFORM) {
ret = sysfb_evict_platform(ctx);
if (ret < 0)
return ret;
}
if (ctx->flags & SYSFB_EVICT_FBDEV) {
ret = sysfb_evict_fbdev(ctx);
if (ret < 0)
return ret;
}
if (ctx->flags & SYSFB_EVICT_VGACON) {
ret = sysfb_evict_vgacon(ctx);
if (ret < 0)
return ret;
}
return 0;
+} +EXPORT_SYMBOL(sysfb_evict_conflicts);
+/**
- sysfb_evict_conflicts_firmware() - remove all firmware framebuffers
- This is similar to sysfb_evict_conflicts() but uses a fake aperture spanning
- the entire address-space. This is suitable for any GFX driver that just
- wants to get rid of all available firmware framebuffers.
- RETURNS:
- 0 on success, negative error code on failure.
- */
+int sysfb_evict_conflicts_firmware(void) +{
struct sysfb_evict_ctx ctx = {};
int ret;
ctx.ap = alloc_apertures(1);
if (!ctx.ap)
return -ENOMEM;
ctx.ap->ranges[0].base = 0;
ctx.ap->ranges[0].size = ~0;
ctx.flags |= SYSFB_EVICT_FBDEV | SYSFB_EVICT_PLATFORM;
ret = sysfb_evict_conflicts(&ctx);
kfree(ctx.ap);
return ret;
+} +EXPORT_SYMBOL(sysfb_evict_conflicts_firmware);
+#ifdef CONFIG_PCI +/**
- sysfb_evict_conflicts_pci() - remove all system framebuffers conflicting
with the given pci device
- @pdev: pci device
- This is similar to sysfb_evict_conflicts() but generates the eviction
- context based on the given pci device @pdev.
- RETURNS:
- 0 on success, negative error code on failure.
- */
+int sysfb_evict_conflicts_pci(struct pci_dev *pdev) +{
struct sysfb_evict_ctx ctx = {};
size_t i, n, offset;
int ret;
/*
* If this device is used as primary VGA device, it is shadowed at the
* VBE base address, so make sure to include it in the apertures.
*/
if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
ctx.flags |= SYSFB_EVICT_VBE;
/*
* If a device is a VGA device, make sure to kick out vgacon. We cannot
* rely on the IORESOURCE_ROM_SHADOW, since vgacon can switch between
* vga devices at runtime. So kick out vgacon anyway.
*/
if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
ctx.flags |= SYSFB_EVICT_VGACON;
/*
* Allocate apertures for all standard PCI resources. Skip them in case
* they are empty.
*/
ctx.ap = alloc_apertures(PCI_STD_RESOURCE_END - PCI_STD_RESOURCES + 1);
if (!ctx.ap)
return -ENOMEM;
offset = PCI_STD_RESOURCES;
for (n = 0, i = 0; i < ctx.ap->count; ++i) {
if (pci_resource_len(pdev, offset + i) < 1)
continue;
ctx.ap->ranges[n].base = pci_resource_start(pdev, offset + i);
ctx.ap->ranges[n].size = pci_resource_len(pdev, offset + i);
++n;
}
ctx.ap->count = n;
/*
* Evict all matching fbdev devices, VBE devices if they shadow this
* device, vgacon if this is a vga device, and platform devices if they
* match.
*/
ctx.flags |= SYSFB_EVICT_FBDEV | SYSFB_EVICT_PLATFORM;
ret = sysfb_evict_conflicts(&ctx);
kfree(ctx.ap);
return ret;
+} +EXPORT_SYMBOL(sysfb_evict_conflicts_pci); +#endif /* CONFIG_PCI */ diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h new file mode 100644 index 0000000..b67c74a --- /dev/null +++ b/include/linux/sysfb.h @@ -0,0 +1,34 @@ +#ifndef __LINUX_SYSFB_H +#define __LINUX_SYSFB_H
+/*
- Copyright (C) 2013-2016 Red Hat, Inc.
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2.1 of the License, or (at your
- option) any later version.
- */
+#include <linux/kernel.h>
+struct apertures_struct; +struct pci_dev;
+enum {
SYSFB_EVICT_PLATFORM = (1U << 0),
SYSFB_EVICT_FBDEV = (1U << 1),
SYSFB_EVICT_VGACON = (1U << 2),
SYSFB_EVICT_VBE = (1U << 3),
+};
+struct sysfb_evict_ctx {
struct apertures_struct *ap;
unsigned int flags;
+};
+int sysfb_evict_conflicts(struct sysfb_evict_ctx *ctx); +int sysfb_evict_conflicts_firmware(void); +int sysfb_evict_conflicts_pci(struct pci_dev *pdev);
+#endif /* __LINUX_SYSFB_H */
2.9.3
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Den 02.09.2016 10:22, skrev David Herrmann:
There are several situations where we want hardware handover from an early boot GFX driver (e.g., vgacon, vesafb, efifb, simplefb) to a full fletched GFX driver (e.g., most DRM drivers). So far, we relied on remove_conflicting_framebuffers() to do this for us, however, this had a bunch of downsides:
o It only removes conflicting fbdev drivers. It does not drop vgacon, early boot console drivers, conflicting DRM drivers, etc.
o It only unloads the fbdev driver, it does not modify the underlying device or resources. In case of "screen_info" drivers (e.g., efifb) this is fine, since no resources are pinned. However, if the driver binds to a platform-device like "simple-framebuffer", we must make sure to unregister that device as well. Otherwise, pinned resources like IORESOURCE_MEM stay around, triggering WARN_ONs if the following driver requests those resources.
o It is only available if CONFIG_FB is selected.
This commit adds a new infrastructure that manages system-framebuffers (short: sysfb). The initial commit provides conflict-resolution for system-framebuffers. At its core it provides sysfb_evict_conflicts(), which implements conflict detection and removal for all known types of GFX driver hand-overs. So far, this includes platform-device removal, fbdev-firmware-device removal, vgacon removal and VBE detection. To further simplify the callers, it also provides helpers to figure out what hand-over to do, based on the device the new drivers binds to:
o PCI drivers can use sysfb_evict_conflicts_pci(), which will figure out the apertures automatically, and does VGA/VBE detection.
o Generic firmware drivers that might be shadowed at any address in memory can use sysfb_evict_conflicts_firmware(), basically removing *all* firmware framebuffers in effect.
This only adds the generic sysfb helpers. No users are converted, yet.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
drivers/video/Kconfig | 4 + drivers/video/Makefile | 1 + drivers/video/sysfb.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysfb.h | 34 +++++ 4 files changed, 366 insertions(+) create mode 100644 drivers/video/sysfb.c create mode 100644 include/linux/sysfb.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3c20af9..56a8294 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -36,6 +36,10 @@ config VIDEOMODE_HELPERS config HDMI bool
+config SYSFB
- bool
- select DUMMY_CONSOLE if VT
- if VT source "drivers/video/console/Kconfig" endif
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9ad3c17..df7bd75 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_VGASTATE) += vgastate.o obj-$(CONFIG_HDMI) += hdmi.o +obj-$(CONFIG_SYSFB) += sysfb.o
obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ diff --git a/drivers/video/sysfb.c b/drivers/video/sysfb.c new file mode 100644 index 0000000..00585c9 --- /dev/null +++ b/drivers/video/sysfb.c @@ -0,0 +1,327 @@ +/*
- Copyright (C) 2013-2016 Red Hat, Inc.
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2.1 of the License, or (at your
- option) any later version.
- */
+#define pr_fmt(fmt) "sysfb: " fmt +#include <linux/console.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/fb.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sysfb.h> +#include <linux/vt.h>
+static bool sysfb_evict_match_resource(struct sysfb_evict_ctx *ctx,
struct resource *mem)
+{
- struct aperture *g;
- unsigned int i;
- for (i = 0; i < ctx->ap->count; ++i) {
g = &ctx->ap->ranges[i];
if (mem->start == g->base)
return true;
if (mem->start >= g->base && mem->end < g->base + g->size)
return true;
if ((ctx->flags & SYSFB_EVICT_VBE) && mem->start == 0xA0000)
return true;
- }
- return false;
+}
+static int sysfb_evict_platform_device(struct device *dev, void *userdata) +{
- struct sysfb_evict_ctx *ctx = userdata;
- struct platform_device *pdev = to_platform_device(dev);
- struct resource *mem;
- if (!pdev->name)
return 0;
- if (!strcmp(pdev->name, "simple-framebuffer")) {
This doesn't work in my case where the device comes from DT and the name also contains the address: pdev->name=1e887000.framebuffer
This is one way of solving it:
if (!pdev->dev.driver || !pdev->dev.driver->name) return 0;
if (!strcmp(pdev->dev.driver->name, "simple-framebuffer")) {
Noralf.
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem)
return 0;
if (!sysfb_evict_match_resource(ctx, mem))
return 0;
+#ifdef CONFIG_OF_ADDRESS
if (dev->of_node)
of_platform_device_destroy(dev);
else
+#endif
if (dev_get_platdata(&pdev->dev))
platform_device_del(pdev);
- }
- return 0;
+}
+static int sysfb_evict_platform(struct sysfb_evict_ctx *ctx) +{
- /*
* Early-boot architecture setup and boot-loarders sometimes create
* preliminary platform devices with a generic framebuffer setup. This
* allows graphics access during boot-up, without a real graphics
* driver loaded. However, once a real graphics driver takes over, we
* have to destroy those platform devices. In the legacy fbdev case, we
* just used to unload the fbdev driver. However, to make sure any kind
* of driver is unloaded, the platform-eviction code here simply
* removes any conflicting platform device directly. This causes any
* bound driver to be detached, and then removes the device entirely so
* it cannot be bound to later on.
*
* Please note that any such platform device must be registered by
* early architecture setup code. If they are registered after regular
* GFX drivers, this will fail horribly.
*/
- static DEFINE_MUTEX(lock);
- int ret;
- /*
* In case of static platform-devices, we must iterate the bus and
* remove them manually. We know that we're the only code that might
* remove them, so a simple static lock serializes all calls here.
*/
- mutex_lock(&lock);
- ret = bus_for_each_dev(&platform_bus_type, NULL, ctx,
sysfb_evict_platform_device);
- mutex_unlock(&lock);
- return ret;
+}
+static int sysfb_evict_fbdev(struct sysfb_evict_ctx *ctx) +{
- /*
* Usually, evicting platform devices should be enough to also trigger
* fbdev unloading. However, some legacy devices (e.g., uvesafb) have
* no platform devices that can be evicted, so we still fall back to
* the legacy fbdev removal code. Note that this only removes fbdev
* devices marked as FBINFO_MISC_FIRMWARE. Anything else is left
* untouched.
*
* As usual, this only works if the fbdev device is probed early,
* before any real GFX driver wants to take over.
*/
- int ret = 0;
+#ifdef CONFIG_FB
- ret = remove_conflicting_framebuffers(ctx->ap, "sysfb",
ctx->flags & SYSFB_EVICT_VBE);
+#endif
- return ret;
+}
+static int sysfb_evict_vgacon(struct sysfb_evict_ctx *ctx) +{
- /*
* The VGACON console driver pokes at VGA registers randomly. If a GFX
* driver cannot keep the VGA support alive, it better makes sure to
* unload VGACON before probing.
*
* Unloading VGACON requires us to first force dummycon to take over
* from vgacon (but only if vgacon is really in use), followed by a
* deregistration of vgacon. Note that this prevents vgacon from being
* used again after the GFX driver is unloaded. But that is usually
* fine, since VGA state is rarely restored on driver-unload, anyway.
*
* Note that we rely on VGACON to be probed in early boot (actually
* done by ARCH setup code). If it is probed after GFX drivers, this
* will fail horribly. You better make sure VGACON is probed early and
* GFX drivers are probed as normal modules.
*/
- int ret = 0;
+#ifdef CONFIG_VGA_CONSOLE
- console_lock();
- if (con_is_bound(&vga_con))
ret = do_take_over_console(&dummy_con, 0,
MAX_NR_CONSOLES - 1, 1);
- if (ret == 0) {
ret = do_unregister_con_driver(&vga_con);
if (ret == -ENODEV) /* ignore "already unregistered" */
ret = 0;
- }
- console_unlock();
+#endif
- return ret;
+}
+/**
- sysfb_evict_conflicts - remove any conflicting system-framebuffers
- @ctx: eviction context
- This function evicts any conflicting system-framebuffers and their bound
- drivers, according to the data given in @ctx.
- Depending on @ctx->flags, the following operations are performed:
- SYSFB_EVICT_PLATFORM: Firmware framebuffer platform devices (eg.,
'simple-framebuffer') that overlap @ctx are removed
from the system, causing drivers to be unbound.
If SYSFB_EVICT_VBE is given, this also affects
devices that own the VGA region.
- SYSFB_EVICT_FBDEV: Any firmware fbdev drivers that overlap @ctx are
unloaded.
Furthermore, if SYSFB_EVICT_VBE is given as well, any
fbdev driver that maps the VGA region is unloaded.
- SYSFB_EVICT_VGACON: The vgacon console driver is unbound and unregistered.
- This might call into fbdev driver unregistration, or even device_del() on
- some buses. Hence, make sure you call this from your top-level
- probe-callbacks, rather than with any gfx-subsystem locks held.
- RETURNS:
- 0 on success, negative error code on failure.
- */
+int sysfb_evict_conflicts(struct sysfb_evict_ctx *ctx) +{
- int ret;
- if (WARN_ON(!ctx || !ctx->ap))
return -EINVAL;
- pr_info("removing conflicts (sysfb%s%s%s%s)\n",
(ctx->flags & SYSFB_EVICT_PLATFORM) ? ", platform" : "",
(ctx->flags & SYSFB_EVICT_FBDEV) ? ", fbdev" : "",
(ctx->flags & SYSFB_EVICT_VGACON) ? ", vgacon" : "",
(ctx->flags & SYSFB_EVICT_VBE) ? ", vbe" : "");
- if (ctx->flags & SYSFB_EVICT_PLATFORM) {
ret = sysfb_evict_platform(ctx);
if (ret < 0)
return ret;
- }
- if (ctx->flags & SYSFB_EVICT_FBDEV) {
ret = sysfb_evict_fbdev(ctx);
if (ret < 0)
return ret;
- }
- if (ctx->flags & SYSFB_EVICT_VGACON) {
ret = sysfb_evict_vgacon(ctx);
if (ret < 0)
return ret;
- }
- return 0;
+} +EXPORT_SYMBOL(sysfb_evict_conflicts);
+/**
- sysfb_evict_conflicts_firmware() - remove all firmware framebuffers
- This is similar to sysfb_evict_conflicts() but uses a fake aperture spanning
- the entire address-space. This is suitable for any GFX driver that just
- wants to get rid of all available firmware framebuffers.
- RETURNS:
- 0 on success, negative error code on failure.
- */
+int sysfb_evict_conflicts_firmware(void) +{
- struct sysfb_evict_ctx ctx = {};
- int ret;
- ctx.ap = alloc_apertures(1);
- if (!ctx.ap)
return -ENOMEM;
- ctx.ap->ranges[0].base = 0;
- ctx.ap->ranges[0].size = ~0;
- ctx.flags |= SYSFB_EVICT_FBDEV | SYSFB_EVICT_PLATFORM;
- ret = sysfb_evict_conflicts(&ctx);
- kfree(ctx.ap);
- return ret;
+} +EXPORT_SYMBOL(sysfb_evict_conflicts_firmware);
+#ifdef CONFIG_PCI +/**
- sysfb_evict_conflicts_pci() - remove all system framebuffers conflicting
with the given pci device
- @pdev: pci device
- This is similar to sysfb_evict_conflicts() but generates the eviction
- context based on the given pci device @pdev.
- RETURNS:
- 0 on success, negative error code on failure.
- */
+int sysfb_evict_conflicts_pci(struct pci_dev *pdev) +{
- struct sysfb_evict_ctx ctx = {};
- size_t i, n, offset;
- int ret;
- /*
* If this device is used as primary VGA device, it is shadowed at the
* VBE base address, so make sure to include it in the apertures.
*/
- if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
ctx.flags |= SYSFB_EVICT_VBE;
- /*
* If a device is a VGA device, make sure to kick out vgacon. We cannot
* rely on the IORESOURCE_ROM_SHADOW, since vgacon can switch between
* vga devices at runtime. So kick out vgacon anyway.
*/
- if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
ctx.flags |= SYSFB_EVICT_VGACON;
- /*
* Allocate apertures for all standard PCI resources. Skip them in case
* they are empty.
*/
- ctx.ap = alloc_apertures(PCI_STD_RESOURCE_END - PCI_STD_RESOURCES + 1);
- if (!ctx.ap)
return -ENOMEM;
- offset = PCI_STD_RESOURCES;
- for (n = 0, i = 0; i < ctx.ap->count; ++i) {
if (pci_resource_len(pdev, offset + i) < 1)
continue;
ctx.ap->ranges[n].base = pci_resource_start(pdev, offset + i);
ctx.ap->ranges[n].size = pci_resource_len(pdev, offset + i);
++n;
- }
- ctx.ap->count = n;
- /*
* Evict all matching fbdev devices, VBE devices if they shadow this
* device, vgacon if this is a vga device, and platform devices if they
* match.
*/
- ctx.flags |= SYSFB_EVICT_FBDEV | SYSFB_EVICT_PLATFORM;
- ret = sysfb_evict_conflicts(&ctx);
- kfree(ctx.ap);
- return ret;
+} +EXPORT_SYMBOL(sysfb_evict_conflicts_pci); +#endif /* CONFIG_PCI */ diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h new file mode 100644 index 0000000..b67c74a --- /dev/null +++ b/include/linux/sysfb.h @@ -0,0 +1,34 @@ +#ifndef __LINUX_SYSFB_H +#define __LINUX_SYSFB_H
+/*
- Copyright (C) 2013-2016 Red Hat, Inc.
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2.1 of the License, or (at your
- option) any later version.
- */
+#include <linux/kernel.h>
+struct apertures_struct; +struct pci_dev;
+enum {
- SYSFB_EVICT_PLATFORM = (1U << 0),
- SYSFB_EVICT_FBDEV = (1U << 1),
- SYSFB_EVICT_VGACON = (1U << 2),
- SYSFB_EVICT_VBE = (1U << 3),
+};
+struct sysfb_evict_ctx {
- struct apertures_struct *ap;
- unsigned int flags;
+};
+int sysfb_evict_conflicts(struct sysfb_evict_ctx *ctx); +int sysfb_evict_conflicts_firmware(void); +int sysfb_evict_conflicts_pci(struct pci_dev *pdev);
+#endif /* __LINUX_SYSFB_H */
Hi
On Sat, Sep 3, 2016 at 2:06 PM, Noralf Trønnes noralf@tronnes.org wrote:
Den 02.09.2016 10:22, skrev David Herrmann:
There are several situations where we want hardware handover from an early boot GFX driver (e.g., vgacon, vesafb, efifb, simplefb) to a full fletched GFX driver (e.g., most DRM drivers). So far, we relied on remove_conflicting_framebuffers() to do this for us, however, this had a bunch of downsides:
o It only removes conflicting fbdev drivers. It does not drop vgacon, early boot console drivers, conflicting DRM drivers, etc.
o It only unloads the fbdev driver, it does not modify the underlying device or resources. In case of "screen_info" drivers (e.g., efifb) this is fine, since no resources are pinned. However, if the driver binds to a platform-device like "simple-framebuffer", we must make sure to unregister that device as well. Otherwise, pinned resources like IORESOURCE_MEM stay around, triggering WARN_ONs if the following driver requests those resources.
o It is only available if CONFIG_FB is selected.
This commit adds a new infrastructure that manages system-framebuffers (short: sysfb). The initial commit provides conflict-resolution for system-framebuffers. At its core it provides sysfb_evict_conflicts(), which implements conflict detection and removal for all known types of GFX driver hand-overs. So far, this includes platform-device removal, fbdev-firmware-device removal, vgacon removal and VBE detection. To further simplify the callers, it also provides helpers to figure out what hand-over to do, based on the device the new drivers binds to:
o PCI drivers can use sysfb_evict_conflicts_pci(), which will figure out the apertures automatically, and does VGA/VBE detection.
o Generic firmware drivers that might be shadowed at any address in memory can use sysfb_evict_conflicts_firmware(), basically removing *all* firmware framebuffers in effect.
This only adds the generic sysfb helpers. No users are converted, yet.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
drivers/video/Kconfig | 4 + drivers/video/Makefile | 1 + drivers/video/sysfb.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysfb.h | 34 +++++ 4 files changed, 366 insertions(+) create mode 100644 drivers/video/sysfb.c create mode 100644 include/linux/sysfb.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3c20af9..56a8294 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -36,6 +36,10 @@ config VIDEOMODE_HELPERS config HDMI bool +config SYSFB
bool
select DUMMY_CONSOLE if VT
- if VT source "drivers/video/console/Kconfig" endif
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9ad3c17..df7bd75 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_VGASTATE) += vgastate.o obj-$(CONFIG_HDMI) += hdmi.o +obj-$(CONFIG_SYSFB) += sysfb.o obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ diff --git a/drivers/video/sysfb.c b/drivers/video/sysfb.c new file mode 100644 index 0000000..00585c9 --- /dev/null +++ b/drivers/video/sysfb.c @@ -0,0 +1,327 @@ +/*
- Copyright (C) 2013-2016 Red Hat, Inc.
- This program is free software; you can redistribute it and/or modify
it
- under the terms of the GNU Lesser General Public License as published
by the
- Free Software Foundation; either version 2.1 of the License, or (at
your
- option) any later version.
- */
+#define pr_fmt(fmt) "sysfb: " fmt +#include <linux/console.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/fb.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sysfb.h> +#include <linux/vt.h>
+static bool sysfb_evict_match_resource(struct sysfb_evict_ctx *ctx,
struct resource *mem)
+{
struct aperture *g;
unsigned int i;
for (i = 0; i < ctx->ap->count; ++i) {
g = &ctx->ap->ranges[i];
if (mem->start == g->base)
return true;
if (mem->start >= g->base && mem->end < g->base + g->size)
return true;
if ((ctx->flags & SYSFB_EVICT_VBE) && mem->start ==
0xA0000)
return true;
}
return false;
+}
+static int sysfb_evict_platform_device(struct device *dev, void *userdata) +{
struct sysfb_evict_ctx *ctx = userdata;
struct platform_device *pdev = to_platform_device(dev);
struct resource *mem;
if (!pdev->name)
return 0;
if (!strcmp(pdev->name, "simple-framebuffer")) {
This doesn't work in my case where the device comes from DT and the name also contains the address: pdev->name=1e887000.framebuffer
This is one way of solving it:
if (!pdev->dev.driver || !pdev->dev.driver->name) return 0; if (!strcmp(pdev->dev.driver->name, "simple-framebuffer")) {
Thanks for the hint, but this would skip devices that are not bound. How about the attached hunk?
Thanks David
diff --git a/drivers/video/sysfb.c b/drivers/video/sysfb.c index 0f039c6..a4ace3f 100644 --- a/drivers/video/sysfb.c +++ b/drivers/video/sysfb.c @@ -50,10 +50,8 @@ static int sysfb_evict_platform_device struct platform_device *pdev = to_platform_device(dev); struct resource *mem;
- if (!pdev->name) - return 0; - - if (!strcmp(pdev->name, "simple-framebuffer")) { + if ((pdev->name && !strcmp(pdev->name, "simple-framebuffer")) || + of_device_is_compatible(pdev->dev.of_node, "simple-framebuffer")) { mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) return 0;
Den 05.09.2016 13:19, skrev David Herrmann:
Hi
On Sat, Sep 3, 2016 at 2:06 PM, Noralf Trønnes noralf@tronnes.org wrote:
Den 02.09.2016 10:22, skrev David Herrmann:
There are several situations where we want hardware handover from an early boot GFX driver (e.g., vgacon, vesafb, efifb, simplefb) to a full fletched GFX driver (e.g., most DRM drivers). So far, we relied on remove_conflicting_framebuffers() to do this for us, however, this had a bunch of downsides:
o It only removes conflicting fbdev drivers. It does not drop vgacon, early boot console drivers, conflicting DRM drivers, etc. o It only unloads the fbdev driver, it does not modify the underlying device or resources. In case of "screen_info" drivers (e.g., efifb) this is fine, since no resources are pinned. However, if the driver binds to a platform-device like "simple-framebuffer", we must make sure to unregister that device as well. Otherwise, pinned resources like IORESOURCE_MEM stay around, triggering WARN_ONs if the following driver requests those resources. o It is only available if CONFIG_FB is selected.
This commit adds a new infrastructure that manages system-framebuffers (short: sysfb). The initial commit provides conflict-resolution for system-framebuffers. At its core it provides sysfb_evict_conflicts(), which implements conflict detection and removal for all known types of GFX driver hand-overs. So far, this includes platform-device removal, fbdev-firmware-device removal, vgacon removal and VBE detection. To further simplify the callers, it also provides helpers to figure out what hand-over to do, based on the device the new drivers binds to:
o PCI drivers can use sysfb_evict_conflicts_pci(), which will figure
out the apertures automatically, and does VGA/VBE detection.
o Generic firmware drivers that might be shadowed at any address in memory can use sysfb_evict_conflicts_firmware(), basically removing *all* firmware framebuffers in effect.
This only adds the generic sysfb helpers. No users are converted, yet.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
drivers/video/Kconfig | 4 + drivers/video/Makefile | 1 + drivers/video/sysfb.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysfb.h | 34 +++++ 4 files changed, 366 insertions(+) create mode 100644 drivers/video/sysfb.c create mode 100644 include/linux/sysfb.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3c20af9..56a8294 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -36,6 +36,10 @@ config VIDEOMODE_HELPERS config HDMI bool +config SYSFB
bool
select DUMMY_CONSOLE if VT
- if VT source "drivers/video/console/Kconfig" endif
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9ad3c17..df7bd75 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_VGASTATE) += vgastate.o obj-$(CONFIG_HDMI) += hdmi.o +obj-$(CONFIG_SYSFB) += sysfb.o obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ diff --git a/drivers/video/sysfb.c b/drivers/video/sysfb.c new file mode 100644 index 0000000..00585c9 --- /dev/null +++ b/drivers/video/sysfb.c @@ -0,0 +1,327 @@ +/*
- Copyright (C) 2013-2016 Red Hat, Inc.
- This program is free software; you can redistribute it and/or modify
it
- under the terms of the GNU Lesser General Public License as published
by the
- Free Software Foundation; either version 2.1 of the License, or (at
your
- option) any later version.
- */
+#define pr_fmt(fmt) "sysfb: " fmt +#include <linux/console.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/fb.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sysfb.h> +#include <linux/vt.h>
+static bool sysfb_evict_match_resource(struct sysfb_evict_ctx *ctx,
struct resource *mem)
+{
struct aperture *g;
unsigned int i;
for (i = 0; i < ctx->ap->count; ++i) {
g = &ctx->ap->ranges[i];
if (mem->start == g->base)
return true;
if (mem->start >= g->base && mem->end < g->base + g->size)
return true;
if ((ctx->flags & SYSFB_EVICT_VBE) && mem->start ==
0xA0000)
return true;
}
return false;
+}
+static int sysfb_evict_platform_device(struct device *dev, void *userdata) +{
struct sysfb_evict_ctx *ctx = userdata;
struct platform_device *pdev = to_platform_device(dev);
struct resource *mem;
if (!pdev->name)
return 0;
if (!strcmp(pdev->name, "simple-framebuffer")) {
This doesn't work in my case where the device comes from DT and the name also contains the address: pdev->name=1e887000.framebuffer
This is one way of solving it:
if (!pdev->dev.driver || !pdev->dev.driver->name) return 0; if (!strcmp(pdev->dev.driver->name, "simple-framebuffer")) {
Thanks for the hint, but this would skip devices that are not bound. How about the attached hunk?
Thanks David
diff --git a/drivers/video/sysfb.c b/drivers/video/sysfb.c index 0f039c6..a4ace3f 100644 --- a/drivers/video/sysfb.c +++ b/drivers/video/sysfb.c @@ -50,10 +50,8 @@ static int sysfb_evict_platform_device struct platform_device *pdev = to_platform_device(dev); struct resource *mem;
if (!pdev->name)
return 0;
if (!strcmp(pdev->name, "simple-framebuffer")) {
if ((pdev->name && !strcmp(pdev->name, "simple-framebuffer")) ||
of_device_is_compatible(pdev->dev.of_node, "simple-framebuffer")) { mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) return 0;
This worked fine.
Noralf.
Switch over all DRM drivers to use the new sysfb_evict_conflicts() infrastructure. The only non-trivial conversion is i915, since it does not make use of the generic PCI resources, but assembles the apertures via intel ggtt queries.
Signed-off-by: David Herrmann dh.herrmann@gmail.com --- drivers/gpu/drm/Kconfig | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 24 +---------- drivers/gpu/drm/bochs/bochs_drv.c | 19 +-------- drivers/gpu/drm/i915/i915_drv.c | 73 ++++++-------------------------- drivers/gpu/drm/mgag200/mgag200_drv.c | 27 +++--------- drivers/gpu/drm/mgag200/mgag200_main.c | 9 ---- drivers/gpu/drm/nouveau/nouveau_drm.c | 33 +++------------ drivers/gpu/drm/radeon/radeon_drv.c | 24 +---------- drivers/gpu/drm/sun4i/sun4i_drv.c | 24 +++-------- drivers/gpu/drm/vc4/vc4_drv.c | 25 +++-------- drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 24 +---------- 11 files changed, 44 insertions(+), 239 deletions(-)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index fc35731..f27f9b5 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -12,6 +12,7 @@ menuconfig DRM select I2C select I2C_ALGOBIT select DMA_SHARED_BUFFER + select SYSFB help Kernel-level support for the Direct Rendering Infrastructure (DRI) introduced in XFree86 4.0. If you say Y here, you need to select diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 9aa533c..a1e67da 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -38,6 +38,7 @@ #include <linux/console.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/sysfb.h> #include <linux/vga_switcheroo.h> #include "drm_crtc_helper.h"
@@ -326,27 +327,6 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
static struct drm_driver kms_driver;
-static int amdgpu_kick_out_firmware_fb(struct pci_dev *pdev) -{ - struct apertures_struct *ap; - bool primary = false; - - ap = alloc_apertures(1); - if (!ap) - return -ENOMEM; - - ap->ranges[0].base = pci_resource_start(pdev, 0); - ap->ranges[0].size = pci_resource_len(pdev, 0); - -#ifdef CONFIG_X86 - primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; -#endif - remove_conflicting_framebuffers(ap, "amdgpudrmfb", primary); - kfree(ap); - - return 0; -} - static int amdgpu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -368,7 +348,7 @@ static int amdgpu_pci_probe(struct pci_dev *pdev, return ret;
/* Get rid of things like offb */ - ret = amdgpu_kick_out_firmware_fb(pdev); + ret = sysfb_evict_conflicts_pci(pdev); if (ret) return ret;
diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c index abace82..99c4ea3 100644 --- a/drivers/gpu/drm/bochs/bochs_drv.c +++ b/drivers/gpu/drm/bochs/bochs_drv.c @@ -8,6 +8,7 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/sysfb.h>
#include "bochs.h"
@@ -143,28 +144,12 @@ static const struct dev_pm_ops bochs_pm_ops = { /* ---------------------------------------------------------------------- */ /* pci interface */
-static int bochs_kick_out_firmware_fb(struct pci_dev *pdev) -{ - struct apertures_struct *ap; - - ap = alloc_apertures(1); - if (!ap) - return -ENOMEM; - - ap->ranges[0].base = pci_resource_start(pdev, 0); - ap->ranges[0].size = pci_resource_len(pdev, 0); - remove_conflicting_framebuffers(ap, "bochsdrmfb", false); - kfree(ap); - - return 0; -} - static int bochs_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int ret;
- ret = bochs_kick_out_firmware_fb(pdev); + ret = sysfb_evict_conflicts_pci(pdev); if (ret) return ret;
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 95ddd56..4d6a65dd 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -36,6 +36,7 @@ #include <linux/pm_runtime.h> #include <linux/pnp.h> #include <linux/slab.h> +#include <linux/sysfb.h> #include <linux/vgaarb.h> #include <linux/vga_switcheroo.h> #include <linux/vt.h> @@ -687,70 +688,32 @@ out: return ret; }
-#if IS_ENABLED(CONFIG_FB) static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) { - struct apertures_struct *ap; + struct sysfb_evict_ctx ctx = {}; struct pci_dev *pdev = dev_priv->drm.pdev; struct i915_ggtt *ggtt = &dev_priv->ggtt; - bool primary; int ret;
- ap = alloc_apertures(1); - if (!ap) - return -ENOMEM; - - ap->ranges[0].base = ggtt->mappable_base; - ap->ranges[0].size = ggtt->mappable_end; - - primary = - pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; - - ret = remove_conflicting_framebuffers(ap, "inteldrmfb", primary); - - kfree(ap); - - return ret; -} -#else -static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) -{ - return 0; -} -#endif + ctx.flags = SYSFB_EVICT_PLATFORM | + SYSFB_EVICT_FBDEV | + SYSFB_EVICT_VGACON;
-#if !defined(CONFIG_VGA_CONSOLE) -static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv) -{ - return 0; -} -#elif !defined(CONFIG_DUMMY_CONSOLE) -static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv) -{ - return -ENODEV; -} -#else -static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv) -{ - int ret = 0; + ctx.ap = alloc_apertures(1); + if (!ctx.ap) + return -ENOMEM;
- DRM_INFO("Replacing VGA console driver\n"); + ctx.ap->ranges[0].base = ggtt->mappable_base; + ctx.ap->ranges[0].size = ggtt->mappable_end;
- console_lock(); - if (con_is_bound(&vga_con)) - ret = do_take_over_console(&dummy_con, 0, MAX_NR_CONSOLES - 1, 1); - if (ret == 0) { - ret = do_unregister_con_driver(&vga_con); + if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) + ctx.flags |= SYSFB_EVICT_VBE;
- /* Ignore "already unregistered". */ - if (ret == -ENODEV) - ret = 0; - } - console_unlock(); + ret = sysfb_evict_conflicts(&ctx);
+ kfree(ctx.ap); return ret; } -#endif
static void intel_init_dpio(struct drm_i915_private *dev_priv) { @@ -1032,20 +995,12 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) goto out_ggtt; }
- /* WARNING: Apparently we must kick fbdev drivers before vgacon, - * otherwise the vga fbdev driver falls over. */ ret = i915_kick_out_firmware_fb(dev_priv); if (ret) { DRM_ERROR("failed to remove conflicting framebuffer drivers\n"); goto out_ggtt; }
- ret = i915_kick_out_vgacon(dev_priv); - if (ret) { - DRM_ERROR("failed to remove conflicting VGA console\n"); - goto out_ggtt; - } - pci_set_master(dev->pdev);
/* overlay on gen2 is broken and can't address above 1G */ diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 2b4b125..f30105b 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -10,6 +10,7 @@ */ #include <linux/module.h> #include <linux/console.h> +#include <linux/sysfb.h> #include <drm/drmP.h>
#include "mgag200_drv.h" @@ -41,29 +42,13 @@ static const struct pci_device_id pciidlist[] = {
MODULE_DEVICE_TABLE(pci, pciidlist);
-static void mgag200_kick_out_firmware_fb(struct pci_dev *pdev) -{ - struct apertures_struct *ap; - bool primary = false; - - ap = alloc_apertures(1); - if (!ap) - return; - - ap->ranges[0].base = pci_resource_start(pdev, 0); - ap->ranges[0].size = pci_resource_len(pdev, 0); - -#ifdef CONFIG_X86 - primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; -#endif - remove_conflicting_framebuffers(ap, "mgag200drmfb", primary); - kfree(ap); -} - - static int mga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - mgag200_kick_out_firmware_fb(pdev); + int ret; + + ret = sysfb_evict_conflicts_pci(pdev); + if (ret < 0) + return ret;
return drm_get_pci_dev(pdev, ent, &driver); } diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index 13798b3..4723407 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -124,20 +124,11 @@ static int mga_probe_vram(struct mga_device *mdev, void __iomem *mem) static int mga_vram_init(struct mga_device *mdev) { void __iomem *mem; - struct apertures_struct *aper = alloc_apertures(1); - if (!aper) - return -ENOMEM;
/* BAR 0 is VRAM */ mdev->mc.vram_base = pci_resource_start(mdev->dev->pdev, 0); mdev->mc.vram_window = pci_resource_len(mdev->dev->pdev, 0);
- aper->ranges[0].base = mdev->mc.vram_base; - aper->ranges[0].size = mdev->mc.vram_window; - - remove_conflicting_framebuffers(aper, "mgafb", true); - kfree(aper); - if (!devm_request_mem_region(mdev->dev->dev, mdev->mc.vram_base, mdev->mc.vram_window, "mgadrmfb_vram")) { DRM_ERROR("can't reserve VRAM\n"); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 66c1280..193e833 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -27,6 +27,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/pm_runtime.h> +#include <linux/sysfb.h> #include <linux/vga_switcheroo.h>
#include "drmP.h" @@ -310,8 +311,6 @@ static int nouveau_drm_probe(struct pci_dev *pdev, const struct pci_device_id *pent) { struct nvkm_device *device; - struct apertures_struct *aper; - bool boot = false; int ret;
if (vga_switcheroo_client_probe_defer(pdev)) @@ -326,34 +325,12 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
nvkm_device_del(&device);
- /* Remove conflicting drivers (vesafb, efifb etc). */ - aper = alloc_apertures(3); - if (!aper) - return -ENOMEM; - - aper->ranges[0].base = pci_resource_start(pdev, 1); - aper->ranges[0].size = pci_resource_len(pdev, 1); - aper->count = 1; - - if (pci_resource_len(pdev, 2)) { - aper->ranges[aper->count].base = pci_resource_start(pdev, 2); - aper->ranges[aper->count].size = pci_resource_len(pdev, 2); - aper->count++; - } - - if (pci_resource_len(pdev, 3)) { - aper->ranges[aper->count].base = pci_resource_start(pdev, 3); - aper->ranges[aper->count].size = pci_resource_len(pdev, 3); - aper->count++; + if (nouveau_modeset != 2) { + ret = sysfb_evict_conflicts_pci(pdev); + if (ret < 0) + return ret; }
-#ifdef CONFIG_X86 - boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; -#endif - if (nouveau_modeset != 2) - remove_conflicting_framebuffers(aper, "nouveaufb", boot); - kfree(aper); - ret = nvkm_device_pci_new(pdev, nouveau_config, nouveau_debug, true, true, ~0ULL, &device); if (ret) diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index c01a7c6..a0c11bd 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -37,6 +37,7 @@ #include <linux/console.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/sysfb.h> #include <linux/vga_switcheroo.h> #include <drm/drm_gem.h>
@@ -309,27 +310,6 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
static struct drm_driver kms_driver;
-static int radeon_kick_out_firmware_fb(struct pci_dev *pdev) -{ - struct apertures_struct *ap; - bool primary = false; - - ap = alloc_apertures(1); - if (!ap) - return -ENOMEM; - - ap->ranges[0].base = pci_resource_start(pdev, 0); - ap->ranges[0].size = pci_resource_len(pdev, 0); - -#ifdef CONFIG_X86 - primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; -#endif - remove_conflicting_framebuffers(ap, "radeondrmfb", primary); - kfree(ap); - - return 0; -} - static int radeon_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -347,7 +327,7 @@ static int radeon_pci_probe(struct pci_dev *pdev, return -EPROBE_DEFER;
/* Get rid of things like offb */ - ret = radeon_kick_out_firmware_fb(pdev); + ret = sysfb_evict_conflicts_pci(pdev); if (ret) return ret;
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 7092daa..ac388b5 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -12,6 +12,7 @@
#include <linux/component.h> #include <linux/of_graph.h> +#include <linux/sysfb.h>
#include <drm/drmP.h> #include <drm/drm_crtc_helper.h> @@ -97,28 +98,16 @@ static struct drm_driver sun4i_drv_driver = { .disable_vblank = sun4i_drv_disable_vblank, };
-static void sun4i_remove_framebuffers(void) -{ - struct apertures_struct *ap; - - ap = alloc_apertures(1); - if (!ap) - return; - - /* The framebuffer can be located anywhere in RAM */ - ap->ranges[0].base = 0; - ap->ranges[0].size = ~0; - - remove_conflicting_framebuffers(ap, "sun4i-drm-fb", false); - kfree(ap); -} - static int sun4i_drv_bind(struct device *dev) { struct drm_device *drm; struct sun4i_drv *drv; int ret;
+ ret = sysfb_evict_conflicts_firmware(); + if (ret < 0) + return ret; + drm = drm_dev_alloc(&sun4i_drv_driver, dev); if (!drm) return -ENOMEM; @@ -156,9 +145,6 @@ static int sun4i_drv_bind(struct device *dev) } drm->irq_enabled = true;
- /* Remove early framebuffers (ie. simplefb) */ - sun4i_remove_framebuffers(); - /* Create our framebuffer */ drv->fbdev = sun4i_framebuffer_init(drm); if (IS_ERR(drv->fbdev)) { diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 8b42d31..679e65a 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -15,6 +15,7 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/sysfb.h> #include "drm_fb_cma_helper.h"
#include "uapi/drm/vc4_drm.h" @@ -200,24 +201,6 @@ static void vc4_match_add_drivers(struct device *dev, } }
-static void vc4_kick_out_firmware_fb(void) -{ - struct apertures_struct *ap; - - ap = alloc_apertures(1); - if (!ap) - return; - - /* Since VC4 is a UMA device, the simplefb node may have been - * located anywhere in memory. - */ - ap->ranges[0].base = 0; - ap->ranges[0].size = ~0; - - remove_conflicting_framebuffers(ap, "vc4drmfb", false); - kfree(ap); -} - static int vc4_drm_bind(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -225,6 +208,10 @@ static int vc4_drm_bind(struct device *dev) struct vc4_dev *vc4; int ret = 0;
+ ret = sysfb_evict_conflicts_firmware(); + if (ret < 0) + return ret; + dev->coherent_dma_mask = DMA_BIT_MASK(32);
vc4 = devm_kzalloc(dev, sizeof(*vc4), GFP_KERNEL); @@ -248,8 +235,6 @@ static int vc4_drm_bind(struct device *dev) if (ret) goto gem_destroy;
- vc4_kick_out_firmware_fb(); - ret = drm_dev_register(drm, 0); if (ret < 0) goto unbind_all; diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c index 7f0e93f87..d6a8a94 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c @@ -24,29 +24,10 @@ */
#include <linux/pci.h> +#include <linux/sysfb.h>
#include "virtgpu_drv.h"
-static void virtio_pci_kick_out_firmware_fb(struct pci_dev *pci_dev) -{ - struct apertures_struct *ap; - bool primary; - - ap = alloc_apertures(1); - if (!ap) - return; - - ap->ranges[0].base = pci_resource_start(pci_dev, 0); - ap->ranges[0].size = pci_resource_len(pci_dev, 0); - - primary = pci_dev->resource[PCI_ROM_RESOURCE].flags - & IORESOURCE_ROM_SHADOW; - - remove_conflicting_framebuffers(ap, "virtiodrmfb", primary); - - kfree(ap); -} - int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) { struct drm_device *dev; @@ -65,8 +46,7 @@ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) DRM_INFO("pci: %s detected\n", vga ? "virtio-vga" : "virtio-gpu-pci"); dev->pdev = pdev; - if (vga) - virtio_pci_kick_out_firmware_fb(pdev); + sysfb_evict_conflicts_pci(pdev); }
ret = drm_dev_register(dev, 0);
Den 02.09.2016 10:22, skrev David Herrmann:
Switch over all DRM drivers to use the new sysfb_evict_conflicts() infrastructure. The only non-trivial conversion is i915, since it does not make use of the generic PCI resources, but assembles the apertures via intel ggtt queries.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
This doesn't apply now with Daniel's wrapper added: drm_fb_helper_remove_conflicting_framebuffers().
Noralf.
drivers/gpu/drm/Kconfig | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 24 +---------- drivers/gpu/drm/bochs/bochs_drv.c | 19 +-------- drivers/gpu/drm/i915/i915_drv.c | 73 ++++++-------------------------- drivers/gpu/drm/mgag200/mgag200_drv.c | 27 +++--------- drivers/gpu/drm/mgag200/mgag200_main.c | 9 ---- drivers/gpu/drm/nouveau/nouveau_drm.c | 33 +++------------ drivers/gpu/drm/radeon/radeon_drv.c | 24 +---------- drivers/gpu/drm/sun4i/sun4i_drv.c | 24 +++-------- drivers/gpu/drm/vc4/vc4_drv.c | 25 +++-------- drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 24 +---------- 11 files changed, 44 insertions(+), 239 deletions(-)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index fc35731..f27f9b5 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -12,6 +12,7 @@ menuconfig DRM select I2C select I2C_ALGOBIT select DMA_SHARED_BUFFER
- select SYSFB help Kernel-level support for the Direct Rendering Infrastructure (DRI) introduced in XFree86 4.0. If you say Y here, you need to select
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 9aa533c..a1e67da 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -38,6 +38,7 @@ #include <linux/console.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/sysfb.h> #include <linux/vga_switcheroo.h> #include "drm_crtc_helper.h"
@@ -326,27 +327,6 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
static struct drm_driver kms_driver;
-static int amdgpu_kick_out_firmware_fb(struct pci_dev *pdev) -{
- struct apertures_struct *ap;
- bool primary = false;
- ap = alloc_apertures(1);
- if (!ap)
return -ENOMEM;
- ap->ranges[0].base = pci_resource_start(pdev, 0);
- ap->ranges[0].size = pci_resource_len(pdev, 0);
-#ifdef CONFIG_X86
- primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
-#endif
- remove_conflicting_framebuffers(ap, "amdgpudrmfb", primary);
- kfree(ap);
- return 0;
-}
- static int amdgpu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) {
@@ -368,7 +348,7 @@ static int amdgpu_pci_probe(struct pci_dev *pdev, return ret;
/* Get rid of things like offb */
- ret = amdgpu_kick_out_firmware_fb(pdev);
- ret = sysfb_evict_conflicts_pci(pdev); if (ret) return ret;
diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c index abace82..99c4ea3 100644 --- a/drivers/gpu/drm/bochs/bochs_drv.c +++ b/drivers/gpu/drm/bochs/bochs_drv.c @@ -8,6 +8,7 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/sysfb.h>
#include "bochs.h"
@@ -143,28 +144,12 @@ static const struct dev_pm_ops bochs_pm_ops = { /* ---------------------------------------------------------------------- */ /* pci interface */
-static int bochs_kick_out_firmware_fb(struct pci_dev *pdev) -{
- struct apertures_struct *ap;
- ap = alloc_apertures(1);
- if (!ap)
return -ENOMEM;
- ap->ranges[0].base = pci_resource_start(pdev, 0);
- ap->ranges[0].size = pci_resource_len(pdev, 0);
- remove_conflicting_framebuffers(ap, "bochsdrmfb", false);
- kfree(ap);
- return 0;
-}
static int bochs_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int ret;
ret = bochs_kick_out_firmware_fb(pdev);
- ret = sysfb_evict_conflicts_pci(pdev); if (ret) return ret;
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 95ddd56..4d6a65dd 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -36,6 +36,7 @@ #include <linux/pm_runtime.h> #include <linux/pnp.h> #include <linux/slab.h> +#include <linux/sysfb.h> #include <linux/vgaarb.h> #include <linux/vga_switcheroo.h> #include <linux/vt.h> @@ -687,70 +688,32 @@ out: return ret; }
-#if IS_ENABLED(CONFIG_FB) static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) {
- struct apertures_struct *ap;
- struct sysfb_evict_ctx ctx = {}; struct pci_dev *pdev = dev_priv->drm.pdev; struct i915_ggtt *ggtt = &dev_priv->ggtt;
bool primary; int ret;
ap = alloc_apertures(1);
if (!ap)
return -ENOMEM;
ap->ranges[0].base = ggtt->mappable_base;
ap->ranges[0].size = ggtt->mappable_end;
primary =
pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
ret = remove_conflicting_framebuffers(ap, "inteldrmfb", primary);
kfree(ap);
return ret;
-} -#else -static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) -{
- return 0;
-} -#endif
- ctx.flags = SYSFB_EVICT_PLATFORM |
SYSFB_EVICT_FBDEV |
SYSFB_EVICT_VGACON;
-#if !defined(CONFIG_VGA_CONSOLE) -static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv) -{
- return 0;
-} -#elif !defined(CONFIG_DUMMY_CONSOLE) -static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv) -{
- return -ENODEV;
-} -#else -static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv) -{
- int ret = 0;
- ctx.ap = alloc_apertures(1);
- if (!ctx.ap)
return -ENOMEM;
- DRM_INFO("Replacing VGA console driver\n");
- ctx.ap->ranges[0].base = ggtt->mappable_base;
- ctx.ap->ranges[0].size = ggtt->mappable_end;
- console_lock();
- if (con_is_bound(&vga_con))
ret = do_take_over_console(&dummy_con, 0, MAX_NR_CONSOLES - 1, 1);
- if (ret == 0) {
ret = do_unregister_con_driver(&vga_con);
- if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
ctx.flags |= SYSFB_EVICT_VBE;
/* Ignore "already unregistered". */
if (ret == -ENODEV)
ret = 0;
- }
- console_unlock();
ret = sysfb_evict_conflicts(&ctx);
kfree(ctx.ap); return ret; }
-#endif
static void intel_init_dpio(struct drm_i915_private *dev_priv) { @@ -1032,20 +995,12 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) goto out_ggtt; }
/* WARNING: Apparently we must kick fbdev drivers before vgacon,
* otherwise the vga fbdev driver falls over. */
ret = i915_kick_out_firmware_fb(dev_priv); if (ret) { DRM_ERROR("failed to remove conflicting framebuffer drivers\n"); goto out_ggtt; }
ret = i915_kick_out_vgacon(dev_priv);
if (ret) {
DRM_ERROR("failed to remove conflicting VGA console\n");
goto out_ggtt;
}
pci_set_master(dev->pdev);
/* overlay on gen2 is broken and can't address above 1G */
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 2b4b125..f30105b 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -10,6 +10,7 @@ */ #include <linux/module.h> #include <linux/console.h> +#include <linux/sysfb.h> #include <drm/drmP.h>
#include "mgag200_drv.h" @@ -41,29 +42,13 @@ static const struct pci_device_id pciidlist[] = {
MODULE_DEVICE_TABLE(pci, pciidlist);
-static void mgag200_kick_out_firmware_fb(struct pci_dev *pdev) -{
- struct apertures_struct *ap;
- bool primary = false;
- ap = alloc_apertures(1);
- if (!ap)
return;
- ap->ranges[0].base = pci_resource_start(pdev, 0);
- ap->ranges[0].size = pci_resource_len(pdev, 0);
-#ifdef CONFIG_X86
- primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
-#endif
- remove_conflicting_framebuffers(ap, "mgag200drmfb", primary);
- kfree(ap);
-}
- static int mga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) {
- mgag200_kick_out_firmware_fb(pdev);
int ret;
ret = sysfb_evict_conflicts_pci(pdev);
if (ret < 0)
return ret;
return drm_get_pci_dev(pdev, ent, &driver); }
diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index 13798b3..4723407 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -124,20 +124,11 @@ static int mga_probe_vram(struct mga_device *mdev, void __iomem *mem) static int mga_vram_init(struct mga_device *mdev) { void __iomem *mem;
struct apertures_struct *aper = alloc_apertures(1);
if (!aper)
return -ENOMEM;
/* BAR 0 is VRAM */ mdev->mc.vram_base = pci_resource_start(mdev->dev->pdev, 0); mdev->mc.vram_window = pci_resource_len(mdev->dev->pdev, 0);
aper->ranges[0].base = mdev->mc.vram_base;
aper->ranges[0].size = mdev->mc.vram_window;
remove_conflicting_framebuffers(aper, "mgafb", true);
kfree(aper);
if (!devm_request_mem_region(mdev->dev->dev, mdev->mc.vram_base, mdev->mc.vram_window, "mgadrmfb_vram")) { DRM_ERROR("can't reserve VRAM\n");
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 66c1280..193e833 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -27,6 +27,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/pm_runtime.h> +#include <linux/sysfb.h> #include <linux/vga_switcheroo.h>
#include "drmP.h" @@ -310,8 +311,6 @@ static int nouveau_drm_probe(struct pci_dev *pdev, const struct pci_device_id *pent) { struct nvkm_device *device;
struct apertures_struct *aper;
bool boot = false; int ret;
if (vga_switcheroo_client_probe_defer(pdev))
@@ -326,34 +325,12 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
nvkm_device_del(&device);
- /* Remove conflicting drivers (vesafb, efifb etc). */
- aper = alloc_apertures(3);
- if (!aper)
return -ENOMEM;
- aper->ranges[0].base = pci_resource_start(pdev, 1);
- aper->ranges[0].size = pci_resource_len(pdev, 1);
- aper->count = 1;
- if (pci_resource_len(pdev, 2)) {
aper->ranges[aper->count].base = pci_resource_start(pdev, 2);
aper->ranges[aper->count].size = pci_resource_len(pdev, 2);
aper->count++;
- }
- if (pci_resource_len(pdev, 3)) {
aper->ranges[aper->count].base = pci_resource_start(pdev, 3);
aper->ranges[aper->count].size = pci_resource_len(pdev, 3);
aper->count++;
- if (nouveau_modeset != 2) {
ret = sysfb_evict_conflicts_pci(pdev);
if (ret < 0)
}return ret;
-#ifdef CONFIG_X86
- boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
-#endif
- if (nouveau_modeset != 2)
remove_conflicting_framebuffers(aper, "nouveaufb", boot);
- kfree(aper);
- ret = nvkm_device_pci_new(pdev, nouveau_config, nouveau_debug, true, true, ~0ULL, &device); if (ret)
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index c01a7c6..a0c11bd 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -37,6 +37,7 @@ #include <linux/console.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/sysfb.h> #include <linux/vga_switcheroo.h> #include <drm/drm_gem.h>
@@ -309,27 +310,6 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
static struct drm_driver kms_driver;
-static int radeon_kick_out_firmware_fb(struct pci_dev *pdev) -{
- struct apertures_struct *ap;
- bool primary = false;
- ap = alloc_apertures(1);
- if (!ap)
return -ENOMEM;
- ap->ranges[0].base = pci_resource_start(pdev, 0);
- ap->ranges[0].size = pci_resource_len(pdev, 0);
-#ifdef CONFIG_X86
- primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
-#endif
- remove_conflicting_framebuffers(ap, "radeondrmfb", primary);
- kfree(ap);
- return 0;
-}
- static int radeon_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) {
@@ -347,7 +327,7 @@ static int radeon_pci_probe(struct pci_dev *pdev, return -EPROBE_DEFER;
/* Get rid of things like offb */
- ret = radeon_kick_out_firmware_fb(pdev);
- ret = sysfb_evict_conflicts_pci(pdev); if (ret) return ret;
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 7092daa..ac388b5 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -12,6 +12,7 @@
#include <linux/component.h> #include <linux/of_graph.h> +#include <linux/sysfb.h>
#include <drm/drmP.h> #include <drm/drm_crtc_helper.h> @@ -97,28 +98,16 @@ static struct drm_driver sun4i_drv_driver = { .disable_vblank = sun4i_drv_disable_vblank, };
-static void sun4i_remove_framebuffers(void) -{
- struct apertures_struct *ap;
- ap = alloc_apertures(1);
- if (!ap)
return;
- /* The framebuffer can be located anywhere in RAM */
- ap->ranges[0].base = 0;
- ap->ranges[0].size = ~0;
- remove_conflicting_framebuffers(ap, "sun4i-drm-fb", false);
- kfree(ap);
-}
- static int sun4i_drv_bind(struct device *dev) { struct drm_device *drm; struct sun4i_drv *drv; int ret;
- ret = sysfb_evict_conflicts_firmware();
- if (ret < 0)
return ret;
- drm = drm_dev_alloc(&sun4i_drv_driver, dev); if (!drm) return -ENOMEM;
@@ -156,9 +145,6 @@ static int sun4i_drv_bind(struct device *dev) } drm->irq_enabled = true;
- /* Remove early framebuffers (ie. simplefb) */
- sun4i_remove_framebuffers();
- /* Create our framebuffer */ drv->fbdev = sun4i_framebuffer_init(drm); if (IS_ERR(drv->fbdev)) {
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 8b42d31..679e65a 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -15,6 +15,7 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/sysfb.h> #include "drm_fb_cma_helper.h"
#include "uapi/drm/vc4_drm.h" @@ -200,24 +201,6 @@ static void vc4_match_add_drivers(struct device *dev, } }
-static void vc4_kick_out_firmware_fb(void) -{
- struct apertures_struct *ap;
- ap = alloc_apertures(1);
- if (!ap)
return;
- /* Since VC4 is a UMA device, the simplefb node may have been
* located anywhere in memory.
*/
- ap->ranges[0].base = 0;
- ap->ranges[0].size = ~0;
- remove_conflicting_framebuffers(ap, "vc4drmfb", false);
- kfree(ap);
-}
- static int vc4_drm_bind(struct device *dev) { struct platform_device *pdev = to_platform_device(dev);
@@ -225,6 +208,10 @@ static int vc4_drm_bind(struct device *dev) struct vc4_dev *vc4; int ret = 0;
ret = sysfb_evict_conflicts_firmware();
if (ret < 0)
return ret;
dev->coherent_dma_mask = DMA_BIT_MASK(32);
vc4 = devm_kzalloc(dev, sizeof(*vc4), GFP_KERNEL);
@@ -248,8 +235,6 @@ static int vc4_drm_bind(struct device *dev) if (ret) goto gem_destroy;
- vc4_kick_out_firmware_fb();
- ret = drm_dev_register(drm, 0); if (ret < 0) goto unbind_all;
diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c index 7f0e93f87..d6a8a94 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c @@ -24,29 +24,10 @@ */
#include <linux/pci.h> +#include <linux/sysfb.h>
#include "virtgpu_drv.h"
-static void virtio_pci_kick_out_firmware_fb(struct pci_dev *pci_dev) -{
- struct apertures_struct *ap;
- bool primary;
- ap = alloc_apertures(1);
- if (!ap)
return;
- ap->ranges[0].base = pci_resource_start(pci_dev, 0);
- ap->ranges[0].size = pci_resource_len(pci_dev, 0);
- primary = pci_dev->resource[PCI_ROM_RESOURCE].flags
& IORESOURCE_ROM_SHADOW;
- remove_conflicting_framebuffers(ap, "virtiodrmfb", primary);
- kfree(ap);
-}
- int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) { struct drm_device *dev;
@@ -65,8 +46,7 @@ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) DRM_INFO("pci: %s detected\n", vga ? "virtio-vga" : "virtio-gpu-pci"); dev->pdev = pdev;
if (vga)
virtio_pci_kick_out_firmware_fb(pdev);
sysfb_evict_conflicts_pci(pdev);
}
ret = drm_dev_register(dev, 0);
The SimpleDRM driver binds to simple-framebuffer devices and provides a DRM/KMS API. It provides only a single CRTC+encoder+connector combination plus one initial mode.
Userspace can create dumb-buffers which can be blit into the real framebuffer similar to UDL. No access to the real framebuffer is allowed (compared to earlier version of this driver) to avoid security issues. Furthermore, this way we can support arbitrary modes as long as we have a conversion-helper.
Signed-off-by: David Herrmann dh.herrmann@gmail.com --- MAINTAINERS | 6 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/simpledrm/Kconfig | 19 ++ drivers/gpu/drm/simpledrm/Makefile | 8 + drivers/gpu/drm/simpledrm/simpledrm.h | 83 +++++ drivers/gpu/drm/simpledrm/simpledrm_damage.c | 194 +++++++++++ drivers/gpu/drm/simpledrm/simpledrm_drv.c | 464 +++++++++++++++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_gem.c | 109 +++++++ drivers/gpu/drm/simpledrm/simpledrm_kms.c | 263 +++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_of.c | 265 +++++++++++++++ 11 files changed, 1414 insertions(+) create mode 100644 drivers/gpu/drm/simpledrm/Kconfig create mode 100644 drivers/gpu/drm/simpledrm/Makefile create mode 100644 drivers/gpu/drm/simpledrm/simpledrm.h create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_damage.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_drv.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_gem.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_kms.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_of.c
diff --git a/MAINTAINERS b/MAINTAINERS index 0bbe4b1..408863d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4140,6 +4140,12 @@ S: Orphan / Obsolete F: drivers/gpu/drm/savage/ F: include/uapi/drm/savage_drm.h
+DRM DRIVER FOR SIMPLE FRAMEBUFFER DEVICES +M: David Herrmann dh.herrmann@gmail.com +L: dri-devel@lists.freedesktop.org +S: Maintained +F: drivers/gpu/drm/simpledrm/ + DRM DRIVER FOR SIS VIDEO CARDS S: Orphan / Obsolete F: drivers/gpu/drm/sis/ diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index f27f9b5..61cbcd1 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -291,3 +291,5 @@ source "drivers/gpu/drm/arc/Kconfig" source "drivers/gpu/drm/hisilicon/Kconfig"
source "drivers/gpu/drm/mediatek/Kconfig" + +source "drivers/gpu/drm/simpledrm/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 0238bf8..3e6fe99 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -83,3 +83,4 @@ obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/ obj-$(CONFIG_DRM_ARCPGU)+= arc/ obj-y += hisilicon/ +obj-y += simpledrm/ diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig new file mode 100644 index 0000000..f45b25d --- /dev/null +++ b/drivers/gpu/drm/simpledrm/Kconfig @@ -0,0 +1,19 @@ +config DRM_SIMPLEDRM + tristate "Simple firmware framebuffer DRM driver" + depends on DRM + select DRM_KMS_HELPER + help + SimpleDRM can run on all systems with pre-initialized graphics + hardware. It uses a framebuffer that was initialized during + firmware boot. No page-flipping, modesetting or other advanced + features are available. However, other DRM drivers can be loaded + later and take over from SimpleDRM if they provide real hardware + support. + + SimpleDRM supports "simple-framebuffer" DeviceTree objects and + compatible platform framebuffers. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called simpledrm. diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile new file mode 100644 index 0000000..d7b179d --- /dev/null +++ b/drivers/gpu/drm/simpledrm/Makefile @@ -0,0 +1,8 @@ +simpledrm-y := \ + simpledrm_damage.o \ + simpledrm_drv.o \ + simpledrm_gem.o \ + simpledrm_kms.o \ + simpledrm_of.o + +obj-$(CONFIG_DRM_SIMPLEDRM) := simpledrm.o diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h new file mode 100644 index 0000000..ed6d725 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm.h @@ -0,0 +1,83 @@ +#ifndef __SDRM_SIMPLEDRM_H +#define __SDRM_SIMPLEDRM_H + +/* + * Copyright (C) 2012-2016 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + */ + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_gem.h> +#include <drm/drm_simple_kms_helper.h> +#include <linux/atomic.h> +#include <linux/kernel.h> +#include <linux/mutex.h> + +struct clk; +struct regulator; +struct simplefb_format; + +struct sdrm_hw { + struct mutex lock; + u32 width; + u32 height; + u32 stride; + u32 bpp; + u32 format; + unsigned long base; + unsigned long size; + void *map; +}; + +struct sdrm_bo { + struct drm_gem_object base; + struct page **pages; + void *vmapping; +}; + +struct sdrm_fb { + struct drm_framebuffer base; + struct sdrm_bo *bo; +}; + +struct sdrm_device { + atomic_t n_used; + struct drm_device *ddev; + struct sdrm_hw *hw; + + size_t n_clks; + size_t n_regulators; + struct clk **clks; + struct regulator **regulators; + + struct drm_simple_display_pipe pipe; + struct drm_connector conn; +}; + +void sdrm_of_bootstrap(void); +int sdrm_of_bind(struct sdrm_device *sdrm); +void sdrm_of_unbind(struct sdrm_device *sdrm); + +int sdrm_kms_bind(struct sdrm_device *sdrm); +void sdrm_kms_unbind(struct sdrm_device *sdrm); + +void sdrm_dirty(struct sdrm_fb *fb, u32 x, u32 y, u32 width, u32 height); + +struct sdrm_bo *sdrm_bo_new(struct drm_device *ddev, size_t size); +void sdrm_bo_free(struct drm_gem_object *obj); +int sdrm_bo_vmap(struct sdrm_bo *bo); + +int sdrm_dumb_create(struct drm_file *dfile, + struct drm_device *ddev, + struct drm_mode_create_dumb *arg); +int sdrm_dumb_map_offset(struct drm_file *dfile, + struct drm_device *ddev, + uint32_t handle, + uint64_t *offset); + +#endif /* __SDRM_SIMPLEDRM_H */ diff --git a/drivers/gpu/drm/simpledrm/simpledrm_damage.c b/drivers/gpu/drm/simpledrm/simpledrm_damage.c new file mode 100644 index 0000000..4f7af5d --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_damage.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2012-2016 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <asm/unaligned.h> +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <linux/kernel.h> +#include "simpledrm.h" + +static inline void sdrm_put(u8 *dst, u32 four_cc, u16 r, u16 g, u16 b) +{ + switch (four_cc) { + case DRM_FORMAT_RGB565: + r >>= 11; + g >>= 10; + b >>= 11; + put_unaligned((u16)((r << 11) | (g << 5) | b), (u16 *)dst); + break; + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_ARGB1555: + r >>= 11; + g >>= 11; + b >>= 11; + put_unaligned((u16)((r << 10) | (g << 5) | b), (u16 *)dst); + break; + case DRM_FORMAT_RGB888: + r >>= 8; + g >>= 8; + b >>= 8; +#ifdef __LITTLE_ENDIAN + dst[2] = r; + dst[1] = g; + dst[0] = b; +#elif defined(__BIG_ENDIAN) + dst[0] = r; + dst[1] = g; + dst[2] = b; +#endif + break; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + r >>= 8; + g >>= 8; + b >>= 8; + put_unaligned((u32)((r << 16) | (g << 8) | b), (u32 *)dst); + break; + case DRM_FORMAT_ABGR8888: + r >>= 8; + g >>= 8; + b >>= 8; + put_unaligned((u32)((b << 16) | (g << 8) | r), (u32 *)dst); + break; + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ARGB2101010: + r >>= 4; + g >>= 4; + b >>= 4; + put_unaligned((u32)((r << 20) | (g << 10) | b), (u32 *)dst); + break; + } +} + +static void sdrm_blit_from_xrgb8888(const u8 *src, + u32 src_stride, + u32 src_bpp, + u8 *dst, + u32 dst_stride, + u32 dst_bpp, + u32 dst_four_cc, + u32 width, + u32 height) +{ + u32 val, i; + + while (height--) { + for (i = 0; i < width; ++i) { + val = get_unaligned((const u32 *)&src[i * src_bpp]); + sdrm_put(&dst[i * dst_bpp], dst_four_cc, + (val & 0x00ff0000U) >> 8, + (val & 0x0000ff00U), + (val & 0x000000ffU) << 8); + } + + src += src_stride; + dst += dst_stride; + } +} + +static void sdrm_blit_from_rgb565(const u8 *src, + u32 src_stride, + u32 src_bpp, + u8 *dst, + u32 dst_stride, + u32 dst_bpp, + u32 dst_four_cc, + u32 width, + u32 height) +{ + u32 val, i; + + while (height--) { + for (i = 0; i < width; ++i) { + val = get_unaligned((const u16 *)&src[i * src_bpp]); + sdrm_put(&dst[i * dst_bpp], dst_four_cc, + (val & 0xf800), + (val & 0x07e0) << 5, + (val & 0x001f) << 11); + } + + src += src_stride; + dst += dst_stride; + } +} + +static void sdrm_blit_lines(const u8 *src, + u32 src_stride, + u8 *dst, + u32 dst_stride, + u32 bpp, + u32 width, + u32 height) +{ + u32 len; + + len = width * bpp; + + while (height--) { + memcpy(dst, src, len); + src += src_stride; + dst += dst_stride; + } +} + +static void sdrm_blit(struct sdrm_hw *hw, + struct sdrm_fb *fb, + u32 x, + u32 y, + u32 width, + u32 height) +{ + u32 src_bpp, dst_bpp; + u8 *src, *dst; + + src = fb->bo->vmapping; + src_bpp = DIV_ROUND_UP(fb->base.bits_per_pixel, 8); + src += fb->base.offsets[0] + y * fb->base.pitches[0] + x * src_bpp; + + dst = hw->map; + dst_bpp = DIV_ROUND_UP(hw->bpp, 8); + dst += y * hw->stride + x * dst_bpp; + + if (fb->base.pixel_format == hw->format) { + /* if formats are identical, do a line-by-line copy.. */ + sdrm_blit_lines(src, fb->base.pitches[0], + dst, hw->stride, src_bpp, width, height); + } else { + /* ..otherwise call slow blit-function */ + switch (fb->base.pixel_format) { + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + sdrm_blit_from_xrgb8888(src, fb->base.pitches[0], + src_bpp, dst, hw->stride, + dst_bpp, hw->format, + width, height); + break; + case DRM_FORMAT_RGB565: + sdrm_blit_from_rgb565(src, fb->base.pitches[0], + src_bpp, dst, hw->stride, + dst_bpp, hw->format, + width, height); + break; + } + } +} + +void sdrm_dirty(struct sdrm_fb *fb, u32 x, u32 y, u32 width, u32 height) +{ + struct sdrm_device *sdrm = fb->base.dev->dev_private; + + if (WARN_ON(!fb->bo->vmapping)) + return; + + mutex_lock(&sdrm->hw->lock); + if (sdrm->hw->map) + sdrm_blit(sdrm->hw, fb, x, y, width, height); + mutex_unlock(&sdrm->hw->lock); +} diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c new file mode 100644 index 0000000..d569120 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c @@ -0,0 +1,464 @@ +/* + * Copyright (C) 2012-2016 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <drm/drmP.h> +#include <linux/atomic.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_data/simplefb.h> +#include <linux/string.h> +#include "simpledrm.h" + +static struct drm_driver sdrm_drm_driver; +static DEFINE_MUTEX(sdrm_lock); + +static int sdrm_hw_identify(struct platform_device *pdev, + struct simplefb_platform_data *modep, + struct simplefb_format *formatp, + struct resource **memp, + u32 *bppp) +{ + static const struct simplefb_format valid_formats[] = SIMPLEFB_FORMATS; + struct simplefb_platform_data pm = {}, *mode = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; + const struct simplefb_format *format = NULL; + struct resource *mem; + unsigned int depth; + int r, bpp; + size_t i; + + if (!mode) { + if (!np) + return -ENODEV; + + mode = ± + + r = of_property_read_u32(np, "width", &mode->width); + if (r >= 0) + r = of_property_read_u32(np, "height", &mode->height); + if (r >= 0) + r = of_property_read_u32(np, "stride", &mode->stride); + if (r >= 0) + r = of_property_read_string(np, "format", + &mode->format); + if (r < 0) + return r; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(valid_formats); ++i) { + if (!strcmp(mode->format, valid_formats[i].name)) { + format = &valid_formats[i]; + break; + } + } + + if (!format) + return -ENODEV; + + switch (format->fourcc) { + case DRM_FORMAT_RGB565: + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ARGB2101010: + /* + * You must adjust sdrm_put() whenever you add a new format + * here, otherwise, blitting operations will not work. + * Furthermore, include/linux/platform_data/simplefb.h needs + * to be adjusted so the platform-device actually allows this + * format. + */ + break; + default: + return -ENODEV; + } + + drm_fb_get_bpp_depth(format->fourcc, &depth, &bpp); + if (!bpp) + return -ENODEV; + if (resource_size(mem) < mode->stride * mode->height) + return -ENODEV; + if ((bpp + 7) / 8 * mode->width > mode->stride) + return -ENODEV; + + *modep = *mode; + *formatp = *format; + *memp = mem; + *bppp = bpp; + return 0; +} + +static struct sdrm_hw *sdrm_hw_new(const struct simplefb_platform_data *mode, + const struct simplefb_format *format, + const struct resource *mem, + u32 bpp) +{ + struct sdrm_hw *hw; + + hw = kzalloc(sizeof(*hw), GFP_KERNEL); + if (!hw) + return ERR_PTR(-ENOMEM); + + mutex_init(&hw->lock); + hw->width = mode->width; + hw->height = mode->height; + hw->stride = mode->stride; + hw->bpp = bpp; + hw->format = format->fourcc; + hw->base = mem->start; + hw->size = resource_size(mem); + + return hw; +} + +static struct sdrm_hw *sdrm_hw_free(struct sdrm_hw *hw) +{ + if (!hw) + return NULL; + + WARN_ON(hw->map); + mutex_destroy(&hw->lock); + kfree(hw); + + return NULL; +} + +static int sdrm_hw_bind(struct sdrm_hw *hw) +{ + mutex_lock(&hw->lock); + if (!hw->map) + hw->map = ioremap_wc(hw->base, hw->size); + mutex_unlock(&hw->lock); + + return hw->map ? 0 : -EIO; +} + +static void sdrm_hw_unbind(struct sdrm_hw *hw) +{ + if (!hw) + return; + + mutex_lock(&hw->lock); + if (hw->map) { + iounmap(hw->map); + hw->map = NULL; + } + mutex_unlock(&hw->lock); +} + +static struct sdrm_device *sdrm_device_free(struct sdrm_device *sdrm) +{ + if (!sdrm) + return NULL; + + WARN_ON(atomic_read(&sdrm->n_used) != INT_MIN); + sdrm->hw = sdrm_hw_free(sdrm->hw); + drm_dev_unref(sdrm->ddev); + kfree(sdrm); + + return NULL; +} + +static struct sdrm_device *sdrm_device_new(struct platform_device *pdev, + struct sdrm_hw *hw) +{ + struct sdrm_device *sdrm; + int r; + + sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL); + if (!sdrm) + return ERR_PTR(-ENOMEM); + + atomic_set(&sdrm->n_used, INT_MIN); + + sdrm->ddev = drm_dev_alloc(&sdrm_drm_driver, &pdev->dev); + if (!sdrm->ddev) { + r = -ENOMEM; + goto error; + } + + sdrm->ddev->dev_private = sdrm; + sdrm->hw = hw; + + return sdrm; + +error: + sdrm_device_free(sdrm); + return ERR_PTR(r); +} + +static void sdrm_device_unbind(struct sdrm_device *sdrm) +{ + if (sdrm) { + sdrm_kms_unbind(sdrm); + sdrm_hw_unbind(sdrm->hw); + sdrm_of_unbind(sdrm); + } +} + +static int sdrm_device_bind(struct sdrm_device *sdrm) +{ + int r; + + r = sdrm_of_bind(sdrm); + if (r < 0) + goto error; + + r = sdrm_hw_bind(sdrm->hw); + if (r < 0) + goto error; + + r = sdrm_kms_bind(sdrm); + if (r < 0) + goto error; + + return 0; + +error: + sdrm_device_unbind(sdrm); + return r; +} + +static int sdrm_device_acquire(struct sdrm_device *sdrm) +{ + return (sdrm && atomic_inc_unless_negative(&sdrm->n_used)) + ? 0 : -ENODEV; +} + +static void sdrm_device_release(struct sdrm_device *sdrm) +{ + if (sdrm && atomic_dec_return(&sdrm->n_used) == INT_MIN) { + sdrm_device_unbind(sdrm); + sdrm_device_free(sdrm); + } +} + +static int sdrm_fop_open(struct inode *inode, struct file *file) +{ + struct drm_device *ddev; + int r; + + mutex_lock(&sdrm_lock); + r = drm_open(inode, file); + if (r >= 0) { + ddev = file->private_data; + r = sdrm_device_acquire(ddev->dev_private); + if (r < 0) + drm_release(inode, file); + } + mutex_unlock(&sdrm_lock); + + return r; +} + +static int sdrm_fop_release(struct inode *inode, struct file *file) +{ + struct drm_file *dfile = file->private_data; + struct drm_device *ddev = dfile->minor->dev; + struct sdrm_device *sdrm = ddev->dev_private; + int res; + + res = drm_release(inode, file); + sdrm_device_release(sdrm); + return res; +} + +static int sdrm_fop_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct drm_file *dfile = file->private_data; + struct drm_device *dev = dfile->minor->dev; + struct drm_gem_object *obj = NULL; + struct drm_vma_offset_node *node; + int r; + + drm_vma_offset_lock_lookup(dev->vma_offset_manager); + node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager, + vma->vm_pgoff, + vma_pages(vma)); + if (likely(node)) { + obj = container_of(node, struct drm_gem_object, vma_node); + if (!kref_get_unless_zero(&obj->refcount)) + obj = NULL; + } + drm_vma_offset_unlock_lookup(dev->vma_offset_manager); + + if (!obj) + return -EINVAL; + + if (!drm_vma_node_is_allowed(node, dfile)) { + drm_gem_object_unreference_unlocked(obj); + return -EACCES; + } + + if (vma->vm_file) + fput(vma->vm_file); + vma->vm_file = get_file(obj->filp); + vma->vm_pgoff = 0; + + r = obj->filp->f_op->mmap(obj->filp, vma); + drm_gem_object_unreference_unlocked(obj); + return r; +} + +static int sdrm_simplefb_probe(struct platform_device *pdev) +{ + struct simplefb_platform_data hw_mode; + struct simplefb_format hw_format; + struct sdrm_device *sdrm = NULL; + struct sdrm_hw *hw = NULL; + struct resource *hw_mem; + u32 hw_bpp; + int r; + + r = sdrm_hw_identify(pdev, &hw_mode, &hw_format, &hw_mem, &hw_bpp); + if (r < 0) + goto error; + + hw = sdrm_hw_new(&hw_mode, &hw_format, hw_mem, hw_bpp); + if (IS_ERR(hw)) { + r = PTR_ERR(hw); + hw = NULL; + goto error; + } + + sdrm = sdrm_device_new(pdev, hw); + if (IS_ERR(sdrm)) { + r = PTR_ERR(sdrm); + sdrm = NULL; + goto error; + } + hw = NULL; + platform_set_drvdata(pdev, sdrm); + + r = sdrm_device_bind(sdrm); + if (r < 0) + goto error; + + /* mark device as enabled and acquire bus ref */ + WARN_ON(atomic_read(&sdrm->n_used) != INT_MIN); + atomic_set(&sdrm->n_used, 1); + + r = drm_dev_register(sdrm->ddev, 0); + if (r < 0) { + /* mark device as disabled and drop bus ref */ + WARN_ON(atomic_add_return(INT_MIN, &sdrm->n_used) != INT_MIN); + sdrm_device_release(sdrm); + return r; + } + + dev_info(sdrm->ddev->dev, "initialized %s on minor %d\n", + sdrm->ddev->driver->name, sdrm->ddev->primary->index); + + return 0; + +error: + sdrm_device_unbind(sdrm); + sdrm_device_free(sdrm); + sdrm_hw_free(hw); + return r; +} + +static int sdrm_simplefb_remove(struct platform_device *pdev) +{ + struct sdrm_device *sdrm = platform_get_drvdata(pdev); + + /* mark device as disabled */ + atomic_add(INT_MIN, &sdrm->n_used); + sdrm_hw_unbind(sdrm->hw); + + mutex_lock(&sdrm_lock); + drm_dev_unregister(sdrm->ddev); + sdrm_device_release(sdrm); + mutex_unlock(&sdrm_lock); + + return 0; +} + +static const struct file_operations sdrm_drm_fops = { + .owner = THIS_MODULE, + .open = sdrm_fop_open, + .release = sdrm_fop_release, + .mmap = sdrm_fop_mmap, + .poll = drm_poll, + .read = drm_read, + .unlocked_ioctl = drm_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .llseek = noop_llseek, +}; + +static struct drm_driver sdrm_drm_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops = &sdrm_drm_fops, + + .gem_free_object = sdrm_bo_free, + + .dumb_create = sdrm_dumb_create, + .dumb_map_offset = sdrm_dumb_map_offset, + .dumb_destroy = drm_gem_dumb_destroy, + + .name = "simpledrm", + .desc = "Simple firmware framebuffer DRM driver", + .date = "20160901", +}; + +static const struct of_device_id sdrm_simplefb_of_match[] = { + { .compatible = "simple-framebuffer", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sdrm_simplefb_of_match); + +static struct platform_driver sdrm_simplefb_driver = { + .probe = sdrm_simplefb_probe, + .remove = sdrm_simplefb_remove, + .driver = { + .name = "simple-framebuffer", + .mod_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .of_match_table = sdrm_simplefb_of_match, + }, +}; + +static int __init sdrm_init(void) +{ + int r; + + r = platform_driver_register(&sdrm_simplefb_driver); + if (r < 0) + return r; + + sdrm_of_bootstrap(); + return 0; +} + +static void __exit sdrm_exit(void) +{ + platform_driver_unregister(&sdrm_simplefb_driver); +} + +module_init(sdrm_init); +module_exit(sdrm_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Simple firmware framebuffer DRM driver"); +MODULE_ALIAS("platform:simple-framebuffer"); diff --git a/drivers/gpu/drm/simpledrm/simpledrm_gem.c b/drivers/gpu/drm/simpledrm/simpledrm_gem.c new file mode 100644 index 0000000..4aaae6e --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012-2016 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <drm/drmP.h> +#include <drm/drm_gem.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include "simpledrm.h" + +struct sdrm_bo *sdrm_bo_new(struct drm_device *ddev, size_t size) +{ + struct sdrm_bo *bo; + + WARN_ON(!size || (size & ~PAGE_MASK) != 0); + + bo = kzalloc(sizeof(*bo), GFP_KERNEL); + if (!bo) + return NULL; + + if (drm_gem_object_init(ddev, &bo->base, size)) { + kfree(bo); + return NULL; + } + + return bo; +} + +void sdrm_bo_free(struct drm_gem_object *dobj) +{ + struct sdrm_bo *bo = container_of(dobj, struct sdrm_bo, base); + + if (bo->vmapping) + vunmap(bo->vmapping); + if (bo->pages) + drm_gem_put_pages(dobj, bo->pages, false, false); + drm_gem_object_release(dobj); + kfree(bo); +} + +int sdrm_bo_vmap(struct sdrm_bo *bo) +{ + int r; + + if (!bo->pages) { + bo->pages = drm_gem_get_pages(&bo->base); + if (IS_ERR(bo->pages)) { + r = PTR_ERR(bo->pages); + bo->pages = NULL; + return r; + } + } + + if (!bo->vmapping) { + bo->vmapping = vmap(bo->pages, bo->base.size / PAGE_SIZE, 0, + PAGE_KERNEL); + if (!bo->vmapping) + return -ENOMEM; + } + + return 0; +} + +int sdrm_dumb_create(struct drm_file *dfile, + struct drm_device *ddev, + struct drm_mode_create_dumb *args) +{ + struct sdrm_bo *bo; + int r; + + /* overflow checks are done by DRM core */ + args->pitch = (args->bpp + 7) / 8 * args->width; + args->size = PAGE_ALIGN(args->pitch * args->height); + + bo = sdrm_bo_new(ddev, args->size); + if (!bo) + return -ENOMEM; + + r = drm_gem_handle_create(dfile, &bo->base, &args->handle); + drm_gem_object_unreference_unlocked(&bo->base); + return r; +} + +int sdrm_dumb_map_offset(struct drm_file *dfile, + struct drm_device *ddev, + uint32_t handle, + uint64_t *offset) +{ + struct drm_gem_object *dobj; + int r; + + dobj = drm_gem_object_lookup(dfile, handle); + if (!dobj) + return -ENOENT; + + r = drm_gem_create_mmap_offset(dobj); + if (r >= 0) + *offset = drm_vma_node_offset_addr(&dobj->vma_node); + drm_gem_object_unreference_unlocked(dobj); + return r; +} diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c new file mode 100644 index 0000000..00101c9 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2012-2016 Red Hat, Inc. + * Copyright (C) 2016 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_gem.h> +#include <drm/drm_simple_kms_helper.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include "simpledrm.h" + +static const uint32_t sdrm_formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XRGB8888, +}; + +static int sdrm_conn_get_modes(struct drm_connector *conn) +{ + struct sdrm_device *sdrm = conn->dev->dev_private; + struct drm_display_mode *mode; + + mode = drm_cvt_mode(sdrm->ddev, sdrm->hw->width, sdrm->hw->height, + 60, false, false, false); + if (!mode) + return 0; + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_set_name(mode); + drm_mode_probed_add(conn, mode); + + return 1; +} + +static const struct drm_connector_helper_funcs sdrm_conn_hfuncs = { + .get_modes = sdrm_conn_get_modes, + .best_encoder = drm_atomic_helper_best_encoder, +}; + +static enum drm_connector_status sdrm_conn_detect(struct drm_connector *conn, + bool force) +{ + /* + * We simulate an always connected monitor. simple-fb doesn't + * provide any way to detect whether the connector is active. Hence, + * signal DRM core that it is always connected. + */ + return connector_status_connected; +} + +static const struct drm_connector_funcs sdrm_conn_ops = { + .dpms = drm_atomic_helper_connector_dpms, + .reset = drm_atomic_helper_connector_reset, + .detect = sdrm_conn_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static void sdrm_crtc_send_vblank_event(struct drm_crtc *crtc) +{ + if (crtc->state && crtc->state->event) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + crtc->state->event = NULL; + } +} + +void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state) +{ + struct drm_framebuffer *dfb = pipe->plane.state->fb; + struct sdrm_fb *fb; + + sdrm_crtc_send_vblank_event(&pipe->crtc); + + if (dfb) { + fb = container_of(dfb, struct sdrm_fb, base); + pipe->plane.fb = dfb; + sdrm_dirty(fb, 0, 0, dfb->width, dfb->height); + } +} + +static void sdrm_display_pipe_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state) +{ + sdrm_crtc_send_vblank_event(&pipe->crtc); +} + +static void sdrm_display_pipe_disable(struct drm_simple_display_pipe *pipe) +{ + sdrm_crtc_send_vblank_event(&pipe->crtc); +} + +static const struct drm_simple_display_pipe_funcs sdrm_pipe_funcs = { + .update = sdrm_display_pipe_update, + .enable = sdrm_display_pipe_enable, + .disable = sdrm_display_pipe_disable, +}; + +static int sdrm_fb_create_handle(struct drm_framebuffer *dfb, + struct drm_file *dfile, + unsigned int *handle) +{ + struct sdrm_fb *fb = container_of(dfb, struct sdrm_fb, base); + + return drm_gem_handle_create(dfile, &fb->bo->base, handle); +} + +static int sdrm_fb_dirty(struct drm_framebuffer *dfb, + struct drm_file *dfile, + unsigned int flags, + unsigned int color, + struct drm_clip_rect *clips, + unsigned int n_clips) +{ + struct sdrm_fb *fb = container_of(dfb, struct sdrm_fb, base); + struct sdrm_device *sdrm = dfb->dev->dev_private; + unsigned int i; + + drm_modeset_lock_all(sdrm->ddev); + if (dfb == sdrm->pipe.plane.fb) { + if (!clips || !n_clips) { + sdrm_dirty(fb, 0, 0, dfb->width, dfb->height); + } else { + for (i = 0; i < n_clips; i++) { + if (clips[i].x1 > clips[i].x2 || + clips[i].x2 > dfb->width || + clips[i].y1 > clips[i].y2 || + clips[i].y2 > dfb->height) + continue; + + sdrm_dirty(fb, clips[i].x1, clips[i].y1, + clips[i].x2 - clips[i].x1, + clips[i].y2 - clips[i].y1); + } + } + } + drm_modeset_unlock_all(sdrm->ddev); + + return 0; +} + +static void sdrm_fb_destroy(struct drm_framebuffer *dfb) +{ + struct sdrm_fb *fb = container_of(dfb, struct sdrm_fb, base); + + drm_framebuffer_cleanup(dfb); + drm_gem_object_unreference_unlocked(&fb->bo->base); + kfree(fb); +} + +static const struct drm_framebuffer_funcs sdrm_fb_ops = { + .create_handle = sdrm_fb_create_handle, + .dirty = sdrm_fb_dirty, + .destroy = sdrm_fb_destroy, +}; + +static struct drm_framebuffer * +sdrm_fb_create(struct drm_device *ddev, + struct drm_file *dfile, + const struct drm_mode_fb_cmd2 *cmd) +{ + struct drm_gem_object *dobj; + struct sdrm_fb *fb = NULL; + struct sdrm_bo *bo; + int r; + + if (cmd->flags) + return ERR_PTR(-EINVAL); + + dobj = drm_gem_object_lookup(dfile, cmd->handles[0]); + if (!dobj) + return ERR_PTR(-EINVAL); + + bo = container_of(dobj, struct sdrm_bo, base); + + r = sdrm_bo_vmap(bo); + if (r < 0) + goto error; + + fb = kzalloc(sizeof(*fb), GFP_KERNEL); + if (!fb) { + r = -ENOMEM; + goto error; + } + + fb->bo = bo; + drm_helper_mode_fill_fb_struct(&fb->base, cmd); + + r = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops); + if (r < 0) + goto error; + + DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id, + drm_get_format_name(fb->base.pixel_format)); + + return &fb->base; + +error: + kfree(fb); + drm_gem_object_unreference_unlocked(dobj); + return ERR_PTR(r); +} + +static const struct drm_mode_config_funcs sdrm_mode_config_ops = { + .fb_create = sdrm_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +int sdrm_kms_bind(struct sdrm_device *sdrm) +{ + struct drm_connector *conn = &sdrm->conn; + struct drm_device *ddev = sdrm->ddev; + int r; + + drm_mode_config_init(ddev); + ddev->mode_config.min_width = sdrm->hw->width; + ddev->mode_config.max_width = sdrm->hw->width; + ddev->mode_config.min_height = sdrm->hw->height; + ddev->mode_config.max_height = sdrm->hw->height; + ddev->mode_config.preferred_depth = sdrm->hw->bpp; + ddev->mode_config.funcs = &sdrm_mode_config_ops; + drm_connector_helper_add(conn, &sdrm_conn_hfuncs); + + r = drm_connector_init(ddev, conn, &sdrm_conn_ops, + DRM_MODE_CONNECTOR_VIRTUAL); + if (r < 0) + goto error; + + r = drm_simple_display_pipe_init(ddev, &sdrm->pipe, &sdrm_pipe_funcs, + sdrm_formats, + ARRAY_SIZE(sdrm_formats), conn); + if (r < 0) + goto error; + + drm_mode_config_reset(ddev); + return 0; + +error: + drm_mode_config_cleanup(ddev); + return r; +} + +void sdrm_kms_unbind(struct sdrm_device *sdrm) +{ + if (sdrm->ddev->mode_config.funcs) + drm_mode_config_cleanup(sdrm->ddev); +} diff --git a/drivers/gpu/drm/simpledrm/simpledrm_of.c b/drivers/gpu/drm/simpledrm/simpledrm_of.c new file mode 100644 index 0000000..5620000 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_of.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2012-2016 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <drm/drmP.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include "simpledrm.h" + +#ifdef CONFIG_COMMON_CLK + +static int sdrm_of_bind_clocks(struct sdrm_device *sdrm, + struct device_node *np) +{ + struct clk *clock; + size_t i, n; + int r; + + n = of_clk_get_parent_count(np); + if (n < 1) + return 0; + + sdrm->clks = kcalloc(n, sizeof(*sdrm->clks), GFP_KERNEL); + if (!sdrm->clks) + return -ENOMEM; + + for (i = 0; i < n; ++i) { + clock = of_clk_get(np, i); + if (!IS_ERR(clock)) { + sdrm->clks[sdrm->n_clks++] = clock; + } else if (PTR_ERR(clock) == -EPROBE_DEFER) { + r = -EPROBE_DEFER; + goto error; + } else { + dev_err(sdrm->ddev->dev, "cannot find clock %zu: %ld\n", + i, PTR_ERR(clock)); + } + } + + for (i = 0; i < sdrm->n_clks; ++i) { + if (!sdrm->clks[i]) + continue; + + r = clk_prepare_enable(sdrm->clks[i]); + if (r < 0) { + dev_err(sdrm->ddev->dev, + "cannot find clock %zu: %d\n", i, r); + clk_put(sdrm->clks[i]); + sdrm->clks[i] = NULL; + } + } + + return 0; + +error: + for (i = 0; i < sdrm->n_clks; ++i) + clk_put(sdrm->clks[i]); + kfree(sdrm->clks); + sdrm->clks = NULL; + sdrm->n_clks = 0; + return r; +} + +static void sdrm_of_unbind_clocks(struct sdrm_device *sdrm) +{ + size_t i; + + for (i = 0; i < sdrm->n_clks; ++i) { + clk_disable_unprepare(sdrm->clks[i]); + clk_put(sdrm->clks[i]); + } + + kfree(sdrm->clks); + sdrm->clks = NULL; + sdrm->n_clks = 0; +} + +#else /* CONFIG_COMMON_CLK */ + +static int sdrm_of_bind_clocks(struct sdrm_device *sdrm, + struct device_node *np) +{ + return 0; +} + +static void sdrm_of_unbind_clocks(struct sdrm_device *sdrm) +{ +} + +#endif /* CONFIG_COMMON_CLK */ + +#ifdef CONFIG_REGULATOR + +static int sdrm_of_bind_regulators(struct sdrm_device *sdrm, + struct device_node *np) +{ + struct regulator *regulator; + struct property *prop; + char *p, *name; + size_t i, n; + int r; + + n = 0; + for_each_property_of_node(np, prop) { + p = strstr(prop->name, "-supply"); + if (p && p != prop->name) + ++n; + } + + if (n < 1) + return 0; + + sdrm->regulators = kcalloc(n, sizeof(*sdrm->regulators), GFP_KERNEL); + if (!sdrm->regulators) + return -ENOMEM; + + for_each_property_of_node(np, prop) { + p = strstr(prop->name, "-supply"); + if (!p || p == prop->name) + continue; + + name = kstrndup(prop->name, p - prop->name, GFP_TEMPORARY); + if (!name) + continue; + + regulator = regulator_get_optional(sdrm->ddev->dev, name); + kfree(name); + + if (!IS_ERR(regulator)) { + sdrm->regulators[sdrm->n_regulators++] = regulator; + } else if (PTR_ERR(regulator) == -EPROBE_DEFER) { + r = -EPROBE_DEFER; + goto error; + } else { + dev_warn(sdrm->ddev->dev, + "cannot find regulator %s: %ld\n", + prop->name, PTR_ERR(regulator)); + } + } + + for (i = 0; i < sdrm->n_regulators; ++i) { + if (!sdrm->regulators[i]) + continue; + + r = regulator_enable(sdrm->regulators[i]); + if (r < 0) { + dev_warn(sdrm->ddev->dev, + "cannot enable regulator %zu: %d\n", i, r); + regulator_put(sdrm->regulators[i]); + sdrm->regulators[i] = NULL; + } + } + + return 0; + +error: + for (i = 0; i < sdrm->n_regulators; ++i) + if (sdrm->regulators[i]) + regulator_put(sdrm->regulators[i]); + kfree(sdrm->regulators); + sdrm->regulators = NULL; + sdrm->n_regulators = 0; + return r; +} + +static void sdrm_of_unbind_regulators(struct sdrm_device *sdrm) +{ + size_t i; + + for (i = 0; i < sdrm->n_regulators; ++i) { + if (sdrm->regulators[i]) { + regulator_disable(sdrm->regulators[i]); + regulator_put(sdrm->regulators[i]); + } + } + + kfree(sdrm->regulators); + sdrm->regulators = NULL; + sdrm->n_regulators = 0; +} + +#else /* CONFIG_REGULATORS */ + +static int sdrm_of_bind_regulators(struct sdrm_device *sdrm, + struct device_node *np) +{ + return 0; +} + +static void sdrm_of_unbind_regulators(struct sdrm_device *sdrm) +{ +} + +#endif /* CONFIG_REGULATORS */ + +#ifdef CONFIG_OF + +void sdrm_of_bootstrap(void) +{ +#ifdef CONFIG_OF_ADDRESS + struct device_node *np; + + for_each_compatible_node(np, NULL, "simple-framebuffer") + of_platform_device_create(np, NULL, NULL); +#endif +} + +int sdrm_of_bind(struct sdrm_device *sdrm) +{ + int r; + + if (WARN_ON(sdrm->n_clks > 0 || sdrm->n_regulators > 0)) + return 0; + if (!sdrm->ddev->dev->of_node) + return 0; + + r = sdrm_of_bind_clocks(sdrm, sdrm->ddev->dev->of_node); + if (r < 0) + goto error; + + r = sdrm_of_bind_regulators(sdrm, sdrm->ddev->dev->of_node); + if (r < 0) + goto error; + + return 0; + +error: + sdrm_of_unbind(sdrm); + return r; +} + +void sdrm_of_unbind(struct sdrm_device *sdrm) +{ + sdrm_of_unbind_regulators(sdrm); + sdrm_of_unbind_clocks(sdrm); +} + +#else /* CONFIG_OF */ + +void sdrm_of_bootstrap(void) +{ +} + +int sdrm_of_bind(struct sdrm_device *sdrm) +{ + return 0; +} + +void sdrm_of_unbind(struct sdrm_device *sdrm) +{ +} + +#endif /* CONFIG_OF */
On Fri, Sep 2, 2016 at 10:22 AM, David Herrmann dh.herrmann@gmail.com wrote:
The SimpleDRM driver binds to simple-framebuffer devices and provides a DRM/KMS API. It provides only a single CRTC+encoder+connector combination plus one initial mode.
Userspace can create dumb-buffers which can be blit into the real framebuffer similar to UDL. No access to the real framebuffer is allowed (compared to earlier version of this driver) to avoid security issues. Furthermore, this way we can support arbitrary modes as long as we have a conversion-helper.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
MAINTAINERS | 6 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/simpledrm/Kconfig | 19 ++ drivers/gpu/drm/simpledrm/Makefile | 8 + drivers/gpu/drm/simpledrm/simpledrm.h | 83 +++++ drivers/gpu/drm/simpledrm/simpledrm_damage.c | 194 +++++++++++ drivers/gpu/drm/simpledrm/simpledrm_drv.c | 464 +++++++++++++++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_gem.c | 109 +++++++ drivers/gpu/drm/simpledrm/simpledrm_kms.c | 263 +++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_of.c | 265 +++++++++++++++ 11 files changed, 1414 insertions(+) create mode 100644 drivers/gpu/drm/simpledrm/Kconfig create mode 100644 drivers/gpu/drm/simpledrm/Makefile create mode 100644 drivers/gpu/drm/simpledrm/simpledrm.h create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_damage.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_drv.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_gem.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_kms.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_of.c
diff --git a/MAINTAINERS b/MAINTAINERS index 0bbe4b1..408863d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4140,6 +4140,12 @@ S: Orphan / Obsolete F: drivers/gpu/drm/savage/ F: include/uapi/drm/savage_drm.h
+DRM DRIVER FOR SIMPLE FRAMEBUFFER DEVICES +M: David Herrmann dh.herrmann@gmail.com +L: dri-devel@lists.freedesktop.org +S: Maintained +F: drivers/gpu/drm/simpledrm/
DRM DRIVER FOR SIS VIDEO CARDS S: Orphan / Obsolete F: drivers/gpu/drm/sis/ diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index f27f9b5..61cbcd1 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -291,3 +291,5 @@ source "drivers/gpu/drm/arc/Kconfig" source "drivers/gpu/drm/hisilicon/Kconfig"
source "drivers/gpu/drm/mediatek/Kconfig"
+source "drivers/gpu/drm/simpledrm/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 0238bf8..3e6fe99 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -83,3 +83,4 @@ obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/ obj-$(CONFIG_DRM_ARCPGU)+= arc/ obj-y += hisilicon/ +obj-y += simpledrm/ diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig new file mode 100644 index 0000000..f45b25d --- /dev/null +++ b/drivers/gpu/drm/simpledrm/Kconfig @@ -0,0 +1,19 @@ +config DRM_SIMPLEDRM
tristate "Simple firmware framebuffer DRM driver"
depends on DRM
select DRM_KMS_HELPER
help
SimpleDRM can run on all systems with pre-initialized graphics
hardware. It uses a framebuffer that was initialized during
firmware boot. No page-flipping, modesetting or other advanced
features are available. However, other DRM drivers can be loaded
later and take over from SimpleDRM if they provide real hardware
support.
SimpleDRM supports "simple-framebuffer" DeviceTree objects and
compatible platform framebuffers.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called simpledrm.
diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile new file mode 100644 index 0000000..d7b179d --- /dev/null +++ b/drivers/gpu/drm/simpledrm/Makefile @@ -0,0 +1,8 @@ +simpledrm-y := \
simpledrm_damage.o \
simpledrm_drv.o \
simpledrm_gem.o \
simpledrm_kms.o \
simpledrm_of.o
+obj-$(CONFIG_DRM_SIMPLEDRM) := simpledrm.o diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h new file mode 100644 index 0000000..ed6d725 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm.h @@ -0,0 +1,83 @@ +#ifndef __SDRM_SIMPLEDRM_H +#define __SDRM_SIMPLEDRM_H
+/*
- Copyright (C) 2012-2016 Red Hat, Inc.
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2.1 of the License, or (at your
- option) any later version.
- */
+#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_gem.h> +#include <drm/drm_simple_kms_helper.h> +#include <linux/atomic.h> +#include <linux/kernel.h> +#include <linux/mutex.h>
+struct clk; +struct regulator; +struct simplefb_format;
+struct sdrm_hw {
struct mutex lock;
u32 width;
u32 height;
u32 stride;
u32 bpp;
u32 format;
unsigned long base;
unsigned long size;
void *map;
+};
+struct sdrm_bo {
struct drm_gem_object base;
struct page **pages;
void *vmapping;
+};
+struct sdrm_fb {
struct drm_framebuffer base;
struct sdrm_bo *bo;
+};
+struct sdrm_device {
atomic_t n_used;
struct drm_device *ddev;
struct sdrm_hw *hw;
size_t n_clks;
size_t n_regulators;
struct clk **clks;
struct regulator **regulators;
struct drm_simple_display_pipe pipe;
struct drm_connector conn;
+};
+void sdrm_of_bootstrap(void); +int sdrm_of_bind(struct sdrm_device *sdrm); +void sdrm_of_unbind(struct sdrm_device *sdrm);
+int sdrm_kms_bind(struct sdrm_device *sdrm); +void sdrm_kms_unbind(struct sdrm_device *sdrm);
+void sdrm_dirty(struct sdrm_fb *fb, u32 x, u32 y, u32 width, u32 height);
+struct sdrm_bo *sdrm_bo_new(struct drm_device *ddev, size_t size); +void sdrm_bo_free(struct drm_gem_object *obj); +int sdrm_bo_vmap(struct sdrm_bo *bo);
+int sdrm_dumb_create(struct drm_file *dfile,
struct drm_device *ddev,
struct drm_mode_create_dumb *arg);
+int sdrm_dumb_map_offset(struct drm_file *dfile,
struct drm_device *ddev,
uint32_t handle,
uint64_t *offset);
+#endif /* __SDRM_SIMPLEDRM_H */ diff --git a/drivers/gpu/drm/simpledrm/simpledrm_damage.c b/drivers/gpu/drm/simpledrm/simpledrm_damage.c new file mode 100644 index 0000000..4f7af5d --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_damage.c @@ -0,0 +1,194 @@ +/*
- Copyright (C) 2012-2016 Red Hat, Inc.
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2.1 of the License, or (at your
- option) any later version.
- */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <asm/unaligned.h> +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <linux/kernel.h> +#include "simpledrm.h"
+static inline void sdrm_put(u8 *dst, u32 four_cc, u16 r, u16 g, u16 b) +{
switch (four_cc) {
case DRM_FORMAT_RGB565:
r >>= 11;
g >>= 10;
b >>= 11;
put_unaligned((u16)((r << 11) | (g << 5) | b), (u16 *)dst);
break;
case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_ARGB1555:
r >>= 11;
g >>= 11;
b >>= 11;
put_unaligned((u16)((r << 10) | (g << 5) | b), (u16 *)dst);
break;
case DRM_FORMAT_RGB888:
r >>= 8;
g >>= 8;
b >>= 8;
+#ifdef __LITTLE_ENDIAN
dst[2] = r;
dst[1] = g;
dst[0] = b;
+#elif defined(__BIG_ENDIAN)
dst[0] = r;
dst[1] = g;
dst[2] = b;
+#endif
break;
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_ARGB8888:
r >>= 8;
g >>= 8;
b >>= 8;
put_unaligned((u32)((r << 16) | (g << 8) | b), (u32 *)dst);
break;
case DRM_FORMAT_ABGR8888:
r >>= 8;
g >>= 8;
b >>= 8;
put_unaligned((u32)((b << 16) | (g << 8) | r), (u32 *)dst);
break;
case DRM_FORMAT_XRGB2101010:
case DRM_FORMAT_ARGB2101010:
r >>= 4;
g >>= 4;
b >>= 4;
put_unaligned((u32)((r << 20) | (g << 10) | b), (u32 *)dst);
break;
}
+}
+static void sdrm_blit_from_xrgb8888(const u8 *src,
u32 src_stride,
u32 src_bpp,
u8 *dst,
u32 dst_stride,
u32 dst_bpp,
u32 dst_four_cc,
u32 width,
u32 height)
+{
u32 val, i;
while (height--) {
for (i = 0; i < width; ++i) {
val = get_unaligned((const u32 *)&src[i * src_bpp]);
sdrm_put(&dst[i * dst_bpp], dst_four_cc,
(val & 0x00ff0000U) >> 8,
(val & 0x0000ff00U),
(val & 0x000000ffU) << 8);
}
src += src_stride;
dst += dst_stride;
}
+}
+static void sdrm_blit_from_rgb565(const u8 *src,
u32 src_stride,
u32 src_bpp,
u8 *dst,
u32 dst_stride,
u32 dst_bpp,
u32 dst_four_cc,
u32 width,
u32 height)
+{
u32 val, i;
while (height--) {
for (i = 0; i < width; ++i) {
val = get_unaligned((const u16 *)&src[i * src_bpp]);
sdrm_put(&dst[i * dst_bpp], dst_four_cc,
(val & 0xf800),
(val & 0x07e0) << 5,
(val & 0x001f) << 11);
}
src += src_stride;
dst += dst_stride;
}
+}
+static void sdrm_blit_lines(const u8 *src,
u32 src_stride,
u8 *dst,
u32 dst_stride,
u32 bpp,
u32 width,
u32 height)
+{
u32 len;
len = width * bpp;
while (height--) {
memcpy(dst, src, len);
src += src_stride;
dst += dst_stride;
}
+}
+static void sdrm_blit(struct sdrm_hw *hw,
struct sdrm_fb *fb,
u32 x,
u32 y,
u32 width,
u32 height)
+{
u32 src_bpp, dst_bpp;
u8 *src, *dst;
src = fb->bo->vmapping;
src_bpp = DIV_ROUND_UP(fb->base.bits_per_pixel, 8);
src += fb->base.offsets[0] + y * fb->base.pitches[0] + x * src_bpp;
dst = hw->map;
dst_bpp = DIV_ROUND_UP(hw->bpp, 8);
dst += y * hw->stride + x * dst_bpp;
if (fb->base.pixel_format == hw->format) {
/* if formats are identical, do a line-by-line copy.. */
sdrm_blit_lines(src, fb->base.pitches[0],
dst, hw->stride, src_bpp, width, height);
} else {
/* ..otherwise call slow blit-function */
switch (fb->base.pixel_format) {
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_XRGB8888:
sdrm_blit_from_xrgb8888(src, fb->base.pitches[0],
src_bpp, dst, hw->stride,
dst_bpp, hw->format,
width, height);
break;
case DRM_FORMAT_RGB565:
sdrm_blit_from_rgb565(src, fb->base.pitches[0],
src_bpp, dst, hw->stride,
dst_bpp, hw->format,
width, height);
break;
}
}
+}
+void sdrm_dirty(struct sdrm_fb *fb, u32 x, u32 y, u32 width, u32 height) +{
struct sdrm_device *sdrm = fb->base.dev->dev_private;
if (WARN_ON(!fb->bo->vmapping))
return;
mutex_lock(&sdrm->hw->lock);
if (sdrm->hw->map)
sdrm_blit(sdrm->hw, fb, x, y, width, height);
mutex_unlock(&sdrm->hw->lock);
+} diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c new file mode 100644 index 0000000..d569120 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c @@ -0,0 +1,464 @@ +/*
- Copyright (C) 2012-2016 Red Hat, Inc.
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2.1 of the License, or (at your
- option) any later version.
- */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <drm/drmP.h> +#include <linux/atomic.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_data/simplefb.h> +#include <linux/string.h> +#include "simpledrm.h"
+static struct drm_driver sdrm_drm_driver; +static DEFINE_MUTEX(sdrm_lock);
+static int sdrm_hw_identify(struct platform_device *pdev,
struct simplefb_platform_data *modep,
struct simplefb_format *formatp,
struct resource **memp,
u32 *bppp)
+{
static const struct simplefb_format valid_formats[] = SIMPLEFB_FORMATS;
struct simplefb_platform_data pm = {}, *mode = pdev->dev.platform_data;
struct device_node *np = pdev->dev.of_node;
const struct simplefb_format *format = NULL;
struct resource *mem;
unsigned int depth;
int r, bpp;
size_t i;
if (!mode) {
if (!np)
return -ENODEV;
mode = ±
r = of_property_read_u32(np, "width", &mode->width);
if (r >= 0)
r = of_property_read_u32(np, "height", &mode->height);
if (r >= 0)
r = of_property_read_u32(np, "stride", &mode->stride);
if (r >= 0)
r = of_property_read_string(np, "format",
&mode->format);
if (r < 0)
return r;
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(valid_formats); ++i) {
if (!strcmp(mode->format, valid_formats[i].name)) {
format = &valid_formats[i];
break;
}
}
if (!format)
return -ENODEV;
switch (format->fourcc) {
case DRM_FORMAT_RGB565:
case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_ARGB1555:
case DRM_FORMAT_RGB888:
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_XRGB2101010:
case DRM_FORMAT_ARGB2101010:
/*
* You must adjust sdrm_put() whenever you add a new format
* here, otherwise, blitting operations will not work.
* Furthermore, include/linux/platform_data/simplefb.h needs
* to be adjusted so the platform-device actually allows this
* format.
*/
break;
default:
return -ENODEV;
}
drm_fb_get_bpp_depth(format->fourcc, &depth, &bpp);
if (!bpp)
return -ENODEV;
if (resource_size(mem) < mode->stride * mode->height)
return -ENODEV;
if ((bpp + 7) / 8 * mode->width > mode->stride)
DIV_ROUND_UP?
return -ENODEV;
*modep = *mode;
*formatp = *format;
*memp = mem;
*bppp = bpp;
return 0;
+}
+static struct sdrm_hw *sdrm_hw_new(const struct simplefb_platform_data *mode,
const struct simplefb_format *format,
const struct resource *mem,
u32 bpp)
+{
struct sdrm_hw *hw;
hw = kzalloc(sizeof(*hw), GFP_KERNEL);
if (!hw)
return ERR_PTR(-ENOMEM);
mutex_init(&hw->lock);
hw->width = mode->width;
hw->height = mode->height;
hw->stride = mode->stride;
hw->bpp = bpp;
hw->format = format->fourcc;
hw->base = mem->start;
hw->size = resource_size(mem);
return hw;
+}
+static struct sdrm_hw *sdrm_hw_free(struct sdrm_hw *hw) +{
if (!hw)
return NULL;
WARN_ON(hw->map);
mutex_destroy(&hw->lock);
kfree(hw);
return NULL;
+}
+static int sdrm_hw_bind(struct sdrm_hw *hw) +{
mutex_lock(&hw->lock);
if (!hw->map)
hw->map = ioremap_wc(hw->base, hw->size);
mutex_unlock(&hw->lock);
return hw->map ? 0 : -EIO;
+}
+static void sdrm_hw_unbind(struct sdrm_hw *hw) +{
if (!hw)
return;
mutex_lock(&hw->lock);
if (hw->map) {
iounmap(hw->map);
hw->map = NULL;
}
mutex_unlock(&hw->lock);
+}
+static struct sdrm_device *sdrm_device_free(struct sdrm_device *sdrm) +{
if (!sdrm)
return NULL;
WARN_ON(atomic_read(&sdrm->n_used) != INT_MIN);
sdrm->hw = sdrm_hw_free(sdrm->hw);
drm_dev_unref(sdrm->ddev);
kfree(sdrm);
return NULL;
+}
+static struct sdrm_device *sdrm_device_new(struct platform_device *pdev,
struct sdrm_hw *hw)
+{
struct sdrm_device *sdrm;
int r;
sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL);
if (!sdrm)
return ERR_PTR(-ENOMEM);
atomic_set(&sdrm->n_used, INT_MIN);
sdrm->ddev = drm_dev_alloc(&sdrm_drm_driver, &pdev->dev);
if (!sdrm->ddev) {
r = -ENOMEM;
goto error;
}
sdrm->ddev->dev_private = sdrm;
sdrm->hw = hw;
return sdrm;
+error:
sdrm_device_free(sdrm);
return ERR_PTR(r);
+}
+static void sdrm_device_unbind(struct sdrm_device *sdrm) +{
if (sdrm) {
sdrm_kms_unbind(sdrm);
sdrm_hw_unbind(sdrm->hw);
sdrm_of_unbind(sdrm);
}
+}
+static int sdrm_device_bind(struct sdrm_device *sdrm) +{
int r;
r = sdrm_of_bind(sdrm);
if (r < 0)
goto error;
r = sdrm_hw_bind(sdrm->hw);
if (r < 0)
goto error;
r = sdrm_kms_bind(sdrm);
if (r < 0)
goto error;
return 0;
+error:
sdrm_device_unbind(sdrm);
return r;
+}
+static int sdrm_device_acquire(struct sdrm_device *sdrm) +{
return (sdrm && atomic_inc_unless_negative(&sdrm->n_used))
? 0 : -ENODEV;
+}
+static void sdrm_device_release(struct sdrm_device *sdrm) +{
if (sdrm && atomic_dec_return(&sdrm->n_used) == INT_MIN) {
sdrm_device_unbind(sdrm);
sdrm_device_free(sdrm);
}
+}
+static int sdrm_fop_open(struct inode *inode, struct file *file) +{
struct drm_device *ddev;
int r;
mutex_lock(&sdrm_lock);
r = drm_open(inode, file);
if (r >= 0) {
ddev = file->private_data;
r = sdrm_device_acquire(ddev->dev_private);
if (r < 0)
drm_release(inode, file);
}
mutex_unlock(&sdrm_lock);
return r;
+}
+static int sdrm_fop_release(struct inode *inode, struct file *file) +{
struct drm_file *dfile = file->private_data;
struct drm_device *ddev = dfile->minor->dev;
struct sdrm_device *sdrm = ddev->dev_private;
int res;
res = drm_release(inode, file);
sdrm_device_release(sdrm);
return res;
+}
+static int sdrm_fop_mmap(struct file *file, struct vm_area_struct *vma) +{
struct drm_file *dfile = file->private_data;
struct drm_device *dev = dfile->minor->dev;
struct drm_gem_object *obj = NULL;
struct drm_vma_offset_node *node;
int r;
drm_vma_offset_lock_lookup(dev->vma_offset_manager);
node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager,
vma->vm_pgoff,
vma_pages(vma));
if (likely(node)) {
obj = container_of(node, struct drm_gem_object, vma_node);
if (!kref_get_unless_zero(&obj->refcount))
obj = NULL;
}
drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
if (!obj)
return -EINVAL;
if (!drm_vma_node_is_allowed(node, dfile)) {
drm_gem_object_unreference_unlocked(obj);
return -EACCES;
}
if (vma->vm_file)
fput(vma->vm_file);
vma->vm_file = get_file(obj->filp);
vma->vm_pgoff = 0;
r = obj->filp->f_op->mmap(obj->filp, vma);
drm_gem_object_unreference_unlocked(obj);
return r;
+}
+static int sdrm_simplefb_probe(struct platform_device *pdev) +{
struct simplefb_platform_data hw_mode;
struct simplefb_format hw_format;
struct sdrm_device *sdrm = NULL;
struct sdrm_hw *hw = NULL;
struct resource *hw_mem;
u32 hw_bpp;
int r;
r = sdrm_hw_identify(pdev, &hw_mode, &hw_format, &hw_mem, &hw_bpp);
if (r < 0)
goto error;
hw = sdrm_hw_new(&hw_mode, &hw_format, hw_mem, hw_bpp);
if (IS_ERR(hw)) {
r = PTR_ERR(hw);
hw = NULL;
goto error;
}
sdrm = sdrm_device_new(pdev, hw);
if (IS_ERR(sdrm)) {
r = PTR_ERR(sdrm);
sdrm = NULL;
goto error;
}
hw = NULL;
platform_set_drvdata(pdev, sdrm);
r = sdrm_device_bind(sdrm);
if (r < 0)
goto error;
/* mark device as enabled and acquire bus ref */
WARN_ON(atomic_read(&sdrm->n_used) != INT_MIN);
atomic_set(&sdrm->n_used, 1);
r = drm_dev_register(sdrm->ddev, 0);
if (r < 0) {
/* mark device as disabled and drop bus ref */
WARN_ON(atomic_add_return(INT_MIN, &sdrm->n_used) != INT_MIN);
sdrm_device_release(sdrm);
return r;
}
dev_info(sdrm->ddev->dev, "initialized %s on minor %d\n",
sdrm->ddev->driver->name, sdrm->ddev->primary->index);
return 0;
+error:
sdrm_device_unbind(sdrm);
sdrm_device_free(sdrm);
sdrm_hw_free(hw);
return r;
+}
+static int sdrm_simplefb_remove(struct platform_device *pdev) +{
struct sdrm_device *sdrm = platform_get_drvdata(pdev);
/* mark device as disabled */
atomic_add(INT_MIN, &sdrm->n_used);
sdrm_hw_unbind(sdrm->hw);
mutex_lock(&sdrm_lock);
drm_dev_unregister(sdrm->ddev);
sdrm_device_release(sdrm);
mutex_unlock(&sdrm_lock);
return 0;
+}
+static const struct file_operations sdrm_drm_fops = {
.owner = THIS_MODULE,
.open = sdrm_fop_open,
.release = sdrm_fop_release,
.mmap = sdrm_fop_mmap,
.poll = drm_poll,
.read = drm_read,
.unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
.compat_ioctl = drm_compat_ioctl,
+#endif
.llseek = noop_llseek,
+};
+static struct drm_driver sdrm_drm_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
.fops = &sdrm_drm_fops,
.gem_free_object = sdrm_bo_free,
.dumb_create = sdrm_dumb_create,
.dumb_map_offset = sdrm_dumb_map_offset,
.dumb_destroy = drm_gem_dumb_destroy,
.name = "simpledrm",
.desc = "Simple firmware framebuffer DRM driver",
.date = "20160901",
+};
+static const struct of_device_id sdrm_simplefb_of_match[] = {
{ .compatible = "simple-framebuffer", },
{},
+}; +MODULE_DEVICE_TABLE(of, sdrm_simplefb_of_match);
+static struct platform_driver sdrm_simplefb_driver = {
.probe = sdrm_simplefb_probe,
.remove = sdrm_simplefb_remove,
.driver = {
.name = "simple-framebuffer",
.mod_name = KBUILD_MODNAME,
.owner = THIS_MODULE,
.of_match_table = sdrm_simplefb_of_match,
},
+};
+static int __init sdrm_init(void) +{
int r;
r = platform_driver_register(&sdrm_simplefb_driver);
if (r < 0)
return r;
sdrm_of_bootstrap();
return 0;
+}
+static void __exit sdrm_exit(void) +{
platform_driver_unregister(&sdrm_simplefb_driver);
+}
+module_init(sdrm_init); +module_exit(sdrm_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Simple firmware framebuffer DRM driver"); +MODULE_ALIAS("platform:simple-framebuffer"); diff --git a/drivers/gpu/drm/simpledrm/simpledrm_gem.c b/drivers/gpu/drm/simpledrm/simpledrm_gem.c new file mode 100644 index 0000000..4aaae6e --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c @@ -0,0 +1,109 @@ +/*
- Copyright (C) 2012-2016 Red Hat, Inc.
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2.1 of the License, or (at your
- option) any later version.
- */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <drm/drmP.h> +#include <drm/drm_gem.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include "simpledrm.h"
+struct sdrm_bo *sdrm_bo_new(struct drm_device *ddev, size_t size) +{
struct sdrm_bo *bo;
WARN_ON(!size || (size & ~PAGE_MASK) != 0);
bo = kzalloc(sizeof(*bo), GFP_KERNEL);
if (!bo)
return NULL;
if (drm_gem_object_init(ddev, &bo->base, size)) {
kfree(bo);
return NULL;
}
return bo;
+}
+void sdrm_bo_free(struct drm_gem_object *dobj) +{
struct sdrm_bo *bo = container_of(dobj, struct sdrm_bo, base);
if (bo->vmapping)
vunmap(bo->vmapping);
if (bo->pages)
drm_gem_put_pages(dobj, bo->pages, false, false);
drm_gem_object_release(dobj);
kfree(bo);
+}
+int sdrm_bo_vmap(struct sdrm_bo *bo) +{
int r;
if (!bo->pages) {
bo->pages = drm_gem_get_pages(&bo->base);
if (IS_ERR(bo->pages)) {
r = PTR_ERR(bo->pages);
bo->pages = NULL;
return r;
}
}
if (!bo->vmapping) {
bo->vmapping = vmap(bo->pages, bo->base.size / PAGE_SIZE, 0,
PAGE_KERNEL);
if (!bo->vmapping)
return -ENOMEM;
}
return 0;
+}
+int sdrm_dumb_create(struct drm_file *dfile,
struct drm_device *ddev,
struct drm_mode_create_dumb *args)
+{
struct sdrm_bo *bo;
int r;
/* overflow checks are done by DRM core */
args->pitch = (args->bpp + 7) / 8 * args->width;
DIV_ROUND_UP?
args->size = PAGE_ALIGN(args->pitch * args->height);
bo = sdrm_bo_new(ddev, args->size);
if (!bo)
return -ENOMEM;
r = drm_gem_handle_create(dfile, &bo->base, &args->handle);
drm_gem_object_unreference_unlocked(&bo->base);
return r;
+}
+int sdrm_dumb_map_offset(struct drm_file *dfile,
struct drm_device *ddev,
uint32_t handle,
uint64_t *offset)
+{
struct drm_gem_object *dobj;
int r;
dobj = drm_gem_object_lookup(dfile, handle);
if (!dobj)
return -ENOENT;
r = drm_gem_create_mmap_offset(dobj);
if (r >= 0)
*offset = drm_vma_node_offset_addr(&dobj->vma_node);
drm_gem_object_unreference_unlocked(dobj);
return r;
+} diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c new file mode 100644 index 0000000..00101c9 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c @@ -0,0 +1,263 @@ +/*
- Copyright (C) 2012-2016 Red Hat, Inc.
- Copyright (C) 2016 Noralf Trønnes
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2.1 of the License, or (at your
- option) any later version.
- */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_gem.h> +#include <drm/drm_simple_kms_helper.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include "simpledrm.h"
+static const uint32_t sdrm_formats[] = {
DRM_FORMAT_RGB565,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_XRGB8888,
+};
+static int sdrm_conn_get_modes(struct drm_connector *conn) +{
struct sdrm_device *sdrm = conn->dev->dev_private;
struct drm_display_mode *mode;
mode = drm_cvt_mode(sdrm->ddev, sdrm->hw->width, sdrm->hw->height,
60, false, false, false);
if (!mode)
return 0;
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_set_name(mode);
drm_mode_probed_add(conn, mode);
return 1;
+}
+static const struct drm_connector_helper_funcs sdrm_conn_hfuncs = {
.get_modes = sdrm_conn_get_modes,
.best_encoder = drm_atomic_helper_best_encoder,
+};
+static enum drm_connector_status sdrm_conn_detect(struct drm_connector *conn,
bool force)
+{
/*
* We simulate an always connected monitor. simple-fb doesn't
* provide any way to detect whether the connector is active. Hence,
* signal DRM core that it is always connected.
*/
return connector_status_connected;
+}
+static const struct drm_connector_funcs sdrm_conn_ops = {
.dpms = drm_atomic_helper_connector_dpms,
.reset = drm_atomic_helper_connector_reset,
.detect = sdrm_conn_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+static void sdrm_crtc_send_vblank_event(struct drm_crtc *crtc) +{
if (crtc->state && crtc->state->event) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
}
+}
+void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state)
+{
struct drm_framebuffer *dfb = pipe->plane.state->fb;
struct sdrm_fb *fb;
sdrm_crtc_send_vblank_event(&pipe->crtc);
if (dfb) {
fb = container_of(dfb, struct sdrm_fb, base);
pipe->plane.fb = dfb;
sdrm_dirty(fb, 0, 0, dfb->width, dfb->height);
}
+}
+static void sdrm_display_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state)
+{
sdrm_crtc_send_vblank_event(&pipe->crtc);
+}
+static void sdrm_display_pipe_disable(struct drm_simple_display_pipe *pipe) +{
sdrm_crtc_send_vblank_event(&pipe->crtc);
+}
+static const struct drm_simple_display_pipe_funcs sdrm_pipe_funcs = {
.update = sdrm_display_pipe_update,
.enable = sdrm_display_pipe_enable,
.disable = sdrm_display_pipe_disable,
+};
+static int sdrm_fb_create_handle(struct drm_framebuffer *dfb,
struct drm_file *dfile,
unsigned int *handle)
+{
struct sdrm_fb *fb = container_of(dfb, struct sdrm_fb, base);
return drm_gem_handle_create(dfile, &fb->bo->base, handle);
+}
+static int sdrm_fb_dirty(struct drm_framebuffer *dfb,
struct drm_file *dfile,
unsigned int flags,
unsigned int color,
struct drm_clip_rect *clips,
unsigned int n_clips)
+{
struct sdrm_fb *fb = container_of(dfb, struct sdrm_fb, base);
struct sdrm_device *sdrm = dfb->dev->dev_private;
unsigned int i;
drm_modeset_lock_all(sdrm->ddev);
if (dfb == sdrm->pipe.plane.fb) {
if (!clips || !n_clips) {
sdrm_dirty(fb, 0, 0, dfb->width, dfb->height);
} else {
for (i = 0; i < n_clips; i++) {
if (clips[i].x1 > clips[i].x2 ||
clips[i].x2 > dfb->width ||
clips[i].y1 > clips[i].y2 ||
clips[i].y2 > dfb->height)
As discussed in private, overlapping clip_rects should be cropped to fit the fb, rather than discarded.
continue;
sdrm_dirty(fb, clips[i].x1, clips[i].y1,
clips[i].x2 - clips[i].x1,
clips[i].y2 - clips[i].y1);
}
}
}
drm_modeset_unlock_all(sdrm->ddev);
return 0;
+}
+static void sdrm_fb_destroy(struct drm_framebuffer *dfb) +{
struct sdrm_fb *fb = container_of(dfb, struct sdrm_fb, base);
drm_framebuffer_cleanup(dfb);
drm_gem_object_unreference_unlocked(&fb->bo->base);
kfree(fb);
+}
+static const struct drm_framebuffer_funcs sdrm_fb_ops = {
.create_handle = sdrm_fb_create_handle,
.dirty = sdrm_fb_dirty,
.destroy = sdrm_fb_destroy,
+};
+static struct drm_framebuffer * +sdrm_fb_create(struct drm_device *ddev,
struct drm_file *dfile,
const struct drm_mode_fb_cmd2 *cmd)
+{
struct drm_gem_object *dobj;
struct sdrm_fb *fb = NULL;
struct sdrm_bo *bo;
int r;
if (cmd->flags)
return ERR_PTR(-EINVAL);
dobj = drm_gem_object_lookup(dfile, cmd->handles[0]);
if (!dobj)
return ERR_PTR(-EINVAL);
bo = container_of(dobj, struct sdrm_bo, base);
r = sdrm_bo_vmap(bo);
if (r < 0)
goto error;
fb = kzalloc(sizeof(*fb), GFP_KERNEL);
if (!fb) {
r = -ENOMEM;
goto error;
}
fb->bo = bo;
drm_helper_mode_fill_fb_struct(&fb->base, cmd);
r = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops);
if (r < 0)
goto error;
DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id,
drm_get_format_name(fb->base.pixel_format));
return &fb->base;
+error:
kfree(fb);
drm_gem_object_unreference_unlocked(dobj);
return ERR_PTR(r);
+}
+static const struct drm_mode_config_funcs sdrm_mode_config_ops = {
.fb_create = sdrm_fb_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
+};
+int sdrm_kms_bind(struct sdrm_device *sdrm) +{
struct drm_connector *conn = &sdrm->conn;
struct drm_device *ddev = sdrm->ddev;
int r;
drm_mode_config_init(ddev);
ddev->mode_config.min_width = sdrm->hw->width;
ddev->mode_config.max_width = sdrm->hw->width;
ddev->mode_config.min_height = sdrm->hw->height;
ddev->mode_config.max_height = sdrm->hw->height;
ddev->mode_config.preferred_depth = sdrm->hw->bpp;
ddev->mode_config.funcs = &sdrm_mode_config_ops;
drm_connector_helper_add(conn, &sdrm_conn_hfuncs);
r = drm_connector_init(ddev, conn, &sdrm_conn_ops,
DRM_MODE_CONNECTOR_VIRTUAL);
if (r < 0)
goto error;
r = drm_simple_display_pipe_init(ddev, &sdrm->pipe, &sdrm_pipe_funcs,
sdrm_formats,
ARRAY_SIZE(sdrm_formats), conn);
if (r < 0)
goto error;
drm_mode_config_reset(ddev);
return 0;
+error:
drm_mode_config_cleanup(ddev);
return r;
+}
+void sdrm_kms_unbind(struct sdrm_device *sdrm) +{
if (sdrm->ddev->mode_config.funcs)
drm_mode_config_cleanup(sdrm->ddev);
+} diff --git a/drivers/gpu/drm/simpledrm/simpledrm_of.c b/drivers/gpu/drm/simpledrm/simpledrm_of.c new file mode 100644 index 0000000..5620000 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_of.c @@ -0,0 +1,265 @@ +/*
- Copyright (C) 2012-2016 Red Hat, Inc.
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2.1 of the License, or (at your
- option) any later version.
- */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <drm/drmP.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include "simpledrm.h"
+#ifdef CONFIG_COMMON_CLK
+static int sdrm_of_bind_clocks(struct sdrm_device *sdrm,
struct device_node *np)
+{
struct clk *clock;
size_t i, n;
int r;
n = of_clk_get_parent_count(np);
if (n < 1)
return 0;
sdrm->clks = kcalloc(n, sizeof(*sdrm->clks), GFP_KERNEL);
if (!sdrm->clks)
return -ENOMEM;
for (i = 0; i < n; ++i) {
clock = of_clk_get(np, i);
if (!IS_ERR(clock)) {
sdrm->clks[sdrm->n_clks++] = clock;
} else if (PTR_ERR(clock) == -EPROBE_DEFER) {
r = -EPROBE_DEFER;
goto error;
} else {
dev_err(sdrm->ddev->dev, "cannot find clock %zu: %ld\n",
i, PTR_ERR(clock));
}
}
for (i = 0; i < sdrm->n_clks; ++i) {
if (!sdrm->clks[i])
continue;
r = clk_prepare_enable(sdrm->clks[i]);
if (r < 0) {
dev_err(sdrm->ddev->dev,
"cannot find clock %zu: %d\n", i, r);
"cannot enable clock" ?
clk_put(sdrm->clks[i]);
sdrm->clks[i] = NULL;
}
}
return 0;
+error:
for (i = 0; i < sdrm->n_clks; ++i)
clk_put(sdrm->clks[i]);
kfree(sdrm->clks);
sdrm->clks = NULL;
sdrm->n_clks = 0;
return r;
+}
+static void sdrm_of_unbind_clocks(struct sdrm_device *sdrm) +{
size_t i;
for (i = 0; i < sdrm->n_clks; ++i) {
clk_disable_unprepare(sdrm->clks[i]);
clk_put(sdrm->clks[i]);
}
kfree(sdrm->clks);
sdrm->clks = NULL;
sdrm->n_clks = 0;
+}
+#else /* CONFIG_COMMON_CLK */
+static int sdrm_of_bind_clocks(struct sdrm_device *sdrm,
struct device_node *np)
+{
return 0;
+}
+static void sdrm_of_unbind_clocks(struct sdrm_device *sdrm) +{ +}
+#endif /* CONFIG_COMMON_CLK */
+#ifdef CONFIG_REGULATOR
+static int sdrm_of_bind_regulators(struct sdrm_device *sdrm,
struct device_node *np)
+{
struct regulator *regulator;
struct property *prop;
char *p, *name;
size_t i, n;
int r;
n = 0;
for_each_property_of_node(np, prop) {
p = strstr(prop->name, "-supply");
if (p && p != prop->name)
++n;
}
if (n < 1)
return 0;
sdrm->regulators = kcalloc(n, sizeof(*sdrm->regulators), GFP_KERNEL);
if (!sdrm->regulators)
return -ENOMEM;
for_each_property_of_node(np, prop) {
p = strstr(prop->name, "-supply");
if (!p || p == prop->name)
continue;
name = kstrndup(prop->name, p - prop->name, GFP_TEMPORARY);
if (!name)
continue;
regulator = regulator_get_optional(sdrm->ddev->dev, name);
kfree(name);
if (!IS_ERR(regulator)) {
sdrm->regulators[sdrm->n_regulators++] = regulator;
} else if (PTR_ERR(regulator) == -EPROBE_DEFER) {
r = -EPROBE_DEFER;
goto error;
} else {
dev_warn(sdrm->ddev->dev,
"cannot find regulator %s: %ld\n",
prop->name, PTR_ERR(regulator));
}
}
for (i = 0; i < sdrm->n_regulators; ++i) {
if (!sdrm->regulators[i])
continue;
r = regulator_enable(sdrm->regulators[i]);
if (r < 0) {
dev_warn(sdrm->ddev->dev,
"cannot enable regulator %zu: %d\n", i, r);
regulator_put(sdrm->regulators[i]);
sdrm->regulators[i] = NULL;
}
}
return 0;
+error:
for (i = 0; i < sdrm->n_regulators; ++i)
if (sdrm->regulators[i])
regulator_put(sdrm->regulators[i]);
kfree(sdrm->regulators);
sdrm->regulators = NULL;
sdrm->n_regulators = 0;
return r;
+}
+static void sdrm_of_unbind_regulators(struct sdrm_device *sdrm) +{
size_t i;
for (i = 0; i < sdrm->n_regulators; ++i) {
if (sdrm->regulators[i]) {
regulator_disable(sdrm->regulators[i]);
regulator_put(sdrm->regulators[i]);
}
}
kfree(sdrm->regulators);
sdrm->regulators = NULL;
sdrm->n_regulators = 0;
+}
+#else /* CONFIG_REGULATORS */
+static int sdrm_of_bind_regulators(struct sdrm_device *sdrm,
struct device_node *np)
+{
return 0;
+}
+static void sdrm_of_unbind_regulators(struct sdrm_device *sdrm) +{ +}
+#endif /* CONFIG_REGULATORS */
+#ifdef CONFIG_OF
+void sdrm_of_bootstrap(void) +{ +#ifdef CONFIG_OF_ADDRESS
struct device_node *np;
for_each_compatible_node(np, NULL, "simple-framebuffer")
of_platform_device_create(np, NULL, NULL);
+#endif +}
+int sdrm_of_bind(struct sdrm_device *sdrm) +{
int r;
if (WARN_ON(sdrm->n_clks > 0 || sdrm->n_regulators > 0))
return 0;
if (!sdrm->ddev->dev->of_node)
return 0;
r = sdrm_of_bind_clocks(sdrm, sdrm->ddev->dev->of_node);
if (r < 0)
goto error;
r = sdrm_of_bind_regulators(sdrm, sdrm->ddev->dev->of_node);
if (r < 0)
goto error;
return 0;
+error:
sdrm_of_unbind(sdrm);
return r;
+}
+void sdrm_of_unbind(struct sdrm_device *sdrm) +{
sdrm_of_unbind_regulators(sdrm);
sdrm_of_unbind_clocks(sdrm);
+}
+#else /* CONFIG_OF */
+void sdrm_of_bootstrap(void) +{ +}
+int sdrm_of_bind(struct sdrm_device *sdrm) +{
return 0;
+}
+void sdrm_of_unbind(struct sdrm_device *sdrm) +{ +}
+#endif /* CONFIG_OF */
2.9.3
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Den 02.09.2016 10:22, skrev 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 dumb-buffers which can be blit into the real framebuffer similar to UDL. No access to the real framebuffer is allowed (compared to earlier version of this driver) to avoid security issues. Furthermore, this way we can support arbitrary modes as long as we have a conversion-helper.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
[...]
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
[...]
+static int sdrm_fop_mmap(struct file *file, struct vm_area_struct *vma) +{
- struct drm_file *dfile = file->private_data;
- struct drm_device *dev = dfile->minor->dev;
- struct drm_gem_object *obj = NULL;
- struct drm_vma_offset_node *node;
- int r;
- drm_vma_offset_lock_lookup(dev->vma_offset_manager);
- node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager,
vma->vm_pgoff,
vma_pages(vma));
- if (likely(node)) {
obj = container_of(node, struct drm_gem_object, vma_node);
if (!kref_get_unless_zero(&obj->refcount))
obj = NULL;
- }
- drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
- if (!obj)
return -EINVAL;
- if (!drm_vma_node_is_allowed(node, dfile)) {
I get: drivers/gpu/drm/simpledrm/simpledrm_drv.c:320:2: warning: passing argument 2 of ‘drm_vma_node_is_allowed’ from incompatible pointer type [enabled by default]
dfile -> file
drm_gem_object_unreference_unlocked(obj);
return -EACCES;
- }
- if (vma->vm_file)
fput(vma->vm_file);
- vma->vm_file = get_file(obj->filp);
- vma->vm_pgoff = 0;
- r = obj->filp->f_op->mmap(obj->filp, vma);
- drm_gem_object_unreference_unlocked(obj);
- return r;
+}
[...]
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
[...]
+static void sdrm_crtc_send_vblank_event(struct drm_crtc *crtc) +{
- if (crtc->state && crtc->state->event) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
- }
+}
+void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state)
+{
- struct drm_framebuffer *dfb = pipe->plane.state->fb;
- struct sdrm_fb *fb;
- sdrm_crtc_send_vblank_event(&pipe->crtc);
- if (dfb) {
fb = container_of(dfb, struct sdrm_fb, base);
pipe->plane.fb = dfb;
sdrm_dirty(fb, 0, 0, dfb->width, dfb->height);
- }
+}
+static void sdrm_display_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state)
+{
- sdrm_crtc_send_vblank_event(&pipe->crtc);
+}
+static void sdrm_display_pipe_disable(struct drm_simple_display_pipe *pipe) +{
- sdrm_crtc_send_vblank_event(&pipe->crtc);
+}
+static const struct drm_simple_display_pipe_funcs sdrm_pipe_funcs = {
- .update = sdrm_display_pipe_update,
- .enable = sdrm_display_pipe_enable,
- .disable = sdrm_display_pipe_disable,
+};
The enable and disable callbacks can be removed. This commit in drm-misc fixed the flip done timeout: drm/simple-helpers: Always add planes to the state update
Noralf.
Hey
On Sat, Sep 3, 2016 at 2:01 PM, Noralf Trønnes noralf@tronnes.org wrote:
Den 02.09.2016 10:22, skrev 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 dumb-buffers which can be blit into the real framebuffer similar to UDL. No access to the real framebuffer is allowed (compared to earlier version of this driver) to avoid security issues. Furthermore, this way we can support arbitrary modes as long as we have a conversion-helper.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
[...]
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
[...]
+static int sdrm_fop_mmap(struct file *file, struct vm_area_struct *vma) +{
struct drm_file *dfile = file->private_data;
struct drm_device *dev = dfile->minor->dev;
struct drm_gem_object *obj = NULL;
struct drm_vma_offset_node *node;
int r;
drm_vma_offset_lock_lookup(dev->vma_offset_manager);
node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager,
vma->vm_pgoff,
vma_pages(vma));
if (likely(node)) {
obj = container_of(node, struct drm_gem_object, vma_node);
if (!kref_get_unless_zero(&obj->refcount))
obj = NULL;
}
drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
if (!obj)
return -EINVAL;
if (!drm_vma_node_is_allowed(node, dfile)) {
I get: drivers/gpu/drm/simpledrm/simpledrm_drv.c:320:2: warning: passing argument 2 of ‘drm_vma_node_is_allowed’ from incompatible pointer type [enabled by default]
dfile -> file
Yeah, either change that, or apply "[PATCH 0/6] DRM Core Cleanups" before. I suspect the cleanups to go in before this driver, so didn't bother changing it.
drm_gem_object_unreference_unlocked(obj);
return -EACCES;
}
if (vma->vm_file)
fput(vma->vm_file);
vma->vm_file = get_file(obj->filp);
vma->vm_pgoff = 0;
r = obj->filp->f_op->mmap(obj->filp, vma);
drm_gem_object_unreference_unlocked(obj);
return r;
+}
[...]
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
[...]
+static void sdrm_crtc_send_vblank_event(struct drm_crtc *crtc) +{
if (crtc->state && crtc->state->event) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
}
+}
+void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state)
+{
struct drm_framebuffer *dfb = pipe->plane.state->fb;
struct sdrm_fb *fb;
sdrm_crtc_send_vblank_event(&pipe->crtc);
if (dfb) {
fb = container_of(dfb, struct sdrm_fb, base);
pipe->plane.fb = dfb;
sdrm_dirty(fb, 0, 0, dfb->width, dfb->height);
}
+}
+static void sdrm_display_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state)
+{
sdrm_crtc_send_vblank_event(&pipe->crtc);
+}
+static void sdrm_display_pipe_disable(struct drm_simple_display_pipe *pipe) +{
sdrm_crtc_send_vblank_event(&pipe->crtc);
+}
+static const struct drm_simple_display_pipe_funcs sdrm_pipe_funcs = {
.update = sdrm_display_pipe_update,
.enable = sdrm_display_pipe_enable,
.disable = sdrm_display_pipe_disable,
+};
The enable and disable callbacks can be removed. This commit in drm-misc fixed the flip done timeout: drm/simple-helpers: Always add planes to the state update
Right, Daniel told me on IRC already. With this fix applied, I can run Xorg on top of it just fine.
I dropped the enable/disable callbacks as well now.
Thanks David
Den 02.09.2016 10:22, skrev 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 dumb-buffers which can be blit into the real framebuffer similar to UDL. No access to the real framebuffer is allowed (compared to earlier version of this driver) to avoid security issues. Furthermore, this way we can support arbitrary modes as long as we have a conversion-helper.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
[...]
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c new file mode 100644 index 0000000..d569120 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c @@ -0,0 +1,464 @@ +/*
- Copyright (C) 2012-2016 Red Hat, Inc.
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2.1 of the License, or (at your
- option) any later version.
- */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <drm/drmP.h> +#include <linux/atomic.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_data/simplefb.h> +#include <linux/string.h> +#include "simpledrm.h"
+static struct drm_driver sdrm_drm_driver; +static DEFINE_MUTEX(sdrm_lock);
+static int sdrm_hw_identify(struct platform_device *pdev,
struct simplefb_platform_data *modep,
struct simplefb_format *formatp,
struct resource **memp,
u32 *bppp)
+{
- static const struct simplefb_format valid_formats[] = SIMPLEFB_FORMATS;
- struct simplefb_platform_data pm = {}, *mode = pdev->dev.platform_data;
- struct device_node *np = pdev->dev.of_node;
- const struct simplefb_format *format = NULL;
- struct resource *mem;
- unsigned int depth;
- int r, bpp;
- size_t i;
- if (!mode) {
if (!np)
return -ENODEV;
mode = ±
r = of_property_read_u32(np, "width", &mode->width);
if (r >= 0)
r = of_property_read_u32(np, "height", &mode->height);
if (r >= 0)
r = of_property_read_u32(np, "stride", &mode->stride);
if (r >= 0)
r = of_property_read_string(np, "format",
&mode->format);
if (r < 0)
return r;
- }
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem)
return -ENODEV;
- for (i = 0; i < ARRAY_SIZE(valid_formats); ++i) {
if (!strcmp(mode->format, valid_formats[i].name)) {
format = &valid_formats[i];
break;
}
- }
- if (!format)
return -ENODEV;
- switch (format->fourcc) {
- case DRM_FORMAT_RGB565:
- case DRM_FORMAT_XRGB1555:
- case DRM_FORMAT_ARGB1555:
- case DRM_FORMAT_RGB888:
- case DRM_FORMAT_XRGB8888:
- case DRM_FORMAT_ARGB8888:
- case DRM_FORMAT_ABGR8888:
- case DRM_FORMAT_XRGB2101010:
- case DRM_FORMAT_ARGB2101010:
/*
* You must adjust sdrm_put() whenever you add a new format
* here, otherwise, blitting operations will not work.
* Furthermore, include/linux/platform_data/simplefb.h needs
* to be adjusted so the platform-device actually allows this
* format.
*/
break;
- default:
return -ENODEV;
- }
- drm_fb_get_bpp_depth(format->fourcc, &depth, &bpp);
- if (!bpp)
return -ENODEV;
- if (resource_size(mem) < mode->stride * mode->height)
return -ENODEV;
- if ((bpp + 7) / 8 * mode->width > mode->stride)
return -ENODEV;
- *modep = *mode;
- *formatp = *format;
- *memp = mem;
- *bppp = bpp;
- return 0;
+}
+static struct sdrm_hw *sdrm_hw_new(const struct simplefb_platform_data *mode,
const struct simplefb_format *format,
const struct resource *mem,
u32 bpp)
+{
- struct sdrm_hw *hw;
- hw = kzalloc(sizeof(*hw), GFP_KERNEL);
- if (!hw)
return ERR_PTR(-ENOMEM);
- mutex_init(&hw->lock);
- hw->width = mode->width;
- hw->height = mode->height;
- hw->stride = mode->stride;
- hw->bpp = bpp;
- hw->format = format->fourcc;
- hw->base = mem->start;
- hw->size = resource_size(mem);
- return hw;
+}
+static struct sdrm_hw *sdrm_hw_free(struct sdrm_hw *hw) +{
- if (!hw)
return NULL;
- WARN_ON(hw->map);
- mutex_destroy(&hw->lock);
- kfree(hw);
- return NULL;
+}
+static int sdrm_hw_bind(struct sdrm_hw *hw) +{
- mutex_lock(&hw->lock);
- if (!hw->map)
hw->map = ioremap_wc(hw->base, hw->size);
- mutex_unlock(&hw->lock);
- return hw->map ? 0 : -EIO;
+}
+static void sdrm_hw_unbind(struct sdrm_hw *hw) +{
- if (!hw)
return;
- mutex_lock(&hw->lock);
- if (hw->map) {
iounmap(hw->map);
hw->map = NULL;
- }
- mutex_unlock(&hw->lock);
+}
+static struct sdrm_device *sdrm_device_free(struct sdrm_device *sdrm) +{
- if (!sdrm)
return NULL;
- WARN_ON(atomic_read(&sdrm->n_used) != INT_MIN);
- sdrm->hw = sdrm_hw_free(sdrm->hw);
- drm_dev_unref(sdrm->ddev);
- kfree(sdrm);
- return NULL;
+}
+static struct sdrm_device *sdrm_device_new(struct platform_device *pdev,
struct sdrm_hw *hw)
+{
- struct sdrm_device *sdrm;
- int r;
- sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL);
- if (!sdrm)
return ERR_PTR(-ENOMEM);
- atomic_set(&sdrm->n_used, INT_MIN);
- sdrm->ddev = drm_dev_alloc(&sdrm_drm_driver, &pdev->dev);
- if (!sdrm->ddev) {
r = -ENOMEM;
goto error;
- }
- sdrm->ddev->dev_private = sdrm;
- sdrm->hw = hw;
- return sdrm;
+error:
- sdrm_device_free(sdrm);
- return ERR_PTR(r);
+}
+static void sdrm_device_unbind(struct sdrm_device *sdrm) +{
- if (sdrm) {
sdrm_kms_unbind(sdrm);
sdrm_hw_unbind(sdrm->hw);
sdrm_of_unbind(sdrm);
- }
+}
+static int sdrm_device_bind(struct sdrm_device *sdrm) +{
- int r;
- r = sdrm_of_bind(sdrm);
- if (r < 0)
goto error;
- r = sdrm_hw_bind(sdrm->hw);
- if (r < 0)
goto error;
- r = sdrm_kms_bind(sdrm);
- if (r < 0)
goto error;
- return 0;
+error:
- sdrm_device_unbind(sdrm);
- return r;
+}
+static int sdrm_device_acquire(struct sdrm_device *sdrm) +{
- return (sdrm && atomic_inc_unless_negative(&sdrm->n_used))
? 0 : -ENODEV;
+}
+static void sdrm_device_release(struct sdrm_device *sdrm) +{
- if (sdrm && atomic_dec_return(&sdrm->n_used) == INT_MIN) {
sdrm_device_unbind(sdrm);
sdrm_device_free(sdrm);
- }
+}
+static int sdrm_fop_open(struct inode *inode, struct file *file) +{
- struct drm_device *ddev;
- int r;
- mutex_lock(&sdrm_lock);
- r = drm_open(inode, file);
- if (r >= 0) {
ddev = file->private_data;
r = sdrm_device_acquire(ddev->dev_private);
if (r < 0)
drm_release(inode, file);
- }
- mutex_unlock(&sdrm_lock);
- return r;
+}
+static int sdrm_fop_release(struct inode *inode, struct file *file) +{
- struct drm_file *dfile = file->private_data;
- struct drm_device *ddev = dfile->minor->dev;
- struct sdrm_device *sdrm = ddev->dev_private;
- int res;
- res = drm_release(inode, file);
- sdrm_device_release(sdrm);
- return res;
+}
+static int sdrm_fop_mmap(struct file *file, struct vm_area_struct *vma) +{
- struct drm_file *dfile = file->private_data;
- struct drm_device *dev = dfile->minor->dev;
- struct drm_gem_object *obj = NULL;
- struct drm_vma_offset_node *node;
- int r;
- drm_vma_offset_lock_lookup(dev->vma_offset_manager);
- node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager,
vma->vm_pgoff,
vma_pages(vma));
- if (likely(node)) {
obj = container_of(node, struct drm_gem_object, vma_node);
if (!kref_get_unless_zero(&obj->refcount))
obj = NULL;
- }
- drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
- if (!obj)
return -EINVAL;
- if (!drm_vma_node_is_allowed(node, dfile)) {
drm_gem_object_unreference_unlocked(obj);
return -EACCES;
- }
- if (vma->vm_file)
fput(vma->vm_file);
- vma->vm_file = get_file(obj->filp);
- vma->vm_pgoff = 0;
- r = obj->filp->f_op->mmap(obj->filp, vma);
- drm_gem_object_unreference_unlocked(obj);
- return r;
+}
+static int sdrm_simplefb_probe(struct platform_device *pdev) +{
- struct simplefb_platform_data hw_mode;
- struct simplefb_format hw_format;
- struct sdrm_device *sdrm = NULL;
- struct sdrm_hw *hw = NULL;
- struct resource *hw_mem;
- u32 hw_bpp;
- int r;
- r = sdrm_hw_identify(pdev, &hw_mode, &hw_format, &hw_mem, &hw_bpp);
- if (r < 0)
goto error;
- hw = sdrm_hw_new(&hw_mode, &hw_format, hw_mem, hw_bpp);
- if (IS_ERR(hw)) {
r = PTR_ERR(hw);
hw = NULL;
goto error;
- }
- sdrm = sdrm_device_new(pdev, hw);
- if (IS_ERR(sdrm)) {
r = PTR_ERR(sdrm);
sdrm = NULL;
goto error;
- }
- hw = NULL;
- platform_set_drvdata(pdev, sdrm);
- r = sdrm_device_bind(sdrm);
- if (r < 0)
goto error;
- /* mark device as enabled and acquire bus ref */
- WARN_ON(atomic_read(&sdrm->n_used) != INT_MIN);
- atomic_set(&sdrm->n_used, 1);
- r = drm_dev_register(sdrm->ddev, 0);
- if (r < 0) {
/* mark device as disabled and drop bus ref */
WARN_ON(atomic_add_return(INT_MIN, &sdrm->n_used) != INT_MIN);
sdrm_device_release(sdrm);
return r;
- }
- dev_info(sdrm->ddev->dev, "initialized %s on minor %d\n",
sdrm->ddev->driver->name, sdrm->ddev->primary->index);
- return 0;
+error:
- sdrm_device_unbind(sdrm);
- sdrm_device_free(sdrm);
- sdrm_hw_free(hw);
- return r;
+}
+static int sdrm_simplefb_remove(struct platform_device *pdev) +{
- struct sdrm_device *sdrm = platform_get_drvdata(pdev);
- /* mark device as disabled */
- atomic_add(INT_MIN, &sdrm->n_used);
- sdrm_hw_unbind(sdrm->hw);
- mutex_lock(&sdrm_lock);
- drm_dev_unregister(sdrm->ddev);
- sdrm_device_release(sdrm);
- mutex_unlock(&sdrm_lock);
- return 0;
+}
There is something wrong with the ref counting. Load, unload is fine:
$ sudo modprobe simpledrm $ echo 0 | sudo tee /sys/class/vtconsole/vtcon1/bind $ sudo modprobe -r simpledrm $ ls -l /dev/fb* ls: cannot access /dev/fb*: No such file or directory
But if I run modetest in between it's not released:
$ sudo modprobe simpledrm $ ./libdrm/tests/modetest/modetest -M "simpledrm" -s 23:1824x984 setting mode 1824x984-60Hz@XR24 on connectors 23, crtc 25 $ echo 0 | sudo tee /sys/class/vtconsole/vtcon1/bind $ sudo modprobe -r simpledrm $ ls -l /dev/fb* crw-rw---- 1 root video 29, 0 Sep 5 15:43 /dev/fb0
Noralf.
+static const struct file_operations sdrm_drm_fops = {
- .owner = THIS_MODULE,
- .open = sdrm_fop_open,
- .release = sdrm_fop_release,
- .mmap = sdrm_fop_mmap,
- .poll = drm_poll,
- .read = drm_read,
- .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
- .compat_ioctl = drm_compat_ioctl,
+#endif
- .llseek = noop_llseek,
+};
+static struct drm_driver sdrm_drm_driver = {
- .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
- .fops = &sdrm_drm_fops,
- .gem_free_object = sdrm_bo_free,
- .dumb_create = sdrm_dumb_create,
- .dumb_map_offset = sdrm_dumb_map_offset,
- .dumb_destroy = drm_gem_dumb_destroy,
- .name = "simpledrm",
- .desc = "Simple firmware framebuffer DRM driver",
- .date = "20160901",
+};
+static const struct of_device_id sdrm_simplefb_of_match[] = {
- { .compatible = "simple-framebuffer", },
- {},
+}; +MODULE_DEVICE_TABLE(of, sdrm_simplefb_of_match);
+static struct platform_driver sdrm_simplefb_driver = {
- .probe = sdrm_simplefb_probe,
- .remove = sdrm_simplefb_remove,
- .driver = {
.name = "simple-framebuffer",
.mod_name = KBUILD_MODNAME,
.owner = THIS_MODULE,
.of_match_table = sdrm_simplefb_of_match,
- },
+};
+static int __init sdrm_init(void) +{
- int r;
- r = platform_driver_register(&sdrm_simplefb_driver);
- if (r < 0)
return r;
- sdrm_of_bootstrap();
- return 0;
+}
+static void __exit sdrm_exit(void) +{
- platform_driver_unregister(&sdrm_simplefb_driver);
+}
+module_init(sdrm_init); +module_exit(sdrm_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Simple firmware framebuffer DRM driver"); +MODULE_ALIAS("platform:simple-framebuffer");
Create a simple fbdev device during SimpleDRM setup so legacy user-space and fbcon can use it.
Signed-off-by: David Herrmann dh.herrmann@gmail.com --- drivers/gpu/drm/simpledrm/Makefile | 1 + drivers/gpu/drm/simpledrm/simpledrm.h | 8 ++ drivers/gpu/drm/simpledrm/simpledrm_drv.c | 13 +++ drivers/gpu/drm/simpledrm/simpledrm_fbdev.c | 143 ++++++++++++++++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_kms.c | 55 ++++++----- 5 files changed, 196 insertions(+), 24 deletions(-) create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile index d7b179d..e368d14 100644 --- a/drivers/gpu/drm/simpledrm/Makefile +++ b/drivers/gpu/drm/simpledrm/Makefile @@ -1,6 +1,7 @@ simpledrm-y := \ simpledrm_damage.o \ simpledrm_drv.o \ + simpledrm_fbdev.o \ simpledrm_gem.o \ simpledrm_kms.o \ simpledrm_of.o diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h index ed6d725..d7a2045 100644 --- a/drivers/gpu/drm/simpledrm/simpledrm.h +++ b/drivers/gpu/drm/simpledrm/simpledrm.h @@ -19,6 +19,7 @@ #include <linux/mutex.h>
struct clk; +struct drm_fb_helper; struct regulator; struct simplefb_format;
@@ -49,6 +50,7 @@ struct sdrm_device { atomic_t n_used; struct drm_device *ddev; struct sdrm_hw *hw; + struct drm_fb_helper *fbdev;
size_t n_clks; size_t n_regulators; @@ -66,6 +68,9 @@ void sdrm_of_unbind(struct sdrm_device *sdrm); int sdrm_kms_bind(struct sdrm_device *sdrm); void sdrm_kms_unbind(struct sdrm_device *sdrm);
+void sdrm_fbdev_bind(struct sdrm_device *sdrm); +void sdrm_fbdev_unbind(struct sdrm_device *sdrm); + void sdrm_dirty(struct sdrm_fb *fb, u32 x, u32 y, u32 width, u32 height);
struct sdrm_bo *sdrm_bo_new(struct drm_device *ddev, size_t size); @@ -80,4 +85,7 @@ int sdrm_dumb_map_offset(struct drm_file *dfile, uint32_t handle, uint64_t *offset);
+struct sdrm_fb *sdrm_fb_new(struct sdrm_bo *bo, + const struct drm_mode_fb_cmd2 *cmd); + #endif /* __SDRM_SIMPLEDRM_H */ diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c index d569120..6aefe49 100644 --- a/drivers/gpu/drm/simpledrm/simpledrm_drv.c +++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c @@ -9,6 +9,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <drm/drmP.h> +#include <drm/drm_fb_helper.h> #include <linux/atomic.h> #include <linux/err.h> #include <linux/io.h> @@ -210,6 +211,7 @@ error: static void sdrm_device_unbind(struct sdrm_device *sdrm) { if (sdrm) { + sdrm_fbdev_unbind(sdrm); sdrm_kms_unbind(sdrm); sdrm_hw_unbind(sdrm->hw); sdrm_of_unbind(sdrm); @@ -232,6 +234,8 @@ static int sdrm_device_bind(struct sdrm_device *sdrm) if (r < 0) goto error;
+ sdrm_fbdev_bind(sdrm); + return 0;
error: @@ -253,6 +257,14 @@ static void sdrm_device_release(struct sdrm_device *sdrm) } }
+static void sdrm_device_lastclose(struct drm_device *ddev) +{ + struct sdrm_device *sdrm = ddev->dev_private; + + if (sdrm->fbdev) + drm_fb_helper_restore_fbdev_mode_unlocked(sdrm->fbdev); +} + static int sdrm_fop_open(struct inode *inode, struct file *file) { struct drm_device *ddev; @@ -411,6 +423,7 @@ static const struct file_operations sdrm_drm_fops = { static struct drm_driver sdrm_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &sdrm_drm_fops, + .lastclose = sdrm_device_lastclose,
.gem_free_object = sdrm_bo_free,
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c new file mode 100644 index 0000000..17e4e83 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2012-2016 Red Hat, Inc. + * Copyright (C) 2016 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include "simpledrm.h" + +static int sdrm_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + return -ENODEV; +} + +static struct fb_ops sdrm_fbdev_ops = { + .owner = THIS_MODULE, + .fb_fillrect = drm_fb_helper_sys_fillrect, + .fb_copyarea = drm_fb_helper_sys_copyarea, + .fb_imageblit = drm_fb_helper_sys_imageblit, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_setcmap = drm_fb_helper_setcmap, + .fb_mmap = sdrm_fbdev_mmap, +}; + +static int sdrm_fbdev_probe(struct drm_fb_helper *fbdev, + struct drm_fb_helper_surface_size *sizes) +{ + struct sdrm_device *sdrm = fbdev->dev->dev_private; + struct drm_mode_fb_cmd2 cmd = { + .width = sdrm->hw->width, + .height = sdrm->hw->height, + .pitches[0] = sdrm->hw->stride, + .pixel_format = sdrm->hw->format, + }; + struct fb_info *fbi; + struct sdrm_bo *bo; + struct sdrm_fb *fb; + int r; + + fbi = drm_fb_helper_alloc_fbi(fbdev); + if (IS_ERR(fbi)) + return PTR_ERR(fbi); + + bo = sdrm_bo_new(sdrm->ddev, + PAGE_ALIGN(sdrm->hw->height * sdrm->hw->stride)); + if (!bo) { + r = -ENOMEM; + goto error; + } + + fb = sdrm_fb_new(bo, &cmd); + drm_gem_object_unreference_unlocked(&bo->base); + if (IS_ERR(fb)) { + r = PTR_ERR(fb); + goto error; + } + + fbdev->fb = &fb->base; + fbi->par = fbdev; + fbi->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE; + fbi->fbops = &sdrm_fbdev_ops; + + drm_fb_helper_fill_fix(fbi, fb->base.pitches[0], fb->base.depth); + drm_fb_helper_fill_var(fbi, fbdev, fb->base.width, fb->base.height); + + strncpy(fbi->fix.id, "simpledrmfb", sizeof(fbi->fix.id) - 1); + fbi->screen_base = bo->vmapping; + fbi->fix.smem_len = bo->base.size; + + return 0; + +error: + drm_fb_helper_release_fbi(fbdev); + return r; +} + +static const struct drm_fb_helper_funcs sdrm_fbdev_funcs = { + .fb_probe = sdrm_fbdev_probe, +}; + +void sdrm_fbdev_bind(struct sdrm_device *sdrm) +{ + struct drm_fb_helper *fbdev; + int r; + + fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); + if (!fbdev) + return; + + drm_fb_helper_prepare(sdrm->ddev, fbdev, &sdrm_fbdev_funcs); + + r = drm_fb_helper_init(sdrm->ddev, fbdev, 1, 1); + if (r < 0) + goto error; + + r = drm_fb_helper_single_add_all_connectors(fbdev); + if (r < 0) + goto error; + + r = drm_fb_helper_initial_config(fbdev, + sdrm->ddev->mode_config.preferred_depth); + if (r < 0) + goto error; + + if (!fbdev->fbdev) + goto error; + + sdrm->fbdev = fbdev; + return; + +error: + drm_fb_helper_fini(fbdev); + kfree(fbdev); +} + +void sdrm_fbdev_unbind(struct sdrm_device *sdrm) +{ + struct drm_fb_helper *fbdev = sdrm->fbdev; + + if (!fbdev) + return; + + sdrm->fbdev = NULL; + drm_fb_helper_unregister_fbi(fbdev); + cancel_work_sync(&fbdev->dirty_work); + drm_fb_helper_release_fbi(fbdev); + drm_framebuffer_unreference(fbdev->fb); + fbdev->fb = NULL; + drm_fb_helper_fini(fbdev); + kfree(fbdev); +} diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c index 00101c9..8f67fe5 100644 --- a/drivers/gpu/drm/simpledrm/simpledrm_kms.c +++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c @@ -169,53 +169,60 @@ static const struct drm_framebuffer_funcs sdrm_fb_ops = { .destroy = sdrm_fb_destroy, };
-static struct drm_framebuffer * -sdrm_fb_create(struct drm_device *ddev, - struct drm_file *dfile, - const struct drm_mode_fb_cmd2 *cmd) +struct sdrm_fb *sdrm_fb_new(struct sdrm_bo *bo, + const struct drm_mode_fb_cmd2 *cmd) { - struct drm_gem_object *dobj; - struct sdrm_fb *fb = NULL; - struct sdrm_bo *bo; + struct sdrm_fb *fb; int r;
if (cmd->flags) return ERR_PTR(-EINVAL);
- dobj = drm_gem_object_lookup(dfile, cmd->handles[0]); - if (!dobj) - return ERR_PTR(-EINVAL); - - bo = container_of(dobj, struct sdrm_bo, base); - r = sdrm_bo_vmap(bo); if (r < 0) - goto error; + return ERR_PTR(r);
fb = kzalloc(sizeof(*fb), GFP_KERNEL); - if (!fb) { - r = -ENOMEM; - goto error; - } + if (!fb) + return ERR_PTR(r);
+ drm_gem_object_reference(&bo->base); fb->bo = bo; drm_helper_mode_fill_fb_struct(&fb->base, cmd);
- r = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops); + r = drm_framebuffer_init(bo->base.dev, &fb->base, &sdrm_fb_ops); if (r < 0) goto error;
- DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id, - drm_get_format_name(fb->base.pixel_format)); - - return &fb->base; + return fb;
error: + drm_gem_object_unreference_unlocked(&bo->base); kfree(fb); - drm_gem_object_unreference_unlocked(dobj); return ERR_PTR(r); }
+static struct drm_framebuffer * +sdrm_fb_create(struct drm_device *ddev, + struct drm_file *dfile, + const struct drm_mode_fb_cmd2 *cmd) +{ + struct drm_gem_object *dobj; + struct sdrm_fb *fb; + + dobj = drm_gem_object_lookup(dfile, cmd->handles[0]); + if (!dobj) + return ERR_PTR(-EINVAL); + + fb = sdrm_fb_new(container_of(dobj, struct sdrm_bo, base), cmd); + drm_gem_object_unreference_unlocked(dobj); + + if (IS_ERR(fb)) + return ERR_CAST(fb); + + return &fb->base; +} + static const struct drm_mode_config_funcs sdrm_mode_config_ops = { .fb_create = sdrm_fb_create, .atomic_check = drm_atomic_helper_check,
Den 02.09.2016 10:22, skrev David Herrmann:
Create a simple fbdev device during SimpleDRM setup so legacy user-space and fbcon can use it.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
[...]
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
[...]
+void sdrm_fbdev_bind(struct sdrm_device *sdrm) +{
- struct drm_fb_helper *fbdev;
- int r;
- fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
- if (!fbdev)
return;
- drm_fb_helper_prepare(sdrm->ddev, fbdev, &sdrm_fbdev_funcs);
- r = drm_fb_helper_init(sdrm->ddev, fbdev, 1, 1);
- if (r < 0)
goto error;
- r = drm_fb_helper_single_add_all_connectors(fbdev);
- if (r < 0)
goto error;
- r = drm_fb_helper_initial_config(fbdev,
sdrm->ddev->mode_config.preferred_depth);
- if (r < 0)
goto error;
- if (!fbdev->fbdev)
goto error;
- sdrm->fbdev = fbdev;
- return;
+error:
- drm_fb_helper_fini(fbdev);
- kfree(fbdev);
+}
+void sdrm_fbdev_unbind(struct sdrm_device *sdrm) +{
- struct drm_fb_helper *fbdev = sdrm->fbdev;
- if (!fbdev)
return;
- sdrm->fbdev = NULL;
- drm_fb_helper_unregister_fbi(fbdev);
- cancel_work_sync(&fbdev->dirty_work);
- drm_fb_helper_release_fbi(fbdev);
- drm_framebuffer_unreference(fbdev->fb);
I get a warning that there are still fb's left during unbind:
[ 48.666003] WARNING: CPU: 0 PID: 716 at drivers/gpu/drm/drm_crtc.c:3855 drm_mode_config_cleanup+0x180/0x1f4 [drm]
This worked:
- drm_framebuffer_unreference(fbdev->fb); + drm_framebuffer_unregister_private(fbdev->fb); + drm_framebuffer_cleanup(fbdev->fb);
Noralf.
- fbdev->fb = NULL;
- drm_fb_helper_fini(fbdev);
- kfree(fbdev);
+}
Den 03.09.2016 14:04, skrev Noralf Trønnes:
Den 02.09.2016 10:22, skrev David Herrmann:
Create a simple fbdev device during SimpleDRM setup so legacy user-space and fbcon can use it.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
[...]
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
[...]
+void sdrm_fbdev_bind(struct sdrm_device *sdrm) +{
- struct drm_fb_helper *fbdev;
- int r;
- fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
- if (!fbdev)
return;
- drm_fb_helper_prepare(sdrm->ddev, fbdev, &sdrm_fbdev_funcs);
- r = drm_fb_helper_init(sdrm->ddev, fbdev, 1, 1);
- if (r < 0)
goto error;
- r = drm_fb_helper_single_add_all_connectors(fbdev);
- if (r < 0)
goto error;
- r = drm_fb_helper_initial_config(fbdev,
sdrm->ddev->mode_config.preferred_depth);
- if (r < 0)
goto error;
- if (!fbdev->fbdev)
goto error;
- sdrm->fbdev = fbdev;
- return;
+error:
- drm_fb_helper_fini(fbdev);
- kfree(fbdev);
+}
+void sdrm_fbdev_unbind(struct sdrm_device *sdrm) +{
- struct drm_fb_helper *fbdev = sdrm->fbdev;
- if (!fbdev)
return;
- sdrm->fbdev = NULL;
- drm_fb_helper_unregister_fbi(fbdev);
- cancel_work_sync(&fbdev->dirty_work);
- drm_fb_helper_release_fbi(fbdev);
- drm_framebuffer_unreference(fbdev->fb);
I get a warning that there are still fb's left during unbind:
[ 48.666003] WARNING: CPU: 0 PID: 716 at drivers/gpu/drm/drm_crtc.c:3855 drm_mode_config_cleanup+0x180/0x1f4 [drm]
This worked:
drm_framebuffer_unreference(fbdev->fb);
drm_framebuffer_unregister_private(fbdev->fb);
drm_framebuffer_cleanup(fbdev->fb);
Well not quite, this doesn't free the bo, so maybe this:
- drm_framebuffer_unreference(fbdev->fb); + drm_framebuffer_unregister_private(fbdev->fb); + sdrm_fb_destroy(fbdev->fb);
IIRC the reason ref count doesn't drop to zero, had something to do with multiple fb_set_par calls taking reference but not being dropped later.
Noralf.
Noralf.
- fbdev->fb = NULL;
- drm_fb_helper_fini(fbdev);
- kfree(fbdev);
+}
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Hi
On Sat, Sep 3, 2016 at 7:15 PM, Noralf Trønnes noralf@tronnes.org wrote:
Den 03.09.2016 14:04, skrev Noralf Trønnes:
Den 02.09.2016 10:22, skrev David Herrmann:
Create a simple fbdev device during SimpleDRM setup so legacy user-space and fbcon can use it.
Signed-off-by: David Herrmann dh.herrmann@gmail.com
[...]
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
[...]
+void sdrm_fbdev_bind(struct sdrm_device *sdrm) +{
- struct drm_fb_helper *fbdev;
- int r;
- fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
- if (!fbdev)
return;
- drm_fb_helper_prepare(sdrm->ddev, fbdev, &sdrm_fbdev_funcs);
- r = drm_fb_helper_init(sdrm->ddev, fbdev, 1, 1);
- if (r < 0)
goto error;
- r = drm_fb_helper_single_add_all_connectors(fbdev);
- if (r < 0)
goto error;
- r = drm_fb_helper_initial_config(fbdev,
sdrm->ddev->mode_config.preferred_depth);
- if (r < 0)
goto error;
- if (!fbdev->fbdev)
goto error;
- sdrm->fbdev = fbdev;
- return;
+error:
- drm_fb_helper_fini(fbdev);
- kfree(fbdev);
+}
+void sdrm_fbdev_unbind(struct sdrm_device *sdrm) +{
- struct drm_fb_helper *fbdev = sdrm->fbdev;
- if (!fbdev)
return;
- sdrm->fbdev = NULL;
- drm_fb_helper_unregister_fbi(fbdev);
- cancel_work_sync(&fbdev->dirty_work);
- drm_fb_helper_release_fbi(fbdev);
- drm_framebuffer_unreference(fbdev->fb);
I get a warning that there are still fb's left during unbind:
[ 48.666003] WARNING: CPU: 0 PID: 716 at drivers/gpu/drm/drm_crtc.c:3855 drm_mode_config_cleanup+0x180/0x1f4 [drm]
This worked:
drm_framebuffer_unreference(fbdev->fb);
drm_framebuffer_unregister_private(fbdev->fb);
drm_framebuffer_cleanup(fbdev->fb);
Well not quite, this doesn't free the bo, so maybe this:
drm_framebuffer_unreference(fbdev->fb);
drm_framebuffer_unregister_private(fbdev->fb);
sdrm_fb_destroy(fbdev->fb);
IIRC the reason ref count doesn't drop to zero, had something to do with multiple fb_set_par calls taking reference but not being dropped later.
So I don't like this at all. If we leak references, we should fix the ref-leak or properly document why this is fine to do. Daniel, what is the exact reason we have unregister_private()? Maybe I should try and trace the ref-leak.
Thanks David
On Friday, September 2, 2016 4:22:38 AM EST David Herrmann wrote:
Hey
On request of Noralf, I picked up the patches and prepared v5. Works fine with Xorg, if configured according to: https://lists.freedesktop.org/archives/dri-devel/2014-January/052777.html If anyone knows how to make Xorg pick it up dynamically without such a static configuration, please let me know.
Thanks David
David Herrmann (7): x86/sysfb: add support for 64bit EFI lfb_base x86/sysfb: fix lfb_size calculation of/platform: expose of_platform_device_destroy() video: add generic framebuffer eviction drm: switch to sysfb_evict_conflicts() drm: add SimpleDRM driver drm/simpledrm: add fbdev fallback support
MAINTAINERS | 6 + arch/x86/kernel/sysfb_simplefb.c | 39 ++- drivers/gpu/drm/Kconfig | 3 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 24 +- drivers/gpu/drm/bochs/bochs_drv.c | 19 +- drivers/gpu/drm/i915/i915_drv.c | 73 +--- drivers/gpu/drm/mgag200/mgag200_drv.c | 27 +- drivers/gpu/drm/mgag200/mgag200_main.c | 9 - drivers/gpu/drm/nouveau/nouveau_drm.c | 33 +- drivers/gpu/drm/radeon/radeon_drv.c | 24 +- drivers/gpu/drm/simpledrm/Kconfig | 19 ++ drivers/gpu/drm/simpledrm/Makefile | 9 + drivers/gpu/drm/simpledrm/simpledrm.h | 91 +++++ drivers/gpu/drm/simpledrm/simpledrm_damage.c | 194 +++++++++++ drivers/gpu/drm/simpledrm/simpledrm_drv.c | 477 +++++++++++++++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_fbdev.c | 143 ++++++++ drivers/gpu/drm/simpledrm/simpledrm_gem.c | 109 ++++++ drivers/gpu/drm/simpledrm/simpledrm_kms.c | 270 +++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_of.c | 265 +++++++++++++++ drivers/gpu/drm/sun4i/sun4i_drv.c | 24 +- drivers/gpu/drm/vc4/vc4_drv.c | 25 +- drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 24 +- drivers/of/platform.c | 35 +- drivers/video/Kconfig | 4 + drivers/video/Makefile | 1 + drivers/video/sysfb.c | 327 ++++++++++++++++++ include/linux/of_platform.h | 1 + include/linux/sysfb.h | 34 ++ 29 files changed, 2054 insertions(+), 256 deletions(-) create mode 100644 drivers/gpu/drm/simpledrm/Kconfig create mode 100644 drivers/gpu/drm/simpledrm/Makefile create mode 100644 drivers/gpu/drm/simpledrm/simpledrm.h create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_damage.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_drv.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_gem.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_kms.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_of.c create mode 100644 drivers/video/sysfb.c create mode 100644 include/linux/sysfb.h
Hi
I am kind of curious as I do have interest in seeing this merged as well.
There is an email in this thread from 2018, but when I tried to import an mbox file from the whole month for August 2018, for some reason, kmail doesn't see the sender and mailing list recipient in that one, so I will reply to this one, because I was able to import this into my mail client. https://www.spinics.net/lists/dri-devel/msg185519.html
I was able to get this to build against Linux 4.8, but not against a newer version, some headers seem to have been split, and some things are off by 8 and other things. I could NOT find a git repo, but I was able to find the newest patches I could find, and import those with git am against 4.8 with some tweaks. If that is needed, I can link it, but only if you want.
However in QEMU I wasn't able to figure out how to make it create a /dev/dri/card0 device, even after blacklisting the other modules for qxl, cirrus, etc, and then modprobe-ing simpledrm
In my view something like this is would be useful. There still could be hardware devices that don't have modesetting support (like vmvga in qemu/virt-manager as an example). And most wayland servers need a /dev/dri/card0 device as well as a potential user-mode TTY replacement would also need /dev/dri/card0
I will admit I unfortunately failed to get it to build against master. I couldn't figure out some of the changes, where some new structs were off by a factor of 8.
Thanks
Hi
Am 10.03.21 um 03:50 schrieb nerdopolis:
On Friday, September 2, 2016 4:22:38 AM EST David Herrmann wrote:
Hey
On request of Noralf, I picked up the patches and prepared v5. Works fine with Xorg, if configured according to: https://lists.freedesktop.org/archives/dri-devel/2014-January/052777.html If anyone knows how to make Xorg pick it up dynamically without such a static configuration, please let me know.
Hi
I am kind of curious as I do have interest in seeing this merged as well.
Please take a look at [1]. It's not the same driver, but something to the same effect. I know it's been almost a year, but I do work on this and intend to come back with a new version during 2021.
I currently work on fastboot support for the new driver. But it's a complicated matter and takes time. If there's interest, we could talk about merging what's already there.
Best regards Thomas
[1] https://lore.kernel.org/dri-devel/20200625120011.16168-1-tzimmermann@suse.de...
There is an email in this thread from 2018, but when I tried to import an mbox file from the whole month for August 2018, for some reason, kmail doesn't see the sender and mailing list recipient in that one, so I will reply to this one, because I was able to import this into my mail client. https://www.spinics.net/lists/dri-devel/msg185519.html
I was able to get this to build against Linux 4.8, but not against a newer version, some headers seem to have been split, and some things are off by 8 and other things. I could NOT find a git repo, but I was able to find the newest patches I could find, and import those with git am against 4.8 with some tweaks. If that is needed, I can link it, but only if you want.
However in QEMU I wasn't able to figure out how to make it create a /dev/dri/card0 device, even after blacklisting the other modules for qxl, cirrus, etc, and then modprobe-ing simpledrm
In my view something like this is would be useful. There still could be hardware devices that don't have modesetting support (like vmvga in qemu/virt-manager as an example). And most wayland servers need a /dev/dri/card0 device as well as a potential user-mode TTY replacement would also need /dev/dri/card0
I will admit I unfortunately failed to get it to build against master. I couldn't figure out some of the changes, where some new structs were off by a factor of 8.
Thanks
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
On Wednesday, March 10, 2021 4:10:35 AM EST Thomas Zimmermann wrote:
Hi
Am 10.03.21 um 03:50 schrieb nerdopolis:
On Friday, September 2, 2016 4:22:38 AM EST David Herrmann wrote:
Hey
On request of Noralf, I picked up the patches and prepared v5. Works fine with Xorg, if configured according to: https://lists.freedesktop.org/archives/dri-devel/2014-January/052777.html If anyone knows how to make Xorg pick it up dynamically without such a static configuration, please let me know.
Hi
I am kind of curious as I do have interest in seeing this merged as well.
Please take a look at [1]. It's not the same driver, but something to the same effect. I know it's been almost a year, but I do work on this and intend to come back with a new version during 2021.
Thanks, I'll try that out
I currently work on fastboot support for the new driver. But it's a complicated matter and takes time. If there's interest, we could talk about merging what's already there.
Best regards Thomas
[1] https://lore.kernel.org/dri-devel/20200625120011.16168-1-tzimmermann@suse.de...
There is an email in this thread from 2018, but when I tried to import an mbox file from the whole month for August 2018, for some reason, kmail doesn't see the sender and mailing list recipient in that one, so I will reply to this one, because I was able to import this into my mail client. https://www.spinics.net/lists/dri-devel/msg185519.html
I was able to get this to build against Linux 4.8, but not against a newer version, some headers seem to have been split, and some things are off by 8 and other things. I could NOT find a git repo, but I was able to find the newest patches I could find, and import those with git am against 4.8 with some tweaks. If that is needed, I can link it, but only if you want.
However in QEMU I wasn't able to figure out how to make it create a /dev/dri/card0 device, even after blacklisting the other modules for qxl, cirrus, etc, and then modprobe-ing simpledrm
In my view something like this is would be useful. There still could be hardware devices that don't have modesetting support (like vmvga in qemu/virt-manager as an example). And most wayland servers need a /dev/dri/card0 device as well as a potential user-mode TTY replacement would also need /dev/dri/card0
I will admit I unfortunately failed to get it to build against master. I couldn't figure out some of the changes, where some new structs were off by a factor of 8.
Thanks
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
On Wednesday, March 10, 2021 4:10:35 AM EST Thomas Zimmermann wrote:
Hi
Am 10.03.21 um 03:50 schrieb nerdopolis:
On Friday, September 2, 2016 4:22:38 AM EST David Herrmann wrote:
Hey
On request of Noralf, I picked up the patches and prepared v5. Works fine with Xorg, if configured according to: https://lists.freedesktop.org/archives/dri-devel/2014-January/052777.html If anyone knows how to make Xorg pick it up dynamically without such a static configuration, please let me know.
Hi
I am kind of curious as I do have interest in seeing this merged as well.
Please take a look at [1]. It's not the same driver, but something to the same effect. I know it's been almost a year, but I do work on this and intend to come back with a new version during 2021.
I currently work on fastboot support for the new driver. But it's a complicated matter and takes time. If there's interest, we could talk about merging what's already there.
Best regards Thomas
[1] https://lore.kernel.org/dri-devel/20200625120011.16168-1-tzimmermann@suse.de...
There is an email in this thread from 2018, but when I tried to import an mbox file from the whole month for August 2018, for some reason, kmail doesn't see the sender and mailing list recipient in that one, so I will reply to this one, because I was able to import this into my mail client. https://www.spinics.net/lists/dri-devel/msg185519.html
I was able to get this to build against Linux 4.8, but not against a newer version, some headers seem to have been split, and some things are off by 8 and other things. I could NOT find a git repo, but I was able to find the newest patches I could find, and import those with git am against 4.8 with some tweaks. If that is needed, I can link it, but only if you want.
However in QEMU I wasn't able to figure out how to make it create a /dev/dri/card0 device, even after blacklisting the other modules for qxl, cirrus, etc, and then modprobe-ing simpledrm
In my view something like this is would be useful. There still could be hardware devices that don't have modesetting support (like vmvga in qemu/virt-manager as an example). And most wayland servers need a /dev/dri/card0 device as well as a potential user-mode TTY replacement would also need /dev/dri/card0
I will admit I unfortunately failed to get it to build against master. I couldn't figure out some of the changes, where some new structs were off by a factor of 8.
Thanks
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Hi
I tried simplekms against v5.9, and it built, and it runs, and is pretty neat.
I tried using the qxl, cirrus, and vmware card in QEMU. Weston starts on all of them. And I did ensure that the simplekms driver was being used
That is, it works after adding GRUB_GFXPAYLOAD_LINUX=keep , to avoid having to set a VGA option. (although not sure the equivalent in syslinux yet)
Thanks.
Hi
Am 12.03.21 um 04:49 schrieb nerdopolis:
On Wednesday, March 10, 2021 4:10:35 AM EST Thomas Zimmermann wrote:
Hi
Am 10.03.21 um 03:50 schrieb nerdopolis:
On Friday, September 2, 2016 4:22:38 AM EST David Herrmann wrote:
Hey
On request of Noralf, I picked up the patches and prepared v5. Works fine with Xorg, if configured according to: https://lists.freedesktop.org/archives/dri-devel/2014-January/052777.html If anyone knows how to make Xorg pick it up dynamically without such a static configuration, please let me know.
Hi
I am kind of curious as I do have interest in seeing this merged as well.
Please take a look at [1]. It's not the same driver, but something to the same effect. I know it's been almost a year, but I do work on this and intend to come back with a new version during 2021.
I currently work on fastboot support for the new driver. But it's a complicated matter and takes time. If there's interest, we could talk about merging what's already there.
Best regards Thomas
[1] https://lore.kernel.org/dri-devel/20200625120011.16168-1-tzimmermann@suse.de...
There is an email in this thread from 2018, but when I tried to import an mbox file from the whole month for August 2018, for some reason, kmail doesn't see the sender and mailing list recipient in that one, so I will reply to this one, because I was able to import this into my mail client. https://www.spinics.net/lists/dri-devel/msg185519.html
I was able to get this to build against Linux 4.8, but not against a newer version, some headers seem to have been split, and some things are off by 8 and other things. I could NOT find a git repo, but I was able to find the newest patches I could find, and import those with git am against 4.8 with some tweaks. If that is needed, I can link it, but only if you want.
However in QEMU I wasn't able to figure out how to make it create a /dev/dri/card0 device, even after blacklisting the other modules for qxl, cirrus, etc, and then modprobe-ing simpledrm
In my view something like this is would be useful. There still could be hardware devices that don't have modesetting support (like vmvga in qemu/virt-manager as an example). And most wayland servers need a /dev/dri/card0 device as well as a potential user-mode TTY replacement would also need /dev/dri/card0
I will admit I unfortunately failed to get it to build against master. I couldn't figure out some of the changes, where some new structs were off by a factor of 8.
Thanks
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Hi
I tried simplekms against v5.9, and it built, and it runs, and is pretty neat.
I tried using the qxl, cirrus, and vmware card in QEMU. Weston starts on all of them. And I did ensure that the simplekms driver was being used
Cool! Thanks a lot. When I submit the next patchset can I add
Tested-by: nerdopolis bluescreen_avenger@verizon.net
to the tags?
That is, it works after adding GRUB_GFXPAYLOAD_LINUX=keep , to avoid having to set a VGA option. (although not sure the equivalent in syslinux yet)
Yeah, it's a known 'limitation.' (1) But it's usually something that Linux distributions take care of.
The rsp kernel feature needs a set up from the firmware/bootloader/etc. Once the driver has been merged, added other generic drivers (EFI, VESA, etc) should be a lot easier. Those would maybe not require the firmware setup.
Best regards Thomas
(1) Well, it's the way it's supposed to work.
Thanks.
On Friday, March 12, 2021 3:03:46 AM EST Thomas Zimmermann wrote:
Hi
Am 12.03.21 um 04:49 schrieb nerdopolis:
On Wednesday, March 10, 2021 4:10:35 AM EST Thomas Zimmermann wrote:
Hi
Am 10.03.21 um 03:50 schrieb nerdopolis:
On Friday, September 2, 2016 4:22:38 AM EST David Herrmann wrote:
Hey
On request of Noralf, I picked up the patches and prepared v5. Works fine with Xorg, if configured according to: https://lists.freedesktop.org/archives/dri-devel/2014-January/052777.html If anyone knows how to make Xorg pick it up dynamically without such a static configuration, please let me know.
Hi
I am kind of curious as I do have interest in seeing this merged as well.
Please take a look at [1]. It's not the same driver, but something to the same effect. I know it's been almost a year, but I do work on this and intend to come back with a new version during 2021.
I currently work on fastboot support for the new driver. But it's a complicated matter and takes time. If there's interest, we could talk about merging what's already there.
Best regards Thomas
[1] https://lore.kernel.org/dri-devel/20200625120011.16168-1-tzimmermann@suse.de...
There is an email in this thread from 2018, but when I tried to import an mbox file from the whole month for August 2018, for some reason, kmail doesn't see the sender and mailing list recipient in that one, so I will reply to this one, because I was able to import this into my mail client. https://www.spinics.net/lists/dri-devel/msg185519.html
I was able to get this to build against Linux 4.8, but not against a newer version, some headers seem to have been split, and some things are off by 8 and other things. I could NOT find a git repo, but I was able to find the newest patches I could find, and import those with git am against 4.8 with some tweaks. If that is needed, I can link it, but only if you want.
However in QEMU I wasn't able to figure out how to make it create a /dev/dri/card0 device, even after blacklisting the other modules for qxl, cirrus, etc, and then modprobe-ing simpledrm
In my view something like this is would be useful. There still could be hardware devices that don't have modesetting support (like vmvga in qemu/virt-manager as an example). And most wayland servers need a /dev/dri/card0 device as well as a potential user-mode TTY replacement would also need /dev/dri/card0
I will admit I unfortunately failed to get it to build against master. I couldn't figure out some of the changes, where some new structs were off by a factor of 8.
Thanks
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Hi
I tried simplekms against v5.9, and it built, and it runs, and is pretty neat.
I tried using the qxl, cirrus, and vmware card in QEMU. Weston starts on all of them. And I did ensure that the simplekms driver was being used
Cool! Thanks a lot. When I submit the next patchset can I add
Tested-by: nerdopolis bluescreen_avenger@verizon.net
to the tags?
Sure!
That is, it works after adding GRUB_GFXPAYLOAD_LINUX=keep , to avoid having to set a VGA option. (although not sure the equivalent in syslinux yet)
Yeah, it's a known 'limitation.' (1) But it's usually something that Linux distributions take care of.
The rsp kernel feature needs a set up from the firmware/bootloader/etc. Once the driver has been merged, added other generic drivers (EFI, VESA, etc) should be a lot easier. Those would maybe not require the firmware setup.
Best regards Thomas
(1) Well, it's the way it's supposed to work.
Thanks.
dri-devel@lists.freedesktop.org