/*
* Copyright (c) 2006-2007 Intel Corporation
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
* Copyright (c) 2008 Red Hat Inc.
*
* DRM core CRTC related functions
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*
* Authors:
* Keith Packard
* Eric Anholt <eric@anholt.net>
* Dave Airlie <airlied@linux.ie>
* Jesse Barnes <jesse.barnes@intel.com>
*/
#include <linux/list.h>
#include "drm.h"
#include "drmP.h"
#include "drm_crtc.h"
struct drm_prop_enum_list {
int type;
char *name;
};
/*
* Global properties
*/
static struct drm_prop_enum_list drm_dpms_enum_list[] =
{ { DRM_MODE_DPMS_ON, "On" },
{ DRM_MODE_DPMS_STANDBY, "Standby" },
{ DRM_MODE_DPMS_SUSPEND, "Suspend" },
{ DRM_MODE_DPMS_OFF, "Off" }
};
char *drm_get_dpms_name(int val)
{
int i;
for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++)
if (drm_dpms_enum_list[i].type == val)
return drm_dpms_enum_list[i].name;
return "unknown";
}
static struct drm_prop_enum_list drm_select_subconnector_enum_list[] =
{
{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
{ DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */
{ DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */
{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
{ DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */
{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
};
char *drm_get_select_subconnector_name(int val)
{
int i;
for (i = 0; i < ARRAY_SIZE(drm_select_subconnector_enum_list); i++)
if (drm_select_subconnector_enum_list[i].type == val)
return drm_select_subconnector_enum_list[i].name;
return "unknown";
}
static struct drm_prop_enum_list drm_subconnector_enum_list[] =
{
{ DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */
{ DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */
{ DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */
{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
{ DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */
{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
};
char *drm_get_subconnector_name(int val)
{
int i;
for (i = 0; i < ARRAY_SIZE(drm_subconnector_enum_list); i++)
if (drm_subconnector_enum_list[i].type == val)
return drm_subconnector_enum_list[i].name;
return "unknown";
}
static struct drm_prop_enum_list drm_connector_enum_list[] =
{ { DRM_MODE_CONNECTOR_Unknown, "Unknown" },
{ DRM_MODE_CONNECTOR_VGA, "VGA" },
{ DRM_MODE_CONNECTOR_DVII, "DVI-I" },
{ DRM_MODE_CONNECTOR_DVID, "DVI-D" },
{ DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
{ DRM_MODE_CONNECTOR_Composite, "Composite" },
{ DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" },
{ DRM_MODE_CONNECTOR_LVDS, "LVDS" },
{ DRM_MODE_CONNECTOR_Component, "Component" },
{ DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
{ DRM_MODE_CONNECTOR_DisplayPort, "DisplayPort" },
{ DRM_MODE_CONNECTOR_HDMIA, "HDMI Type A" },
{ DRM_MODE_CONNECTOR_HDMIB, "HDMI Type B" },
};
static struct drm_prop_enum_list drm_encoder_enum_list[] =
{ { DRM_MODE_ENCODER_NONE, "None" },
{ DRM_MODE_ENCODER_DAC, "DAC" },
{ DRM_MODE_ENCODER_TMDS, "TMDS" },
{ DRM_MODE_ENCODER_LVDS, "LVDS" },
{ DRM_MODE_ENCODER_TVDAC, "TV" },
};
char *drm_get_encoder_name(struct drm_encoder *encoder)
{
static char buf[32];
snprintf(buf, 32, "%s-%d", drm_encoder_enum_list[encoder->encoder_type].name,
encoder->base.id);
return buf;
}
char *drm_get_connector_name(struct drm_connector *connector)
{
static char buf[32];
snprintf(buf, 32, "%s-%d", drm_connector_enum_list[connector->connector_type].name,
connector->connector_type_id);
return buf;
}
EXPORT_SYMBOL(drm_get_connector_name);
char *drm_get_connector_status_name(enum drm_connector_status status)
{
if (status == connector_status_connected)
return "connected";
else if (status == connector_status_disconnected)
return "disconnected";
else
return "unknown";
}
/**
* drm_idr_get - allocate a new identifier
* @dev: DRM device
* @ptr: object pointer, used to generate unique ID
*
* LOCKING:
* Caller must hold DRM mode_config lock.
*
* Create a unique identifier based on @ptr in @dev's identifier space. Used
* for tracking modes, CRTCs and connectors.
*
* RETURNS:
* New unique (relative to other objects in @dev) integer identifier for the
* object.
*/
static int drm_mode_object_get(struct drm_device *dev, struct drm_mode_object *obj, uint32_t obj_type)
{
int new_id = 0;
int ret;
again:
if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) {
DRM_ERROR("Ran out memory getting a mode number\n");
return -EINVAL;
}
ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id);
if (ret == -EAGAIN)
goto again;
obj->id = new_id;
obj->type = obj_type;
return 0;
}
/**
* drm_mode_object_put - free an identifer
* @dev: DRM device
* @id: ID to free
*
* LOCKING:
* Caller must hold DRM mode_config lock.
*
* Free @id from @dev's unique identifier pool.
*/
static void drm_mode_object_put(struct drm_device *dev, struct drm_mode_object *object)
{
idr_remove(&dev->mode_config.crtc_idr, object->id);
}
static void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type)
{
struct drm_mode_object *obj;
obj = idr_find(&dev->mode_config.crtc_idr, id);
if (!obj || (obj->type != type) || (obj->id != id))
return NULL;
return obj;
}
/**
* drm_crtc_from_fb - find the CRTC structure associated with an fb
* @dev: DRM device
* @fb: framebuffer in question
*
* LOCKING:
* Caller must hold mode_config lock.
*
* Find CRTC in the mode_config structure that matches @fb.
*
* RETURNS:
* Pointer to the CRTC or NULL if it wasn't found.
*/
struct drm_crtc *drm_crtc_from_fb(struct drm_device *dev,
struct drm_framebuffer *fb)
{
struct drm_crtc *crtc;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
if (crtc->fb == fb)
return crtc;
}
return NULL;
}
/**
* drm_framebuffer_create - create a new framebuffer object
* @dev: DRM device
*
* LOCKING:
* Caller must hold mode config lock.
*
* Creates a new framebuffer objects and adds it to @dev's DRM mode_config.
*
* RETURNS:
* Pointer to new framebuffer or NULL on error.
*/
struct drm_framebuffer *drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
const struct drm_framebuffer_funcs *funcs)
{
drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
fb->dev = dev;
fb->funcs = funcs;
dev->mode_config.num_fb++;
list_add(&fb->head, &dev->mode_config.fb_list);
return fb;
}
EXPORT_SYMBOL(drm_framebuffer_init);
/**
* drm_framebuffer_cleanup - remove a framebuffer object
* @fb: framebuffer to remove
*
* LOCKING:
* Caller must hold mode config lock.
*
* Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes
* it, setting it to NULL.
*/
void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
{
struct drm_device *dev = fb->dev;
struct drm_crtc *crtc;
/* remove from any CRTC */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
if (crtc->fb == fb)
crtc->fb = NULL;
}
drm_mode_object_put(dev, &fb->base);
list_del(&fb->head);
dev->mode_config.num_fb--;
}
EXPORT_SYMBOL(drm_framebuffer_cleanup);
/**
* drm_crtc_init - Initialise a new CRTC object
* @dev: DRM device
* @crtc: CRTC object to init
* @funcs: callbacks for the new CRTC
*
* LOCKING:
* Caller must hold mode config lock.
*
* Inits a new object created as base part of an driver crtc object.
*/
void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
const struct drm_crtc_funcs *funcs)
{
crtc->dev = dev;
crtc->funcs = funcs;
drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
dev->mode_config.num_crtc++;
}
EXPORT_SYMBOL(drm_crtc_init);
/**
* drm_crtc_cleanup - Cleans up the core crtc usage.
* @crtc: CRTC to cleanup
*
* LOCKING:
* Caller must hold mode config lock.
*
* Cleanup @crtc. Removes from drm modesetting space
* does NOT free object, caller does that.
*/
void drm_crtc_cleanup(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
if (crtc->gamma_store) {
kfree(crtc->gamma_store);
crtc->gamma_store = NULL;
}
drm_mode_object_put(dev, &crtc->base);
list_del(&crtc->head);
dev->mode_config.num_crtc--;
}
EXPORT_SYMBOL(drm_crtc_cleanup);
/**
* drm_mode_probed_add - add a mode to the specified connector's probed mode list
|