/*
*/
/*
* Copyright (c) 2006-2008, 2013, 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 "drm.h"
#include "drmP.h"
#include "drm_crtc.h"
#include "drm_edid.h"
#include "drm_fourcc.h"
/**
* drm_modeset_lock_all - take all modeset locks
* @dev: drm device
*
* This function takes all modeset locks, suitable where a more fine-grained
* scheme isn't (yet) implemented.
*/
{
}
/**
* drm_modeset_unlock_all - drop all modeset locks
* @dev: device
*/
{
}
/* Avoid boilerplate. I'm tired of typing. */
{ \
int i; \
for (i = 0; i < ARRAY_SIZE(list); i++) { \
} \
return "(unknown)"; \
}
/*
* Global properties
*/
{ { DRM_MODE_DPMS_ON, "On" },
{ DRM_MODE_DPMS_STANDBY, "Standby" },
{ DRM_MODE_DPMS_SUSPEND, "Suspend" },
{ DRM_MODE_DPMS_OFF, "Off" }
};
/*
* Optional properties
*/
{
{ DRM_MODE_SCALE_NONE, "None" },
{ DRM_MODE_SCALE_FULLSCREEN, "Full" },
{ DRM_MODE_SCALE_CENTER, "Center" },
{ DRM_MODE_SCALE_ASPECT, "Full aspect" },
};
{
{ DRM_MODE_DITHERING_OFF, "Off" },
{ DRM_MODE_DITHERING_ON, "On" },
{ DRM_MODE_DITHERING_AUTO, "Automatic" },
};
/*
* Non-global properties, but "required" for certain connectors.
*/
{
};
{
};
{
};
{
};
{ DRM_MODE_DIRTY_OFF, "Off" },
{ DRM_MODE_DIRTY_ON, "On" },
{ DRM_MODE_DIRTY_ANNOTATE, "Annotate" },
};
struct drm_conn_prop_enum_list {
int type;
const char *name;
int count;
};
/*
* Connector and encoder types.
*/
{ { DRM_MODE_CONNECTOR_Unknown, "Unknown", 0 },
{ DRM_MODE_CONNECTOR_VGA, "VGA", 0 },
{ DRM_MODE_CONNECTOR_DVII, "DVI-I", 0 },
{ DRM_MODE_CONNECTOR_DVID, "DVI-D", 0 },
{ DRM_MODE_CONNECTOR_DVIA, "DVI-A", 0 },
{ DRM_MODE_CONNECTOR_Composite, "Composite", 0 },
{ DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO", 0 },
{ DRM_MODE_CONNECTOR_LVDS, "LVDS", 0 },
{ DRM_MODE_CONNECTOR_Component, "Component", 0 },
{ DRM_MODE_CONNECTOR_9PinDIN, "DIN", 0 },
{ DRM_MODE_CONNECTOR_DisplayPort, "DP", 0 },
{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A", 0 },
{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B", 0 },
{ DRM_MODE_CONNECTOR_TV, "TV", 0 },
{ DRM_MODE_CONNECTOR_eDP, "eDP", 0 },
{ DRM_MODE_CONNECTOR_VIRTUAL, "Virtual", 0},
};
{ { 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" },
{ DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
};
{
return buf;
}
{
return buf;
}
{
if (status == connector_status_connected)
return "connected";
else if (status == connector_status_disconnected)
return "disconnected";
else
return "unknown";
}
static char printable_char(int c)
{
return (char)(c);
}
{
"%c%c%c%c %s-endian (0x%08x)",
format);
return buf;
}
/**
* drm_mode_object_get - allocate a new modeset identifier
* @dev: DRM device
* @obj: object pointer, used to generate unique ID
* @obj_type: object type
*
* 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.
*/
{
int new_id = 0;
int ret;
DRM_ERROR("Ran out memory getting a mode number\n");
return -ENOMEM;
}
if (!ret) {
/*
* Set up the object linking under the protection of the idr
* lock so that other users can't see inconsistent state.
*/
}
goto again;
return ret;
}
/**
* drm_mode_object_put - free an identifer
* @dev: DRM device
* @id: ID to free
*
*
* Free @id from @dev's unique identifier pool.
*/
struct drm_mode_object *object)
{
}
/**
* drm_mode_object_find - look up a drm object with static lifetime
* @dev: drm device
* @id: id of the mode object
* @type: type of the mode object
*
* Note that framebuffers cannot be looked up with this functions - since those
* are reference counted, they need special treatment.
*/
{
/* Framebuffers are reference counted and need their own lookup
* function.*/
return obj;
}
/**
* drm_framebuffer_init - initialize a framebuffer
* @dev: DRM device
*
*
* Allocates an ID for the framebuffer's parent mode object, sets its mode
* functions & device file and adds it to the master fd list.
*
* IMPORTANT:
* This functions publishes the fb and makes it available for concurrent access
* by other users. Which means by this point the fb _must_ be fully set up -
* since all the fb attributes are invariant over its lifetime, no further
* locking but only correct reference counting is required.
*
* RETURNS:
* Zero on success, error code on failure.
*/
const struct drm_framebuffer_funcs *funcs)
{
int ret;
if (ret)
goto out;
/* Grab the idr reference. */
out:
return 0;
}
{
}
{
else
return fb;
}
/**
* drm_framebuffer_lookup - look up a drm framebuffer and grab a reference
* @dev: drm device
* @id: id of the fb object
*
* If successful, this grabs an additional reference to the framebuffer -
* callers need to make sure to eventually unreference the returned framebuffer
* again.
*/
{
if (fb)
return fb;
}
/**
* drm_framebuffer_unreference - unref a framebuffer
*
*/
{
}
/**
* drm_framebuffer_reference - incr the fb refcnt
*/
{
}
/* LINTED E_FUNC_ARG_UNUSED */
{
BUG();
}
{
}
/* dev->mode_config.fb_lock must be held! */
struct drm_framebuffer *fb)
{
}
/**
* drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
* @fb: fb to unregister
*
* Drivers need to call this when cleaning up driver-private framebuffers, e.g.
* those used for fbdev. Note that the caller must hold a reference of it's own,
* i.e. the object may not be destroyed through this call (since it'll lead to a
* locking inversion).
*/
{
/* Mark fb as reaped and drop idr ref. */
}
/**
* drm_framebuffer_cleanup - remove a framebuffer object
* @fb: framebuffer to remove
*
*
* Cleanup references to a user-created framebuffer. This function is intended
* to be used from the drivers ->destroy callback.
*
* Note that this function does not remove the fb from active usuage - if it is
* still used anywhere, hilarity can ensue since userspace could call getfb on
* the id and get back -EINVAL. Obviously no concern at driver unload time.
*
* Also, the framebuffer will not be removed from the lookup idr - for
* user-created framebuffers this will happen in in the rmfb ioctl. For
* driver-private objects (e.g. for fbdev) drivers need to explicitly call
* drm_framebuffer_unregister_private.
*/
{
}
/**
* drm_framebuffer_remove - remove and unreference a framebuffer object
* @fb: framebuffer to remove
*
* using @fb, removes it, setting it to NULL. Then drops the reference to the
* passed-in framebuffer. Might take the modeset locks.
*
* Note that this function optimizes the cleanup away if the caller holds the
* last reference to the framebuffer. It is also guaranteed to not take the
* modeset locks in this case.
*/
{
int ret;
/*
* drm ABI mandates that we remove any deleted framebuffers from active
* useage. But since most sane clients only remove framebuffers they no
* longer need, try to optimize this away.
*
* Since we're holding a reference ourselves, observing a refcount of 1
* means that we're the last holder and can skip it. Also, the refcount
* can never increase from 1 again, so we don't need any barriers or
* locks.
*
* Note that userspace could try to race with use and instate a new
* usage _after_ we've cleared all current ones. End result will be an
* in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot
* in this manner.
*/
/* remove from any CRTC */
/* should turn off the crtc */
if (ret)
}
}
}
}
}
/**
* drm_crtc_init - Initialise a new CRTC object
* @dev: DRM device
* @crtc: CRTC object to init
* @funcs: callbacks for the new CRTC
*
*
* Inits a new object created as base part of an driver crtc object.
*/
const struct drm_crtc_funcs *funcs)
{
int ret;
crtc->invert_dimensions = false;
if (ret)
goto out;
out:
return ret;
}
/**
* drm_crtc_cleanup - Cleans up the core crtc usage.
* @crtc: CRTC to cleanup
*
*
* Cleanup @crtc. Removes from drm modesetting space
* does NOT free object, caller does that.
*/
{
if (crtc->gamma_store) {
}
}
/**
* drm_mode_probed_add - add a mode to a connector's probed mode list
* @connector: connector the new mode
* @mode: mode data
*
*
* Add @mode to @connector's mode list for later use.
*/
struct drm_display_mode *mode)
{
}
/**
* drm_mode_remove - remove and free a mode
* @connector: connector list to modify
* @mode: mode to remove
*
*
* Remove @mode from @connector's mode list, then free it.
*/
struct drm_display_mode *mode)
{
}
/**
* drm_connector_init - Init a preallocated connector
* @dev: DRM device
* @connector: the connector to init
* @funcs: callbacks for this connector
* @name: user visible name of the connector
*
*
* Initialises a preallocated connector. Connectors should be
* subclassed as part of driver connector objects.
*
* RETURNS:
* Zero on success, error code on failure.
*/
struct drm_connector *connector,
const struct drm_connector_funcs *funcs,
int connector_type)
{
int ret;
if (ret)
goto out;
0);
out:
return ret;
}
/**
* drm_connector_cleanup - cleans up an initialised connector
* @connector: connector to cleanup
*
*
* Cleans up the connector but doesn't free the object.
*/
{
}
/* LINTED E_FUNC_ARG_UNUSED */
{
/*
struct drm_connector *connector;
*/
/* taking the mode config mutex ends up in a clash with sysfs */
/*
list_for_each_entry(connector, struct drm_connector, &dev->mode_config.connector_list, head)
drm_sysfs_connector_remove(connector);
*/
}
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type)
{
int ret;
if (ret)
goto out;
out:
return ret;
}
{
}
/**
* drm_plane_init - Initialise a new plane object
* @dev: DRM device
* @plane: plane object to init
* @possible_crtcs: bitmask of possible CRTCs
* @funcs: callbacks for the new plane
* @formats: array of supported formats (%DRM_FORMAT_*)
* @format_count: number of elements in @formats
* @priv: plane is private (hidden from userspace)?
*
* Inits a new object created as base part of a driver plane object.
*
* RETURNS:
* Zero on success, error code on failure.
*/
unsigned long possible_crtcs,
const struct drm_plane_funcs *funcs,
bool priv)
{
int ret;
if (ret)
goto out;
if (!plane->format_types) {
DRM_DEBUG_KMS("out of memory when allocating plane\n");
goto out;
}
/* private planes are not exposed to userspace, but depending on
* display hardware, might be convenient to allow sharing programming
* for the scanout engine with the crtc implementation.
*/
if (!priv) {
} else {
}
out:
return ret;
}
/**
* drm_plane_cleanup - Clean up the core plane usage
* @plane: plane to cleanup
*
* This function cleans up @plane and removes it from the DRM mode setting
* core. Note that the function does *not* free the plane structure itself,
* this is the responsibility of the caller.
*/
{
/* if not added to a list, it must be a private plane */
}
}
/**
* drm_plane_force_disable - Forcibly disable a plane
* @plane: plane to disable
*
* Forces the plane to be disabled.
*
* Used when the plane's current framebuffer is destroyed,
* and when restoring fbdev mode.
*/
{
int ret;
return;
if (ret)
DRM_ERROR("failed to disable plane with busy fb\n");
/* disconnect the plane from the fb and crtc: */
}
/**
* drm_mode_create - create a new display mode
* @dev: DRM device
*
*
* Create a new drm_display_mode, give it an ID, and return it.
*
* RETURNS:
* Pointer to new mode on success, NULL on error.
*/
{
if (!nmode)
return NULL;
return NULL;
}
return nmode;
}
/**
* drm_mode_destroy - remove a mode
* @dev: DRM device
* @mode: mode to remove
*
*
* Free @mode's unique identifier, then free it.
*/
{
if (!mode)
return;
}
{
/*
* Standard properties (apply to all connectors)
*/
"EDID", 0);
"DPMS", drm_dpms_enum_list,
return 0;
}
/**
* drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
* @dev: DRM device
*
* Called by a driver the first time a DVI-I connector is made.
*/
{
return 0;
"select subconnector",
"subconnector",
return 0;
}
/**
* drm_create_tv_properties - create TV specific connector properties
* @dev: DRM device
* @num_modes: number of different TV formats (modes) supported
* @modes: array of pointers to strings containing name of each format
*
* Called by a driver's TV initialization routine, this function creates
* the TV specific connector properties for a given device. Caller is
* responsible for allocating a list of format names and passing them to
* this routine.
*/
char *modes[])
{
int i;
return 0;
/*
* Basic connector properties
*/
"select subconnector",
"subconnector",
/*
* Other, TV specific properties: margins & TV modes.
*/
"mode", num_modes);
for (i = 0; i < num_modes; i++)
i, modes[i]);
return 0;
}
/**
* drm_mode_create_scaling_mode_property - create scaling mode property
* @dev: DRM device
*
* Called by a driver the first time it's needed, must be attached to desired
* connectors.
*/
{
return 0;
return 0;
}
/**
* drm_mode_create_dithering_property - create dithering property
* @dev: DRM device
*
* Called by a driver the first time it's needed, must be attached to desired
* connectors.
*/
{
return 0;
return 0;
}
/**
* drm_mode_create_dirty_property - create dirty property
* @dev: DRM device
*
* Called by a driver the first time it's needed, must be attached to desired
* connectors.
*/
{
return 0;
"dirty",
return 0;
}
{
return -ENOMEM;
group->num_connectors = 0;
group->num_encoders = 0;
return 0;
}
struct drm_mode_group *group)
{
int ret;
return ret;
return 0;
}
/**
* drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
* @out: drm_mode_modeinfo struct to return to the user
* @in: drm_display_mode to use
*
*
* Convert a drm_display_mode into a drm_mode_modeinfo structure to return to
* the user.
*/
const struct drm_display_mode *in)
{
DRM_ERROR("timing values too large for mode info\n");
}
/**
* drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode
* @out: drm_display_mode to return to the user
* @in: drm_mode_modeinfo to use
*
*
* Convert a drm_mode_modeinfo into a drm_display_mode structure to return to
* the caller.
*/
const struct drm_mode_modeinfo *in)
{
return -ERANGE;
return 0;
}
/**
* drm_mode_getresources - get graphics configuration
* @inode: inode from the ioctl
* @filp: file * from the ioctl
* @cmd: cmd from ioctl
* @arg: arg from ioctl
*
*
* Construct a set of configuration description structures and return
* them to the user, including CRTC, connector and framebuffer configuration.
*
* Called by the user via ioctl.
*
* RETURNS:
* Zero on success, errno on failure.
*/
/* LINTED E_FUNC_ARG_UNUSED */
{
int ret = 0;
int connector_count = 0;
int crtc_count = 0;
int fb_count = 0;
int encoder_count = 0;
int copied = 0, i;
return -EINVAL;
/*
* For the non-control nodes we need to limit the list of resources
* by IDs in the group list for this node
*/
fb_count++;
/* handle this in 4 parts */
/* FBs */
copied = 0;
return -EFAULT;
}
copied++;
}
}
crtc_count++;
} else {
}
/* CRTCs */
copied = 0;
head) {
goto out;
}
copied++;
}
} else {
for (i = 0; i < mode_group->num_crtcs; i++) {
goto out;
}
copied++;
}
}
}
/* Encoders */
copied = 0;
head) {
copied)) {
goto out;
}
copied++;
}
} else {
encoder_id + copied)) {
goto out;
}
copied++;
}
}
}
/* Connectors */
copied = 0;
head) {
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector_id + copied)) {
goto out;
}
copied++;
}
} else {
connector_id + copied)) {
goto out;
}
copied++;
}
}
}
out:
return ret;
}
/**
* drm_mode_getcrtc - get CRTC configuration
* @inode: inode from the ioctl
* @filp: file * from the ioctl
* @cmd: cmd from ioctl
* @arg: arg from ioctl
*
*
* Construct a CRTC configuration structure to return to the user.
*
* Called by the user via ioctl.
*
* RETURNS:
* Zero on success, errno on failure.
*/
/* LINTED E_FUNC_ARG_UNUSED */
{
int ret = 0;
return -EINVAL;
if (!obj) {
goto out;
}
else
} else {
crtc_resp->mode_valid = 0;
}
out:
return ret;
}
/**
* drm_mode_getconnector - get connector configuration
* @inode: inode from the ioctl
* @filp: file * from the ioctl
* @cmd: cmd from ioctl
* @arg: arg from ioctl
*
*
* Construct a connector configuration structure to return to the user.
*
* Called by the user via ioctl.
*
* RETURNS:
* Zero on success, errno on failure.
*/
/* LINTED E_FUNC_ARG_UNUSED */
{
int mode_count = 0;
int props_count = 0;
int encoders_count = 0;
int ret = 0;
int copied = 0;
int i;
return -EINVAL;
if (!obj) {
goto out;
}
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
if (connector->encoder_ids[i] != 0) {
}
}
if (out_resp->count_modes == 0) {
}
/* delayed so we get modes regardless of pre-fill_modes state */
mode_count++;
else
out_resp->encoder_id = 0;
/*
* This ioctl is called twice, once to determine how much space is
* needed, and the 2nd time to fill it.
*/
copied = 0;
goto out;
}
copied++;
}
}
copied = 0;
goto out;
}
prop_values + copied)) {
goto out;
}
copied++;
}
}
copied = 0;
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
if (connector->encoder_ids[i] != 0) {
encoder_ptr + copied)) {
goto out;
}
copied++;
}
}
}
out:
return ret;
}
/* LINTED E_FUNC_ARG_UNUSED */
{
int ret = 0;
return -EINVAL;
if (!obj) {
goto out;
}
else
out:
return ret;
}
/**
* drm_mode_getplane_res - get plane info
* @dev: DRM device
* @data: ioctl data
* @file_priv: DRM file info
*
*
* Return an plane count and set of IDs.
*/
/* LINTED E_FUNC_ARG_UNUSED */
{
return -EINVAL;
/*
* This ioctl is called twice, once to determine how much space is
* needed, and the 2nd time to fill it.
*/
goto out;
}
copied++;
}
}
out:
return ret;
}
/**
* drm_mode_getplane - get plane info
* @dev: DRM device
* @data: ioctl data
* @file_priv: DRM file info
*
*
* Return plane info, including formats supported, gamma size, any
* current fb, etc.
*/
/* LINTED E_FUNC_ARG_UNUSED */
{
int ret = 0;
return -EINVAL;
if (!obj) {
goto out;
}
else
plane_resp->crtc_id = 0;
else
plane_resp->fb_id = 0;
plane_resp->gamma_size = 0;
/*
* This ioctl is called twice, once to determine how much space is
* needed, and the 2nd time to fill it.
*/
if (plane->format_count &&
goto out;
}
}
out:
return ret;
}
/**
* drm_mode_setplane - set up or tear down an plane
* @dev: DRM device
* @data: ioctl data*
* @file_prive: DRM file info
*
*
* Set plane info, including placement, fb, scaling, and other factors.
* Or pass a NULL fb to disable.
*/
/* LINTED E_FUNC_ARG_UNUSED */
{
int ret = 0;
int i;
return -EINVAL;
/*
* First, find the plane, crtc, and fb objects. If not available,
* we don't bother to call the driver.
*/
if (!obj) {
DRM_DEBUG_KMS("Unknown plane ID %d\n",
return -ENOENT;
}
/* No fb means shut it down */
goto out;
}
if (!obj) {
DRM_DEBUG_KMS("Unknown crtc ID %d\n",
goto out;
}
if (!fb) {
DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
goto out;
}
/* Check whether this plane supports the fb pixel format. */
for (i = 0; i < plane->format_count; i++)
break;
if (i == plane->format_count) {
goto out;
}
/* Make sure source coordinates are inside the fb. */
DRM_DEBUG_KMS("Invalid source coordinates "
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
goto out;
}
/* Give drivers some help against integer overflows */
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
goto out;
}
if (!ret) {
}
out:
if (fb)
if (old_fb)
return ret;
}
/**
* drm_mode_set_config_internal - helper to call ->set_config
* @set: modeset config to set
*
* This is a little helper to wrap internal calls to the ->set_config driver
* interface. The only thing it adds is correct refcounting dance.
*/
{
int ret;
/*
* NOTE: ->set_config can also disable other crtcs (if we steal all
* connectors from it), hence we need to refcount the fbs across all
* crtcs. Atomic modeset will have saner semantics ...
*/
if (ret == 0) {
/* crtc->fb must be updated by ->set_config, enforces this. */
}
}
return ret;
}
/**
* drm_mode_setcrtc - set CRTC configuration
* @inode: inode from the ioctl
* @filp: file * from the ioctl
* @cmd: cmd from ioctl
* @arg: arg from ioctl
*
*
* Build a new CRTC configuration based on user request.
*
* Called by the user via ioctl.
*
* RETURNS:
* Zero on success, errno on failure.
*/
/* LINTED E_FUNC_ARG_UNUSED */
{
int ret;
int i;
return -EINVAL;
/* For some reason crtc x/y offsets are signed internally. */
return -ERANGE;
if (!obj) {
goto out;
}
if (crtc_req->mode_valid) {
/* If we have a mode we need a framebuffer. */
/* If we pass -1, set the mode with the currently bound fb */
/* LINTED */
DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
goto out;
}
/* Make refcounting symmetric with the lookup path. */
} else {
if (!fb) {
DRM_DEBUG_KMS("Unknown FB ID%d\n",
goto out;
}
}
if (!mode) {
goto out;
}
if (ret) {
DRM_DEBUG_KMS("Invalid mode\n");
goto out;
}
if (crtc->invert_dimensions)
DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
goto out;
}
}
DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
goto out;
}
DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
goto out;
}
if (crtc_req->count_connectors > 0) {
/* Avoid unbounded kernel memory allocation */
goto out;
}
sizeof(struct drm_connector *),
if (!connector_set) {
goto out;
}
for (i = 0; i < crtc_req->count_connectors; i++) {
goto out;
}
if (!obj) {
DRM_DEBUG_KMS("Connector id %d unknown\n",
out_id);
goto out;
}
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector_set[i] = connector;
}
}
out:
if (fb)
return ret;
}
struct drm_mode_cursor2 *req,
{
int ret = 0;
return -EINVAL;
return -EINVAL;
if (!obj) {
return -EINVAL;
}
goto out;
}
/* Turns off the cursor if handle is 0 */
else
}
} else {
goto out;
}
}
out:
return ret;
}
/* LINTED E_FUNC_ARG_UNUSED */
{
}
/* LINTED E_FUNC_ARG_UNUSED */
{
}
/* Original addfb only supported RGB formats, so figure out which one */
{
switch (bpp) {
case 8:
fmt = DRM_FORMAT_C8;
break;
case 16:
if (depth == 15)
else
break;
case 24:
break;
case 32:
if (depth == 24)
else if (depth == 30)
else
break;
default:
DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n");
break;
}
return fmt;
}
/**
* drm_mode_addfb - add an FB to the graphics configuration
* @inode: inode from the ioctl
* @filp: file * from the ioctl
* @cmd: cmd from ioctl
* @arg: arg from ioctl
*
*
* Add a new FB to the specified CRTC, given a user request.
*
* Called by the user via ioctl.
*
* RETURNS:
* Zero on success, errno on failure.
*/
/* LINTED E_FUNC_ARG_UNUSED */
{
struct drm_mode_fb_cmd2 r;
int ret = 0;
(void) memset(&r, 0, sizeof(struct drm_mode_fb_cmd2));
/* Use new struct with format internally */
return -EINVAL;
return -EINVAL;
return -EINVAL;
DRM_DEBUG_KMS("could not create framebuffer\n");
return -ENOMEM;
}
return ret;
}
{
switch (format) {
case DRM_FORMAT_C8:
case DRM_FORMAT_RGB332:
case DRM_FORMAT_BGR233:
case DRM_FORMAT_XRGB4444:
case DRM_FORMAT_XBGR4444:
case DRM_FORMAT_RGBX4444:
case DRM_FORMAT_BGRX4444:
case DRM_FORMAT_ARGB4444:
case DRM_FORMAT_ABGR4444:
case DRM_FORMAT_RGBA4444:
case DRM_FORMAT_BGRA4444:
case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_XBGR1555:
case DRM_FORMAT_RGBX5551:
case DRM_FORMAT_BGRX5551:
case DRM_FORMAT_ARGB1555:
case DRM_FORMAT_ABGR1555:
case DRM_FORMAT_RGBA5551:
case DRM_FORMAT_BGRA5551:
case DRM_FORMAT_RGB565:
case DRM_FORMAT_BGR565:
case DRM_FORMAT_RGB888:
case DRM_FORMAT_BGR888:
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_RGBX8888:
case DRM_FORMAT_BGRX8888:
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_BGRA8888:
case DRM_FORMAT_XRGB2101010:
case DRM_FORMAT_XBGR2101010:
case DRM_FORMAT_RGBX1010102:
case DRM_FORMAT_BGRX1010102:
case DRM_FORMAT_ARGB2101010:
case DRM_FORMAT_ABGR2101010:
case DRM_FORMAT_RGBA1010102:
case DRM_FORMAT_BGRA1010102:
case DRM_FORMAT_YUYV:
case DRM_FORMAT_YVYU:
case DRM_FORMAT_UYVY:
case DRM_FORMAT_VYUY:
case DRM_FORMAT_AYUV:
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV61:
case DRM_FORMAT_NV24:
case DRM_FORMAT_NV42:
case DRM_FORMAT_YUV410:
case DRM_FORMAT_YVU410:
case DRM_FORMAT_YUV411:
case DRM_FORMAT_YVU411:
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YVU420:
case DRM_FORMAT_YUV422:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_YUV444:
case DRM_FORMAT_YVU444:
return 0;
default:
return -EINVAL;
}
}
{
ret = format_check(r);
if (ret) {
DRM_DEBUG_KMS("bad framebuffer format %s\n",
return ret;
}
return -EINVAL;
}
return -EINVAL;
}
for (i = 0; i < num_planes; i++) {
if (!r->handles[i]) {
DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i);
return -EINVAL;
}
return -ERANGE;
return -ERANGE;
return -EINVAL;
}
}
return 0;
}
/**
* drm_mode_addfb2 - add an FB to the graphics configuration
* @inode: inode from the ioctl
* @filp: file * from the ioctl
* @arg: arg from ioctl
*
* Add a new FB to the specified CRTC, given a user request with format.
*
* Called by the user via ioctl.
*
* RETURNS:
* Zero on success, errno on failure.
*/
/* LINTED E_FUNC_ARG_UNUSED */
{
struct drm_mode_fb_cmd2 *r = data;
int ret;
return -EINVAL;
if (r->flags & ~DRM_MODE_FB_INTERLACED) {
return -EINVAL;
}
DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",
return -EINVAL;
}
DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",
return -EINVAL;
}
ret = framebuffer_check(r);
if (ret)
return ret;
DRM_DEBUG_KMS("could not create framebuffer\n");
return -ENOMEM;
}
return ret;
}
/**
* drm_mode_rmfb - remove an FB from the configuration
* @inode: inode from the ioctl
* @filp: file * from the ioctl
* @arg: arg from ioctl
*
* Remove the FB specified by the user.
*
* Called by the user via ioctl.
*
* RETURNS:
* Zero on success, errno on failure.
*/
/* LINTED */
{
int found = 0;
return -EINVAL;
if (!fb)
goto fail_lookup;
found = 1;
if (!found)
goto fail_lookup;
/* Mark fb as reaped, we still have a ref from fpriv->fbs. */
return 0;
return -EINVAL;
}
/**
* drm_mode_getfb - get FB info
* @inode: inode from the ioctl
* @filp: file * from the ioctl
* @arg: arg from ioctl
*
* Lookup the FB given its ID and return info about it.
*
* Called by the user via ioctl.
*
* RETURNS:
* Zero on success, errno on failure.
*/
/* LINTED */
{
struct drm_mode_fb_cmd *r = data;
int ret;
return -EINVAL;
if (!fb)
return -EINVAL;
else
return ret;
}
/* LINTED */
{
struct drm_mode_fb_dirty_cmd *r = data;
unsigned flags;
int num_clips;
int ret;
return -EINVAL;
if (!fb)
return -EINVAL;
goto out_err1;
}
/* If userspace annotates copy, clips must come in pairs */
goto out_err1;
}
goto out_err1;
}
if (!clips) {
goto out_err1;
}
if (ret) {
goto out_err2;
}
}
} else {
}
if (clips)
return ret;
}
/**
* drm_fb_release - remove and free the FBs on this file
* @filp: file * from the ioctl
*
*
* Destroy all the FBs associated with @filp.
*
* Called by the user via ioctl.
*
* RETURNS:
* Zero on success, errno on failure.
*/
{
/* Mark fb as reaped, we still have a ref from fpriv->fbs. */
/* This will also drop the fpriv->fbs reference. */
}
}
const char *name, int num_values)
{
int ret;
if (!property)
return NULL;
if (num_values) {
goto fail;
}
if (ret)
goto fail;
if (name) {
}
return property;
fail:
return NULL;
}
const char *name,
const struct drm_prop_enum_list *props,
int num_values)
{
int i, ret;
if (!property)
return NULL;
for (i = 0; i < num_values; i++) {
if (ret) {
return NULL;
}
}
return property;
}
const struct drm_prop_enum_list *props,
int num_values)
{
int i, ret;
if (!property)
return NULL;
for (i = 0; i < num_values; i++) {
if (ret) {
return NULL;
}
}
return property;
}
const char *name,
{
if (!property)
return NULL;
return property;
}
{
return -EINVAL;
/*
* Bitmask enum properties have the additional constraint of values
* from 0 to 63
*/
return -EINVAL;
return 0;
}
}
}
if (!prop_enum)
return -ENOMEM;
return 0;
}
{
list_for_each_entry_safe(prop_enum, pt, struct drm_property_enum, &property->enum_blob_list, head) {
}
if (property->num_values)
}
struct drm_property *property,
{
if (count == DRM_OBJECT_MAX_PROPERTY) {
DRM_ERROR("Failed to attach object property (type: 0x%x). Please "
"increase DRM_OBJECT_MAX_PROPERTY by 1 for each time "
"you see this message on the same object type.\n",
return;
}
}
{
int i;
return 0;
}
}
return -EINVAL;
}
{
int i;
return 0;
}
}
return -EINVAL;
}
/* LINTED */
{
int enum_count = 0;
int blob_count = 0;
int value_count = 0;
int ret = 0, i;
int copied;
return -EINVAL;
if (!obj) {
goto done;
}
enum_count++;
blob_count++;
}
for (i = 0; i < value_count; i++) {
goto done;
}
}
}
copied = 0;
goto done;
}
goto done;
}
copied++;
}
}
}
copied = 0;
goto done;
}
goto done;
}
copied++;
}
}
}
done:
return ret;
}
void *data)
{
int ret;
return NULL;
if (!blob)
return NULL;
if (ret) {
return NULL;
}
return blob;
}
struct drm_property_blob *blob)
{
}
/* LINTED */
{
int ret = 0;
void *blob_ptr;
return -EINVAL;
if (!obj) {
goto done;
}
goto done;
}
}
done:
return ret;
}
{
if (connector->edid_blob_ptr)
/* Delete edid, when there is none. */
if (!edid) {
return ret;
}
if (!connector->edid_blob_ptr)
return -EINVAL;
return ret;
}
{
return false;
return false;
return true;
int i;
for (i = 0; i < property->num_values; i++)
return !(value & ~valid_mask);
/* Only the driver knows */
return true;
} else {
int i;
for (i = 0; i < property->num_values; i++)
return true;
return false;
}
}
{
};
/* It does all the locking and checking we need */
}
struct drm_property *property,
{
/* Do DPMS ourselves */
ret = 0;
/* store the property value if successful */
if (!ret)
return ret;
}
struct drm_property *property,
{
if (!ret)
return ret;
}
struct drm_property *property,
{
if (!ret)
return ret;
}
/* LINTED */
{
int ret = 0;
int i;
int copied = 0;
int props_count = 0;
return -EINVAL;
if (!obj) {
goto out;
}
if (!obj->properties) {
goto out;
}
/* This ioctl is called twice, once to determine how much space is
* needed, and the 2nd time to fill it. */
copied = 0;
(arg->prop_values_ptr);
for (i = 0; i < props_count; i++) {
goto out;
}
prop_values_ptr + copied)) {
goto out;
}
copied++;
}
}
out:
return ret;
}
/* LINTED */
{
int i;
return -EINVAL;
if (!arg_obj)
goto out;
if (!arg_obj->properties)
goto out;
break;
goto out;
if (!prop_obj)
goto out;
goto out;
break;
case DRM_MODE_OBJECT_CRTC:
break;
case DRM_MODE_OBJECT_PLANE:
break;
}
out:
return ret;
}
struct drm_encoder *encoder)
{
int i;
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
if (connector->encoder_ids[i] == 0) {
return 0;
}
}
return -ENOMEM;
}
struct drm_encoder *encoder)
{
int i;
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
connector->encoder_ids[i] = 0;
break;
}
}
}
int gamma_size)
{
if (!crtc->gamma_store) {
crtc->gamma_size = 0;
return -ENOMEM;
}
return 0;
}
/* LINTED */
{
int size;
int ret = 0;
return -EINVAL;
if (!obj) {
goto out;
}
goto out;
}
/* memcpy into gamma store */
goto out;
}
goto out;
}
goto out;
}
goto out;
}
out:
return ret;
}
/* LINTED */
{
int size;
int ret = 0;
return -EINVAL;
if (!obj) {
goto out;
}
/* memcpy into gamma store */
goto out;
}
goto out;
}
goto out;
}
goto out;
}
out:
return ret;
}
/* LINTED */
{
struct drm_pending_vblank_event *e = NULL;
unsigned long flags;
return -EINVAL;
if (!obj)
return -EINVAL;
/* The framebuffer is currently unbound, presumably
* due to a hotplug event, that userspace has not
* yet discovered.
*/
goto out;
}
goto out;
if (!fb)
goto out;
if (crtc->invert_dimensions)
DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
goto out;
}
DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
goto out;
}
goto out;
}
e = kzalloc(sizeof *e, GFP_KERNEL);
if (e == NULL) {
goto out;
}
}
if (ret) {
kfree(e, sizeof(*e));
}
/* Keep the old fb, don't unref it. */
} else {
/*
* Warn if the driver hasn't properly updated the crtc->fb
* field to reflect that the new framebuffer is now used.
* Failing to do so will screw with the reference counting
* on framebuffers.
*/
/* Unref only the old framebuffer. */
}
out:
if (fb)
if (old_fb)
return ret;
}
{
}
/* LINTED E_FUNC_ARG_UNUSED */
{
return -ENOSYS;
}
/* LINTED E_FUNC_ARG_UNUSED */
{
/* call driver ioctl to get mmap offset */
return -ENOSYS;
}
/* LINTED E_FUNC_ARG_UNUSED */
{
return -ENOSYS;
}
/*
* Just need to support RGB formats here for compat with code that doesn't
* use pixel formats directly yet.
*/
int *bpp)
{
switch (format) {
case DRM_FORMAT_C8:
case DRM_FORMAT_RGB332:
case DRM_FORMAT_BGR233:
*depth = 8;
*bpp = 8;
break;
case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_XBGR1555:
case DRM_FORMAT_RGBX5551:
case DRM_FORMAT_BGRX5551:
case DRM_FORMAT_ARGB1555:
case DRM_FORMAT_ABGR1555:
case DRM_FORMAT_RGBA5551:
case DRM_FORMAT_BGRA5551:
*depth = 15;
*bpp = 16;
break;
case DRM_FORMAT_RGB565:
case DRM_FORMAT_BGR565:
*depth = 16;
*bpp = 16;
break;
case DRM_FORMAT_RGB888:
case DRM_FORMAT_BGR888:
*depth = 24;
*bpp = 24;
break;
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_RGBX8888:
case DRM_FORMAT_BGRX8888:
*depth = 24;
*bpp = 32;
break;
case DRM_FORMAT_XRGB2101010:
case DRM_FORMAT_XBGR2101010:
case DRM_FORMAT_RGBX1010102:
case DRM_FORMAT_BGRX1010102:
case DRM_FORMAT_ARGB2101010:
case DRM_FORMAT_ABGR2101010:
case DRM_FORMAT_RGBA1010102:
case DRM_FORMAT_BGRA1010102:
*depth = 30;
*bpp = 32;
break;
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_BGRA8888:
*depth = 32;
*bpp = 32;
break;
default:
DRM_DEBUG_KMS("unsupported pixel format\n");
*depth = 0;
*bpp = 0;
break;
}
}
/**
* drm_format_num_planes - get the number of planes for format
* @format: pixel format (DRM_FORMAT_*)
*
* RETURNS:
* The number of planes used by the specified pixel format.
*/
{
switch (format) {
case DRM_FORMAT_YUV410:
case DRM_FORMAT_YVU410:
case DRM_FORMAT_YUV411:
case DRM_FORMAT_YVU411:
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YVU420:
case DRM_FORMAT_YUV422:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_YUV444:
case DRM_FORMAT_YVU444:
return 3;
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV61:
case DRM_FORMAT_NV24:
case DRM_FORMAT_NV42:
return 2;
default:
return 1;
}
}
/**
* drm_format_plane_cpp - determine the bytes per pixel value
* @format: pixel format (DRM_FORMAT_*)
* @plane: plane index
*
* RETURNS:
* The bytes per pixel value for the specified plane.
*/
{
unsigned int depth;
int bpp;
return 0;
switch (format) {
case DRM_FORMAT_YUYV:
case DRM_FORMAT_YVYU:
case DRM_FORMAT_UYVY:
case DRM_FORMAT_VYUY:
return 2;
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV61:
case DRM_FORMAT_NV24:
case DRM_FORMAT_NV42:
case DRM_FORMAT_YUV410:
case DRM_FORMAT_YVU410:
case DRM_FORMAT_YUV411:
case DRM_FORMAT_YVU411:
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YVU420:
case DRM_FORMAT_YUV422:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_YUV444:
case DRM_FORMAT_YVU444:
return 1;
default:
return bpp >> 3;
}
}
/**
* drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor
* @format: pixel format (DRM_FORMAT_*)
*
* RETURNS:
* The horizontal chroma subsampling factor for the
* specified pixel format.
*/
{
switch (format) {
case DRM_FORMAT_YUV411:
case DRM_FORMAT_YVU411:
case DRM_FORMAT_YUV410:
case DRM_FORMAT_YVU410:
return 4;
case DRM_FORMAT_YUYV:
case DRM_FORMAT_YVYU:
case DRM_FORMAT_UYVY:
case DRM_FORMAT_VYUY:
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV61:
case DRM_FORMAT_YUV422:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YVU420:
return 2;
default:
return 1;
}
}
/**
* drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor
* @format: pixel format (DRM_FORMAT_*)
*
* RETURNS:
* The vertical chroma subsampling factor for the
* specified pixel format.
*/
{
switch (format) {
case DRM_FORMAT_YUV410:
case DRM_FORMAT_YVU410:
return 4;
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YVU420:
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
return 2;
default:
return 1;
}
}
/**
* drm_mode_config_init - initialize DRM mode_configuration structure
* @dev: DRM device
*
* Initialize @dev's mode_config structure, used for tracking the graphics
* configuration of @dev.
*
* Since this initializes the modeset locks, no locking is possible. Which is no
* problem, since this should happen single threaded at init time. It is the
* driver's problem to ensure this guarantee.
*
*/
{
/* Just to be sure */
}
/**
* drm_mode_config_cleanup - free up DRM mode_config info
* @dev: DRM device
*
* Free up all the connectors and CRTCs associated with this DRM device, then
* free up the framebuffers and associated buffer objects.
*
* teardown time, no locking is required. It's the driver's job to ensure that
* this guarantee actually holds true.
*
* FIXME: cleanup any dangling user buffer objects too
*/
{
head) {
}
}
head) {
}
head) {
}
/*
* Single-threaded teardown context, so it's not required to grab the
* fb_lock to protect against concurrent fb_list access. Contrary, it
* would actually deadlock with the drm_framebuffer_cleanup function.
*
* Also, if there are any framebuffers left, that's a driver leak now,
* so politely WARN about this.
*/
}
head) {
}
}
}