Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
e172807
Documentation
arch
block
crypto
drivers
accessibility
acpi
amba
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
clk
clocksource
connector
cpufreq
cpuidle
crypto
dca
devfreq
dio
dma
edac
eisa
extcon
firewire
firmware
fmc
gpio
gpu
drm
armada
ast
bochs
bridge
cirrus
exynos
gma500
i2c
i810
i915
mga
mgag200
msm
nouveau
omapdrm
panel
qxl
r128
radeon
rcar-du
savage
shmobile
sis
sti
tdfx
tegra
tilcdc
ttm
udl
via
vmwgfx
Kconfig
Makefile
README.drm
ati_pcigart.c
drm_agpsupport.c
drm_auth.c
drm_buffer.c
drm_bufs.c
drm_cache.c
drm_context.c
drm_crtc.c
drm_crtc_helper.c
drm_crtc_internal.h
drm_debugfs.c
drm_dma.c
drm_dp_helper.c
drm_dp_mst_topology.c
drm_edid.c
drm_edid_load.c
drm_encoder_slave.c
drm_fb_cma_helper.c
drm_fb_helper.c
drm_flip_work.c
drm_fops.c
drm_gem.c
drm_gem_cma_helper.c
drm_global.c
drm_hashtab.c
drm_info.c
drm_ioc32.c
drm_ioctl.c
drm_irq.c
drm_lock.c
drm_memory.c
drm_mipi_dsi.c
drm_mm.c
drm_modes.c
drm_modeset_lock.c
drm_of.c
drm_panel.c
drm_pci.c
drm_plane_helper.c
drm_platform.c
drm_prime.c
drm_probe_helper.c
drm_rect.c
drm_scatter.c
drm_stub.c
drm_sysfs.c
drm_trace.h
drm_trace_points.c
drm_usb.c
drm_vm.c
drm_vma_manager.c
host1x
ipu-v3
vga
Makefile
hid
hsi
hv
hwmon
hwspinlock
i2c
ide
idle
iio
infiniband
input
iommu
ipack
irqchip
isdn
leds
lguest
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
mtd
net
nfc
ntb
nubus
of
oprofile
parisc
parport
pci
pcmcia
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sfi
sh
sn
soc
spi
spmi
ssb
staging
target
tc
thermal
tty
uio
usb
uwb
vfio
vhost
video
virt
virtio
vlynq
vme
w1
watchdog
xen
zorro
Kconfig
Makefile
firmware
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
REPORTING-BUGS
Breadcrumbs
linux
/
drivers
/
gpu
/
drm
/
drm_sysfs.c
Copy path
Blame
Blame
Latest commit
History
History
569 lines (486 loc) · 14.5 KB
Breadcrumbs
linux
/
drivers
/
gpu
/
drm
/
drm_sysfs.c
Top
File metadata and controls
Code
Blame
569 lines (486 loc) · 14.5 KB
Raw
/* * drm_sysfs.c - Modifications to drm_sysfs_class.c to support * extra sysfs attribute from DRM. Normal drm_sysfs_class * does not allow adding attributes. * * Copyright (c) 2004 Jon Smirl <jonsmirl@gmail.com> * Copyright (c) 2003-2004 Greg Kroah-Hartman <greg@kroah.com> * Copyright (c) 2003-2004 IBM Corp. * * This file is released under the GPLv2 * */ #include <linux/device.h> #include <linux/kdev_t.h> #include <linux/gfp.h> #include <linux/err.h> #include <linux/export.h> #include <drm/drm_sysfs.h> #include <drm/drm_core.h> #include <drm/drmP.h> #define to_drm_minor(d) dev_get_drvdata(d) #define to_drm_connector(d) dev_get_drvdata(d) static struct device_type drm_sysfs_device_minor = { .name = "drm_minor" }; /** * __drm_class_suspend - internal DRM class suspend routine * @dev: Linux device to suspend * @state: power state to enter * * Just figures out what the actual struct drm_device associated with * @dev is and calls its suspend hook, if present. */ static int __drm_class_suspend(struct device *dev, pm_message_t state) { if (dev->type == &drm_sysfs_device_minor) { struct drm_minor *drm_minor = to_drm_minor(dev); struct drm_device *drm_dev = drm_minor->dev; if (drm_minor->type == DRM_MINOR_LEGACY && !drm_core_check_feature(drm_dev, DRIVER_MODESET) && drm_dev->driver->suspend) return drm_dev->driver->suspend(drm_dev, state); } return 0; } /** * drm_class_suspend - internal DRM class suspend hook. Simply calls * __drm_class_suspend() with the correct pm state. * @dev: Linux device to suspend */ static int drm_class_suspend(struct device *dev) { return __drm_class_suspend(dev, PMSG_SUSPEND); } /** * drm_class_freeze - internal DRM class freeze hook. Simply calls * __drm_class_suspend() with the correct pm state. * @dev: Linux device to freeze */ static int drm_class_freeze(struct device *dev) { return __drm_class_suspend(dev, PMSG_FREEZE); } /** * drm_class_resume - DRM class resume hook * @dev: Linux device to resume * * Just figures out what the actual struct drm_device associated with * @dev is and calls its resume hook, if present. */ static int drm_class_resume(struct device *dev) { if (dev->type == &drm_sysfs_device_minor) { struct drm_minor *drm_minor = to_drm_minor(dev); struct drm_device *drm_dev = drm_minor->dev; if (drm_minor->type == DRM_MINOR_LEGACY && !drm_core_check_feature(drm_dev, DRIVER_MODESET) && drm_dev->driver->resume) return drm_dev->driver->resume(drm_dev); } return 0; } static const struct dev_pm_ops drm_class_dev_pm_ops = { .suspend = drm_class_suspend, .resume = drm_class_resume, .freeze = drm_class_freeze, }; static char *drm_devnode(struct device *dev, umode_t *mode) { return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev)); } static CLASS_ATTR_STRING(version, S_IRUGO, CORE_NAME " " __stringify(CORE_MAJOR) "." __stringify(CORE_MINOR) "." __stringify(CORE_PATCHLEVEL) " " CORE_DATE); /** * drm_sysfs_create - create a struct drm_sysfs_class structure * @owner: pointer to the module that is to "own" this struct drm_sysfs_class * @name: pointer to a string for the name of this class. * * This is used to create DRM class pointer that can then be used * in calls to drm_sysfs_device_add(). * * Note, the pointer created here is to be destroyed when finished by making a * call to drm_sysfs_destroy(). */ struct class *drm_sysfs_create(struct module *owner, char *name) { struct class *class; int err; class = class_create(owner, name); if (IS_ERR(class)) { err = PTR_ERR(class); goto err_out; } class->pm = &drm_class_dev_pm_ops; err = class_create_file(class, &class_attr_version.attr); if (err) goto err_out_class; class->devnode = drm_devnode; return class; err_out_class: class_destroy(class); err_out: return ERR_PTR(err); } /** * drm_sysfs_destroy - destroys DRM class * * Destroy the DRM device class. */ void drm_sysfs_destroy(void) { if ((drm_class == NULL) || (IS_ERR(drm_class))) return; class_remove_file(drm_class, &class_attr_version.attr); class_destroy(drm_class); drm_class = NULL; } /* * Connector properties */ static ssize_t status_show(struct device *device, struct device_attribute *attr, char *buf) { struct drm_connector *connector = to_drm_connector(device); enum drm_connector_status status; int ret; ret = mutex_lock_interruptible(&connector->dev->mode_config.mutex); if (ret) return ret; status = connector->funcs->detect(connector, true); mutex_unlock(&connector->dev->mode_config.mutex); return snprintf(buf, PAGE_SIZE, "%s\n", drm_get_connector_status_name(status)); } static ssize_t dpms_show(struct device *device, struct device_attribute *attr, char *buf) { struct drm_connector *connector = to_drm_connector(device); struct drm_device *dev = connector->dev; uint64_t dpms_status; int ret; ret = drm_object_property_get_value(&connector->base, dev->mode_config.dpms_property, &dpms_status); if (ret) return 0; return snprintf(buf, PAGE_SIZE, "%s\n", drm_get_dpms_name((int)dpms_status)); } static ssize_t enabled_show(struct device *device, struct device_attribute *attr, char *buf) { struct drm_connector *connector = to_drm_connector(device); return snprintf(buf, PAGE_SIZE, "%s\n", connector->encoder ? "enabled" : "disabled"); } static ssize_t edid_show(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *connector_dev = container_of(kobj, struct device, kobj); struct drm_connector *connector = to_drm_connector(connector_dev); unsigned char *edid; size_t size; if (!connector->edid_blob_ptr) return 0; edid = connector->edid_blob_ptr->data; size = connector->edid_blob_ptr->length; if (!edid) return 0; if (off >= size) return 0; if (off + count > size) count = size - off; memcpy(buf, edid + off, count); return count; } static ssize_t modes_show(struct device *device, struct device_attribute *attr, char *buf) { struct drm_connector *connector = to_drm_connector(device); struct drm_display_mode *mode; int written = 0; list_for_each_entry(mode, &connector->modes, head) { written += snprintf(buf + written, PAGE_SIZE - written, "%s\n", mode->name); } return written; } static ssize_t subconnector_show(struct device *device, struct device_attribute *attr, char *buf) { struct drm_connector *connector = to_drm_connector(device); struct drm_device *dev = connector->dev; struct drm_property *prop = NULL; uint64_t subconnector; int is_tv = 0; int ret; switch (connector->connector_type) { case DRM_MODE_CONNECTOR_DVII: prop = dev->mode_config.dvi_i_subconnector_property; break; case DRM_MODE_CONNECTOR_Composite: case DRM_MODE_CONNECTOR_SVIDEO: case DRM_MODE_CONNECTOR_Component: case DRM_MODE_CONNECTOR_TV: prop = dev->mode_config.tv_subconnector_property; is_tv = 1; break; default: DRM_ERROR("Wrong connector type for this property\n"); return 0; } if (!prop) { DRM_ERROR("Unable to find subconnector property\n"); return 0; } ret = drm_object_property_get_value(&connector->base, prop, &subconnector); if (ret) return 0; return snprintf(buf, PAGE_SIZE, "%s", is_tv ? drm_get_tv_subconnector_name((int)subconnector) : drm_get_dvi_i_subconnector_name((int)subconnector)); } static ssize_t select_subconnector_show(struct device *device, struct device_attribute *attr, char *buf) { struct drm_connector *connector = to_drm_connector(device); struct drm_device *dev = connector->dev; struct drm_property *prop = NULL; uint64_t subconnector; int is_tv = 0; int ret; switch (connector->connector_type) { case DRM_MODE_CONNECTOR_DVII: prop = dev->mode_config.dvi_i_select_subconnector_property; break; case DRM_MODE_CONNECTOR_Composite: case DRM_MODE_CONNECTOR_SVIDEO: case DRM_MODE_CONNECTOR_Component: case DRM_MODE_CONNECTOR_TV: prop = dev->mode_config.tv_select_subconnector_property; is_tv = 1; break; default: DRM_ERROR("Wrong connector type for this property\n"); return 0; } if (!prop) { DRM_ERROR("Unable to find select subconnector property\n"); return 0; } ret = drm_object_property_get_value(&connector->base, prop, &subconnector); if (ret) return 0; return snprintf(buf, PAGE_SIZE, "%s", is_tv ? drm_get_tv_select_name((int)subconnector) : drm_get_dvi_i_select_name((int)subconnector)); } static struct device_attribute connector_attrs[] = { __ATTR_RO(status), __ATTR_RO(enabled), __ATTR_RO(dpms), __ATTR_RO(modes), }; /* These attributes are for both DVI-I connectors and all types of tv-out. */ static struct device_attribute connector_attrs_opt1[] = { __ATTR_RO(subconnector), __ATTR_RO(select_subconnector), }; static struct bin_attribute edid_attr = { .attr.name = "edid", .attr.mode = 0444, .size = 0, .read = edid_show, }; /** * drm_sysfs_connector_add - add a connector to sysfs * @connector: connector to add * * Create a connector device in sysfs, along with its associated connector * properties (so far, connection status, dpms, mode list & edid) and * generate a hotplug event so userspace knows there's a new connector * available. */ int drm_sysfs_connector_add(struct drm_connector *connector) { struct drm_device *dev = connector->dev; int attr_cnt = 0; int opt_cnt = 0; int i; int ret; if (connector->kdev) return 0; connector->kdev = device_create(drm_class, dev->primary->kdev, 0, connector, "card%d-%s", dev->primary->index, connector->name); DRM_DEBUG("adding \"%s\" to sysfs\n", connector->name); if (IS_ERR(connector->kdev)) { DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev)); ret = PTR_ERR(connector->kdev); goto out; } /* Standard attributes */ for (attr_cnt = 0; attr_cnt < ARRAY_SIZE(connector_attrs); attr_cnt++) { ret = device_create_file(connector->kdev, &connector_attrs[attr_cnt]); if (ret) goto err_out_files; } /* Optional attributes */ /* * In the long run it maybe a good idea to make one set of * optionals per connector type. */ switch (connector->connector_type) { case DRM_MODE_CONNECTOR_DVII: case DRM_MODE_CONNECTOR_Composite: case DRM_MODE_CONNECTOR_SVIDEO: case DRM_MODE_CONNECTOR_Component: case DRM_MODE_CONNECTOR_TV: for (opt_cnt = 0; opt_cnt < ARRAY_SIZE(connector_attrs_opt1); opt_cnt++) { ret = device_create_file(connector->kdev, &connector_attrs_opt1[opt_cnt]); if (ret) goto err_out_files; } break; default: break; } ret = sysfs_create_bin_file(&connector->kdev->kobj, &edid_attr); if (ret) goto err_out_files; /* Let userspace know we have a new connector */ drm_sysfs_hotplug_event(dev); return 0; err_out_files: for (i = 0; i < opt_cnt; i++) device_remove_file(connector->kdev, &connector_attrs_opt1[i]); for (i = 0; i < attr_cnt; i++) device_remove_file(connector->kdev, &connector_attrs[i]); device_unregister(connector->kdev); out: return ret; } /** * drm_sysfs_connector_remove - remove an connector device from sysfs * @connector: connector to remove * * Remove @connector and its associated attributes from sysfs. Note that * the device model core will take care of sending the "remove" uevent * at this time, so we don't need to do it. * * Note: * This routine should only be called if the connector was previously * successfully registered. If @connector hasn't been registered yet, * you'll likely see a panic somewhere deep in sysfs code when called. */ void drm_sysfs_connector_remove(struct drm_connector *connector) { int i; if (!connector->kdev) return; DRM_DEBUG("removing \"%s\" from sysfs\n", connector->name); for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) device_remove_file(connector->kdev, &connector_attrs[i]); sysfs_remove_bin_file(&connector->kdev->kobj, &edid_attr); device_unregister(connector->kdev); connector->kdev = NULL; } /** * drm_sysfs_hotplug_event - generate a DRM uevent * @dev: DRM device * * Send a uevent for the DRM device specified by @dev. Currently we only * set HOTPLUG=1 in the uevent environment, but this could be expanded to * deal with other types of events. */ void drm_sysfs_hotplug_event(struct drm_device *dev) { char *event_string = "HOTPLUG=1"; char *envp[] = { event_string, NULL }; DRM_DEBUG("generating hotplug event\n"); kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp); } EXPORT_SYMBOL(drm_sysfs_hotplug_event); static void drm_sysfs_release(struct device *dev) { kfree(dev); } /** * drm_sysfs_minor_alloc() - Allocate sysfs device for given minor * @minor: minor to allocate sysfs device for * * This allocates a new sysfs device for @minor and returns it. The device is * not registered nor linked. The caller has to use device_add() and * device_del() to register and unregister it. * * Note that dev_get_drvdata() on the new device will return the minor. * However, the device does not hold a ref-count to the minor nor to the * underlying drm_device. This is unproblematic as long as you access the * private data only in sysfs callbacks. device_del() disables those * synchronously, so they cannot be called after you cleanup a minor. */ struct device *drm_sysfs_minor_alloc(struct drm_minor *minor) { const char *minor_str; struct device *kdev; int r; if (minor->type == DRM_MINOR_CONTROL) minor_str = "controlD%d"; else if (minor->type == DRM_MINOR_RENDER) minor_str = "renderD%d"; else minor_str = "card%d"; kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); if (!kdev) return ERR_PTR(-ENOMEM); device_initialize(kdev); kdev->devt = MKDEV(DRM_MAJOR, minor->index); kdev->class = drm_class; kdev->type = &drm_sysfs_device_minor; kdev->parent = minor->dev->dev; kdev->release = drm_sysfs_release; dev_set_drvdata(kdev, minor); r = dev_set_name(kdev, minor_str, minor->index); if (r < 0) goto err_free; return kdev; err_free: put_device(kdev); return ERR_PTR(r); } /** * drm_class_device_register - Register a struct device in the drm class. * * @dev: pointer to struct device to register. * * @dev should have all relevant members pre-filled with the exception * of the class member. In particular, the device_type member must * be set. */ int drm_class_device_register(struct device *dev) { if (!drm_class || IS_ERR(drm_class)) return -ENOENT; dev->class = drm_class; return device_register(dev); } EXPORT_SYMBOL_GPL(drm_class_device_register); void drm_class_device_unregister(struct device *dev) { return device_unregister(dev); } EXPORT_SYMBOL_GPL(drm_class_device_unregister);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
You can’t perform that action at this time.