drm: update DRM sysfs support

Make DRM devices use real Linux devices instead of class devices, which are
going away.  While we're at it, clean up some of the interfaces to take
struct drm_device * or struct device * and use the global drm_class where
needed instead of passing it around.

Signed-off-by: Dave Airlie <airlied@linux.ie>
This commit is contained in:
Jesse Barnes 2007-11-22 14:02:38 +10:00 committed by Dave Airlie
parent 8b40958032
commit e8b962b6df
4 changed files with 117 additions and 62 deletions

View File

@ -568,6 +568,8 @@ struct drm_driver {
void (*postclose) (struct drm_device *, struct drm_file *); void (*postclose) (struct drm_device *, struct drm_file *);
void (*lastclose) (struct drm_device *); void (*lastclose) (struct drm_device *);
int (*unload) (struct drm_device *); int (*unload) (struct drm_device *);
int (*suspend) (struct drm_device *);
int (*resume) (struct drm_device *);
int (*dma_ioctl) (struct drm_device *dev, void *data, struct drm_file *file_priv); int (*dma_ioctl) (struct drm_device *dev, void *data, struct drm_file *file_priv);
void (*dma_ready) (struct drm_device *); void (*dma_ready) (struct drm_device *);
int (*dma_quiescent) (struct drm_device *); int (*dma_quiescent) (struct drm_device *);
@ -643,6 +645,7 @@ struct drm_head {
* may contain multiple heads. * may contain multiple heads.
*/ */
struct drm_device { struct drm_device {
struct device dev; /**< Linux device */
char *unique; /**< Unique identifier: e.g., busid */ char *unique; /**< Unique identifier: e.g., busid */
int unique_len; /**< Length of unique field */ int unique_len; /**< Length of unique field */
char *devname; /**< For /proc/interrupts */ char *devname; /**< For /proc/interrupts */
@ -1063,11 +1066,11 @@ extern void __drm_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah);
extern void drm_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah); extern void drm_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah);
/* sysfs support (drm_sysfs.c) */ /* sysfs support (drm_sysfs.c) */
struct drm_sysfs_class;
extern struct class *drm_sysfs_create(struct module *owner, char *name); extern struct class *drm_sysfs_create(struct module *owner, char *name);
extern void drm_sysfs_destroy(struct class *cs); extern void drm_sysfs_destroy(void);
extern struct class_device *drm_sysfs_device_add(struct class *cs, extern int drm_sysfs_device_add(struct drm_device *dev, struct drm_head *head);
struct drm_head *head); extern void drm_sysfs_device_remove(struct drm_device *dev);
extern void drm_sysfs_device_remove(struct class_device *class_dev);
/* /*
* Basic memory manager support (drm_mm.c) * Basic memory manager support (drm_mm.c)

View File

@ -386,19 +386,19 @@ static int __init drm_core_init(void)
DRM_INFO("Initialized %s %d.%d.%d %s\n", DRM_INFO("Initialized %s %d.%d.%d %s\n",
CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE); CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE);
return 0; return 0;
err_p3: err_p3:
drm_sysfs_destroy(drm_class); drm_sysfs_destroy();
err_p2: err_p2:
unregister_chrdev(DRM_MAJOR, "drm"); unregister_chrdev(DRM_MAJOR, "drm");
drm_free(drm_heads, sizeof(*drm_heads) * drm_cards_limit, DRM_MEM_STUB); drm_free(drm_heads, sizeof(*drm_heads) * drm_cards_limit, DRM_MEM_STUB);
err_p1: err_p1:
return ret; return ret;
} }
static void __exit drm_core_exit(void) static void __exit drm_core_exit(void)
{ {
remove_proc_entry("dri", NULL); remove_proc_entry("dri", NULL);
drm_sysfs_destroy(drm_class); drm_sysfs_destroy();
unregister_chrdev(DRM_MAJOR, "drm"); unregister_chrdev(DRM_MAJOR, "drm");

View File

@ -168,11 +168,10 @@ static int drm_get_head(struct drm_device * dev, struct drm_head * head)
goto err_g1; goto err_g1;
} }
head->dev_class = drm_sysfs_device_add(drm_class, head); ret = drm_sysfs_device_add(dev, head);
if (IS_ERR(head->dev_class)) { if (ret) {
printk(KERN_ERR printk(KERN_ERR
"DRM: Error sysfs_device_add.\n"); "DRM: Error sysfs_device_add.\n");
ret = PTR_ERR(head->dev_class);
goto err_g2; goto err_g2;
} }
*heads = head; *heads = head;
@ -283,7 +282,7 @@ int drm_put_head(struct drm_head * head)
DRM_DEBUG("release secondary minor %d\n", minor); DRM_DEBUG("release secondary minor %d\n", minor);
drm_proc_cleanup(minor, drm_proc_root, head->dev_root); drm_proc_cleanup(minor, drm_proc_root, head->dev_root);
drm_sysfs_device_remove(head->dev_class); drm_sysfs_device_remove(head->dev);
*head = (struct drm_head) {.dev = NULL}; *head = (struct drm_head) {.dev = NULL};

View File

@ -19,6 +19,45 @@
#include "drm_core.h" #include "drm_core.h"
#include "drmP.h" #include "drmP.h"
#define to_drm_device(d) container_of(d, struct drm_device, dev)
/**
* drm_sysfs_suspend - DRM class suspend hook
* @dev: Linux device to suspend
* @state: power state to enter
*
* Just figures out what the actual struct drm_device associated with
* @dev is and calls its suspend hook, if present.
*/
static int drm_sysfs_suspend(struct device *dev, pm_message_t state)
{
struct drm_device *drm_dev = to_drm_device(dev);
printk(KERN_ERR "%s\n", __FUNCTION__);
if (drm_dev->driver->suspend)
return drm_dev->driver->suspend(drm_dev);
return 0;
}
/**
* drm_sysfs_resume - DRM class resume hook
* @dev: Linux device to resume
*
* Just figures out what the actual struct drm_device associated with
* @dev is and calls its resume hook, if present.
*/
static int drm_sysfs_resume(struct device *dev)
{
struct drm_device *drm_dev = to_drm_device(dev);
if (drm_dev->driver->resume)
return drm_dev->driver->resume(drm_dev);
return 0;
}
/* Display the version of drm_core. This doesn't work right in current design */ /* Display the version of drm_core. This doesn't work right in current design */
static ssize_t version_show(struct class *dev, char *buf) static ssize_t version_show(struct class *dev, char *buf)
{ {
@ -33,7 +72,7 @@ static CLASS_ATTR(version, S_IRUGO, version_show, NULL);
* @owner: pointer to the module that is to "own" this struct drm_sysfs_class * @owner: pointer to the module that is to "own" this struct drm_sysfs_class
* @name: pointer to a string for the name of this class. * @name: pointer to a string for the name of this class.
* *
* This is used to create a struct drm_sysfs_class pointer that can then be used * This is used to create DRM class pointer that can then be used
* in calls to drm_sysfs_device_add(). * in calls to drm_sysfs_device_add().
* *
* Note, the pointer created here is to be destroyed when finished by making a * Note, the pointer created here is to be destroyed when finished by making a
@ -50,6 +89,9 @@ struct class *drm_sysfs_create(struct module *owner, char *name)
goto err_out; goto err_out;
} }
class->suspend = drm_sysfs_suspend;
class->resume = drm_sysfs_resume;
err = class_create_file(class, &class_attr_version); err = class_create_file(class, &class_attr_version);
if (err) if (err)
goto err_out_class; goto err_out_class;
@ -63,94 +105,105 @@ err_out:
} }
/** /**
* drm_sysfs_destroy - destroys a struct drm_sysfs_class structure * drm_sysfs_destroy - destroys DRM class
* @cs: pointer to the struct drm_sysfs_class that is to be destroyed
* *
* Note, the pointer to be destroyed must have been created with a call to * Destroy the DRM device class.
* drm_sysfs_create().
*/ */
void drm_sysfs_destroy(struct class *class) void drm_sysfs_destroy(void)
{ {
if ((class == NULL) || (IS_ERR(class))) if ((drm_class == NULL) || (IS_ERR(drm_class)))
return; return;
class_remove_file(drm_class, &class_attr_version);
class_remove_file(class, &class_attr_version); class_destroy(drm_class);
class_destroy(class);
} }
static ssize_t show_dri(struct class_device *class_device, char *buf) static ssize_t show_dri(struct device *device, struct device_attribute *attr,
char *buf)
{ {
struct drm_device * dev = ((struct drm_head *)class_get_devdata(class_device))->dev; struct drm_device *dev = to_drm_device(device);
if (dev->driver->dri_library_name) if (dev->driver->dri_library_name)
return dev->driver->dri_library_name(dev, buf); return dev->driver->dri_library_name(dev, buf);
return snprintf(buf, PAGE_SIZE, "%s\n", dev->driver->pci_driver.name); return snprintf(buf, PAGE_SIZE, "%s\n", dev->driver->pci_driver.name);
} }
static struct class_device_attribute class_device_attrs[] = { static struct device_attribute device_attrs[] = {
__ATTR(dri_library_name, S_IRUGO, show_dri, NULL), __ATTR(dri_library_name, S_IRUGO, show_dri, NULL),
}; };
/** /**
* drm_sysfs_device_add - adds a class device to sysfs for a character driver * drm_sysfs_device_release - do nothing
* @cs: pointer to the struct class that this device should be registered to. * @dev: Linux device
* @dev: the dev_t for the device to be added.
* @device: a pointer to a struct device that is assiociated with this class device.
* @fmt: string for the class device's name
* *
* A struct class_device will be created in sysfs, registered to the specified * Normally, this would free the DRM device associated with @dev, along
* class. A "dev" file will be created, showing the dev_t for the device. The * with cleaning up any other stuff. But we do that in the DRM core, so
* pointer to the struct class_device will be returned from the call. Any further * this function can just return and hope that the core does its job.
* sysfs files that might be required can be created using this pointer.
* Note: the struct class passed to this function must have previously been
* created with a call to drm_sysfs_create().
*/ */
struct class_device *drm_sysfs_device_add(struct class *cs, struct drm_head *head) static void drm_sysfs_device_release(struct device *dev)
{ {
struct class_device *class_dev; return;
int i, j, err; }
class_dev = class_device_create(cs, NULL, /**
MKDEV(DRM_MAJOR, head->minor), * drm_sysfs_device_add - adds a class device to sysfs for a character driver
&(head->dev->pdev)->dev, * @dev: DRM device to be added
"card%d", head->minor); * @head: DRM head in question
if (IS_ERR(class_dev)) { *
err = PTR_ERR(class_dev); * Add a DRM device to the DRM's device model class. We use @dev's PCI device
* as the parent for the Linux device, and make sure it has a file containing
* the driver we're using (for userspace compatibility).
*/
int drm_sysfs_device_add(struct drm_device *dev, struct drm_head *head)
{
int err;
int i, j;
dev->dev.parent = &dev->pdev->dev;
dev->dev.class = drm_class;
dev->dev.release = drm_sysfs_device_release;
/*
* This will actually add the major:minor file so that udev
* will create the device node. We don't want to do that just
* yet...
*/
/* dev->dev.devt = head->device; */
snprintf(dev->dev.bus_id, BUS_ID_SIZE, "card%d", head->minor);
err = device_register(&dev->dev);
if (err) {
DRM_ERROR("device add failed: %d\n", err);
goto err_out; goto err_out;
} }
class_set_devdata(class_dev, head); for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
err = device_create_file(&dev->dev, &device_attrs[i]);
for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) {
err = class_device_create_file(class_dev,
&class_device_attrs[i]);
if (err) if (err)
goto err_out_files; goto err_out_files;
} }
return class_dev; return 0;
err_out_files: err_out_files:
if (i > 0) if (i > 0)
for (j = 0; j < i; j++) for (j = 0; j < i; j++)
class_device_remove_file(class_dev, device_remove_file(&dev->dev, &device_attrs[i]);
&class_device_attrs[i]); device_unregister(&dev->dev);
class_device_unregister(class_dev);
err_out: err_out:
return ERR_PTR(err);
return err;
} }
/** /**
* drm_sysfs_device_remove - removes a class device that was created with drm_sysfs_device_add() * drm_sysfs_device_remove - remove DRM device
* @dev: the dev_t of the device that was previously registered. * @dev: DRM device to remove
* *
* This call unregisters and cleans up a class device that was created with a * This call unregisters and cleans up a class device that was created with a
* call to drm_sysfs_device_add() * call to drm_sysfs_device_add()
*/ */
void drm_sysfs_device_remove(struct class_device *class_dev) void drm_sysfs_device_remove(struct drm_device *dev)
{ {
int i; int i;
for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
class_device_remove_file(class_dev, &class_device_attrs[i]); device_remove_file(&dev->dev, &device_attrs[i]);
class_device_unregister(class_dev); device_unregister(&dev->dev);
} }