]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/gpu/drm/drm_sysfs.c
UBUNTU: Ubuntu-4.15.0-96.97
[mirror_ubuntu-bionic-kernel.git] / drivers / gpu / drm / drm_sysfs.c
CommitLineData
0650fd58 1
1da177e4
LT
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
1da177e4
LT
15#include <linux/device.h>
16#include <linux/kdev_t.h>
5a0e3ad6 17#include <linux/gfp.h>
1da177e4 18#include <linux/err.h>
2d1a8a48 19#include <linux/export.h>
1da177e4 20
760285e7 21#include <drm/drm_sysfs.h>
760285e7 22#include <drm/drmP.h>
67d0ec4e 23#include "drm_internal.h"
1da177e4 24
5bdebb18
DA
25#define to_drm_minor(d) dev_get_drvdata(d)
26#define to_drm_connector(d) dev_get_drvdata(d)
e8b962b6 27
e2271704
DV
28/**
29 * DOC: overview
30 *
31 * DRM provides very little additional support to drivers for sysfs
32 * interactions, beyond just all the standard stuff. Drivers who want to expose
33 * additional sysfs properties and property groups can attach them at either
34 * &drm_device.dev or &drm_connector.kdev.
35 *
36 * Registration is automatically handled when calling drm_dev_register(), or
37 * drm_connector_register() in case of hot-plugged connectors. Unregistration is
38 * also automatically handled by drm_dev_unregister() and
39 * drm_connector_unregister().
40 */
41
08e4d534
TH
42static struct device_type drm_sysfs_device_minor = {
43 .name = "drm_minor"
44};
45
fcc90213
DH
46struct class *drm_class;
47
2c9ede55 48static char *drm_devnode(struct device *dev, umode_t *mode)
02200d06
KS
49{
50 return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev));
51}
52
82d5e73f 53static CLASS_ATTR_STRING(version, S_IRUGO, "drm 1.1.0 20060810");
1da177e4
LT
54
55/**
fcc90213
DH
56 * drm_sysfs_init - initialize sysfs helpers
57 *
58 * This is used to create the DRM class, which is the implicit parent of any
59 * other top-level DRM sysfs objects.
1da177e4 60 *
fcc90213 61 * You must call drm_sysfs_destroy() to release the allocated resources.
1da177e4 62 *
fcc90213 63 * Return: 0 on success, negative error code on failure.
1da177e4 64 */
fcc90213 65int drm_sysfs_init(void)
1da177e4 66{
24f73c92 67 int err;
0650fd58 68
fcc90213
DH
69 drm_class = class_create(THIS_MODULE, "drm");
70 if (IS_ERR(drm_class))
71 return PTR_ERR(drm_class);
e8b962b6 72
fcc90213
DH
73 err = class_create_file(drm_class, &class_attr_version.attr);
74 if (err) {
75 class_destroy(drm_class);
76 drm_class = NULL;
77 return err;
78 }
24f73c92 79
fcc90213
DH
80 drm_class->devnode = drm_devnode;
81 return 0;
1da177e4
LT
82}
83
84/**
e8b962b6 85 * drm_sysfs_destroy - destroys DRM class
1da177e4 86 *
e8b962b6 87 * Destroy the DRM device class.
1da177e4 88 */
e8b962b6 89void drm_sysfs_destroy(void)
1da177e4 90{
26b91ae4 91 if (IS_ERR_OR_NULL(drm_class))
1da177e4 92 return;
0933e2d9 93 class_remove_file(drm_class, &class_attr_version.attr);
e8b962b6 94 class_destroy(drm_class);
49099c49 95 drm_class = NULL;
1da177e4
LT
96}
97
f453ba04
DA
98/*
99 * Connector properties
100 */
c484f02d 101static ssize_t status_store(struct device *device,
f453ba04 102 struct device_attribute *attr,
c484f02d 103 const char *buf, size_t count)
f453ba04
DA
104{
105 struct drm_connector *connector = to_drm_connector(device);
c484f02d 106 struct drm_device *dev = connector->dev;
ed293f77 107 enum drm_connector_force old_force;
007c80a5
CW
108 int ret;
109
c484f02d 110 ret = mutex_lock_interruptible(&dev->mode_config.mutex);
007c80a5
CW
111 if (ret)
112 return ret;
f453ba04 113
ed293f77 114 old_force = connector->force;
c484f02d 115
ed293f77 116 if (sysfs_streq(buf, "detect"))
c484f02d 117 connector->force = 0;
ed293f77 118 else if (sysfs_streq(buf, "on"))
c484f02d 119 connector->force = DRM_FORCE_ON;
ed293f77 120 else if (sysfs_streq(buf, "on-digital"))
c484f02d 121 connector->force = DRM_FORCE_ON_DIGITAL;
ed293f77 122 else if (sysfs_streq(buf, "off"))
c484f02d 123 connector->force = DRM_FORCE_OFF;
ed293f77 124 else
c484f02d
CW
125 ret = -EINVAL;
126
ed293f77
DV
127 if (old_force != connector->force || !connector->force) {
128 DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force updated from %d to %d or reprobing\n",
c484f02d
CW
129 connector->base.id,
130 connector->name,
ed293f77 131 old_force, connector->force);
c484f02d 132
ed293f77
DV
133 connector->funcs->fill_modes(connector,
134 dev->mode_config.max_width,
135 dev->mode_config.max_height);
c484f02d
CW
136 }
137
138 mutex_unlock(&dev->mode_config.mutex);
139
38d8571d 140 return ret ? ret : count;
c484f02d
CW
141}
142
143static ssize_t status_show(struct device *device,
144 struct device_attribute *attr,
145 char *buf)
146{
147 struct drm_connector *connector = to_drm_connector(device);
4eb9b945
DV
148 enum drm_connector_status status;
149
150 status = READ_ONCE(connector->status);
007c80a5 151
75185c92 152 return snprintf(buf, PAGE_SIZE, "%s\n",
4eb9b945 153 drm_get_connector_status_name(status));
f453ba04
DA
154}
155
156static ssize_t dpms_show(struct device *device,
157 struct device_attribute *attr,
158 char *buf)
159{
160 struct drm_connector *connector = to_drm_connector(device);
621bd0f6 161 int dpms;
f453ba04 162
621bd0f6 163 dpms = READ_ONCE(connector->dpms);
f453ba04 164
75185c92 165 return snprintf(buf, PAGE_SIZE, "%s\n",
621bd0f6 166 drm_get_dpms_name(dpms));
f453ba04
DA
167}
168
169static ssize_t enabled_show(struct device *device,
170 struct device_attribute *attr,
171 char *buf)
172{
173 struct drm_connector *connector = to_drm_connector(device);
4eb9b945
DV
174 bool enabled;
175
176 enabled = READ_ONCE(connector->encoder);
f453ba04 177
4eb9b945 178 return snprintf(buf, PAGE_SIZE, enabled ? "enabled\n" : "disabled\n");
f453ba04
DA
179}
180
2c3c8bea
CW
181static ssize_t edid_show(struct file *filp, struct kobject *kobj,
182 struct bin_attribute *attr, char *buf, loff_t off,
183 size_t count)
f453ba04 184{
d122cbf1 185 struct device *connector_dev = kobj_to_dev(kobj);
f453ba04
DA
186 struct drm_connector *connector = to_drm_connector(connector_dev);
187 unsigned char *edid;
188 size_t size;
a48a62bc 189 ssize_t ret = 0;
f453ba04 190
a48a62bc 191 mutex_lock(&connector->dev->mode_config.mutex);
f453ba04 192 if (!connector->edid_blob_ptr)
a48a62bc 193 goto unlock;
f453ba04
DA
194
195 edid = connector->edid_blob_ptr->data;
196 size = connector->edid_blob_ptr->length;
197 if (!edid)
a48a62bc 198 goto unlock;
f453ba04
DA
199
200 if (off >= size)
a48a62bc 201 goto unlock;
f453ba04
DA
202
203 if (off + count > size)
204 count = size - off;
205 memcpy(buf, edid + off, count);
206
a48a62bc
DV
207 ret = count;
208unlock:
209 mutex_unlock(&connector->dev->mode_config.mutex);
210
211 return ret;
f453ba04
DA
212}
213
214static ssize_t modes_show(struct device *device,
215 struct device_attribute *attr,
216 char *buf)
217{
218 struct drm_connector *connector = to_drm_connector(device);
219 struct drm_display_mode *mode;
220 int written = 0;
221
a48a62bc 222 mutex_lock(&connector->dev->mode_config.mutex);
f453ba04
DA
223 list_for_each_entry(mode, &connector->modes, head) {
224 written += snprintf(buf + written, PAGE_SIZE - written, "%s\n",
225 mode->name);
226 }
a48a62bc 227 mutex_unlock(&connector->dev->mode_config.mutex);
f453ba04
DA
228
229 return written;
230}
231
c484f02d 232static DEVICE_ATTR_RW(status);
335f1a62
TI
233static DEVICE_ATTR_RO(enabled);
234static DEVICE_ATTR_RO(dpms);
235static DEVICE_ATTR_RO(modes);
236
237static struct attribute *connector_dev_attrs[] = {
238 &dev_attr_status.attr,
239 &dev_attr_enabled.attr,
240 &dev_attr_dpms.attr,
241 &dev_attr_modes.attr,
242 NULL
f453ba04
DA
243};
244
f453ba04
DA
245static struct bin_attribute edid_attr = {
246 .attr.name = "edid",
e36ebaf4 247 .attr.mode = 0444,
7466f4cc 248 .size = 0,
f453ba04
DA
249 .read = edid_show,
250};
251
335f1a62
TI
252static struct bin_attribute *connector_bin_attrs[] = {
253 &edid_attr,
254 NULL
255};
256
257static const struct attribute_group connector_dev_group = {
258 .attrs = connector_dev_attrs,
259 .bin_attrs = connector_bin_attrs,
260};
261
335f1a62
TI
262static const struct attribute_group *connector_dev_groups[] = {
263 &connector_dev_group,
335f1a62
TI
264 NULL
265};
266
f453ba04
DA
267int drm_sysfs_connector_add(struct drm_connector *connector)
268{
269 struct drm_device *dev = connector->dev;
f453ba04 270
5bdebb18
DA
271 if (connector->kdev)
272 return 0;
f453ba04 273
335f1a62
TI
274 connector->kdev =
275 device_create_with_groups(drm_class, dev->primary->kdev, 0,
276 connector, connector_dev_groups,
277 "card%d-%s", dev->primary->index,
278 connector->name);
f453ba04 279 DRM_DEBUG("adding \"%s\" to sysfs\n",
25933820 280 connector->name);
f453ba04 281
5bdebb18
DA
282 if (IS_ERR(connector->kdev)) {
283 DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev));
335f1a62 284 return PTR_ERR(connector->kdev);
f453ba04
DA
285 }
286
f453ba04
DA
287 /* Let userspace know we have a new connector */
288 drm_sysfs_hotplug_event(dev);
289
290 return 0;
f453ba04 291}
f453ba04 292
f453ba04
DA
293void drm_sysfs_connector_remove(struct drm_connector *connector)
294{
5bdebb18 295 if (!connector->kdev)
1828fe6c 296 return;
f453ba04 297 DRM_DEBUG("removing \"%s\" from sysfs\n",
25933820 298 connector->name);
f453ba04 299
5bdebb18
DA
300 device_unregister(connector->kdev);
301 connector->kdev = NULL;
f453ba04 302}
f453ba04 303
c6b7c0ab
DV
304void drm_sysfs_lease_event(struct drm_device *dev)
305{
306 char *event_string = "LEASE=1";
307 char *envp[] = { event_string, NULL };
308
309 DRM_DEBUG("generating lease event\n");
310
311 kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
312}
313
f453ba04
DA
314/**
315 * drm_sysfs_hotplug_event - generate a DRM uevent
316 * @dev: DRM device
317 *
318 * Send a uevent for the DRM device specified by @dev. Currently we only
319 * set HOTPLUG=1 in the uevent environment, but this could be expanded to
320 * deal with other types of events.
321 */
322void drm_sysfs_hotplug_event(struct drm_device *dev)
323{
324 char *event_string = "HOTPLUG=1";
325 char *envp[] = { event_string, NULL };
326
327 DRM_DEBUG("generating hotplug event\n");
328
5bdebb18 329 kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
f453ba04 330}
5ca58282 331EXPORT_SYMBOL(drm_sysfs_hotplug_event);
f453ba04 332
760c960b
DH
333static void drm_sysfs_release(struct device *dev)
334{
335 kfree(dev);
336}
337
e1728075 338struct device *drm_sysfs_minor_alloc(struct drm_minor *minor)
1da177e4 339{
e1728075
DH
340 const char *minor_str;
341 struct device *kdev;
760c960b 342 int r;
e8b962b6 343
f453ba04
DA
344 if (minor->type == DRM_MINOR_CONTROL)
345 minor_str = "controlD%d";
e1728075
DH
346 else if (minor->type == DRM_MINOR_RENDER)
347 minor_str = "renderD%d";
348 else
349 minor_str = "card%d";
350
351 kdev = kzalloc(sizeof(*kdev), GFP_KERNEL);
352 if (!kdev)
353 return ERR_PTR(-ENOMEM);
354
355 device_initialize(kdev);
356 kdev->devt = MKDEV(DRM_MAJOR, minor->index);
357 kdev->class = drm_class;
358 kdev->type = &drm_sysfs_device_minor;
359 kdev->parent = minor->dev->dev;
360 kdev->release = drm_sysfs_release;
361 dev_set_drvdata(kdev, minor);
362
363 r = dev_set_name(kdev, minor_str, minor->index);
760c960b 364 if (r < 0)
e1728075 365 goto err_free;
760c960b 366
e1728075 367 return kdev;
1da177e4 368
e1728075
DH
369err_free:
370 put_device(kdev);
371 return ERR_PTR(r);
1da177e4 372}
327c225b 373
327c225b 374/**
e2271704
DV
375 * drm_class_device_register - register new device with the DRM sysfs class
376 * @dev: device to register
327c225b 377 *
e2271704
DV
378 * Registers a new &struct device within the DRM sysfs class. Essentially only
379 * used by ttm to have a place for its global settings. Drivers should never use
380 * this.
327c225b 381 */
327c225b
TH
382int drm_class_device_register(struct device *dev)
383{
49099c49
DA
384 if (!drm_class || IS_ERR(drm_class))
385 return -ENOENT;
386
327c225b
TH
387 dev->class = drm_class;
388 return device_register(dev);
389}
390EXPORT_SYMBOL_GPL(drm_class_device_register);
391
e2271704
DV
392/**
393 * drm_class_device_unregister - unregister device with the DRM sysfs class
394 * @dev: device to unregister
395 *
396 * Unregisters a &struct device from the DRM sysfs class. Essentially only used
397 * by ttm to have a place for its global settings. Drivers should never use
398 * this.
399 */
327c225b
TH
400void drm_class_device_unregister(struct device *dev)
401{
402 return device_unregister(dev);
403}
404EXPORT_SYMBOL_GPL(drm_class_device_unregister);