drm_sysfs.c 16.0 KB
Newer Older
1

L
Linus Torvalds 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * 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>
17
#include <linux/gfp.h>
L
Linus Torvalds 已提交
18
#include <linux/err.h>
19
#include <linux/export.h>
L
Linus Torvalds 已提交
20

21 22 23
#include <drm/drm_sysfs.h>
#include <drm/drm_core.h>
#include <drm/drmP.h>
24
#include "drm_internal.h"
L
Linus Torvalds 已提交
25

26 27
#define to_drm_minor(d) dev_get_drvdata(d)
#define to_drm_connector(d) dev_get_drvdata(d)
J
Jesse Barnes 已提交
28

29 30 31 32
static struct device_type drm_sysfs_device_minor = {
	.name = "drm_minor"
};

J
Jesse Barnes 已提交
33
/**
34
 * __drm_class_suspend - internal DRM class suspend routine
J
Jesse Barnes 已提交
35 36 37 38 39 40
 * @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.
 */
41
static int __drm_class_suspend(struct device *dev, pm_message_t state)
J
Jesse Barnes 已提交
42
{
43 44 45 46 47 48 49 50 51
	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);
	}
J
Jesse Barnes 已提交
52 53 54
	return 0;
}

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
/**
 * 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);
}

J
Jesse Barnes 已提交
75
/**
76
 * drm_class_resume - DRM class resume hook
J
Jesse Barnes 已提交
77 78 79 80 81
 * @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.
 */
82
static int drm_class_resume(struct device *dev)
J
Jesse Barnes 已提交
83
{
84 85 86 87 88 89 90 91 92
	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);
	}
J
Jesse Barnes 已提交
93 94 95
	return 0;
}

96 97 98 99 100 101
static const struct dev_pm_ops drm_class_dev_pm_ops = {
	.suspend	= drm_class_suspend,
	.resume		= drm_class_resume,
	.freeze		= drm_class_freeze,
};

102
static char *drm_devnode(struct device *dev, umode_t *mode)
103 104 105 106
{
	return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev));
}

107 108 109 110 111 112
static CLASS_ATTR_STRING(version, S_IRUGO,
		CORE_NAME " "
		__stringify(CORE_MAJOR) "."
		__stringify(CORE_MINOR) "."
		__stringify(CORE_PATCHLEVEL) " "
		CORE_DATE);
L
Linus Torvalds 已提交
113 114 115 116 117 118

/**
 * 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.
 *
J
Jesse Barnes 已提交
119
 * This is used to create DRM class pointer that can then be used
L
Linus Torvalds 已提交
120 121 122 123 124
 * 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().
 */
125
struct class *drm_sysfs_create(struct module *owner, char *name)
L
Linus Torvalds 已提交
126
{
127
	struct class *class;
128
	int err;
129 130

	class = class_create(owner, name);
A
Akinobu Mita 已提交
131 132
	if (IS_ERR(class)) {
		err = PTR_ERR(class);
133 134 135
		goto err_out;
	}

136
	class->pm = &drm_class_dev_pm_ops;
J
Jesse Barnes 已提交
137

138
	err = class_create_file(class, &class_attr_version.attr);
139 140
	if (err)
		goto err_out_class;
141

142
	class->devnode = drm_devnode;
143

144
	return class;
145 146 147 148 149

err_out_class:
	class_destroy(class);
err_out:
	return ERR_PTR(err);
L
Linus Torvalds 已提交
150 151 152
}

/**
J
Jesse Barnes 已提交
153
 * drm_sysfs_destroy - destroys DRM class
L
Linus Torvalds 已提交
154
 *
J
Jesse Barnes 已提交
155
 * Destroy the DRM device class.
L
Linus Torvalds 已提交
156
 */
J
Jesse Barnes 已提交
157
void drm_sysfs_destroy(void)
L
Linus Torvalds 已提交
158
{
J
Jesse Barnes 已提交
159
	if ((drm_class == NULL) || (IS_ERR(drm_class)))
L
Linus Torvalds 已提交
160
		return;
161
	class_remove_file(drm_class, &class_attr_version.attr);
J
Jesse Barnes 已提交
162
	class_destroy(drm_class);
163
	drm_class = NULL;
L
Linus Torvalds 已提交
164 165
}

D
Dave Airlie 已提交
166 167 168
/*
 * Connector properties
 */
169
static ssize_t status_store(struct device *device,
D
Dave Airlie 已提交
170
			   struct device_attribute *attr,
171
			   const char *buf, size_t count)
