xref: /linux/drivers/gpu/drm/drm_sysfs.c (revision 7466f4cc508878a8328dff1c328a2b4108888d2e)
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_sysfs.h"
20 #include "drm_core.h"
21 #include "drmP.h"
22 
23 #define to_drm_minor(d) container_of(d, struct drm_minor, kdev)
24 #define to_drm_connector(d) container_of(d, struct drm_connector, kdev)
25 
26 static struct device_type drm_sysfs_device_minor = {
27 	.name = "drm_minor"
28 };
29 
30 /**
31  * drm_class_suspend - DRM class suspend hook
32  * @dev: Linux device to suspend
33  * @state: power state to enter
34  *
35  * Just figures out what the actual struct drm_device associated with
36  * @dev is and calls its suspend hook, if present.
37  */
38 static int drm_class_suspend(struct device *dev, pm_message_t state)
39 {
40 	if (dev->type == &drm_sysfs_device_minor) {
41 		struct drm_minor *drm_minor = to_drm_minor(dev);
42 		struct drm_device *drm_dev = drm_minor->dev;
43 
44 		if (drm_minor->type == DRM_MINOR_LEGACY &&
45 		    !drm_core_check_feature(drm_dev, DRIVER_MODESET) &&
46 		    drm_dev->driver->suspend)
47 			return drm_dev->driver->suspend(drm_dev, state);
48 	}
49 	return 0;
50 }
51 
52 /**
53  * drm_class_resume - DRM class resume hook
54  * @dev: Linux device to resume
55  *
56  * Just figures out what the actual struct drm_device associated with
57  * @dev is and calls its resume hook, if present.
58  */
59 static int drm_class_resume(struct device *dev)
60 {
61 	if (dev->type == &drm_sysfs_device_minor) {
62 		struct drm_minor *drm_minor = to_drm_minor(dev);
63 		struct drm_device *drm_dev = drm_minor->dev;
64 
65 		if (drm_minor->type == DRM_MINOR_LEGACY &&
66 		    !drm_core_check_feature(drm_dev, DRIVER_MODESET) &&
67 		    drm_dev->driver->resume)
68 			return drm_dev->driver->resume(drm_dev);
69 	}
70 	return 0;
71 }
72 
73 static char *drm_devnode(struct device *dev, mode_t *mode)
74 {
75 	return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev));
76 }
77 
78 static CLASS_ATTR_STRING(version, S_IRUGO,
79 		CORE_NAME " "
80 		__stringify(CORE_MAJOR) "."
81 		__stringify(CORE_MINOR) "."
82 		__stringify(CORE_PATCHLEVEL) " "
83 		CORE_DATE);
84 
85 /**
86  * drm_sysfs_create - create a struct drm_sysfs_class structure
87  * @owner: pointer to the module that is to "own" this struct drm_sysfs_class
88  * @name: pointer to a string for the name of this class.
89  *
90  * This is used to create DRM class pointer that can then be used
91  * in calls to drm_sysfs_device_add().
92  *
93  * Note, the pointer created here is to be destroyed when finished by making a
94  * call to drm_sysfs_destroy().
95  */
96 struct class *drm_sysfs_create(struct module *owner, char *name)
97 {
98 	struct class *class;
99 	int err;
100 
101 	class = class_create(owner, name);
102 	if (IS_ERR(class)) {
103 		err = PTR_ERR(class);
104 		goto err_out;
105 	}
106 
107 	class->suspend = drm_class_suspend;
108 	class->resume = drm_class_resume;
109 
110 	err = class_create_file(class, &class_attr_version.attr);
111 	if (err)
112 		goto err_out_class;
113 
114 	class->devnode = drm_devnode;
115 
116 	return class;
117 
118 err_out_class:
119 	class_destroy(class);
120 err_out:
121 	return ERR_PTR(err);
122 }
123 
124 /**
125  * drm_sysfs_destroy - destroys DRM class
126  *
127  * Destroy the DRM device class.
128  */
129 void drm_sysfs_destroy(void)
130 {
131 	if ((drm_class == NULL) || (IS_ERR(drm_class)))
132 		return;
133 	class_remove_file(drm_class, &class_attr_version.attr);
134 	class_destroy(drm_class);
135 }
136 
137 /**
138  * drm_sysfs_device_release - do nothing
139  * @dev: Linux device
140  *
141  * Normally, this would free the DRM device associated with @dev, along
142  * with cleaning up any other stuff.  But we do that in the DRM core, so
143  * this function can just return and hope that the core does its job.
144  */
145 static void drm_sysfs_device_release(struct device *dev)
146 {
147 	memset(dev, 0, sizeof(struct device));
148 	return;
149 }
150 
151 /*
152  * Connector properties
153  */
154 static ssize_t status_show(struct device *device,
155 			   struct device_attribute *attr,
156 			   char *buf)
157 {
158 	struct drm_connector *connector = to_drm_connector(device);
159 	enum drm_connector_status status;
160 
161 	status = connector->funcs->detect(connector);
162 	return snprintf(buf, PAGE_SIZE, "%s\n",
163 			drm_get_connector_status_name(status));
164 }
165 
166 static ssize_t dpms_show(struct device *device,
167 			   struct device_attribute *attr,
168 			   char *buf)
169 {
170 	struct drm_connector *connector = to_drm_connector(device);
171 	struct drm_device *dev = connector->dev;
172 	uint64_t dpms_status;
173 	int ret;
174 
175 	ret = drm_connector_property_get_value(connector,
176 					    dev->mode_config.dpms_property,
177 					    &dpms_status);
178 	if (ret)
179 		return 0;
180 
181 	return snprintf(buf, PAGE_SIZE, "%s\n",
182 			drm_get_dpms_name((int)dpms_status));
183 }
184 
185 static ssize_t enabled_show(struct device *device,
186 			    struct device_attribute *attr,
187 			   char *buf)
188 {
189 	struct drm_connector *connector = to_drm_connector(device);
190 
191 	return snprintf(buf, PAGE_SIZE, "%s\n", connector->encoder ? "enabled" :
192 			"disabled");
193 }
194 
195 static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *attr,
196 			 char *buf, loff_t off, size_t count)
197 {
198 	struct device *connector_dev = container_of(kobj, struct device, kobj);
199 	struct drm_connector *connector = to_drm_connector(connector_dev);
200 	unsigned char *edid;
201 	size_t size;
202 
203 	if (!connector->edid_blob_ptr)
204 		return 0;
205 
206 	edid = connector->edid_blob_ptr->data;
207 	size = connector->edid_blob_ptr->length;
208 	if (!edid)
209 		return 0;
210 
211 	if (off >= size)
212 		return 0;
213 
214 	if (off + count > size)
215 		count = size - off;
216 	memcpy(buf, edid + off, count);
217 
218 	return count;
219 }
220 
221 static ssize_t modes_show(struct device *device,
222 			   struct device_attribute *attr,
223 			   char *buf)
224 {
225 	struct drm_connector *connector = to_drm_connector(device);
226 	struct drm_display_mode *mode;
227 	int written = 0;
228 
229 	list_for_each_entry(mode, &connector->modes, head) {
230 		written += snprintf(buf + written, PAGE_SIZE - written, "%s\n",
231 				    mode->name);
232 	}
233 
234 	return written;
235 }
236 
237 static ssize_t subconnector_show(struct device *device,
238 			   struct device_attribute *attr,
239 			   char *buf)
240 {
241 	struct drm_connector *connector = to_drm_connector(device);
242 	struct drm_device *dev = connector->dev;
243 	struct drm_property *prop = NULL;
244 	uint64_t subconnector;
245 	int is_tv = 0;
246 	int ret;
247 
248 	switch (connector->connector_type) {
249 		case DRM_MODE_CONNECTOR_DVII:
250 			prop = dev->mode_config.dvi_i_subconnector_property;
251 			break;
252 		case DRM_MODE_CONNECTOR_Composite:
253 		case DRM_MODE_CONNECTOR_SVIDEO:
254 		case DRM_MODE_CONNECTOR_Component:
255 		case DRM_MODE_CONNECTOR_TV:
256 			prop = dev->mode_config.tv_subconnector_property;
257 			is_tv = 1;
258 			break;
259 		default:
260 			DRM_ERROR("Wrong connector type for this property\n");
261 			return 0;
262 	}
263 
264 	if (!prop) {
265 		DRM_ERROR("Unable to find subconnector property\n");
266 		return 0;
267 	}
268 
269 	ret = drm_connector_property_get_value(connector, prop, &subconnector);
270 	if (ret)
271 		return 0;
272 
273 	return snprintf(buf, PAGE_SIZE, "%s", is_tv ?
274 			drm_get_tv_subconnector_name((int)subconnector) :
275 			drm_get_dvi_i_subconnector_name((int)subconnector));
276 }
277 
278 static ssize_t select_subconnector_show(struct device *device,
279 			   struct device_attribute *attr,
280 			   char *buf)
281 {
282 	struct drm_connector *connector = to_drm_connector(device);
283 	struct drm_device *dev = connector->dev;
284 	struct drm_property *prop = NULL;
285 	uint64_t subconnector;
286 	int is_tv = 0;
287 	int ret;
288 
289 	switch (connector->connector_type) {
290 		case DRM_MODE_CONNECTOR_DVII:
291 			prop = dev->mode_config.dvi_i_select_subconnector_property;
292 			break;
293 		case DRM_MODE_CONNECTOR_Composite:
294 		case DRM_MODE_CONNECTOR_SVIDEO:
295 		case DRM_MODE_CONNECTOR_Component:
296 		case DRM_MODE_CONNECTOR_TV:
297 			prop = dev->mode_config.tv_select_subconnector_property;
298 			is_tv = 1;
299 			break;
300 		default:
301 			DRM_ERROR("Wrong connector type for this property\n");
302 			return 0;
303 	}
304 
305 	if (!prop) {
306 		DRM_ERROR("Unable to find select subconnector property\n");
307 		return 0;
308 	}
309 
310 	ret = drm_connector_property_get_value(connector, prop, &subconnector);
311 	if (ret)
312 		return 0;
313 
314 	return snprintf(buf, PAGE_SIZE, "%s", is_tv ?
315 			drm_get_tv_select_name((int)subconnector) :
316 			drm_get_dvi_i_select_name((int)subconnector));
317 }
318 
319 static struct device_attribute connector_attrs[] = {
320 	__ATTR_RO(status),
321 	__ATTR_RO(enabled),
322 	__ATTR_RO(dpms),
323 	__ATTR_RO(modes),
324 };
325 
326 /* These attributes are for both DVI-I connectors and all types of tv-out. */
327 static struct device_attribute connector_attrs_opt1[] = {
328 	__ATTR_RO(subconnector),
329 	__ATTR_RO(select_subconnector),
330 };
331 
332 static struct bin_attribute edid_attr = {
333 	.attr.name = "edid",
334 	.attr.mode = 0444,
335 	.size = 0,
336 	.read = edid_show,
337 };
338 
339 /**
340  * drm_sysfs_connector_add - add an connector to sysfs
341  * @connector: connector to add
342  *
343  * Create an connector device in sysfs, along with its associated connector
344  * properties (so far, connection status, dpms, mode list & edid) and
345  * generate a hotplug event so userspace knows there's a new connector
346  * available.
347  *
348  * Note:
349  * This routine should only be called *once* for each DRM minor registered.
350  * A second call for an already registered device will trigger the BUG_ON
351  * below.
352  */
353 int drm_sysfs_connector_add(struct drm_connector *connector)
354 {
355 	struct drm_device *dev = connector->dev;
356 	int ret = 0, i, j;
357 
358 	/* We shouldn't get called more than once for the same connector */
359 	BUG_ON(device_is_registered(&connector->kdev));
360 
361 	connector->kdev.parent = &dev->primary->kdev;
362 	connector->kdev.class = drm_class;
363 	connector->kdev.release = drm_sysfs_device_release;
364 
365 	DRM_DEBUG("adding \"%s\" to sysfs\n",
366 		  drm_get_connector_name(connector));
367 
368 	dev_set_name(&connector->kdev, "card%d-%s",
369 		     dev->primary->index, drm_get_connector_name(connector));
370 	ret = device_register(&connector->kdev);
371 
372 	if (ret) {
373 		DRM_ERROR("failed to register connector device: %d\n", ret);
374 		goto out;
375 	}
376 
377 	/* Standard attributes */
378 
379 	for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) {
380 		ret = device_create_file(&connector->kdev, &connector_attrs[i]);
381 		if (ret)
382 			goto err_out_files;
383 	}
384 
385 	/* Optional attributes */
386 	/*
387 	 * In the long run it maybe a good idea to make one set of
388 	 * optionals per connector type.
389 	 */
390 	switch (connector->connector_type) {
391 		case DRM_MODE_CONNECTOR_DVII:
392 		case DRM_MODE_CONNECTOR_Composite:
393 		case DRM_MODE_CONNECTOR_SVIDEO:
394 		case DRM_MODE_CONNECTOR_Component:
395 		case DRM_MODE_CONNECTOR_TV:
396 			for (i = 0; i < ARRAY_SIZE(connector_attrs_opt1); i++) {
397 				ret = device_create_file(&connector->kdev, &connector_attrs_opt1[i]);
398 				if (ret)
399 					goto err_out_files;
400 			}
401 			break;
402 		default:
403 			break;
404 	}
405 
406 	ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr);
407 	if (ret)
408 		goto err_out_files;
409 
410 	/* Let userspace know we have a new connector */
411 	drm_sysfs_hotplug_event(dev);
412 
413 	return 0;
414 
415 err_out_files:
416 	if (i > 0)
417 		for (j = 0; j < i; j++)
418 			device_remove_file(&connector->kdev,
419 					   &connector_attrs[i]);
420 	device_unregister(&connector->kdev);
421 
422 out:
423 	return ret;
424 }
425 EXPORT_SYMBOL(drm_sysfs_connector_add);
426 
427 /**
428  * drm_sysfs_connector_remove - remove an connector device from sysfs
429  * @connector: connector to remove
430  *
431  * Remove @connector and its associated attributes from sysfs.  Note that
432  * the device model core will take care of sending the "remove" uevent
433  * at this time, so we don't need to do it.
434  *
435  * Note:
436  * This routine should only be called if the connector was previously
437  * successfully registered.  If @connector hasn't been registered yet,
438  * you'll likely see a panic somewhere deep in sysfs code when called.
439  */
440 void drm_sysfs_connector_remove(struct drm_connector *connector)
441 {
442 	int i;
443 
444 	DRM_DEBUG("removing \"%s\" from sysfs\n",
445 		  drm_get_connector_name(connector));
446 
447 	for (i = 0; i < ARRAY_SIZE(connector_attrs); i++)
448 		device_remove_file(&connector->kdev, &connector_attrs[i]);
449 	sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr);
450 	device_unregister(&connector->kdev);
451 }
452 EXPORT_SYMBOL(drm_sysfs_connector_remove);
453 
454 /**
455  * drm_sysfs_hotplug_event - generate a DRM uevent
456  * @dev: DRM device
457  *
458  * Send a uevent for the DRM device specified by @dev.  Currently we only
459  * set HOTPLUG=1 in the uevent environment, but this could be expanded to
460  * deal with other types of events.
461  */
462 void drm_sysfs_hotplug_event(struct drm_device *dev)
463 {
464 	char *event_string = "HOTPLUG=1";
465 	char *envp[] = { event_string, NULL };
466 
467 	DRM_DEBUG("generating hotplug event\n");
468 
469 	kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp);
470 }
471 EXPORT_SYMBOL(drm_sysfs_hotplug_event);
472 
473 /**
474  * drm_sysfs_device_add - adds a class device to sysfs for a character driver
475  * @dev: DRM device to be added
476  * @head: DRM head in question
477  *
478  * Add a DRM device to the DRM's device model class.  We use @dev's PCI device
479  * as the parent for the Linux device, and make sure it has a file containing
480  * the driver we're using (for userspace compatibility).
481  */
482 int drm_sysfs_device_add(struct drm_minor *minor)
483 {
484 	int err;
485 	char *minor_str;
486 
487 	minor->kdev.parent = &minor->dev->pdev->dev;
488 	minor->kdev.class = drm_class;
489 	minor->kdev.release = drm_sysfs_device_release;
490 	minor->kdev.devt = minor->device;
491 	minor->kdev.type = &drm_sysfs_device_minor;
492 	if (minor->type == DRM_MINOR_CONTROL)
493 		minor_str = "controlD%d";
494         else if (minor->type == DRM_MINOR_RENDER)
495                 minor_str = "renderD%d";
496         else
497                 minor_str = "card%d";
498 
499 	dev_set_name(&minor->kdev, minor_str, minor->index);
500 
501 	err = device_register(&minor->kdev);
502 	if (err) {
503 		DRM_ERROR("device add failed: %d\n", err);
504 		goto err_out;
505 	}
506 
507 	return 0;
508 
509 err_out:
510 	return err;
511 }
512 
513 /**
514  * drm_sysfs_device_remove - remove DRM device
515  * @dev: DRM device to remove
516  *
517  * This call unregisters and cleans up a class device that was created with a
518  * call to drm_sysfs_device_add()
519  */
520 void drm_sysfs_device_remove(struct drm_minor *minor)
521 {
522 	device_unregister(&minor->kdev);
523 }
524 
525 
526 /**
527  * drm_class_device_register - Register a struct device in the drm class.
528  *
529  * @dev: pointer to struct device to register.
530  *
531  * @dev should have all relevant members pre-filled with the exception
532  * of the class member. In particular, the device_type member must
533  * be set.
534  */
535 
536 int drm_class_device_register(struct device *dev)
537 {
538 	dev->class = drm_class;
539 	return device_register(dev);
540 }
541 EXPORT_SYMBOL_GPL(drm_class_device_register);
542 
543 void drm_class_device_unregister(struct device *dev)
544 {
545 	return device_unregister(dev);
546 }
547 EXPORT_SYMBOL_GPL(drm_class_device_unregister);
548