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