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