D
Dave Airlie 已提交
172 173
{
	struct drm_connector *connector = to_drm_connector(device);
174 175
	struct drm_device *dev = connector->dev;
	enum drm_connector_status old_status;
176 177
	int ret;

178
	ret = mutex_lock_interruptible(&dev->mode_config.mutex);
179 180
	if (ret)
		return ret;
D
Dave Airlie 已提交
181

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
	old_status = connector->status;

	if (sysfs_streq(buf, "detect")) {
		connector->force = 0;
		connector->status = connector->funcs->detect(connector, true);
	} else if (sysfs_streq(buf, "on")) {
		connector->force = DRM_FORCE_ON;
	} else if (sysfs_streq(buf, "on-digital")) {
		connector->force = DRM_FORCE_ON_DIGITAL;
	} else if (sysfs_streq(buf, "off")) {
		connector->force = DRM_FORCE_OFF;
	} else
		ret = -EINVAL;

	if (ret == 0 && connector->force) {
		if (connector->force == DRM_FORCE_ON ||
		    connector->force == DRM_FORCE_ON_DIGITAL)
			connector->status = connector_status_connected;
		else
			connector->status = connector_status_disconnected;
		if (connector->funcs->force)
			connector->funcs->force(connector);
	}

	if (old_status != connector->status) {
		DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n",
			      connector->base.id,
			      connector->name,
			      old_status, connector->status);

		dev->mode_config.delayed_event = true;
		if (dev->mode_config.poll_enabled)
			schedule_delayed_work(&dev->mode_config.output_poll_work,
					      0);
	}

	mutex_unlock(&dev->mode_config.mutex);

	return ret;
}

static ssize_t status_show(struct device *device,
			   struct device_attribute *attr,
			   char *buf)
{
	struct drm_connector *connector = to_drm_connector(device);
228

229
	return snprintf(buf, PAGE_SIZE, "%s\n",
230
			drm_get_connector_status_name(connector->status));
D
Dave Airlie 已提交
231 232 233 234 235 236 237 238 239 240 241
}

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;

242
	ret = drm_object_property_get_value(&connector->base,
D
Dave Airlie 已提交
243 244 245 246 247
					    dev->mode_config.dpms_property,
					    &dpms_status);
	if (ret)
		return 0;

248
	return snprintf(buf, PAGE_SIZE, "%s\n",
D
Dave Airlie 已提交
249 250 251 252 253 254 255 256 257
			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);

258
	return snprintf(buf, PAGE_SIZE, "%s\n", connector->encoder ? "enabled" :
D
Dave Airlie 已提交
259 260 261
			"disabled");
}

262 263 264
static ssize_t edid_show(struct file *filp, struct kobject *kobj,
			 struct bin_attribute *attr, char *buf, loff_t off,
			 size_t count)
D
Dave Airlie 已提交
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
{
	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:
323
		case DRM_MODE_CONNECTOR_TV:
D
Dave Airlie 已提交
324 325 326 327 328 329 330 331 332 333 334 335 336
			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;
	}

337
	ret = drm_object_property_get_value(&connector->base, prop, &subconnector);
D
Dave Airlie 已提交
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
	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:
364
		case DRM_MODE_CONNECTOR_TV:
D
Dave Airlie 已提交
365 366 367 368 369 370 371 372 373 374 375 376 377
			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;
	}

378
	ret = drm_object_property_get_value(&connector->base, prop, &subconnector);
D
Dave Airlie 已提交
379 380 381 382 383 384 385 386
	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));
}

387
static DEVICE_ATTR_RW(status);
388 389 390 391 392 393 394 395 396 397
static DEVICE_ATTR_RO(enabled);
static DEVICE_ATTR_RO(dpms);
static DEVICE_ATTR_RO(modes);

static struct attribute *connector_dev_attrs[] = {
	&dev_attr_status.attr,
	&dev_attr_enabled.attr,
	&dev_attr_dpms.attr,
	&dev_attr_modes.attr,
	NULL
D
Dave Airlie 已提交
398 399 400
};

/* These attributes are for both DVI-I connectors and all types of tv-out. */
401 402 403
static DEVICE_ATTR_RO(subconnector);
static DEVICE_ATTR_RO(select_subconnector);

404
static struct attribute *connector_tv_dev_attrs[] = {
405 406 407
	&dev_attr_subconnector.attr,
	&dev_attr_select_subconnector.attr,
	NULL
D
Dave Airlie 已提交
408 409
};

410 411
/* Connector type related helpers */
static int kobj_connector_type(struct kobject *kobj)
412 413 414 415
{
	struct device *dev = kobj_to_dev(kobj);
	struct drm_connector *connector = to_drm_connector(dev);

416 417 418
	return connector->connector_type;
}

419 420 421 422 423 424 425 426 427
static umode_t connector_is_dvii(struct kobject *kobj,
				 struct attribute *attr, int idx)
{
	return kobj_connector_type(kobj) == DRM_MODE_CONNECTOR_DVII ?
		attr->mode : 0;
}

