Messages in this thread | | | Subject | Re: radeon-pre-2 | From | Alan Cox <> | Date | Mon, 13 Sep 2004 12:26:33 +0100 |
| |
On Sul, 2004-09-12 at 23:42, Dave Airlie wrote: > The worst things that will happen for all concerened is this: > Jon does all this work on a merged solution outside the kernel, and it > works well, and the X team decide to do a decent X on mesa-solo on Jons > super-DRM, now the super-DRM gets pushed via the X tree and distributions > start relasing kernels with it merged into it
Unlikely. Its rapidly unmaintainable because the core kernel changes will obsolete it (see for example KGI).
> I think yourself and Linus's ideas for a locking scheme look good, I also > know they won't please Jon too much as he can see where the potential > ineffecienes with saving/restore card state on driver swap are, especailly > on running fbcon and X on a dual-head card with different users.
Well this is what I came up with so far. It creates a vga class so you can bind the drivers to functions of the card (and we can add/remove functions later as appropriate), tells functions about each other and now implements Linux lock proposal as I understood it.
Alan
/* * drivers/video/vga-driver.c * */
#include <linux/pci.h> #include <linux/module.h> #include <linux/init.h> #include <linux/device.h> #include <asm/semaphore.h> #include "vga_class.h"
/* * Registration of video drivers and handling of hot-pluggable devices. */
static LIST_HEAD(vga_devices); /* No I don't know why its not DECLARE_LIST_HEAD either */
/** * vga_match_one_device - Tell if a PCI device structure has a matching * PCI device id structure * @id: single PCI device id structure to match * @dev: the PCI device structure to match against * * Returns the matching vga_device_id structure or %NULL if there is no match. */
static inline const struct vga_device_id * vga_match_one_device(const struct vga_device_id *id, const struct pci_dev *dev) { if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) && (id->device == PCI_ANY_ID || id->device == dev->device) && (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) && (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) && !((id->class ^ dev->class) & id->class_mask)) return id; return NULL; }
/** * vga_match_device - Tell if a VGA device structure has a matching * PCI device id structure * @ids: array of PCI device id structures to search in * @dev: the PCI device structure to match against * * Used by a driver to check whether a PCI device present in the * system is in its list of supported devices.Returns the matching * vga_device_id structure or %NULL if there is no match. */ static const struct vga_device_id * vga_match_device(const struct vga_device_id *ids, const struct vga_dev *vdev) { struct pci_dev *pdev = vdev->pci_dev; while (ids->vendor || ids->subvendor || ids->class_mask) { if (vga_match_one_device(ids, pdev) && ids->unit == vdev->unit) return ids; ids++; } return NULL; }
static void vga_device_notify_install(struct vga_dev *vdev, int type) { int i; for(i = TYPE_MEM; i < TYPE_LAST; i++) { struct vga_dev *v = vdev->shared->device[i]; if(v != NULL && v->driver && i != type) v->driver->notify_attach(v, type); } }
static void vga_device_notify_remove(struct vga_dev *vdev, int type) { int i; for(i = TYPE_MEM; i < TYPE_LAST; i++) { struct vga_dev *v = vdev->shared->device[i]; if(v != NULL && v->driver && i != type) v->driver->notify_detach(v, type); } }
/** * vga_device_probe_static() * * returns 0 and sets vdev->driver when drv claims vdev, else error. */ static int vga_device_probe_static(struct vga_driver *vdrv, struct vga_dev *vdev) { int error = -ENODEV; const struct vga_device_id *id;
if (!vdrv->id_table) return error; id = vga_match_device(vdrv->id_table, vdev); if (id) error = vdrv->probe(vdev, id); if (error >= 0) { vdev->driver = vdrv; down(&vdev->shared->shared_sem); vdev->shared->users++; vga_device_notify_install(vdev, vdev->unit); up(&vdev->shared->shared_sem); error = 0; } return error; }
/** * __vga_device_probe() * * returns 0 on success, else error. * side-effect: vdev->driver is set to vdrv when drv claims vdev. */ static int __vga_device_probe(struct vga_driver *vdrv, struct vga_dev *vdev) { int error = 0;
if (!vdev->driver && vdrv->probe) { error = vga_device_probe_static(vdrv, vdev); } return error; }
static int vga_device_probe(struct device *dev) { int error = 0; struct vga_driver *drv; struct vga_dev *vdev;
drv = to_vga_driver(dev->driver); vdev = to_vga_dev(dev); vga_dev_get(vdev); error = __vga_device_probe(drv, vdev); if (error) vga_dev_put(vdev);
return error; }
static int vga_device_remove(struct device *dev) { struct vga_dev *vdev = to_vga_dev(dev); struct vga_driver *vdrv = vdev->driver;
down(&vdev->shared->shared_sem); vdev->shared->users--; if (vdrv) { vga_device_notify_remove(vdev, vdev->unit); if (vdrv->remove) vdrv->remove(vdev, vdev->shared->count); vdev->driver = NULL; } vdev->shared->device[vdev->unit] = NULL; up(&vdev->shared->shared_sem); vga_dev_put(vdev); return 0; }
static int vga_device_suspend(struct device *dev, u32 state) { return 0; }
/* * Default resume method for devices that have no driver provided resume, * or not even a driver at all. */
static int vga_device_resume(struct device *dev) { return 0; }
static struct kobj_type vga_driver_kobj_type = { };
/** * vga_register_driver - register a new pci driver * @drv: the driver structure to register * * Adds the driver structure to the list of registered drivers * Returns the number of vga devices which were claimed by the driver * during registration. The driver remains registered even if the * return value is zero. */
int vga_register_driver(struct vga_driver *drv) { /* initialize common driver fields */ drv->driver.name = drv->name; drv->driver.bus = &vga_bus_type; drv->driver.probe = vga_device_probe; drv->driver.remove = vga_device_remove; drv->driver.kobj.ktype = &vga_driver_kobj_type;
/* register with core */ return driver_register(&drv->driver); }
/** * vga_unregister_driver - unregister a pci driver * @drv: the driver structure to unregister * * Deletes the driver structure from the list of registered VGA drivers, * gives it a chance to clean up by calling its remove() function for * each device it was responsible for, and marks those devices as * driverless. */
void vga_unregister_driver(struct vga_driver *vdrv) { driver_unregister(&vdrv->driver); }
/** * vga_dev_driver - get the vga_driver of a device * @dev: the device to query * @type: type of device * * Returns the appropriate vga_driver structure or %NULL if there is no * registered driver for the device. The shared block is used so that * you can pass your own vdev to receive driver information about * other attached drivers to the same PCI device. Caller must be careful * about locking and use of shared_sem. */
struct vga_driver *vga_dev_driver(const struct vga_dev *vdev, int type) { struct vga_dev *v = vdev->shared->device[type]; if(v == NULL) return NULL; return v->driver; }
/** * vga_bus_match - Tell if a VGA device structure has a matching PCI device id structure * @ids: array of PCI device id structures to search in * @dev: the VGA device structure to match against * * Used by a driver to check whether a VGA device present in the * system is in its list of supported devices.Returns the matching * vga_device_id structure or %NULL if there is no match. */
static int vga_bus_match(struct device *dev, struct device_driver * drv) { const struct vga_dev *vdev = to_vga_dev(dev); struct vga_driver *vdrv = to_vga_driver(drv); const struct vga_device_id * ids = vdrv->id_table; if (!ids) return 0; return vga_match_device(ids, vdev) ? 1 : 0; }
/** * vga_dev_get - increments the reference count of the pci device structure * @dev: the device being referenced * * Each live reference to a device should be refcounted. * * Drivers for VGA devices should normally record such references in * their probe() methods, when they bind to a device, and release * them by calling vga_dev_put(), in their disconnect() methods. * * A pointer to the device with the incremented reference counter is returned. */ struct vga_dev *vga_dev_get(struct vga_dev *vdev) { struct device *tmp;
if (!vdev) return NULL;
tmp = get_device(&vdev->dev); if (tmp) return to_vga_dev(tmp); else return NULL; }
/** * vga_dev_put - release a use of the vga_dev structure * @dev: device that's been disconnected * * Must be called when a user of a device is finished with it. When the last * user of the device calls this function, the memory of the device is freed. */ void vga_dev_put(struct vga_dev *dev) { if (dev) put_device(&dev->dev); }
/* For now */ int vga_hotplug (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) { return -ENODEV; }
struct bus_type vga_bus_type = { .name = "vga", .match = vga_bus_match, .hotplug = vga_hotplug, .suspend = vga_device_suspend, .resume = vga_device_resume, };
/* * Helper for VESAfb and friends. */
static int mmio_resource_overlap(struct pci_dev *dev, int i, unsigned long mmio) { unsigned long st = pci_resource_start(dev, i); unsigned long size = pci_resource_len(dev, i); unsigned long flags = pci_resource_flags(dev, i); if(st == 0 || size == 0) return 0; if(st + size < mmio) return 0; if(st > mmio) return 0; if(flags & IORESOURCE_IO) return 0; return 1; }
static struct pci_dev *vga_find_by_mmio(unsigned long mmio) { struct list_head *l; list_for_each(l, &vga_devices) { struct vga_dev *vdev = list_entry(l, struct vga_dev, next); int i; for (i = 0; i < 6; i++) { if (mmio_resource_overlap(vdev->pci_dev, i, mmio)) return vdev->pci_dev; } } /* Check ISA window routing ? */ return NULL; }
/* * Big locking as suggested by Linus. Drivers can of course be * more friendly and work together on some things. */ void vga_take_lock(struct vga_dev *vdev, struct vga_driver *vdrv, void *context) { struct vga_shared *v = vdev->shared; down(&v->shared_sem); down(&v->fb_sem); if(v->lock_owner != vdrv || v->lock_context != context) { if(v->lock_release) v->lock_release(vdev, v->lock_context); v->lock_release = NULL; } v->lock_owner = vdrv; v->lock_context = context; }
EXPORT_SYMBOL_GPL(vga_take_lock);
/* * Drop the big locking */ void vga_drop_lock(struct vga_dev *vdev, void (*lock_release)(struct vga_dev *, void *)) { struct vga_shared *v = vdev->shared; v->lock_release = lock_release; up(&v->fb_sem); up(&v->shared_sem); }
EXPORT_SYMBOL_GPL(vga_drop_lock);
/* * VGA device discovery from the PCI side */ /** * vga_release_dev - free a pci device structure when all users of it are finished. * @dev: device that's been disconnected * * Will be called only by the device core when all users of this vga device are * done. */
static void vga_release_dev(struct device *dev) { struct vga_dev *vdev = to_vga_dev(dev); /* We want this very late because the remove methods might want to use the locks */ vga_take_lock(vdev, NULL, NULL); vga_drop_lock(vdev, NULL); down(&vdev->shared->shared_sem); vdev->shared->device[vdev->unit] = NULL; vdev->shared->count --; up(&vdev->shared->shared_sem); if(vdev->shared->count == 0) kfree(vdev->shared); kfree(vdev); }
/** * vga_remove_one - Remove vga adapter * @pdev: PCI device * * A VGA adapter has been removed. We must propogate this into the * VGA bus world */ static void vga_remove_one(struct pci_dev *pdev) { struct vga_dev *vdev = (struct vga_dev *)pdev->dev.driver_data; device_unregister(&vdev->dev); /* Remove from lists etc here */ vga_dev_put(vdev); }
/** * vga_found_one - Add vga adapter * @pdev: PCI device * @ent: matching PCI entity * * Allocate and install a new VGA class entity set after the PCI layer * discovers it. We create device objects for the frame buffer, * memory manager and dri objects at the moment. Additional frame * buffer objects (multihead) can be allocated by the callers. */ static int vga_found_one(struct pci_dev *pdev, const struct pci_device_id *ent) { struct vga_shared *vshar = kmalloc(sizeof(*vshar), GFP_KERNEL); int i; if(vshar == NULL) return -ENOMEM; memset(vshar, 0, sizeof(*vshar)); init_MUTEX(&vshar->shared_sem); init_MUTEX(&vshar->fb_sem); vshar->lock_owner = NULL; for(i = TYPE_MEM; i <= TYPE_FB0; i++) { struct vga_dev *vdev = kmalloc(sizeof(*vdev), GFP_KERNEL); if(vdev == NULL) return -ENOMEM; memset(vdev, 0, sizeof(*vdev)); vdev->pci_dev = pdev; vdev->shared = vshar; vdev->unit = i; vshar->device[i] = vdev; vshar->count ++; vdev->dev.bus = &vga_bus_type; vdev->dev.parent = NULL; /* ? */ vdev->dev.driver_data = pdev; device_initialize(&vdev->dev); vdev->dev.release = vga_release_dev; vdev->dev.dma_mask = pdev->dev.dma_mask; vdev->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask; vga_dev_get(vdev);
INIT_LIST_HEAD(&vdev->next); list_add_tail(&vdev->next, &vga_devices); } return 1; }
/* * Match all video VGA class objects */ static struct pci_device_id vga_id_table[] = { { PCI_DEVICE_CLASS(((PCI_CLASS_DISPLAY_VGA << 8) | 0x00), ~0), }, { 0, } };
MODULE_DEVICE_TABLE(pci, vga_id_table);
static struct pci_driver vga_driver = { .name = "vga", .probe = vga_found_one, .remove = vga_remove_one, .id_table = vga_id_table, /* Could use suspend/resume hooks ? */ }; EXPORT_SYMBOL(vga_register_driver); EXPORT_SYMBOL(vga_unregister_driver); EXPORT_SYMBOL(vga_dev_driver); EXPORT_SYMBOL(vga_bus_type); EXPORT_SYMBOL(vga_dev_get); EXPORT_SYMBOL(vga_dev_put);
int __init vga_driver_init(void) { if( bus_register(&vga_bus_type) < 0) printk(KERN_ERR "Unable to register VGA bus type.\n"); return pci_module_init(&vga_driver); }
postcore_initcall(vga_driver_init); #define TYPE_MEM 0 /* Memory manager */ #define TYPE_DRI 1 /* Direct render agent */ #define TYPE_FB0 2 /* Frame buffer head 0 */ #define TYPE_FB1 3 /* Frame buffer head 1 */ #define TYPE_FB2 4 /* Frame buffer head 2 */ #define TYPE_FB3 5 /* Frame buffer head 3 */ #define TYPE_LAST 5 #define NUM_TYPES 6
struct vga_shared { struct vga_dev *device[NUM_TYPES]; struct semaphore shared_sem; int count; /* Devices */ int users; /* Active users */ struct semaphore fb_sem; void (*lock_release)(struct vga_dev *, void *); void *lock_context; struct vga_driver *lock_owner; };
struct vga_dev { struct list_head next; /* All VGA devices */ struct list_head router_list; /* By VGA router (not yet done) */ struct vga_driver *driver; struct pci_dev *pci_dev; struct device dev; int unit; struct vga_shared *shared; };
struct vga_device_id { __u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/ __u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */ __u32 class, class_mask; /* (class,subclass,prog-if) triplet */ __u32 unit; /* Logical unit for attach */ kernel_ulong_t driver_data; /* Data private to the driver */ };
#define to_vga_dev(n) container_of(n, struct vga_dev, dev)
struct vga_driver { struct list_head node; int type; char *name; int (*probe) (struct vga_dev *vdev, const struct vga_device_id *id); void (*remove) (struct vga_dev *dev, int users); /* Device removed (NULL if not a hot-plug capable driver) */ int (*suspend) (struct vga_dev *vdev, u32 state); /* Device suspended */ int (*resume) (struct vga_dev *vdev); /* Device woken up */ void (*notify_attach) (struct vga_dev *, int); void (*notify_detach) (struct vga_dev *, int); struct device_driver driver; struct vga_device_id *id_table; };
#define to_vga_driver(drv) container_of(drv, struct vga_driver, driver)
extern struct bus_type vga_bus_type;
extern int vga_register_driver(struct vga_driver *drv); extern void vg_unregister_driver(struct vga_driver *drv); extern struct vga_driver *vga_dev_driver(const struct vga_dev *, int); extern struct bus_type vga_bus_type; extern struct vga_dev *vga_dev_get(struct vga_dev *); extern void vga_dev_put(struct vga_dev *);
| |