summaryrefslogtreecommitdiff
path: root/shared-core/i915_drm.h
AgeCommit message (Expand)Author
2008-08-14Merge branch 'radeon-gem-cs' into modesetting-gemDave Airlie
2008-08-01Merge commit 'origin/drm-gem' into modesetting-gemKristian Høgsberg
2008-07-31TTM: remove API and userspace objects.Dave Airlie
2008-07-30intel-gem: Replace version bump signalling GEM with I915_PARAM_HAS_GEM.Eric Anholt
2008-07-11intel-gem: Add two new ioctls for managing tiling on objects.Eric Anholt
2008-07-07Merge commit 'origin/drm-gem' into ms-gemKristian Høgsberg
2008-06-20[intel-gem] Add DRM_IOCTL_I915_GEM_SW_FINISH to flag CPU writesKeith Packard
2008-06-18Merge commit 'origin/drm-gem' into modesetting-gemJesse Barnes
2008-06-11[gem] Another round of cleanups from checkpatch.plEric Anholt
2008-06-11[gem] Move potentially device-specific ioctls to the intel driver.Eric Anholt
2008-06-11Merge commit 'origin/drm-gem' into modesetting-gemJesse Barnes
2008-06-10[gem] Manage the ringbuffer from the kernel in the GEM case.Eric Anholt
2008-06-10Merge commit 'origin/drm-gem' into modesetting-gemJesse Barnes
2008-06-06[intel-gem] Add explicit throttle ioctlKeith Packard
2008-05-30Merge commit 'origin/master' into drm-gemEric Anholt
2008-05-25[intel-gem] Add DRM_I915_GEM_BUSY ioctl to check for idle buffers.Keith Packard
2008-05-12[GEM] Typo (and thinking) fixes in drm-gem.txt and doxygen.Eric Anholt
2008-05-10[intel-GEM] Clean up GEM ioctl naming.Keith Packard
2008-05-08[intel-gem] Move domains to relocation records. add set_domain ioctl.Keith Packard
2008-05-07GEM: Extend cache domain stuff for 965.Eric Anholt
2008-05-06Start coding up memory domainsKeith Packard
2008-05-01Add alignment to all aperture allocation requests.Keith Packard
2008-05-01Fix gem ioctls to be 32/64-bit clean.Keith Packard
2008-05-01Add pin/unpin object ioctls for gem.Eric Anholt
2008-05-01checkpoint: relocations support.Eric Anholt
2008-05-01checkpoint: rename to GEM and a few more i915 bits.Eric Anholt
2008-04-30Hacking towards hooking up execbuffer.Eric Anholt
2008-04-28Merge branch 'master' into modesetting-101Thomas Hellstrom
2008-04-23i915: fix for compatibility modeXiang, Haihao
2008-03-02Add FENCE registers to MMIO listAlan Hourihane
2008-02-29Working revision.Thomas Hellstrom
2008-02-26i915_mmio: add overlay regsDave Airlie
2008-02-22i915: Remove leading underscore from struct tags.Kristian Høgsberg
2008-02-20fix SAREAAlan Hourihane
2008-02-20fix SAREAAlan Hourihane
2008-02-18Merge branch 'master' of git+ssh://git.freedesktop.org/git/mesa/drm into mode...Alan Hourihane
2008-02-13i915: Add a dri2 init path that gets the lock from the dri2 sarea.Kristian Høgsberg
2008-02-13i915: Add I915_PARAM_CHIPSET_ID param to get chipset ID.Kristian Høgsberg
2008-01-25Merge remote branch 'origin/master' into modesetting-101Dave Airlie
2008-01-24i915 make relocs use copy from userDave Airlie
2007-12-11Merge branch 'master' of ssh://git.freedesktop.org/git/mesa/drm into modesett...Dave Airlie
2007-11-29drm: more cleanupsDave Airlie
2007-11-22Merge branch 'origin' into modesetting-101Dave Airlie
2007-11-22drm: major whitespace/coding style realignment with kernelDave Airlie
2007-11-22i915: add context handle to superioctl structDave Airlie
2007-11-05Merge branch 'master' into modesetting-101Thomas Hellstrom
2007-11-05drm: remove lots of spurious whitespace.Dave Airlie
2007-10-16Merge branch 'master' of git+ssh://git.freedesktop.org/git/mesa/drm into mode...Alan Hourihane
2007-10-12i915: add superioctl support to i915Dave Airlie
2007-09-28Revert drm_i915_flip_t braindamageJesse Barnes
>401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 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