static umode_t connector_is_tv(struct kobject *kobj,
			       struct attribute *attr, int idx)
428 429
{
	switch (kobj_connector_type(kobj)) {
430 431 432 433 434 435 436 437 438 439
	case DRM_MODE_CONNECTOR_Composite:
	case DRM_MODE_CONNECTOR_SVIDEO:
	case DRM_MODE_CONNECTOR_Component:
	case DRM_MODE_CONNECTOR_TV:
		return attr->mode;
	}

	return 0;
}

D
Dave Airlie 已提交
440 441
static struct bin_attribute edid_attr = {
	.attr.name = "edid",
442
	.attr.mode = 0444,
443
	.size = 0,
D
Dave Airlie 已提交
444 445 446
	.read = edid_show,
};

447 448 449 450 451 452 453 454 455 456
static struct bin_attribute *connector_bin_attrs[] = {
	&edid_attr,
	NULL
};

static const struct attribute_group connector_dev_group = {
	.attrs = connector_dev_attrs,
	.bin_attrs = connector_bin_attrs,
};

457 458 459 460 461 462 463 464
static const struct attribute_group connector_tv_dev_group = {
	.attrs = connector_tv_dev_attrs,
	.is_visible = connector_is_tv,
};

static const struct attribute_group connector_dvii_dev_group = {
	.attrs = connector_tv_dev_attrs, /* same as tv */
	.is_visible = connector_is_dvii,
465 466 467 468
};

static const struct attribute_group *connector_dev_groups[] = {
	&connector_dev_group,
469 470
	&connector_tv_dev_group,
	&connector_dvii_dev_group,
471 472 473
	NULL
};

D
Dave Airlie 已提交
474
/**
475
 * drm_sysfs_connector_add - add a connector to sysfs
D
Dave Airlie 已提交
476 477
 * @connector: connector to add
 *
478
 * Create a connector device in sysfs, along with its associated connector
D
Dave Airlie 已提交
479 480 481 482 483 484 485 486
 * 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;

487 488
	if (connector->kdev)
		return 0;
D
Dave Airlie 已提交
489

490 491 492 493 494
	connector->kdev =
		device_create_with_groups(drm_class, dev->primary->kdev, 0,
					  connector, connector_dev_groups,
					  "card%d-%s", dev->primary->index,
					  connector->name);
D
Dave Airlie 已提交
495
	DRM_DEBUG("adding \"%s\" to sysfs\n",
496
		  connector->name);
D
Dave Airlie 已提交
497

498 499
	if (IS_ERR(connector->kdev)) {
		DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev));
500
		return PTR_ERR(connector->kdev);
D
Dave Airlie 已提交
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
	}

	/* Let userspace know we have a new connector */
	drm_sysfs_hotplug_event(dev);

	return 0;
}

/**
 * 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)
{
524
	if (!connector->kdev)
525
		return;
D
Dave Airlie 已提交
526
	DRM_DEBUG("removing \"%s\" from sysfs\n",
527
		  connector->name);
D
Dave Airlie 已提交
528

529 530
	device_unregister(connector->kdev);
	connector->kdev = NULL;
D
Dave Airlie 已提交
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
}

/**
 * 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");

548
	kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
D
Dave Airlie 已提交
549
}
550
EXPORT_SYMBOL(drm_sysfs_hotplug_event);
D
Dave Airlie 已提交
551

552 553 554 555 556
static void drm_sysfs_release(struct device *dev)
{
	kfree(dev);
}

L
Linus Torvalds 已提交
557
/**
558 559
 * drm_sysfs_minor_alloc() - Allocate sysfs device for given minor
 * @minor: minor to allocate sysfs device for
L
Linus Torvalds 已提交
560
 *
561 562 563 564 565 566 567 568 569
 * 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.
L
Linus Torvalds 已提交
570
 */
571
struct device *drm_sysfs_minor_alloc(struct drm_minor *minor)
L
Linus Torvalds 已提交
572
{
573 574
	const char *minor_str;
	struct device *kdev;
575
	int r;
J
Jesse Barnes 已提交
576

D
Dave Airlie 已提交
577 578
	if (minor->type == DRM_MINOR_CONTROL)
		minor_str = "controlD%d";
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
	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);
597
	if (r < 0)
598
		goto err_free;
599

600
	return kdev;
L
Linus Torvalds 已提交
601

602 603 604
err_free:
	put_device(kdev);
	return ERR_PTR(r);
L
Linus Torvalds 已提交
605
}
606 607 608 609 610 611 612 613 614 615 616 617 618

/**
 * 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)
{
619 620 621
	if (!drm_class || IS_ERR(drm_class))
		return -ENOENT;

622 623 624 625 626 627 628 629 630 631
	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);