xref: /openbmc/linux/drivers/gpu/drm/drm_sysfs.c (revision 38eda211)
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