/*
 * 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/err.h>

#include "drm_core.h"
#include "drmP.h"

#define to_drm_minor(d) container_of(d, struct drm_minor, kdev)

/**
 * drm_sysfs_suspend - DRM class suspend hook
 * @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_sysfs_suspend(struct device *dev, pm_message_t state)
{
	struct drm_minor *drm_minor = to_drm_minor(dev);
	struct drm_device *drm_dev = drm_minor->dev;

	printk(KERN_ERR "%s\n", __FUNCTION__);

	if (drm_minor->type == DRM_MINOR_CONTROL)
		if (drm_dev->driver->suspend)
			return drm_dev->driver->suspend(drm_dev, state);

	return 0;
}

/**
 * drm_sysfs_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_sysfs_resume(struct device *dev)
{
	struct drm_minor *drm_minor = to_drm_minor(dev);
	struct drm_device *drm_dev = drm_minor->dev;

	if (drm_minor->type == DRM_MINOR_CONTROL)
		if (drm_dev->driver->resume)
			return drm_dev->driver->resume(drm_dev);

	return 0;
}

/* Display the version of drm_core. This doesn't work right in current design */
static ssize_t version_show(struct class *dev, char *buf)
{
	return sprintf(buf, "%s %d.%d.%d %s\n", CORE_NAME, CORE_MAJOR,
		       CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE);
}

static CLASS_ATTR(version, S_IRUGO, version_show, NULL);

/**
 * 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;
	}

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22))
	class->suspend = drm_sysfs_suspend;
	class->resume = drm_sysfs_resume;
#endif

	err = class_create_file(class, &class_attr_version);
	if (err)
		goto err_out_class;

	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);
	class_destroy(drm_class);
}

static ssize_t show_dri(struct device *device, struct device_attribute *attr,
			char *buf)
{
	struct drm_minor *drm_minor = to_drm_minor(device);
	struct drm_device *drm_dev = drm_minor->dev;
	if (drm_dev->driver->dri_library_name)
		return drm_dev->driver->dri_library_name(drm_dev, buf);
	return snprintf(buf, PAGE_SIZE, "%s\n", drm_dev->driver->pci_driver.name);
}

/**
 * drm_sysfs_device_release - do nothing
 * @dev: Linux device
 *
 * Normally, this would free the DRM device associated with @dev, along
 * with cleaning up any other stuff.  But we do that in the DRM core, so
 * this function can just return and hope that the core does its job.
 */
static void drm_sysfs_device_release(struct device *dev)
{
	return;
}

/*
 * Connector properties
 */
static ssize_t status_show(struct device *device,
			   struct device_attribute *attr,
			   char *buf)
{
	struct drm_connector *connector = container_of(device, struct drm_connector, kdev);
	return snprintf(buf, PAGE_SIZE, "%s",
			drm_get_connector_status_name(connector->funcs->detect(connector)));
}

