Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : :
3 : : /*
4 : : * drm_sysfs.c - Modifications to drm_sysfs_class.c to support
5 : : * extra sysfs attribute from DRM. Normal drm_sysfs_class
6 : : * does not allow adding attributes.
7 : : *
8 : : * Copyright (c) 2004 Jon Smirl <jonsmirl@gmail.com>
9 : : * Copyright (c) 2003-2004 Greg Kroah-Hartman <greg@kroah.com>
10 : : * Copyright (c) 2003-2004 IBM Corp.
11 : : */
12 : :
13 : : #include <linux/device.h>
14 : : #include <linux/err.h>
15 : : #include <linux/export.h>
16 : : #include <linux/gfp.h>
17 : : #include <linux/i2c.h>
18 : : #include <linux/kdev_t.h>
19 : : #include <linux/slab.h>
20 : :
21 : : #include <drm/drm_connector.h>
22 : : #include <drm/drm_device.h>
23 : : #include <drm/drm_file.h>
24 : : #include <drm/drm_modes.h>
25 : : #include <drm/drm_print.h>
26 : : #include <drm/drm_property.h>
27 : : #include <drm/drm_sysfs.h>
28 : :
29 : : #include "drm_internal.h"
30 : : #include "drm_crtc_internal.h"
31 : :
32 : : #define to_drm_minor(d) dev_get_drvdata(d)
33 : : #define to_drm_connector(d) dev_get_drvdata(d)
34 : :
35 : : /**
36 : : * DOC: overview
37 : : *
38 : : * DRM provides very little additional support to drivers for sysfs
39 : : * interactions, beyond just all the standard stuff. Drivers who want to expose
40 : : * additional sysfs properties and property groups can attach them at either
41 : : * &drm_device.dev or &drm_connector.kdev.
42 : : *
43 : : * Registration is automatically handled when calling drm_dev_register(), or
44 : : * drm_connector_register() in case of hot-plugged connectors. Unregistration is
45 : : * also automatically handled by drm_dev_unregister() and
46 : : * drm_connector_unregister().
47 : : */
48 : :
49 : : static struct device_type drm_sysfs_device_minor = {
50 : : .name = "drm_minor"
51 : : };
52 : :
53 : : struct class *drm_class;
54 : :
55 : 0 : static char *drm_devnode(struct device *dev, umode_t *mode)
56 : : {
57 [ # # ]: 0 : return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev));
58 : : }
59 : :
60 : : static CLASS_ATTR_STRING(version, S_IRUGO, "drm 1.1.0 20060810");
61 : :
62 : : /**
63 : : * drm_sysfs_init - initialize sysfs helpers
64 : : *
65 : : * This is used to create the DRM class, which is the implicit parent of any
66 : : * other top-level DRM sysfs objects.
67 : : *
68 : : * You must call drm_sysfs_destroy() to release the allocated resources.
69 : : *
70 : : * Return: 0 on success, negative error code on failure.
71 : : */
72 : 30 : int drm_sysfs_init(void)
73 : : {
74 : 30 : int err;
75 : :
76 : 30 : drm_class = class_create(THIS_MODULE, "drm");
77 [ - + ]: 30 : if (IS_ERR(drm_class))
78 : 0 : return PTR_ERR(drm_class);
79 : :
80 : 30 : err = class_create_file(drm_class, &class_attr_version.attr);
81 [ - + ]: 30 : if (err) {
82 : 0 : class_destroy(drm_class);
83 : 0 : drm_class = NULL;
84 : 0 : return err;
85 : : }
86 : :
87 : 30 : drm_class->devnode = drm_devnode;
88 : 30 : drm_setup_hdcp_srm(drm_class);
89 : 30 : return 0;
90 : : }
91 : :
92 : : /**
93 : : * drm_sysfs_destroy - destroys DRM class
94 : : *
95 : : * Destroy the DRM device class.
96 : : */
97 : 0 : void drm_sysfs_destroy(void)
98 : : {
99 [ # # # # ]: 0 : if (IS_ERR_OR_NULL(drm_class))
100 : : return;
101 : 0 : drm_teardown_hdcp_srm(drm_class);
102 : 0 : class_remove_file(drm_class, &class_attr_version.attr);
103 : 0 : class_destroy(drm_class);
104 : 0 : drm_class = NULL;
105 : : }
106 : :
107 : : /*
108 : : * Connector properties
109 : : */
110 : 0 : static ssize_t status_store(struct device *device,
111 : : struct device_attribute *attr,
112 : : const char *buf, size_t count)
113 : : {
114 : 0 : struct drm_connector *connector = to_drm_connector(device);
115 : 0 : struct drm_device *dev = connector->dev;
116 : 0 : enum drm_connector_force old_force;
117 : 0 : int ret;
118 : :
119 : 0 : ret = mutex_lock_interruptible(&dev->mode_config.mutex);
120 [ # # ]: 0 : if (ret)
121 : 0 : return ret;
122 : :
123 : 0 : old_force = connector->force;
124 : :
125 [ # # ]: 0 : if (sysfs_streq(buf, "detect"))
126 : 0 : connector->force = 0;
127 [ # # ]: 0 : else if (sysfs_streq(buf, "on"))
128 : 0 : connector->force = DRM_FORCE_ON;
129 [ # # ]: 0 : else if (sysfs_streq(buf, "on-digital"))
130 : 0 : connector->force = DRM_FORCE_ON_DIGITAL;
131 [ # # ]: 0 : else if (sysfs_streq(buf, "off"))
132 : 0 : connector->force = DRM_FORCE_OFF;
133 : : else
134 : : ret = -EINVAL;
135 : :
136 [ # # # # ]: 0 : if (old_force != connector->force || !connector->force) {
137 : 0 : DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force updated from %d to %d or reprobing\n",
138 : : connector->base.id,
139 : : connector->name,
140 : : old_force, connector->force);
141 : :
142 : 0 : connector->funcs->fill_modes(connector,
143 : 0 : dev->mode_config.max_width,
144 : 0 : dev->mode_config.max_height);
145 : : }
146 : :
147 : 0 : mutex_unlock(&dev->mode_config.mutex);
148 : :
149 [ # # ]: 0 : return ret ? ret : count;
150 : : }
151 : :
152 : 0 : static ssize_t status_show(struct device *device,
153 : : struct device_attribute *attr,
154 : : char *buf)
155 : : {
156 : 0 : struct drm_connector *connector = to_drm_connector(device);
157 : 0 : enum drm_connector_status status;
158 : :
159 : 0 : status = READ_ONCE(connector->status);
160 : :
161 : 0 : return snprintf(buf, PAGE_SIZE, "%s\n",
162 : : drm_get_connector_status_name(status));
163 : : }
164 : :
165 : 0 : static ssize_t dpms_show(struct device *device,
166 : : struct device_attribute *attr,
167 : : char *buf)
168 : : {
169 : 0 : struct drm_connector *connector = to_drm_connector(device);
170 : 0 : int dpms;
171 : :
172 : 0 : dpms = READ_ONCE(connector->dpms);
173 : :
174 : 0 : return snprintf(buf, PAGE_SIZE, "%s\n",
175 : : drm_get_dpms_name(dpms));
176 : : }
177 : :
178 : 0 : static ssize_t enabled_show(struct device *device,
179 : : struct device_attribute *attr,
180 : : char *buf)
181 : : {
182 [ # # ]: 0 : struct drm_connector *connector = to_drm_connector(device);
183 : 0 : bool enabled;
184 : :
185 [ # # ]: 0 : enabled = READ_ONCE(connector->encoder);
186 : :
187 [ # # ]: 0 : return snprintf(buf, PAGE_SIZE, enabled ? "enabled\n" : "disabled\n");
188 : : }
189 : :
190 : 0 : static ssize_t edid_show(struct file *filp, struct kobject *kobj,
191 : : struct bin_attribute *attr, char *buf, loff_t off,
192 : : size_t count)
193 : : {
194 : 0 : struct device *connector_dev = kobj_to_dev(kobj);
195 : 0 : struct drm_connector *connector = to_drm_connector(connector_dev);
196 : 0 : unsigned char *edid;
197 : 0 : size_t size;
198 : 0 : ssize_t ret = 0;
199 : :
200 : 0 : mutex_lock(&connector->dev->mode_config.mutex);
201 [ # # ]: 0 : if (!connector->edid_blob_ptr)
202 : 0 : goto unlock;
203 : :
204 : 0 : edid = connector->edid_blob_ptr->data;
205 : 0 : size = connector->edid_blob_ptr->length;
206 [ # # ]: 0 : if (!edid)
207 : 0 : goto unlock;
208 : :
209 [ # # ]: 0 : if (off >= size)
210 : 0 : goto unlock;
211 : :
212 [ # # ]: 0 : if (off + count > size)
213 : 0 : count = size - off;
214 : 0 : memcpy(buf, edid + off, count);
215 : :
216 : 0 : ret = count;
217 : 0 : unlock:
218 : 0 : mutex_unlock(&connector->dev->mode_config.mutex);
219 : :
220 : 0 : return ret;
221 : : }
222 : :
223 : 0 : static ssize_t modes_show(struct device *device,
224 : : struct device_attribute *attr,
225 : : char *buf)
226 : : {
227 : 0 : struct drm_connector *connector = to_drm_connector(device);
228 : 0 : struct drm_display_mode *mode;
229 : 0 : int written = 0;
230 : :
231 : 0 : mutex_lock(&connector->dev->mode_config.mutex);
232 [ # # ]: 0 : list_for_each_entry(mode, &connector->modes, head) {
233 : 0 : written += snprintf(buf + written, PAGE_SIZE - written, "%s\n",
234 : 0 : mode->name);
235 : : }
236 : 0 : mutex_unlock(&connector->dev->mode_config.mutex);
237 : :
238 : 0 : return written;
239 : : }
240 : :
241 : : static DEVICE_ATTR_RW(status);
242 : : static DEVICE_ATTR_RO(enabled);
243 : : static DEVICE_ATTR_RO(dpms);
244 : : static DEVICE_ATTR_RO(modes);
245 : :
246 : : static struct attribute *connector_dev_attrs[] = {
247 : : &dev_attr_status.attr,
248 : : &dev_attr_enabled.attr,
249 : : &dev_attr_dpms.attr,
250 : : &dev_attr_modes.attr,
251 : : NULL
252 : : };
253 : :
254 : : static struct bin_attribute edid_attr = {
255 : : .attr.name = "edid",
256 : : .attr.mode = 0444,
257 : : .size = 0,
258 : : .read = edid_show,
259 : : };
260 : :
261 : : static struct bin_attribute *connector_bin_attrs[] = {
262 : : &edid_attr,
263 : : NULL
264 : : };
265 : :
266 : : static const struct attribute_group connector_dev_group = {
267 : : .attrs = connector_dev_attrs,
268 : : .bin_attrs = connector_bin_attrs,
269 : : };
270 : :
271 : : static const struct attribute_group *connector_dev_groups[] = {
272 : : &connector_dev_group,
273 : : NULL
274 : : };
275 : :
276 : 0 : int drm_sysfs_connector_add(struct drm_connector *connector)
277 : : {
278 : 0 : struct drm_device *dev = connector->dev;
279 : :
280 [ # # ]: 0 : if (connector->kdev)
281 : : return 0;
282 : :
283 : 0 : connector->kdev =
284 : 0 : device_create_with_groups(drm_class, dev->primary->kdev, 0,
285 : : connector, connector_dev_groups,
286 : 0 : "card%d-%s", dev->primary->index,
287 : : connector->name);
288 : 0 : DRM_DEBUG("adding \"%s\" to sysfs\n",
289 : : connector->name);
290 : :
291 [ # # ]: 0 : if (IS_ERR(connector->kdev)) {
292 : 0 : DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev));
293 : 0 : return PTR_ERR(connector->kdev);
294 : : }
295 : :
296 : : /* Let userspace know we have a new connector */
297 : 0 : drm_sysfs_hotplug_event(dev);
298 : :
299 [ # # ]: 0 : if (connector->ddc)
300 : 0 : return sysfs_create_link(&connector->kdev->kobj,
301 : : &connector->ddc->dev.kobj, "ddc");
302 : : return 0;
303 : : }
304 : :
305 : 0 : void drm_sysfs_connector_remove(struct drm_connector *connector)
306 : : {
307 [ # # ]: 0 : if (!connector->kdev)
308 : : return;
309 : :
310 [ # # ]: 0 : if (connector->ddc)
311 : 0 : sysfs_remove_link(&connector->kdev->kobj, "ddc");
312 : :
313 : 0 : DRM_DEBUG("removing \"%s\" from sysfs\n",
314 : : connector->name);
315 : :
316 : 0 : device_unregister(connector->kdev);
317 : 0 : connector->kdev = NULL;
318 : : }
319 : :
320 : 0 : void drm_sysfs_lease_event(struct drm_device *dev)
321 : : {
322 : 0 : char *event_string = "LEASE=1";
323 : 0 : char *envp[] = { event_string, NULL };
324 : :
325 : 0 : DRM_DEBUG("generating lease event\n");
326 : :
327 : 0 : kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
328 : 0 : }
329 : :
330 : : /**
331 : : * drm_sysfs_hotplug_event - generate a DRM uevent
332 : : * @dev: DRM device
333 : : *
334 : : * Send a uevent for the DRM device specified by @dev. Currently we only
335 : : * set HOTPLUG=1 in the uevent environment, but this could be expanded to
336 : : * deal with other types of events.
337 : : *
338 : : * Any new uapi should be using the drm_sysfs_connector_status_event()
339 : : * for uevents on connector status change.
340 : : */
341 : 0 : void drm_sysfs_hotplug_event(struct drm_device *dev)
342 : : {
343 : 0 : char *event_string = "HOTPLUG=1";
344 : 0 : char *envp[] = { event_string, NULL };
345 : :
346 : 0 : DRM_DEBUG("generating hotplug event\n");
347 : :
348 : 0 : kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
349 : 0 : }
350 : : EXPORT_SYMBOL(drm_sysfs_hotplug_event);
351 : :
352 : : /**
353 : : * drm_sysfs_connector_status_event - generate a DRM uevent for connector
354 : : * property status change
355 : : * @connector: connector on which property status changed
356 : : * @property: connector property whose status changed.
357 : : *
358 : : * Send a uevent for the DRM device specified by @dev. Currently we
359 : : * set HOTPLUG=1 and connector id along with the attached property id
360 : : * related to the status change.
361 : : */
362 : 0 : void drm_sysfs_connector_status_event(struct drm_connector *connector,
363 : : struct drm_property *property)
364 : : {
365 : 0 : struct drm_device *dev = connector->dev;
366 : 0 : char hotplug_str[] = "HOTPLUG=1", conn_id[21], prop_id[21];
367 : 0 : char *envp[4] = { hotplug_str, conn_id, prop_id, NULL };
368 : :
369 [ # # ]: 0 : WARN_ON(!drm_mode_obj_find_prop_id(&connector->base,
370 : : property->base.id));
371 : :
372 : 0 : snprintf(conn_id, ARRAY_SIZE(conn_id),
373 : : "CONNECTOR=%u", connector->base.id);
374 : 0 : snprintf(prop_id, ARRAY_SIZE(prop_id),
375 : : "PROPERTY=%u", property->base.id);
376 : :
377 : 0 : DRM_DEBUG("generating connector status event\n");
378 : :
379 : 0 : kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
380 : 0 : }
381 : : EXPORT_SYMBOL(drm_sysfs_connector_status_event);
382 : :
383 : 0 : static void drm_sysfs_release(struct device *dev)
384 : : {
385 : 0 : kfree(dev);
386 : 0 : }
387 : :
388 : 0 : struct device *drm_sysfs_minor_alloc(struct drm_minor *minor)
389 : : {
390 : 0 : const char *minor_str;
391 : 0 : struct device *kdev;
392 : 0 : int r;
393 : :
394 [ # # ]: 0 : if (minor->type == DRM_MINOR_RENDER)
395 : : minor_str = "renderD%d";
396 : : else
397 : 0 : minor_str = "card%d";
398 : :
399 : 0 : kdev = kzalloc(sizeof(*kdev), GFP_KERNEL);
400 [ # # ]: 0 : if (!kdev)
401 : : return ERR_PTR(-ENOMEM);
402 : :
403 : 0 : device_initialize(kdev);
404 : 0 : kdev->devt = MKDEV(DRM_MAJOR, minor->index);
405 : 0 : kdev->class = drm_class;
406 : 0 : kdev->type = &drm_sysfs_device_minor;
407 : 0 : kdev->parent = minor->dev->dev;
408 : 0 : kdev->release = drm_sysfs_release;
409 : 0 : dev_set_drvdata(kdev, minor);
410 : :
411 : 0 : r = dev_set_name(kdev, minor_str, minor->index);
412 [ # # ]: 0 : if (r < 0)
413 : 0 : goto err_free;
414 : :
415 : : return kdev;
416 : :
417 : : err_free:
418 : 0 : put_device(kdev);
419 : 0 : return ERR_PTR(r);
420 : : }
421 : :
422 : : /**
423 : : * drm_class_device_register - register new device with the DRM sysfs class
424 : : * @dev: device to register
425 : : *
426 : : * Registers a new &struct device within the DRM sysfs class. Essentially only
427 : : * used by ttm to have a place for its global settings. Drivers should never use
428 : : * this.
429 : : */
430 : 0 : int drm_class_device_register(struct device *dev)
431 : : {
432 [ # # # # ]: 0 : if (!drm_class || IS_ERR(drm_class))
433 : : return -ENOENT;
434 : :
435 : 0 : dev->class = drm_class;
436 : 0 : return device_register(dev);
437 : : }
438 : : EXPORT_SYMBOL_GPL(drm_class_device_register);
439 : :
440 : : /**
441 : : * drm_class_device_unregister - unregister device with the DRM sysfs class
442 : : * @dev: device to unregister
443 : : *
444 : : * Unregisters a &struct device from the DRM sysfs class. Essentially only used
445 : : * by ttm to have a place for its global settings. Drivers should never use
446 : : * this.
447 : : */
448 : 0 : void drm_class_device_unregister(struct device *dev)
449 : : {
450 : 0 : return device_unregister(dev);
451 : : }
452 : : EXPORT_SYMBOL_GPL(drm_class_device_unregister);
|