From: David Herrmann <dh.herrmann@gmail.com>
To: linux-kernel@vger.kernel.org
Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>,
linux-fbdev@vger.kernel.org, David Airlie <airlied@linux.ie>,
dri-devel@lists.freedesktop.org,
David Herrmann <dh.herrmann@googlemail.com>
Subject: [PATCH 1/9] video: introduce system framebuffer bus
Date: Sun, 17 Feb 2013 17:59:03 +0000 [thread overview]
Message-ID: <1361123951-587-2-git-send-email-dh.herrmann@gmail.com> (raw)
In-Reply-To: <1361123951-587-1-git-send-email-dh.herrmann@gmail.com>
From: David Herrmann <dh.herrmann@googlemail.com>
For a long time now we have the problem that there are multiple drivers
available that try to use system framebuffers (like EFI, VESA/VBE, ...).
There is no way to control which driver gets access to the devices, but
instead works on a first-come-first-serve basis.
Furthermore, hardware drivers (eg., gpu/drm/*) that get loaded on the
real hardware bus (eg., pci-bus) of the framebuffer devices have a hard
time unloading other drivers that currently use system framebuffers.
This introduces a new bus-type: sysfb (system framebuffer bus)
Any available system framebuffer gets registered as a device on this bus.
A bus-driver can then pick up the device and use it. Standard sysfs
bind/unbind interfaces can be used to change drivers on-the-fly.
There are actually two types of drivers: generic and real drivers
Generic drivers use the generic framebuffer interface exclusively. They
are often used as a fallback interface where no real driver for the
hardware is available. Generic drivers register as sysfb drivers to the
sysfb bus and will get loaded dynamically. User-space can bind/unbind them
via sysfs to control which driver should get access.
Only one driver can be active per device. During probe the driver can
retrieve additional information via a screen_info object of the device.
Generic drivers include: efifb, (u)vesafb, vgacon, ...
Real drivers work differently. Instead of being loaded via sysfb, they
register as drivers on the real bus (eg., pci-bus). During probe they
should verify whether their real device provides a system-framebuffer. If
it does, they call sysfb_claim() to claim exclusive access to the device.
This guarantees that any generic driver gets unloaded and the real
hardware driver can gain access. This also guarantees that a real hardware
driver always takes precedence over generic fallback drivers.
Real drivers include: i915, radeon, nouveau, ...
Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
drivers/video/Kconfig | 17 ++++
drivers/video/Makefile | 1 +
drivers/video/sysfb.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/sysfb.h | 134 ++++++++++++++++++++++++++++
4 files changed, 382 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 d08d799..eac56ef 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -27,6 +27,23 @@ config VGASTATE
tristate
default n
+config SYSFB
+ tristate "System Framebuffer Bus"
+ help
+ Framebuffers like VGA, VESA/VBE, EFI and others can be handled by many
+ different drivers. This bus provides an infrastructure for drivers to
+ register themselves and then get bound/unbound to these system-wide
+ framebuffers.
+ This bus prevents framebuffers from being used by multiple drivers
+ simultaneously and also provides a sysfs API to bind/rebind different
+ drivers to each device from userspace.
+
+ Chipset-specific drivers (like real GPU drivers) will always take
+ precedence over generic framebuffer drivers.
+
+ A driver should normally select this bus-option automatically. Enable
+ it only if you need out-of-tree builds.
+
config VIDEO_OUTPUT_CONTROL
tristate "Lowlevel video output switch controls"
help
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 23e948e..f0eb006 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -5,6 +5,7 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_VGASTATE) += vgastate.o
+obj-$(CONFIG_SYSFB) += sysfb.o
obj-y += fb_notify.o
obj-$(CONFIG_FB) += fb.o
fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
diff --git a/drivers/video/sysfb.c b/drivers/video/sysfb.c
new file mode 100644
index 0000000..8249006
--- /dev/null
+++ b/drivers/video/sysfb.c
@@ -0,0 +1,230 @@
+/*
+ * System framebuffer bus
+ * Copyright (c) 2013 David Herrmann
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+/*
+ * The system framebuffer bus (sysfb) provides a way to register global system
+ * framebuffers and load different drivers for it. This includes VESA/VBE and
+ * EFI framebuffers.
+ * Platform code is responsible of adding the framebuffer devices to the system
+ * platform bus. The sysfb bus will pick up known devices and provide them via
+ * the sysfb bus to system drivers. This guarantees that only one driver uses
+ * a single system framebuffer at a time.
+ *
+ * Drivers that can make use of the generic interfaces of system framebuffers
+ * should register as a sysfb driver. They will get notified via probe/remove
+ * callbacks just like any other hotpluggable driver. Users can load/unload
+ * drivers via the sysfs bus interface so drivers must be hotplug capable.
+ *
+ * Drivers that cannot make use of the generic interfaces but instead control
+ * the real hardware should instead claim the device. These drivers normally
+ * register through PCI or platform devices and control the device via another
+ * interface.
+ * By claiming a device, all other generic drivers are unregistered and no more
+ * drivers will be probed unless the device is released again.
+ *
+ * Only _real_ hardware drivers should claim devices as there is always another
+ * mechanism to control which real hardware driver gets loaded (eg. pci-bus).
+ * Generic drivers which aren't controlled via another bus should use this
+ * generic sysfb driver interface instead of claiming a device.
+ *
+ * All drivers must make sure that after they get unloaded or release a device,
+ * the device is reset to a usable state. If the driver cannot guarantee that,
+ * it should taint the device so other drivers will notice it and can
+ * optionally recover the device.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/screen_info.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/sysfb.h>
+
+static DEFINE_SPINLOCK(sysfb_lock);
+static unsigned int claimed_types;
+
+static int sysfb_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct sysfb_device *sdev = to_sysfb_device(dev);
+ struct sysfb_driver *sdrv = to_sysfb_driver(drv);
+
+ return (sdrv->type_mask & sdev->type) &&
+ (sdrv->allow_tainted || !sdev->tainted);
+}
+
+static int sysfb_bus_probe(struct device *dev)
+{
+ struct sysfb_device *sdev = to_sysfb_device(dev);
+ struct sysfb_driver *sdrv = to_sysfb_driver(dev->driver);
+ unsigned long flags;
+ int ret;
+
+ if (!(sdrv->type_mask & sdev->type))
+ return -ENODEV;
+ if (!sdrv->allow_tainted && sdev->tainted)
+ return -ENODEV;
+
+ spin_lock_irqsave(&sysfb_lock, flags);
+ if ((claimed_types & sdev->type)) {
+ spin_unlock_irqrestore(&sysfb_lock, flags);
+ return -ENODEV;
+ }
+ spin_unlock_irqrestore(&sysfb_lock, flags);
+
+ if (sdrv->probe) {
+ ret = sdrv->probe(sdev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sysfb_bus_remove(struct device *dev)
+{
+ struct sysfb_device *sdev = to_sysfb_device(dev);
+ struct sysfb_driver *sdrv = to_sysfb_driver(dev->driver);
+
+ if (sdrv->remove)
+ sdrv->remove(sdev);
+
+ return 0;
+}
+
+static struct bus_type sysfb_bus_type = {
+ .name = "sysfb",
+ .match = sysfb_bus_match,
+ .probe = sysfb_bus_probe,
+ .remove = sysfb_bus_remove,
+};
+
+int sysfb_register_driver(struct sysfb_driver *drv)
+{
+ int ret;
+
+ drv->driver.bus = &sysfb_bus_type;
+
+ ret = driver_register(&drv->driver);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(sysfb_register_driver);
+
+void sysfb_unregister_driver(struct sysfb_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(sysfb_unregister_driver);
+
+static int __sysfb_rescan(struct device *dev, void *data)
+{
+ return device_attach(dev);
+}
+
+static void sysfb_rescan(void)
+{
+ bus_for_each_dev(&sysfb_bus_type, NULL, NULL, __sysfb_rescan);
+}
+
+static int __sysfb_claim(struct device *dev, void *data)
+{
+ struct sysfb_device *sdev = to_sysfb_device(dev);
+ unsigned int claim = (long)data;
+
+ if (!(sdev->type & claim))
+ return 0;
+
+ device_release_driver(dev);
+ return 0;
+}
+
+int sysfb_claim(unsigned int types)
+{
+ unsigned long flags;
+ int ret;
+
+ if (!(types & SYSFB_TYPES))
+ return -EINVAL;
+
+ spin_lock_irqsave(&sysfb_lock, flags);
+ if ((claimed_types & types)) {
+ spin_unlock_irqrestore(&sysfb_lock, flags);
+ return -EALREADY;
+ }
+ claimed_types |= types;
+ spin_unlock_irqrestore(&sysfb_lock, flags);
+
+ ret = bus_for_each_dev(&sysfb_bus_type, NULL, (void*)(long)types,
+ __sysfb_claim);
+ if (ret)
+ goto err_restore;
+
+ return 0;
+
+err_restore:
+ spin_lock_irqsave(&sysfb_lock, flags);
+ claimed_types &= ~types;
+ spin_unlock_irqrestore(&sysfb_lock, flags);
+
+ sysfb_rescan();
+ return ret;
+}
+EXPORT_SYMBOL(sysfb_claim);
+
+void sysfb_release(unsigned int types)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sysfb_lock, flags);
+ claimed_types &= ~types;
+ spin_unlock_irqrestore(&sysfb_lock, flags);
+
+ sysfb_rescan();
+}
+EXPORT_SYMBOL(sysfb_release);
+
+void sysfb_taint(struct sysfb_device *sdev, bool set)
+{
+ sdev->tainted = set;
+}
+EXPORT_SYMBOL(sysfb_taint);
+
+static int __init sysfb_init(void)
+{
+ int ret;
+
+ ret = bus_register(&sysfb_bus_type);
+ if (ret) {
+ pr_err("cannot register sysfb bus\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit sysfb_exit(void)
+{
+ bus_unregister(&sysfb_bus_type);
+}
+
+module_init(sysfb_init);
+module_exit(sysfb_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
+MODULE_DESCRIPTION("System framebuffer bus");
diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h
new file mode 100644
index 0000000..6cd3c24
--- /dev/null
+++ b/include/linux/sysfb.h
@@ -0,0 +1,134 @@
+#ifndef __LINUX_SYSFB_H_
+#define __LINUX_SYSFB_H_
+
+/*
+ * System framebuffer bus
+ * Copyright (c) 2013 David Herrmann
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/screen_info.h>
+#include <linux/types.h>
+
+/**
+ * sysfb_type
+ *
+ * Different types of available framebuffer devices. Only one device of each
+ * type can be available at a time. In most systems there even is only one
+ * device at all.
+ *
+ * Use the sysfb_device->screen pointer to get information about the framebuffer
+ * devices.
+ */
+enum sysfb_type {
+ SYSFB_TYPES = 0,
+};
+
+/**
+ * sysfb_device
+ * @tainted: whether the device was tainted or not
+ * @type: type of the fb device (@sysfb_type)
+ * @screen: pointer to supplemental screen-info object
+ * @dev: device object
+ *
+ * Each framebuffer device is represented by a sysfb_device object. The sysfb
+ * core manages them and they cannot be registered from the outside.
+ */
+struct sysfb_device {
+ bool tainted;
+ unsigned int type;
+ struct screen_info *screen;
+ struct device dev;
+};
+
+#define to_sysfb_device(_dev) container_of((_dev), struct sysfb_device, dev)
+
+/**
+ * sysfb_driver
+ * @type_mask: mask of device-types that are supported (@sysfb_type)
+ * @allow_tainted: whether the driver can be bound to tainted devices
+ * @driver: driver object
+ * @probe: probe callback
+ * @remove: remove callback
+ *
+ * Each generic framebuffer driver must provide this structure when registering
+ * to the sysfb core. The @driver field must also be provided by the caller
+ * except for the 'driver.bus' field which is initialized by the core.
+ */
+struct sysfb_driver {
+ unsigned int type_mask;
+ bool allow_tainted;
+ struct device_driver driver;
+
+ int (*probe) (struct sysfb_device *dev);
+ void (*remove) (struct sysfb_device *dev);
+};
+
+#define to_sysfb_driver(_drv) container_of((_drv), struct sysfb_driver, driver)
+
+/**
+ * sysfb_register_driver
+ * @drv: Driver object
+ *
+ * Register a new driver on the sysfb bus.
+ */
+int sysfb_register_driver(struct sysfb_driver *drv);
+
+/**
+ * sysfb_unregister_driver
+ * @drv: Driver object
+ *
+ * Remove a driver from the sysfb bus.
+ */
+void sysfb_unregister_driver(struct sysfb_driver *drv);
+
+/**
+ * sysfb_claim
+ * @types: Bitmask of sysfb_type flags
+ *
+ * Unbind all drivers from all devices matching the given types and prevent
+ * further drivers to get loaded on these types of devices. This allows real
+ * hardware drivers that are loaded by other bus-types (eg. pci-bus) to prevent
+ * any generic driver from using the given framebuffer types.
+ *
+ * Return 0 if the types could be claimed, otherwise a negative error code
+ * is returned.
+ */
+int sysfb_claim(unsigned int types);
+
+/**
+ * sysfb_release
+ * @types: Bitmask of sysfb_type flags
+ *
+ * Releases the given previously claimed types. See sysfb_claim(). This does
+ * not check whether the types are actually claimed or who claimed them. So make
+ * sure to call this only when you really claimed these types previously.
+ */
+void sysfb_release(unsigned int types);
+
+/**
+ * sysfb_taint
+ * @sdev: sysfb device
+ * @set: whether to taint or untaint
+ *
+ * This taints a given sysfb device. This should be done by all drivers if they
+ * change the framebuffer device in a way that other generic drivers might not
+ * be able to detect afterwards.
+ * This includes changing the resolution or properties of a framebuffer without
+ * adjusting the screen_info object.
+ * This can be reset to 'false' after all the changes have been undone.
+ *
+ * This is an unlocked function. You must call it from within your probe/remove
+ * callbacks in the driver.
+ */
+void sysfb_taint(struct sysfb_device *sdev, bool set);
+
+#endif /* __LINUX_SYSFB_H_ */
--
1.8.1.3
WARNING: multiple messages have this Message-ID (diff)
From: David Herrmann <dh.herrmann@gmail.com>
To: linux-kernel@vger.kernel.org
Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>,
linux-fbdev@vger.kernel.org, David Airlie <airlied@linux.ie>,
dri-devel@lists.freedesktop.org,
David Herrmann <dh.herrmann@googlemail.com>
Subject: [PATCH 1/9] video: introduce system framebuffer bus
Date: Sun, 17 Feb 2013 18:59:03 +0100 [thread overview]
Message-ID: <1361123951-587-2-git-send-email-dh.herrmann@gmail.com> (raw)
In-Reply-To: <1361123951-587-1-git-send-email-dh.herrmann@gmail.com>
From: David Herrmann <dh.herrmann@googlemail.com>
For a long time now we have the problem that there are multiple drivers
available that try to use system framebuffers (like EFI, VESA/VBE, ...).
There is no way to control which driver gets access to the devices, but
instead works on a first-come-first-serve basis.
Furthermore, hardware drivers (eg., gpu/drm/*) that get loaded on the
real hardware bus (eg., pci-bus) of the framebuffer devices have a hard
time unloading other drivers that currently use system framebuffers.
This introduces a new bus-type: sysfb (system framebuffer bus)
Any available system framebuffer gets registered as a device on this bus.
A bus-driver can then pick up the device and use it. Standard sysfs
bind/unbind interfaces can be used to change drivers on-the-fly.
There are actually two types of drivers: generic and real drivers
Generic drivers use the generic framebuffer interface exclusively. They
are often used as a fallback interface where no real driver for the
hardware is available. Generic drivers register as sysfb drivers to the
sysfb bus and will get loaded dynamically. User-space can bind/unbind them
via sysfs to control which driver should get access.
Only one driver can be active per device. During probe the driver can
retrieve additional information via a screen_info object of the device.
Generic drivers include: efifb, (u)vesafb, vgacon, ...
Real drivers work differently. Instead of being loaded via sysfb, they
register as drivers on the real bus (eg., pci-bus). During probe they
should verify whether their real device provides a system-framebuffer. If
it does, they call sysfb_claim() to claim exclusive access to the device.
This guarantees that any generic driver gets unloaded and the real
hardware driver can gain access. This also guarantees that a real hardware
driver always takes precedence over generic fallback drivers.
Real drivers include: i915, radeon, nouveau, ...
Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
drivers/video/Kconfig | 17 ++++
drivers/video/Makefile | 1 +
drivers/video/sysfb.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/sysfb.h | 134 ++++++++++++++++++++++++++++
4 files changed, 382 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 d08d799..eac56ef 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -27,6 +27,23 @@ config VGASTATE
tristate
default n
+config SYSFB
+ tristate "System Framebuffer Bus"
+ help
+ Framebuffers like VGA, VESA/VBE, EFI and others can be handled by many
+ different drivers. This bus provides an infrastructure for drivers to
+ register themselves and then get bound/unbound to these system-wide
+ framebuffers.
+ This bus prevents framebuffers from being used by multiple drivers
+ simultaneously and also provides a sysfs API to bind/rebind different
+ drivers to each device from userspace.
+
+ Chipset-specific drivers (like real GPU drivers) will always take
+ precedence over generic framebuffer drivers.
+
+ A driver should normally select this bus-option automatically. Enable
+ it only if you need out-of-tree builds.
+
config VIDEO_OUTPUT_CONTROL
tristate "Lowlevel video output switch controls"
help
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 23e948e..f0eb006 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -5,6 +5,7 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_VGASTATE) += vgastate.o
+obj-$(CONFIG_SYSFB) += sysfb.o
obj-y += fb_notify.o
obj-$(CONFIG_FB) += fb.o
fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
diff --git a/drivers/video/sysfb.c b/drivers/video/sysfb.c
new file mode 100644
index 0000000..8249006
--- /dev/null
+++ b/drivers/video/sysfb.c
@@ -0,0 +1,230 @@
+/*
+ * System framebuffer bus
+ * Copyright (c) 2013 David Herrmann
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+/*
+ * The system framebuffer bus (sysfb) provides a way to register global system
+ * framebuffers and load different drivers for it. This includes VESA/VBE and
+ * EFI framebuffers.
+ * Platform code is responsible of adding the framebuffer devices to the system
+ * platform bus. The sysfb bus will pick up known devices and provide them via
+ * the sysfb bus to system drivers. This guarantees that only one driver uses
+ * a single system framebuffer at a time.
+ *
+ * Drivers that can make use of the generic interfaces of system framebuffers
+ * should register as a sysfb driver. They will get notified via probe/remove
+ * callbacks just like any other hotpluggable driver. Users can load/unload
+ * drivers via the sysfs bus interface so drivers must be hotplug capable.
+ *
+ * Drivers that cannot make use of the generic interfaces but instead control
+ * the real hardware should instead claim the device. These drivers normally
+ * register through PCI or platform devices and control the device via another
+ * interface.
+ * By claiming a device, all other generic drivers are unregistered and no more
+ * drivers will be probed unless the device is released again.
+ *
+ * Only _real_ hardware drivers should claim devices as there is always another
+ * mechanism to control which real hardware driver gets loaded (eg. pci-bus).
+ * Generic drivers which aren't controlled via another bus should use this
+ * generic sysfb driver interface instead of claiming a device.
+ *
+ * All drivers must make sure that after they get unloaded or release a device,
+ * the device is reset to a usable state. If the driver cannot guarantee that,
+ * it should taint the device so other drivers will notice it and can
+ * optionally recover the device.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/screen_info.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/sysfb.h>
+
+static DEFINE_SPINLOCK(sysfb_lock);
+static unsigned int claimed_types;
+
+static int sysfb_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct sysfb_device *sdev = to_sysfb_device(dev);
+ struct sysfb_driver *sdrv = to_sysfb_driver(drv);
+
+ return (sdrv->type_mask & sdev->type) &&
+ (sdrv->allow_tainted || !sdev->tainted);
+}
+
+static int sysfb_bus_probe(struct device *dev)
+{
+ struct sysfb_device *sdev = to_sysfb_device(dev);
+ struct sysfb_driver *sdrv = to_sysfb_driver(dev->driver);
+ unsigned long flags;
+ int ret;
+
+ if (!(sdrv->type_mask & sdev->type))
+ return -ENODEV;
+ if (!sdrv->allow_tainted && sdev->tainted)
+ return -ENODEV;
+
+ spin_lock_irqsave(&sysfb_lock, flags);
+ if ((claimed_types & sdev->type)) {
+ spin_unlock_irqrestore(&sysfb_lock, flags);
+ return -ENODEV;
+ }
+ spin_unlock_irqrestore(&sysfb_lock, flags);
+
+ if (sdrv->probe) {
+ ret = sdrv->probe(sdev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sysfb_bus_remove(struct device *dev)
+{
+ struct sysfb_device *sdev = to_sysfb_device(dev);
+ struct sysfb_driver *sdrv = to_sysfb_driver(dev->driver);
+
+ if (sdrv->remove)
+ sdrv->remove(sdev);
+
+ return 0;
+}
+
+static struct bus_type sysfb_bus_type = {
+ .name = "sysfb",
+ .match = sysfb_bus_match,
+ .probe = sysfb_bus_probe,
+ .remove = sysfb_bus_remove,
+};
+
+int sysfb_register_driver(struct sysfb_driver *drv)
+{
+ int ret;
+
+ drv->driver.bus = &sysfb_bus_type;
+
+ ret = driver_register(&drv->driver);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(sysfb_register_driver);
+
+void sysfb_unregister_driver(struct sysfb_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(sysfb_unregister_driver);
+
+static int __sysfb_rescan(struct device *dev, void *data)
+{
+ return device_attach(dev);
+}
+
+static void sysfb_rescan(void)
+{
+ bus_for_each_dev(&sysfb_bus_type, NULL, NULL, __sysfb_rescan);
+}
+
+static int __sysfb_claim(struct device *dev, void *data)
+{
+ struct sysfb_device *sdev = to_sysfb_device(dev);
+ unsigned int claim = (long)data;
+
+ if (!(sdev->type & claim))
+ return 0;
+
+ device_release_driver(dev);
+ return 0;
+}
+
+int sysfb_claim(unsigned int types)
+{
+ unsigned long flags;
+ int ret;
+
+ if (!(types & SYSFB_TYPES))
+ return -EINVAL;
+
+ spin_lock_irqsave(&sysfb_lock, flags);
+ if ((claimed_types & types)) {
+ spin_unlock_irqrestore(&sysfb_lock, flags);
+ return -EALREADY;
+ }
+ claimed_types |= types;
+ spin_unlock_irqrestore(&sysfb_lock, flags);
+
+ ret = bus_for_each_dev(&sysfb_bus_type, NULL, (void*)(long)types,
+ __sysfb_claim);
+ if (ret)
+ goto err_restore;
+
+ return 0;
+
+err_restore:
+ spin_lock_irqsave(&sysfb_lock, flags);
+ claimed_types &= ~types;
+ spin_unlock_irqrestore(&sysfb_lock, flags);
+
+ sysfb_rescan();
+ return ret;
+}
+EXPORT_SYMBOL(sysfb_claim);
+
+void sysfb_release(unsigned int types)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sysfb_lock, flags);
+ claimed_types &= ~types;
+ spin_unlock_irqrestore(&sysfb_lock, flags);
+
+ sysfb_rescan();
+}
+EXPORT_SYMBOL(sysfb_release);
+
+void sysfb_taint(struct sysfb_device *sdev, bool set)
+{
+ sdev->tainted = set;
+}
+EXPORT_SYMBOL(sysfb_taint);
+
+static int __init sysfb_init(void)
+{
+ int ret;
+
+ ret = bus_register(&sysfb_bus_type);
+ if (ret) {
+ pr_err("cannot register sysfb bus\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit sysfb_exit(void)
+{
+ bus_unregister(&sysfb_bus_type);
+}
+
+module_init(sysfb_init);
+module_exit(sysfb_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
+MODULE_DESCRIPTION("System framebuffer bus");
diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h
new file mode 100644
index 0000000..6cd3c24
--- /dev/null
+++ b/include/linux/sysfb.h
@@ -0,0 +1,134 @@
+#ifndef __LINUX_SYSFB_H_
+#define __LINUX_SYSFB_H_
+
+/*
+ * System framebuffer bus
+ * Copyright (c) 2013 David Herrmann
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/screen_info.h>
+#include <linux/types.h>
+
+/**
+ * sysfb_type
+ *
+ * Different types of available framebuffer devices. Only one device of each
+ * type can be available at a time. In most systems there even is only one
+ * device at all.
+ *
+ * Use the sysfb_device->screen pointer to get information about the framebuffer
+ * devices.
+ */
+enum sysfb_type {
+ SYSFB_TYPES = 0,
+};
+
+/**
+ * sysfb_device
+ * @tainted: whether the device was tainted or not
+ * @type: type of the fb device (@sysfb_type)
+ * @screen: pointer to supplemental screen-info object
+ * @dev: device object
+ *
+ * Each framebuffer device is represented by a sysfb_device object. The sysfb
+ * core manages them and they cannot be registered from the outside.
+ */
+struct sysfb_device {
+ bool tainted;
+ unsigned int type;
+ struct screen_info *screen;
+ struct device dev;
+};
+
+#define to_sysfb_device(_dev) container_of((_dev), struct sysfb_device, dev)
+
+/**
+ * sysfb_driver
+ * @type_mask: mask of device-types that are supported (@sysfb_type)
+ * @allow_tainted: whether the driver can be bound to tainted devices
+ * @driver: driver object
+ * @probe: probe callback
+ * @remove: remove callback
+ *
+ * Each generic framebuffer driver must provide this structure when registering
+ * to the sysfb core. The @driver field must also be provided by the caller
+ * except for the 'driver.bus' field which is initialized by the core.
+ */
+struct sysfb_driver {
+ unsigned int type_mask;
+ bool allow_tainted;
+ struct device_driver driver;
+
+ int (*probe) (struct sysfb_device *dev);
+ void (*remove) (struct sysfb_device *dev);
+};
+
+#define to_sysfb_driver(_drv) container_of((_drv), struct sysfb_driver, driver)
+
+/**
+ * sysfb_register_driver
+ * @drv: Driver object
+ *
+ * Register a new driver on the sysfb bus.
+ */
+int sysfb_register_driver(struct sysfb_driver *drv);
+
+/**
+ * sysfb_unregister_driver
+ * @drv: Driver object
+ *
+ * Remove a driver from the sysfb bus.
+ */
+void sysfb_unregister_driver(struct sysfb_driver *drv);
+
+/**
+ * sysfb_claim
+ * @types: Bitmask of sysfb_type flags
+ *
+ * Unbind all drivers from all devices matching the given types and prevent
+ * further drivers to get loaded on these types of devices. This allows real
+ * hardware drivers that are loaded by other bus-types (eg. pci-bus) to prevent
+ * any generic driver from using the given framebuffer types.
+ *
+ * Return 0 if the types could be claimed, otherwise a negative error code
+ * is returned.
+ */
+int sysfb_claim(unsigned int types);
+
+/**
+ * sysfb_release
+ * @types: Bitmask of sysfb_type flags
+ *
+ * Releases the given previously claimed types. See sysfb_claim(). This does
+ * not check whether the types are actually claimed or who claimed them. So make
+ * sure to call this only when you really claimed these types previously.
+ */
+void sysfb_release(unsigned int types);
+
+/**
+ * sysfb_taint
+ * @sdev: sysfb device
+ * @set: whether to taint or untaint
+ *
+ * This taints a given sysfb device. This should be done by all drivers if they
+ * change the framebuffer device in a way that other generic drivers might not
+ * be able to detect afterwards.
+ * This includes changing the resolution or properties of a framebuffer without
+ * adjusting the screen_info object.
+ * This can be reset to 'false' after all the changes have been undone.
+ *
+ * This is an unlocked function. You must call it from within your probe/remove
+ * callbacks in the driver.
+ */
+void sysfb_taint(struct sysfb_device *sdev, bool set);
+
+#endif /* __LINUX_SYSFB_H_ */
--
1.8.1.3
next prev parent reply other threads:[~2013-02-17 17:59 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-02-17 17:59 [PATCH 0/9] System Framebuffer Bus (sysfb) David Herrmann
2013-02-17 17:59 ` David Herrmann
2013-02-17 17:59 ` David Herrmann [this message]
2013-02-17 17:59 ` [PATCH 1/9] video: introduce system framebuffer bus David Herrmann
2013-02-17 17:59 ` [PATCH 2/9] video: sysfb: new vbefb device type David Herrmann
2013-02-17 17:59 ` David Herrmann
2013-02-17 17:59 ` [PATCH 3/9] video: sysfb: always provide vbefb device David Herrmann
2013-02-17 17:59 ` David Herrmann
2013-02-17 17:59 ` [PATCH 4/9] video: vesafb: allow building as module David Herrmann
2013-02-17 17:59 ` David Herrmann
2013-02-17 17:59 ` [PATCH 5/9] video: vesafb: use sysfb bus David Herrmann
2013-02-17 17:59 ` David Herrmann
2013-02-17 17:59 ` [PATCH 6/9] drm: new sysfb DRM bus module David Herrmann
2013-02-17 17:59 ` David Herrmann
2013-02-17 17:59 ` [PATCH 7/9] drm: new VESA BIOS Extension DRM driver stub David Herrmann
2013-02-17 17:59 ` David Herrmann
2013-02-17 17:59 ` [PATCH 8/9] drm: dvbe: implement VBE/VESA blitting backend David Herrmann
2013-02-17 17:59 ` David Herrmann
2013-02-17 17:59 ` [PATCH 9/9] drm: dvbe: add optional fbdev frontend David Herrmann
2013-02-17 17:59 ` David Herrmann
2013-02-17 22:02 ` [PATCH 0/9] System Framebuffer Bus (sysfb) Dave Airlie
2013-02-17 22:02 ` Dave Airlie
2013-02-17 22:02 ` Dave Airlie
2013-02-17 23:35 ` David Herrmann
2013-02-17 23:35 ` David Herrmann
2013-02-17 23:35 ` David Herrmann
2013-02-17 23:47 ` Dave Airlie
2013-02-17 23:47 ` Dave Airlie
2013-02-28 12:20 ` David Herrmann
2013-02-28 12:20 ` David Herrmann
2013-02-28 13:22 ` Geert Uytterhoeven
2013-02-28 13:22 ` Geert Uytterhoeven
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1361123951-587-2-git-send-email-dh.herrmann@gmail.com \
--to=dh.herrmann@gmail.com \
--cc=FlorianSchandinat@gmx.de \
--cc=airlied@linux.ie \
--cc=dh.herrmann@googlemail.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=linux-fbdev@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.