static ssize_t dpms_show(struct device *device,
			   struct device_attribute *attr,
			   char *buf)
{
	struct drm_connector *connector = container_of(device, struct drm_connector, kdev);
	struct drm_device *dev = connector->dev;
	uint64_t dpms_status;
	int ret;

	ret = drm_connector_property_get_value(connector,
					    dev->mode_config.dpms_property,
					    &dpms_status);
	if (ret)
		return 0;
	
	return snprintf(buf, PAGE_SIZE, "%s", 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 = container_of(device, struct drm_connector, kdev);

	if (connector->encoder)
		return snprintf(buf, PAGE_SIZE, "enabled");
	else
		return snprintf(buf, PAGE_SIZE, "disabled");
}

static ssize_t edid_show(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 = container_of(connector_dev, struct drm_connector,
						 kdev);
	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 = container_of(device, struct drm_connector, kdev);
	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 = container_of(device, struct drm_connector, kdev);
	struct drm_device *dev = connector->dev;
	struct drm_property *prop = NULL;
	uint64_t subconnector;
	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:
			prop = dev->mode_config.tv_subconnector_property;
			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_connector_property_get_value(connector, prop, &subconnector);
	if (ret)
		return 0;

	return snprintf(buf, PAGE_SIZE, "%s", drm_get_subconnector_name((int)subconnector));
}

static ssize_t select_subconnector_show(struct device *device,
			   struct device_attribute *attr,
			   char *buf)
{
	struct drm_connector *connector = container_of(device, struct drm_connector, kdev);
	struct drm_device *dev = connector->dev;
	struct drm_property *prop = NULL;
	uint64_t subconnector;
	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:
			prop = dev->mode_config.tv_select_subconnector_property;
			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_connector_property_get_value(connector, prop, &subconnector);
	if (ret)
		return 0;

	return snprintf(buf, PAGE_SIZE, "%s", drm_get_select_subconnector_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",
	.size = 128,
	.read = edid_show,
};

/**
 * drm_sysfs_connector_add - add an connector to sysfs
 * @connector: connector to add
 *
 * Create an 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.
 *
 * Note:
 * This routine should only be called *once* for each DRM minor registered.
 * A second call for an already registered device will trigger the BUG_ON
 * below.
 */
int drm_sysfs_connector_add(struct drm_connector *connector)
{
	struct drm_device *dev = connector->dev;
	int ret = 0, i, j;

	/* We shouldn't get called more than once for the same connector */
	BUG_ON(device_is_registered(&connector->kdev));

	connector->kdev.parent = &dev->primary->kdev;
	connector->kdev.class = drm_class;
	connector->kdev.release = drm_sysfs_device_release;

	DRM_DEBUG("adding \"%s\" to sysfs\n",
		  drm_get_connector_name(connector));

	snprintf(connector->kdev.bus_id, BUS_ID_SIZE, "card%d-%s",
		 dev->primary->index, drm_get_connector_name(connector));
	ret = device_register(&connector->kdev);

	if (ret) {
		DRM_ERROR("failed to register connector device: %d\n", ret);
		goto out;
	}

	/* Standard attributes */

	for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) {
		ret = device_create_file(&connector->kdev, &connector_attrs[i]);
		if (ret)
			goto err_out_files;
	}

	/* Optional attributes */
	/* On 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:
			for (i = 0; i < ARRAY_SIZE(connector_attrs_opt1); i++) {
				ret = device_create_file(&connector->kdev, &connector_attrs_opt1[i]);
				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:
	if (i > 0)
		for (j = 0; j < i; j++)
			device_remove_file(&connector->kdev, &connector_attrs[i]);
	device_unregister(&connector->kdev);

out:
	return ret;
}
EXPORT_SYMBOL(drm_sysfs_connector_add);

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

	DRM_DEBUG("removing \"%s\" from sysfs\n",
		  drm_get_connector_name(connector));

	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);
}
EXPORT_SYMBOL(drm_sysfs_connector_remove);

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

static struct device_attribute dri_attrs[] = {
	__ATTR(dri_library_name, S_IRUGO, show_dri, NULL),
};

/**
 * drm_sysfs_device_add - adds a class device to sysfs for a character driver
 * @dev: DRM device to be added
 * @head: DRM head in question
 *
 * Add a DRM device to the DRM's device model class.  We use @dev's PCI device
 * as the parent for the Linux device, and make sure it has a file containing
 * the driver we're using (for userspace compatibility).
 *
 * Note:
 * This routine should only be called *once* for each DRM minor registered.
 * A second call for an already registered device will trigger the BUG_ON
 * below.
 */
int drm_sysfs_device_add(struct drm_minor *minor)
{
	int err;
	int i, j;
	char *minor_str;

	minor->kdev.parent = &minor->dev->pdev->dev;
	minor->kdev.class = drm_class;
	minor->kdev.release = drm_sysfs_device_release;
	minor->kdev.devt = minor->device;
	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";
	
	snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->index);

	/* Shouldn't register more than once */
	BUG_ON(device_is_registered(&minor->kdev));

	DRM_DEBUG("registering DRM device \"%s\"\n", minor->kdev.bus_id);

	err = device_register(&minor->kdev);
	if (err) {
		DRM_ERROR("device add failed: %d\n", err);
		goto err_out;
	}

	for (i = 0; i < ARRAY_SIZE(dri_attrs); i++) {
		err = device_create_file(&minor->kdev, &dri_attrs[i]);
		if (err)
			goto err_out_files;
	}

	return 0;

err_out_files:
	if (i > 0)
		for (j = 0; j < i; j++)
			device_remove_file(&minor->kdev, &dri_attrs[i]);
	device_unregister(&minor->kdev);
err_out:

	return err;
}

/**
 * drm_sysfs_device_remove - remove DRM device
 * @dev: DRM device to remove
 *
 * This call unregisters and cleans up a class device that was created with a
 * call to drm_sysfs_device_add()
 */
void drm_sysfs_device_remove(struct drm_minor *minor)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(dri_attrs); i++)
		device_remove_file(&minor->kdev, &dri_attrs[i]);
	device_unregister(&minor->kdev);
}