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