1c0e09200SDave Airlie 2c0e09200SDave Airlie /* 3c0e09200SDave Airlie * drm_sysfs.c - Modifications to drm_sysfs_class.c to support 4c0e09200SDave Airlie * extra sysfs attribute from DRM. Normal drm_sysfs_class 5c0e09200SDave Airlie * does not allow adding attributes. 6c0e09200SDave Airlie * 7c0e09200SDave Airlie * Copyright (c) 2004 Jon Smirl <jonsmirl@gmail.com> 8c0e09200SDave Airlie * Copyright (c) 2003-2004 Greg Kroah-Hartman <greg@kroah.com> 9c0e09200SDave Airlie * Copyright (c) 2003-2004 IBM Corp. 10c0e09200SDave Airlie * 11c0e09200SDave Airlie * This file is released under the GPLv2 12c0e09200SDave Airlie * 13c0e09200SDave Airlie */ 14c0e09200SDave Airlie 15c0e09200SDave Airlie #include <linux/device.h> 16c0e09200SDave Airlie #include <linux/kdev_t.h> 17c0e09200SDave Airlie #include <linux/err.h> 18c0e09200SDave Airlie 19c0e09200SDave Airlie #include "drm_core.h" 20c0e09200SDave Airlie #include "drmP.h" 21c0e09200SDave Airlie 22c0e09200SDave Airlie #define to_drm_minor(d) container_of(d, struct drm_minor, kdev) 23c0e09200SDave Airlie 24c0e09200SDave Airlie /** 25c0e09200SDave Airlie * drm_sysfs_suspend - DRM class suspend hook 26c0e09200SDave Airlie * @dev: Linux device to suspend 27c0e09200SDave Airlie * @state: power state to enter 28c0e09200SDave Airlie * 29c0e09200SDave Airlie * Just figures out what the actual struct drm_device associated with 30c0e09200SDave Airlie * @dev is and calls its suspend hook, if present. 31c0e09200SDave Airlie */ 32c0e09200SDave Airlie static int drm_sysfs_suspend(struct device *dev, pm_message_t state) 33c0e09200SDave Airlie { 34c0e09200SDave Airlie struct drm_minor *drm_minor = to_drm_minor(dev); 35c0e09200SDave Airlie struct drm_device *drm_dev = drm_minor->dev; 36c0e09200SDave Airlie 37c0e09200SDave Airlie if (drm_dev->driver->suspend) 38c0e09200SDave Airlie return drm_dev->driver->suspend(drm_dev, state); 39c0e09200SDave Airlie 40c0e09200SDave Airlie return 0; 41c0e09200SDave Airlie } 42c0e09200SDave Airlie 43c0e09200SDave Airlie /** 44c0e09200SDave Airlie * drm_sysfs_resume - DRM class resume hook 45c0e09200SDave Airlie * @dev: Linux device to resume 46c0e09200SDave Airlie * 47c0e09200SDave Airlie * Just figures out what the actual struct drm_device associated with 48c0e09200SDave Airlie * @dev is and calls its resume hook, if present. 49c0e09200SDave Airlie */ 50c0e09200SDave Airlie static int drm_sysfs_resume(struct device *dev) 51c0e09200SDave Airlie { 52c0e09200SDave Airlie struct drm_minor *drm_minor = to_drm_minor(dev); 53c0e09200SDave Airlie struct drm_device *drm_dev = drm_minor->dev; 54c0e09200SDave Airlie 55c0e09200SDave Airlie if (drm_dev->driver->resume) 56c0e09200SDave Airlie return drm_dev->driver->resume(drm_dev); 57c0e09200SDave Airlie 58c0e09200SDave Airlie return 0; 59c0e09200SDave Airlie } 60c0e09200SDave Airlie 61c0e09200SDave Airlie /* Display the version of drm_core. This doesn't work right in current design */ 62c0e09200SDave Airlie static ssize_t version_show(struct class *dev, char *buf) 63c0e09200SDave Airlie { 64c0e09200SDave Airlie return sprintf(buf, "%s %d.%d.%d %s\n", CORE_NAME, CORE_MAJOR, 65c0e09200SDave Airlie CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE); 66c0e09200SDave Airlie } 67c0e09200SDave Airlie 68c0e09200SDave Airlie static CLASS_ATTR(version, S_IRUGO, version_show, NULL); 69c0e09200SDave Airlie 70c0e09200SDave Airlie /** 71c0e09200SDave Airlie * drm_sysfs_create - create a struct drm_sysfs_class structure 72c0e09200SDave Airlie * @owner: pointer to the module that is to "own" this struct drm_sysfs_class 73c0e09200SDave Airlie * @name: pointer to a string for the name of this class. 74c0e09200SDave Airlie * 75c0e09200SDave Airlie * This is used to create DRM class pointer that can then be used 76c0e09200SDave Airlie * in calls to drm_sysfs_device_add(). 77c0e09200SDave Airlie * 78c0e09200SDave Airlie * Note, the pointer created here is to be destroyed when finished by making a 79c0e09200SDave Airlie * call to drm_sysfs_destroy(). 80c0e09200SDave Airlie */ 81c0e09200SDave Airlie struct class *drm_sysfs_create(struct module *owner, char *name) 82c0e09200SDave Airlie { 83c0e09200SDave Airlie struct class *class; 84c0e09200SDave Airlie int err; 85c0e09200SDave Airlie 86c0e09200SDave Airlie class = class_create(owner, name); 87c0e09200SDave Airlie if (IS_ERR(class)) { 88c0e09200SDave Airlie err = PTR_ERR(class); 89c0e09200SDave Airlie goto err_out; 90c0e09200SDave Airlie } 91c0e09200SDave Airlie 92c0e09200SDave Airlie class->suspend = drm_sysfs_suspend; 93c0e09200SDave Airlie class->resume = drm_sysfs_resume; 94c0e09200SDave Airlie 95c0e09200SDave Airlie err = class_create_file(class, &class_attr_version); 96c0e09200SDave Airlie if (err) 97c0e09200SDave Airlie goto err_out_class; 98c0e09200SDave Airlie 99c0e09200SDave Airlie return class; 100c0e09200SDave Airlie 101c0e09200SDave Airlie err_out_class: 102c0e09200SDave Airlie class_destroy(class); 103c0e09200SDave Airlie err_out: 104c0e09200SDave Airlie return ERR_PTR(err); 105c0e09200SDave Airlie } 106c0e09200SDave Airlie 107c0e09200SDave Airlie /** 108c0e09200SDave Airlie * drm_sysfs_destroy - destroys DRM class 109c0e09200SDave Airlie * 110c0e09200SDave Airlie * Destroy the DRM device class. 111c0e09200SDave Airlie */ 112c0e09200SDave Airlie void drm_sysfs_destroy(void) 113c0e09200SDave Airlie { 114c0e09200SDave Airlie if ((drm_class == NULL) || (IS_ERR(drm_class))) 115c0e09200SDave Airlie return; 116c0e09200SDave Airlie class_remove_file(drm_class, &class_attr_version); 117c0e09200SDave Airlie class_destroy(drm_class); 118c0e09200SDave Airlie } 119c0e09200SDave Airlie 120c0e09200SDave Airlie static ssize_t show_dri(struct device *device, struct device_attribute *attr, 121c0e09200SDave Airlie char *buf) 122c0e09200SDave Airlie { 123c0e09200SDave Airlie struct drm_minor *drm_minor = to_drm_minor(device); 124c0e09200SDave Airlie struct drm_device *drm_dev = drm_minor->dev; 125c0e09200SDave Airlie if (drm_dev->driver->dri_library_name) 126c0e09200SDave Airlie return drm_dev->driver->dri_library_name(drm_dev, buf); 127c0e09200SDave Airlie return snprintf(buf, PAGE_SIZE, "%s\n", drm_dev->driver->pci_driver.name); 128c0e09200SDave Airlie } 129c0e09200SDave Airlie 130c0e09200SDave Airlie static struct device_attribute device_attrs[] = { 131c0e09200SDave Airlie __ATTR(dri_library_name, S_IRUGO, show_dri, NULL), 132c0e09200SDave Airlie }; 133c0e09200SDave Airlie 134c0e09200SDave Airlie /** 135c0e09200SDave Airlie * drm_sysfs_device_release - do nothing 136c0e09200SDave Airlie * @dev: Linux device 137c0e09200SDave Airlie * 138c0e09200SDave Airlie * Normally, this would free the DRM device associated with @dev, along 139c0e09200SDave Airlie * with cleaning up any other stuff. But we do that in the DRM core, so 140c0e09200SDave Airlie * this function can just return and hope that the core does its job. 141c0e09200SDave Airlie */ 142c0e09200SDave Airlie static void drm_sysfs_device_release(struct device *dev) 143c0e09200SDave Airlie { 144c0e09200SDave Airlie return; 145c0e09200SDave Airlie } 146c0e09200SDave Airlie 147c0e09200SDave Airlie /** 148c0e09200SDave Airlie * drm_sysfs_device_add - adds a class device to sysfs for a character driver 149c0e09200SDave Airlie * @dev: DRM device to be added 150c0e09200SDave Airlie * @head: DRM head in question 151c0e09200SDave Airlie * 152c0e09200SDave Airlie * Add a DRM device to the DRM's device model class. We use @dev's PCI device 153c0e09200SDave Airlie * as the parent for the Linux device, and make sure it has a file containing 154c0e09200SDave Airlie * the driver we're using (for userspace compatibility). 155c0e09200SDave Airlie */ 156c0e09200SDave Airlie int drm_sysfs_device_add(struct drm_minor *minor) 157c0e09200SDave Airlie { 158c0e09200SDave Airlie int err; 159c0e09200SDave Airlie int i, j; 160c0e09200SDave Airlie char *minor_str; 161c0e09200SDave Airlie 162c0e09200SDave Airlie minor->kdev.parent = &minor->dev->pdev->dev; 163c0e09200SDave Airlie minor->kdev.class = drm_class; 164c0e09200SDave Airlie minor->kdev.release = drm_sysfs_device_release; 165c0e09200SDave Airlie minor->kdev.devt = minor->device; 166c0e09200SDave Airlie minor_str = "card%d"; 167c0e09200SDave Airlie 168c0e09200SDave Airlie snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->index); 169c0e09200SDave Airlie 170c0e09200SDave Airlie err = device_register(&minor->kdev); 171c0e09200SDave Airlie if (err) { 172c0e09200SDave Airlie DRM_ERROR("device add failed: %d\n", err); 173c0e09200SDave Airlie goto err_out; 174c0e09200SDave Airlie } 175c0e09200SDave Airlie 176c0e09200SDave Airlie for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { 177c0e09200SDave Airlie err = device_create_file(&minor->kdev, &device_attrs[i]); 178c0e09200SDave Airlie if (err) 179c0e09200SDave Airlie goto err_out_files; 180c0e09200SDave Airlie } 181c0e09200SDave Airlie 182c0e09200SDave Airlie return 0; 183c0e09200SDave Airlie 184c0e09200SDave Airlie err_out_files: 185c0e09200SDave Airlie if (i > 0) 186c0e09200SDave Airlie for (j = 0; j < i; j++) 18738eda211SDave Airlie device_remove_file(&minor->kdev, &device_attrs[j]); 188c0e09200SDave Airlie device_unregister(&minor->kdev); 189c0e09200SDave Airlie err_out: 190c0e09200SDave Airlie 191c0e09200SDave Airlie return err; 192c0e09200SDave Airlie } 193c0e09200SDave Airlie 194c0e09200SDave Airlie /** 195c0e09200SDave Airlie * drm_sysfs_device_remove - remove DRM device 196c0e09200SDave Airlie * @dev: DRM device to remove 197c0e09200SDave Airlie * 198c0e09200SDave Airlie * This call unregisters and cleans up a class device that was created with a 199c0e09200SDave Airlie * call to drm_sysfs_device_add() 200c0e09200SDave Airlie */ 201c0e09200SDave Airlie void drm_sysfs_device_remove(struct drm_minor *minor) 202c0e09200SDave Airlie { 203c0e09200SDave Airlie int i; 204c0e09200SDave Airlie 205c0e09200SDave Airlie for (i = 0; i < ARRAY_SIZE(device_attrs); i++) 206c0e09200SDave Airlie device_remove_file(&minor->kdev, &device_attrs[i]); 207c0e09200SDave Airlie device_unregister(&minor->kdev); 208c0e09200SDave Airlie } 209