1c0e09200SDave Airlie 2c0e09200SDave Airlie /* 3c0e09200SDave Airlie * drm_sysfs.c - Modifications to drm_sysfs_class.c to support 4c0e09200SDave Airlie * extra sysfs attribute from DRM. Normal drm_sysfs_class 5c0e09200SDave Airlie * does not allow adding attributes. 6c0e09200SDave Airlie * 7c0e09200SDave Airlie * Copyright (c) 2004 Jon Smirl <jonsmirl@gmail.com> 8c0e09200SDave Airlie * Copyright (c) 2003-2004 Greg Kroah-Hartman <greg@kroah.com> 9c0e09200SDave Airlie * Copyright (c) 2003-2004 IBM Corp. 10c0e09200SDave Airlie * 11c0e09200SDave Airlie * This file is released under the GPLv2 12c0e09200SDave Airlie * 13c0e09200SDave Airlie */ 14c0e09200SDave Airlie 15c0e09200SDave Airlie #include <linux/device.h> 16c0e09200SDave Airlie #include <linux/kdev_t.h> 175a0e3ad6STejun Heo #include <linux/gfp.h> 18c0e09200SDave Airlie #include <linux/err.h> 192d1a8a48SPaul Gortmaker #include <linux/export.h> 20c0e09200SDave Airlie 21760285e7SDavid Howells #include <drm/drm_sysfs.h> 22760285e7SDavid Howells #include <drm/drmP.h> 2367d0ec4eSDaniel Vetter #include "drm_internal.h" 24c0e09200SDave Airlie 255bdebb18SDave Airlie #define to_drm_minor(d) dev_get_drvdata(d) 265bdebb18SDave Airlie #define to_drm_connector(d) dev_get_drvdata(d) 27c0e09200SDave Airlie 28e2271704SDaniel Vetter /** 29e2271704SDaniel Vetter * DOC: overview 30e2271704SDaniel Vetter * 31e2271704SDaniel Vetter * DRM provides very little additional support to drivers for sysfs 32e2271704SDaniel Vetter * interactions, beyond just all the standard stuff. Drivers who want to expose 33e2271704SDaniel Vetter * additional sysfs properties and property groups can attach them at either 34e2271704SDaniel Vetter * &drm_device.dev or &drm_connector.kdev. 35e2271704SDaniel Vetter * 36e2271704SDaniel Vetter * Registration is automatically handled when calling drm_dev_register(), or 37e2271704SDaniel Vetter * drm_connector_register() in case of hot-plugged connectors. Unregistration is 38e2271704SDaniel Vetter * also automatically handled by drm_dev_unregister() and 39e2271704SDaniel Vetter * drm_connector_unregister(). 40e2271704SDaniel Vetter */ 41e2271704SDaniel Vetter 4208e4d534SThomas Hellstrom static struct device_type drm_sysfs_device_minor = { 4308e4d534SThomas Hellstrom .name = "drm_minor" 4408e4d534SThomas Hellstrom }; 4508e4d534SThomas Hellstrom 46fcc90213SDavid Herrmann struct class *drm_class; 47fcc90213SDavid Herrmann 482c9ede55SAl Viro static char *drm_devnode(struct device *dev, umode_t *mode) 4902200d06SKay Sievers { 5002200d06SKay Sievers return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev)); 5102200d06SKay Sievers } 5202200d06SKay Sievers 5382d5e73fSDavid Herrmann static CLASS_ATTR_STRING(version, S_IRUGO, "drm 1.1.0 20060810"); 54c0e09200SDave Airlie 55c0e09200SDave Airlie /** 56fcc90213SDavid Herrmann * drm_sysfs_init - initialize sysfs helpers 57c0e09200SDave Airlie * 58fcc90213SDavid Herrmann * This is used to create the DRM class, which is the implicit parent of any 59fcc90213SDavid Herrmann * other top-level DRM sysfs objects. 60c0e09200SDave Airlie * 61fcc90213SDavid Herrmann * You must call drm_sysfs_destroy() to release the allocated resources. 62fcc90213SDavid Herrmann * 63fcc90213SDavid Herrmann * Return: 0 on success, negative error code on failure. 64c0e09200SDave Airlie */ 65fcc90213SDavid Herrmann int drm_sysfs_init(void) 66c0e09200SDave Airlie { 67c0e09200SDave Airlie int err; 68c0e09200SDave Airlie 69fcc90213SDavid Herrmann drm_class = class_create(THIS_MODULE, "drm"); 70fcc90213SDavid Herrmann if (IS_ERR(drm_class)) 71fcc90213SDavid Herrmann return PTR_ERR(drm_class); 72fcc90213SDavid Herrmann 73fcc90213SDavid Herrmann err = class_create_file(drm_class, &class_attr_version.attr); 74fcc90213SDavid Herrmann if (err) { 75fcc90213SDavid Herrmann class_destroy(drm_class); 76fcc90213SDavid Herrmann drm_class = NULL; 77fcc90213SDavid Herrmann return err; 78c0e09200SDave Airlie } 79c0e09200SDave Airlie 80fcc90213SDavid Herrmann drm_class->devnode = drm_devnode; 81fcc90213SDavid Herrmann return 0; 82c0e09200SDave Airlie } 83c0e09200SDave Airlie 84c0e09200SDave Airlie /** 85c0e09200SDave Airlie * drm_sysfs_destroy - destroys DRM class 86c0e09200SDave Airlie * 87c0e09200SDave Airlie * Destroy the DRM device class. 88c0e09200SDave Airlie */ 89c0e09200SDave Airlie void drm_sysfs_destroy(void) 90c0e09200SDave Airlie { 9126b91ae4SDavid Herrmann if (IS_ERR_OR_NULL(drm_class)) 92c0e09200SDave Airlie return; 930933e2d9SAndi Kleen class_remove_file(drm_class, &class_attr_version.attr); 94c0e09200SDave Airlie class_destroy(drm_class); 9549099c49SDave Airlie drm_class = NULL; 96c0e09200SDave Airlie } 97c0e09200SDave Airlie 98f453ba04SDave Airlie /* 99f453ba04SDave Airlie * Connector properties 100f453ba04SDave Airlie */ 101c484f02dSChris Wilson static ssize_t status_store(struct device *device, 102c484f02dSChris Wilson struct device_attribute *attr, 103c484f02dSChris Wilson const char *buf, size_t count) 104c484f02dSChris Wilson { 105c484f02dSChris Wilson struct drm_connector *connector = to_drm_connector(device); 106c484f02dSChris Wilson struct drm_device *dev = connector->dev; 107ed293f77SDaniel Vetter enum drm_connector_force old_force; 108c484f02dSChris Wilson int ret; 109c484f02dSChris Wilson 110c484f02dSChris Wilson ret = mutex_lock_interruptible(&dev->mode_config.mutex); 111c484f02dSChris Wilson if (ret) 112c484f02dSChris Wilson return ret; 113c484f02dSChris Wilson 114ed293f77SDaniel Vetter old_force = connector->force; 115c484f02dSChris Wilson 116ed293f77SDaniel Vetter if (sysfs_streq(buf, "detect")) 117c484f02dSChris Wilson connector->force = 0; 118ed293f77SDaniel Vetter else if (sysfs_streq(buf, "on")) 119c484f02dSChris Wilson connector->force = DRM_FORCE_ON; 120ed293f77SDaniel Vetter else if (sysfs_streq(buf, "on-digital")) 121c484f02dSChris Wilson connector->force = DRM_FORCE_ON_DIGITAL; 122ed293f77SDaniel Vetter else if (sysfs_streq(buf, "off")) 123c484f02dSChris Wilson connector->force = DRM_FORCE_OFF; 124ed293f77SDaniel Vetter else 125c484f02dSChris Wilson ret = -EINVAL; 126c484f02dSChris Wilson 127ed293f77SDaniel Vetter if (old_force != connector->force || !connector->force) { 128ed293f77SDaniel Vetter DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force updated from %d to %d or reprobing\n", 129c484f02dSChris Wilson connector->base.id, 130c484f02dSChris Wilson connector->name, 131ed293f77SDaniel Vetter old_force, connector->force); 132c484f02dSChris Wilson 133ed293f77SDaniel Vetter connector->funcs->fill_modes(connector, 134ed293f77SDaniel Vetter dev->mode_config.max_width, 135ed293f77SDaniel Vetter dev->mode_config.max_height); 136c484f02dSChris Wilson } 137c484f02dSChris Wilson 138c484f02dSChris Wilson mutex_unlock(&dev->mode_config.mutex); 139c484f02dSChris Wilson 14038d8571dSRussell King return ret ? ret : count; 141c484f02dSChris Wilson } 142c484f02dSChris Wilson 143f453ba04SDave Airlie static ssize_t status_show(struct device *device, 144f453ba04SDave Airlie struct device_attribute *attr, 145f453ba04SDave Airlie char *buf) 146f453ba04SDave Airlie { 147f453ba04SDave Airlie struct drm_connector *connector = to_drm_connector(device); 1484eb9b945SDaniel Vetter enum drm_connector_status status; 1494eb9b945SDaniel Vetter 1504eb9b945SDaniel Vetter status = READ_ONCE(connector->status); 151007c80a5SChris Wilson 15275185c92SKeith Packard return snprintf(buf, PAGE_SIZE, "%s\n", 1534eb9b945SDaniel Vetter drm_get_connector_status_name(status)); 154f453ba04SDave Airlie } 155f453ba04SDave Airlie 156f453ba04SDave Airlie static ssize_t dpms_show(struct device *device, 157f453ba04SDave Airlie struct device_attribute *attr, 158f453ba04SDave Airlie char *buf) 159f453ba04SDave Airlie { 160f453ba04SDave Airlie struct drm_connector *connector = to_drm_connector(device); 161621bd0f6SDaniel Vetter int dpms; 162f453ba04SDave Airlie 163621bd0f6SDaniel Vetter dpms = READ_ONCE(connector->dpms); 164f453ba04SDave Airlie 16575185c92SKeith Packard return snprintf(buf, PAGE_SIZE, "%s\n", 166621bd0f6SDaniel Vetter drm_get_dpms_name(dpms)); 167f453ba04SDave Airlie } 168f453ba04SDave Airlie 169f453ba04SDave Airlie static ssize_t enabled_show(struct device *device, 170f453ba04SDave Airlie struct device_attribute *attr, 171f453ba04SDave Airlie char *buf) 172f453ba04SDave Airlie { 173f453ba04SDave Airlie struct drm_connector *connector = to_drm_connector(device); 1744eb9b945SDaniel Vetter bool enabled; 175f453ba04SDave Airlie 1764eb9b945SDaniel Vetter enabled = READ_ONCE(connector->encoder); 1774eb9b945SDaniel Vetter 1784eb9b945SDaniel Vetter return snprintf(buf, PAGE_SIZE, enabled ? "enabled\n" : "disabled\n"); 179f453ba04SDave Airlie } 180f453ba04SDave Airlie 1812c3c8beaSChris Wright static ssize_t edid_show(struct file *filp, struct kobject *kobj, 1822c3c8beaSChris Wright struct bin_attribute *attr, char *buf, loff_t off, 1832c3c8beaSChris Wright size_t count) 184f453ba04SDave Airlie { 185d122cbf1SGeliang Tang struct device *connector_dev = kobj_to_dev(kobj); 186f453ba04SDave Airlie struct drm_connector *connector = to_drm_connector(connector_dev); 187f453ba04SDave Airlie unsigned char *edid; 188f453ba04SDave Airlie size_t size; 189a48a62bcSDaniel Vetter ssize_t ret = 0; 190f453ba04SDave Airlie 191a48a62bcSDaniel Vetter mutex_lock(&connector->dev->mode_config.mutex); 192f453ba04SDave Airlie if (!connector->edid_blob_ptr) 193a48a62bcSDaniel Vetter goto unlock; 194f453ba04SDave Airlie 195f453ba04SDave Airlie edid = connector->edid_blob_ptr->data; 196f453ba04SDave Airlie size = connector->edid_blob_ptr->length; 197f453ba04SDave Airlie if (!edid) 198a48a62bcSDaniel Vetter goto unlock; 199f453ba04SDave Airlie 200f453ba04SDave Airlie if (off >= size) 201a48a62bcSDaniel Vetter goto unlock; 202f453ba04SDave Airlie 203f453ba04SDave Airlie if (off + count > size) 204f453ba04SDave Airlie count = size - off; 205f453ba04SDave Airlie memcpy(buf, edid + off, count); 206f453ba04SDave Airlie 207a48a62bcSDaniel Vetter ret = count; 208a48a62bcSDaniel Vetter unlock: 209a48a62bcSDaniel Vetter mutex_unlock(&connector->dev->mode_config.mutex); 210a48a62bcSDaniel Vetter 211a48a62bcSDaniel Vetter return ret; 212f453ba04SDave Airlie } 213f453ba04SDave Airlie 214f453ba04SDave Airlie static ssize_t modes_show(struct device *device, 215f453ba04SDave Airlie struct device_attribute *attr, 216f453ba04SDave Airlie char *buf) 217f453ba04SDave Airlie { 218f453ba04SDave Airlie struct drm_connector *connector = to_drm_connector(device); 219f453ba04SDave Airlie struct drm_display_mode *mode; 220f453ba04SDave Airlie int written = 0; 221f453ba04SDave Airlie 222a48a62bcSDaniel Vetter mutex_lock(&connector->dev->mode_config.mutex); 223f453ba04SDave Airlie list_for_each_entry(mode, &connector->modes, head) { 224f453ba04SDave Airlie written += snprintf(buf + written, PAGE_SIZE - written, "%s\n", 225f453ba04SDave Airlie mode->name); 226f453ba04SDave Airlie } 227a48a62bcSDaniel Vetter mutex_unlock(&connector->dev->mode_config.mutex); 228f453ba04SDave Airlie 229f453ba04SDave Airlie return written; 230f453ba04SDave Airlie } 231f453ba04SDave Airlie 232c484f02dSChris Wilson static DEVICE_ATTR_RW(status); 233335f1a62STakashi Iwai static DEVICE_ATTR_RO(enabled); 234335f1a62STakashi Iwai static DEVICE_ATTR_RO(dpms); 235335f1a62STakashi Iwai static DEVICE_ATTR_RO(modes); 236335f1a62STakashi Iwai 237335f1a62STakashi Iwai static struct attribute *connector_dev_attrs[] = { 238335f1a62STakashi Iwai &dev_attr_status.attr, 239335f1a62STakashi Iwai &dev_attr_enabled.attr, 240335f1a62STakashi Iwai &dev_attr_dpms.attr, 241335f1a62STakashi Iwai &dev_attr_modes.attr, 242335f1a62STakashi Iwai NULL 243f453ba04SDave Airlie }; 244f453ba04SDave Airlie 245f453ba04SDave Airlie static struct bin_attribute edid_attr = { 246f453ba04SDave Airlie .attr.name = "edid", 247e36ebaf4SKeith Packard .attr.mode = 0444, 2487466f4ccSAdam Jackson .size = 0, 249f453ba04SDave Airlie .read = edid_show, 250f453ba04SDave Airlie }; 251f453ba04SDave Airlie 252335f1a62STakashi Iwai static struct bin_attribute *connector_bin_attrs[] = { 253335f1a62STakashi Iwai &edid_attr, 254335f1a62STakashi Iwai NULL 255335f1a62STakashi Iwai }; 256335f1a62STakashi Iwai 257335f1a62STakashi Iwai static const struct attribute_group connector_dev_group = { 258335f1a62STakashi Iwai .attrs = connector_dev_attrs, 259335f1a62STakashi Iwai .bin_attrs = connector_bin_attrs, 260335f1a62STakashi Iwai }; 261335f1a62STakashi Iwai 262335f1a62STakashi Iwai static const struct attribute_group *connector_dev_groups[] = { 263335f1a62STakashi Iwai &connector_dev_group, 264335f1a62STakashi Iwai NULL 265335f1a62STakashi Iwai }; 266335f1a62STakashi Iwai 267f453ba04SDave Airlie int drm_sysfs_connector_add(struct drm_connector *connector) 268f453ba04SDave Airlie { 269f453ba04SDave Airlie struct drm_device *dev = connector->dev; 270f453ba04SDave Airlie 2715bdebb18SDave Airlie if (connector->kdev) 2725bdebb18SDave Airlie return 0; 2735bdebb18SDave Airlie 274335f1a62STakashi Iwai connector->kdev = 275335f1a62STakashi Iwai device_create_with_groups(drm_class, dev->primary->kdev, 0, 276335f1a62STakashi Iwai connector, connector_dev_groups, 277335f1a62STakashi Iwai "card%d-%s", dev->primary->index, 278335f1a62STakashi Iwai connector->name); 279f453ba04SDave Airlie DRM_DEBUG("adding \"%s\" to sysfs\n", 28025933820SJani Nikula connector->name); 281f453ba04SDave Airlie 2825bdebb18SDave Airlie if (IS_ERR(connector->kdev)) { 2835bdebb18SDave Airlie DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev)); 284335f1a62STakashi Iwai return PTR_ERR(connector->kdev); 285f453ba04SDave Airlie } 286f453ba04SDave Airlie 287f453ba04SDave Airlie /* Let userspace know we have a new connector */ 288f453ba04SDave Airlie drm_sysfs_hotplug_event(dev); 289f453ba04SDave Airlie 290f453ba04SDave Airlie return 0; 291f453ba04SDave Airlie } 292f453ba04SDave Airlie 293f453ba04SDave Airlie void drm_sysfs_connector_remove(struct drm_connector *connector) 294f453ba04SDave Airlie { 2955bdebb18SDave Airlie if (!connector->kdev) 2961828fe6cSDave Airlie return; 297f453ba04SDave Airlie DRM_DEBUG("removing \"%s\" from sysfs\n", 29825933820SJani Nikula connector->name); 299f453ba04SDave Airlie 3005bdebb18SDave Airlie device_unregister(connector->kdev); 3015bdebb18SDave Airlie connector->kdev = NULL; 302f453ba04SDave Airlie } 303f453ba04SDave Airlie 304f453ba04SDave Airlie /** 305f453ba04SDave Airlie * drm_sysfs_hotplug_event - generate a DRM uevent 306f453ba04SDave Airlie * @dev: DRM device 307f453ba04SDave Airlie * 308f453ba04SDave Airlie * Send a uevent for the DRM device specified by @dev. Currently we only 309f453ba04SDave Airlie * set HOTPLUG=1 in the uevent environment, but this could be expanded to 310f453ba04SDave Airlie * deal with other types of events. 311f453ba04SDave Airlie */ 312f453ba04SDave Airlie void drm_sysfs_hotplug_event(struct drm_device *dev) 313f453ba04SDave Airlie { 314f453ba04SDave Airlie char *event_string = "HOTPLUG=1"; 315f453ba04SDave Airlie char *envp[] = { event_string, NULL }; 316f453ba04SDave Airlie 317f453ba04SDave Airlie DRM_DEBUG("generating hotplug event\n"); 318f453ba04SDave Airlie 3195bdebb18SDave Airlie kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp); 320f453ba04SDave Airlie } 3215ca58282SJesse Barnes EXPORT_SYMBOL(drm_sysfs_hotplug_event); 322f453ba04SDave Airlie 323760c960bSDavid Herrmann static void drm_sysfs_release(struct device *dev) 324760c960bSDavid Herrmann { 325760c960bSDavid Herrmann kfree(dev); 326760c960bSDavid Herrmann } 327760c960bSDavid Herrmann 328e1728075SDavid Herrmann struct device *drm_sysfs_minor_alloc(struct drm_minor *minor) 329c0e09200SDave Airlie { 330e1728075SDavid Herrmann const char *minor_str; 331e1728075SDavid Herrmann struct device *kdev; 332760c960bSDavid Herrmann int r; 333c0e09200SDave Airlie 3340d49f303SDaniel Vetter if (minor->type == DRM_MINOR_RENDER) 335f453ba04SDave Airlie minor_str = "renderD%d"; 336f453ba04SDave Airlie else 337c0e09200SDave Airlie minor_str = "card%d"; 338c0e09200SDave Airlie 339e1728075SDavid Herrmann kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); 340e1728075SDavid Herrmann if (!kdev) 341e1728075SDavid Herrmann return ERR_PTR(-ENOMEM); 342760c960bSDavid Herrmann 343e1728075SDavid Herrmann device_initialize(kdev); 344e1728075SDavid Herrmann kdev->devt = MKDEV(DRM_MAJOR, minor->index); 345e1728075SDavid Herrmann kdev->class = drm_class; 346e1728075SDavid Herrmann kdev->type = &drm_sysfs_device_minor; 347e1728075SDavid Herrmann kdev->parent = minor->dev->dev; 348e1728075SDavid Herrmann kdev->release = drm_sysfs_release; 349e1728075SDavid Herrmann dev_set_drvdata(kdev, minor); 350760c960bSDavid Herrmann 351e1728075SDavid Herrmann r = dev_set_name(kdev, minor_str, minor->index); 352760c960bSDavid Herrmann if (r < 0) 353e1728075SDavid Herrmann goto err_free; 354760c960bSDavid Herrmann 355e1728075SDavid Herrmann return kdev; 356760c960bSDavid Herrmann 357e1728075SDavid Herrmann err_free: 358e1728075SDavid Herrmann put_device(kdev); 359e1728075SDavid Herrmann return ERR_PTR(r); 360c0e09200SDave Airlie } 361c0e09200SDave Airlie 362c0e09200SDave Airlie /** 363e2271704SDaniel Vetter * drm_class_device_register - register new device with the DRM sysfs class 364e2271704SDaniel Vetter * @dev: device to register 365327c225bSThomas Hellstrom * 366e2271704SDaniel Vetter * Registers a new &struct device within the DRM sysfs class. Essentially only 367e2271704SDaniel Vetter * used by ttm to have a place for its global settings. Drivers should never use 368e2271704SDaniel Vetter * this. 369327c225bSThomas Hellstrom */ 370327c225bSThomas Hellstrom int drm_class_device_register(struct device *dev) 371327c225bSThomas Hellstrom { 37249099c49SDave Airlie if (!drm_class || IS_ERR(drm_class)) 37349099c49SDave Airlie return -ENOENT; 37449099c49SDave Airlie 375327c225bSThomas Hellstrom dev->class = drm_class; 376327c225bSThomas Hellstrom return device_register(dev); 377327c225bSThomas Hellstrom } 378327c225bSThomas Hellstrom EXPORT_SYMBOL_GPL(drm_class_device_register); 379327c225bSThomas Hellstrom 380e2271704SDaniel Vetter /** 381e2271704SDaniel Vetter * drm_class_device_unregister - unregister device with the DRM sysfs class 382e2271704SDaniel Vetter * @dev: device to unregister 383e2271704SDaniel Vetter * 384e2271704SDaniel Vetter * Unregisters a &struct device from the DRM sysfs class. Essentially only used 385e2271704SDaniel Vetter * by ttm to have a place for its global settings. Drivers should never use 386e2271704SDaniel Vetter * this. 387e2271704SDaniel Vetter */ 388327c225bSThomas Hellstrom void drm_class_device_unregister(struct device *dev) 389327c225bSThomas Hellstrom { 390327c225bSThomas Hellstrom return device_unregister(dev); 391327c225bSThomas Hellstrom } 392327c225bSThomas Hellstrom EXPORT_SYMBOL_GPL(drm_class_device_unregister); 393