37,36 → 37,83 |
#include <drm/drm_crtc.h> |
#include <drm/drm_edid.h> |
#include <drm/drm_fourcc.h> |
#include <drm/drm_modeset_lock.h> |
|
#include "drm_crtc_internal.h" |
|
static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, |
struct drm_mode_fb_cmd2 *r, |
struct drm_file *file_priv); |
|
/** |
* 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. |
* scheme isn't (yet) implemented. Locks must be dropped with |
* drm_modeset_unlock_all. |
*/ |
void drm_modeset_lock_all(struct drm_device *dev) |
{ |
struct drm_crtc *crtc; |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_modeset_acquire_ctx *ctx; |
int ret; |
|
mutex_lock(&dev->mode_config.mutex); |
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); |
if (WARN_ON(!ctx)) |
return; |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) |
mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); |
mutex_lock(&config->mutex); |
|
drm_modeset_acquire_init(ctx, 0); |
|
retry: |
ret = drm_modeset_lock(&config->connection_mutex, ctx); |
if (ret) |
goto fail; |
ret = drm_modeset_lock_all_crtcs(dev, ctx); |
if (ret) |
goto fail; |
|
WARN_ON(config->acquire_ctx); |
|
/* now we hold the locks, so now that it is safe, stash the |
* ctx for drm_modeset_unlock_all(): |
*/ |
config->acquire_ctx = ctx; |
|
drm_warn_on_modeset_not_all_locked(dev); |
|
return; |
|
fail: |
if (ret == -EDEADLK) { |
drm_modeset_backoff(ctx); |
goto retry; |
} |
} |
EXPORT_SYMBOL(drm_modeset_lock_all); |
|
/** |
* drm_modeset_unlock_all - drop all modeset locks |
* @dev: device |
* |
* This function drop all modeset locks taken by drm_modeset_lock_all. |
*/ |
void drm_modeset_unlock_all(struct drm_device *dev) |
{ |
struct drm_crtc *crtc; |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) |
mutex_unlock(&crtc->mutex); |
if (WARN_ON(!ctx)) |
return; |
|
config->acquire_ctx = NULL; |
drm_modeset_drop_locks(ctx); |
drm_modeset_acquire_fini(ctx); |
|
kfree(ctx); |
|
mutex_unlock(&dev->mode_config.mutex); |
} |
EXPORT_SYMBOL(drm_modeset_unlock_all); |
74,6 → 121,8 |
/** |
* drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked |
* @dev: device |
* |
* Useful as a debug assert. |
*/ |
void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) |
{ |
80,8 → 129,9 |
struct drm_crtc *crtc; |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) |
WARN_ON(!mutex_is_locked(&crtc->mutex)); |
WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); |
|
WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); |
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); |
} |
EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); |
110,6 → 160,13 |
|
DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) |
|
static const struct drm_prop_enum_list drm_plane_type_enum_list[] = |
{ |
{ DRM_PLANE_TYPE_OVERLAY, "Overlay" }, |
{ DRM_PLANE_TYPE_PRIMARY, "Primary" }, |
{ DRM_PLANE_TYPE_CURSOR, "Cursor" }, |
}; |
|
/* |
* Optional properties |
*/ |
121,6 → 178,12 |
{ DRM_MODE_SCALE_ASPECT, "Full aspect" }, |
}; |
|
static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = { |
{ DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" }, |
{ DRM_MODE_PICTURE_ASPECT_4_3, "4:3" }, |
{ DRM_MODE_PICTURE_ASPECT_16_9, "16:9" }, |
}; |
|
/* |
* Non-global properties, but "required" for certain connectors. |
*/ |
209,8 → 272,19 |
{ DRM_MODE_ENCODER_TVDAC, "TV" }, |
{ DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, |
{ DRM_MODE_ENCODER_DSI, "DSI" }, |
{ DRM_MODE_ENCODER_DPMST, "DP MST" }, |
}; |
|
static const struct drm_prop_enum_list drm_subpixel_enum_list[] = |
{ |
{ SubPixelUnknown, "Unknown" }, |
{ SubPixelHorizontalRGB, "Horizontal RGB" }, |
{ SubPixelHorizontalBGR, "Horizontal BGR" }, |
{ SubPixelVerticalRGB, "Vertical RGB" }, |
{ SubPixelVerticalBGR, "Vertical BGR" }, |
{ SubPixelNone, "None" }, |
}; |
|
void drm_connector_ida_init(void) |
{ |
int i; |
227,28 → 301,13 |
ida_destroy(&drm_connector_enum_list[i].ida); |
} |
|
const char *drm_get_encoder_name(const 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; |
} |
EXPORT_SYMBOL(drm_get_encoder_name); |
|
const char *drm_get_connector_name(const 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); |
|
/** |
* drm_get_connector_status_name - return a string for connector status |
* @status: connector status to compute name of |
* |
* In contrast to the other drm_get_*_name functions this one here returns a |
* const pointer and hence is threadsafe. |
*/ |
const char *drm_get_connector_status_name(enum drm_connector_status status) |
{ |
if (status == connector_status_connected) |
260,11 → 319,33 |
} |
EXPORT_SYMBOL(drm_get_connector_status_name); |
|
/** |
* drm_get_subpixel_order_name - return a string for a given subpixel enum |
* @order: enum of subpixel_order |
* |
* Note you could abuse this and return something out of bounds, but that |
* would be a caller error. No unscrubbed user data should make it here. |
*/ |
const char *drm_get_subpixel_order_name(enum subpixel_order order) |
{ |
return drm_subpixel_enum_list[order].name; |
} |
EXPORT_SYMBOL(drm_get_subpixel_order_name); |
|
static char printable_char(int c) |
{ |
return isascii(c) && isprint(c) ? c : '?'; |
} |
|
/** |
* drm_get_format_name - return a string for drm fourcc format |
* @format: format to compute name of |
* |
* Note that the buffer used by this function is globally shared and owned by |
* the function itself. |
* |
* FIXME: This isn't really multithreading safe. |
*/ |
const char *drm_get_format_name(uint32_t format) |
{ |
static char buf[32]; |
282,26 → 363,19 |
} |
EXPORT_SYMBOL(drm_get_format_name); |
|
/** |
* 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. |
/* |
* Internal function to assign a slot in the object idr and optionally |
* register the object into the idr. |
*/ |
static int drm_mode_object_get(struct drm_device *dev, |
struct drm_mode_object *obj, uint32_t obj_type) |
static int drm_mode_object_get_reg(struct drm_device *dev, |
struct drm_mode_object *obj, |
uint32_t obj_type, |
bool register_obj) |
{ |
int ret; |
|
mutex_lock(&dev->mode_config.idr_mutex); |
ret = idr_alloc(&dev->mode_config.crtc_idr, obj, 1, 0, GFP_KERNEL); |
ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL); |
if (ret >= 0) { |
/* |
* Set up the object linking under the protection of the idr |
316,13 → 390,44 |
} |
|
/** |
* 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. Note that despite the _get postfix |
* modeset identifiers are _not_ reference counted. Hence don't use this for |
* reference counted modeset objects like framebuffers. |
* |
* Returns: |
* New unique (relative to other objects in @dev) integer identifier for the |
* object. |
*/ |
int drm_mode_object_get(struct drm_device *dev, |
struct drm_mode_object *obj, uint32_t obj_type) |
{ |
return drm_mode_object_get_reg(dev, obj, obj_type, true); |
} |
|
static void drm_mode_object_register(struct drm_device *dev, |
struct drm_mode_object *obj) |
{ |
mutex_lock(&dev->mode_config.idr_mutex); |
idr_replace(&dev->mode_config.crtc_idr, obj, obj->id); |
mutex_unlock(&dev->mode_config.idr_mutex); |
} |
|
/** |
* drm_mode_object_put - free a modeset identifer |
* @dev: DRM device |
* @object: object to free |
* |
* Free @id from @dev's unique identifier pool. |
* Free @id from @dev's unique identifier pool. Note that despite the _get |
* postfix modeset identifiers are _not_ reference counted. Hence don't use this |
* for reference counted modeset objects like framebuffers. |
*/ |
static void drm_mode_object_put(struct drm_device *dev, |
void drm_mode_object_put(struct drm_device *dev, |
struct drm_mode_object *object) |
{ |
mutex_lock(&dev->mode_config.idr_mutex); |
330,6 → 435,25 |
mutex_unlock(&dev->mode_config.idr_mutex); |
} |
|
static struct drm_mode_object *_object_find(struct drm_device *dev, |
uint32_t id, uint32_t type) |
{ |
struct drm_mode_object *obj = NULL; |
|
mutex_lock(&dev->mode_config.idr_mutex); |
obj = idr_find(&dev->mode_config.crtc_idr, id); |
if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type) |
obj = NULL; |
if (obj && obj->id != id) |
obj = NULL; |
/* don't leak out unref'd fb's */ |
if (obj && (obj->type == DRM_MODE_OBJECT_FB)) |
obj = NULL; |
mutex_unlock(&dev->mode_config.idr_mutex); |
|
return obj; |
} |
|
/** |
* drm_mode_object_find - look up a drm object with static lifetime |
* @dev: drm device |
337,7 → 461,9 |
* @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. |
* are reference counted, they need special treatment. Even with |
* DRM_MODE_OBJECT_ANY (although that will simply return NULL |
* rather than WARN_ON()). |
*/ |
struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, |
uint32_t id, uint32_t type) |
347,13 → 473,7 |
/* Framebuffers are reference counted and need their own lookup |
* function.*/ |
WARN_ON(type == DRM_MODE_OBJECT_FB); |
|
mutex_lock(&dev->mode_config.idr_mutex); |
obj = idr_find(&dev->mode_config.crtc_idr, id); |
if (!obj || (obj->type != type) || (obj->id != id)) |
obj = NULL; |
mutex_unlock(&dev->mode_config.idr_mutex); |
|
obj = _object_find(dev, id, type); |
return obj; |
} |
EXPORT_SYMBOL(drm_mode_object_find); |
373,7 → 493,7 |
* since all the fb attributes are invariant over its lifetime, no further |
* locking but only correct reference counting is required. |
* |
* RETURNS: |
* Returns: |
* Zero on success, error code on failure. |
*/ |
int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, |
434,7 → 554,7 |
* |
* If successful, this grabs an additional reference to the framebuffer - |
* callers need to make sure to eventually unreference the returned framebuffer |
* again. |
* again, using @drm_framebuffer_unreference. |
*/ |
struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, |
uint32_t id) |
459,7 → 579,7 |
*/ |
void drm_framebuffer_unreference(struct drm_framebuffer *fb) |
{ |
DRM_DEBUG("FB ID: %d\n", fb->base.id); |
DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount)); |
kref_put(&fb->refcount, drm_framebuffer_free); |
} |
EXPORT_SYMBOL(drm_framebuffer_unreference); |
467,10 → 587,12 |
/** |
* drm_framebuffer_reference - incr the fb refcnt |
* @fb: framebuffer |
* |
* This functions increments the fb's refcount. |
*/ |
void drm_framebuffer_reference(struct drm_framebuffer *fb) |
{ |
DRM_DEBUG("FB ID: %d\n", fb->base.id); |
DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount)); |
kref_get(&fb->refcount); |
} |
EXPORT_SYMBOL(drm_framebuffer_reference); |
482,7 → 604,7 |
|
static void __drm_framebuffer_unreference(struct drm_framebuffer *fb) |
{ |
DRM_DEBUG("FB ID: %d\n", fb->base.id); |
DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount)); |
kref_put(&fb->refcount, drm_framebuffer_free_bug); |
} |
|
523,8 → 645,9 |
* 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. |
* Cleanup framebuffer. This function is intended to be used from the drivers |
* ->destroy callback. It can also be used to clean up driver private |
* framebuffers embedded into a larger structure. |
* |
* 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 |
587,7 → 710,7 |
drm_modeset_lock_all(dev); |
/* remove from any CRTC */ |
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
if (crtc->fb == fb) { |
if (crtc->primary->fb == fb) { |
/* should turn off the crtc */ |
memset(&set, 0, sizeof(struct drm_mode_set)); |
set.crtc = crtc; |
609,20 → 732,28 |
} |
EXPORT_SYMBOL(drm_framebuffer_remove); |
|
DEFINE_WW_CLASS(crtc_ww_class); |
|
/** |
* drm_crtc_init - Initialise a new CRTC object |
* drm_crtc_init_with_planes - Initialise a new CRTC object with |
* specified primary and cursor planes. |
* @dev: DRM device |
* @crtc: CRTC object to init |
* @primary: Primary plane for CRTC |
* @cursor: Cursor plane for CRTC |
* @funcs: callbacks for the new CRTC |
* |
* Inits a new object created as base part of a driver crtc object. |
* |
* RETURNS: |
* Returns: |
* Zero on success, error code on failure. |
*/ |
int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, |
int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, |
struct drm_plane *primary, |
struct drm_plane *cursor, |
const struct drm_crtc_funcs *funcs) |
{ |
struct drm_mode_config *config = &dev->mode_config; |
int ret; |
|
crtc->dev = dev; |
630,8 → 761,9 |
crtc->invert_dimensions = false; |
|
drm_modeset_lock_all(dev); |
mutex_init(&crtc->mutex); |
mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); |
drm_modeset_lock_init(&crtc->mutex); |
/* dropped by _unlock_all(): */ |
drm_modeset_lock(&crtc->mutex, config->acquire_ctx); |
|
ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); |
if (ret) |
639,15 → 771,22 |
|
crtc->base.properties = &crtc->properties; |
|
list_add_tail(&crtc->head, &dev->mode_config.crtc_list); |
dev->mode_config.num_crtc++; |
list_add_tail(&crtc->head, &config->crtc_list); |
config->num_crtc++; |
|
crtc->primary = primary; |
crtc->cursor = cursor; |
if (primary) |
primary->possible_crtcs = 1 << drm_crtc_index(crtc); |
if (cursor) |
cursor->possible_crtcs = 1 << drm_crtc_index(crtc); |
|
out: |
drm_modeset_unlock_all(dev); |
|
return ret; |
} |
EXPORT_SYMBOL(drm_crtc_init); |
EXPORT_SYMBOL(drm_crtc_init_with_planes); |
|
/** |
* drm_crtc_cleanup - Clean up the core crtc usage |
664,6 → 803,8 |
kfree(crtc->gamma_store); |
crtc->gamma_store = NULL; |
|
drm_modeset_lock_fini(&crtc->mutex); |
|
drm_mode_object_put(dev, &crtc->base); |
list_del(&crtc->head); |
dev->mode_config.num_crtc--; |
693,20 → 834,6 |
} |
EXPORT_SYMBOL(drm_crtc_index); |
|
/** |
* 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. |
*/ |
void drm_mode_probed_add(struct drm_connector *connector, |
struct drm_display_mode *mode) |
{ |
list_add_tail(&mode->head, &connector->probed_modes); |
} |
EXPORT_SYMBOL(drm_mode_probed_add); |
|
/* |
* drm_mode_remove - remove and free a mode |
* @connector: connector list to modify |
731,7 → 858,7 |
* Initialises a preallocated connector. Connectors should be |
* subclassed as part of driver connector objects. |
* |
* RETURNS: |
* Returns: |
* Zero on success, error code on failure. |
*/ |
int drm_connector_init(struct drm_device *dev, |
745,9 → 872,9 |
|
drm_modeset_lock_all(dev); |
|
ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); |
ret = drm_mode_object_get_reg(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR, false); |
if (ret) |
goto out; |
goto out_unlock; |
|
connector->base.properties = &connector->properties; |
connector->dev = dev; |
757,9 → 884,17 |
ida_simple_get(connector_ida, 1, 0, GFP_KERNEL); |
if (connector->connector_type_id < 0) { |
ret = connector->connector_type_id; |
drm_mode_object_put(dev, &connector->base); |
goto out; |
goto out_put; |
} |
connector->name = |
kasprintf(GFP_KERNEL, "%s-%d", |
drm_connector_enum_list[connector_type].name, |
connector->connector_type_id); |
if (!connector->name) { |
ret = -ENOMEM; |
goto out_put; |
} |
|
INIT_LIST_HEAD(&connector->probed_modes); |
INIT_LIST_HEAD(&connector->modes); |
connector->edid_blob_ptr = NULL; |
776,7 → 911,13 |
drm_object_attach_property(&connector->base, |
dev->mode_config.dpms_property, 0); |
|
out: |
connector->debugfs_entry = NULL; |
|
out_put: |
if (ret) |
drm_mode_object_put(dev, &connector->base); |
|
out_unlock: |
drm_modeset_unlock_all(dev); |
|
return ret; |
804,22 → 945,75 |
connector->connector_type_id); |
|
drm_mode_object_put(dev, &connector->base); |
kfree(connector->name); |
connector->name = NULL; |
list_del(&connector->head); |
dev->mode_config.num_connector--; |
} |
EXPORT_SYMBOL(drm_connector_cleanup); |
|
/** |
* drm_connector_register - register a connector |
* @connector: the connector to register |
* |
* Register userspace interfaces for a connector |
* |
* Returns: |
* Zero on success, error code on failure. |
*/ |
int drm_connector_register(struct drm_connector *connector) |
{ |
int ret; |
|
drm_mode_object_register(connector->dev, &connector->base); |
|
return 0; |
} |
EXPORT_SYMBOL(drm_connector_register); |
|
/** |
* drm_connector_unregister - unregister a connector |
* @connector: the connector to unregister |
* |
* Unregister userspace interfaces for a connector |
*/ |
void drm_connector_unregister(struct drm_connector *connector) |
{ |
} |
EXPORT_SYMBOL(drm_connector_unregister); |
|
|
/** |
* drm_connector_unplug_all - unregister connector userspace interfaces |
* @dev: drm device |
* |
* This function unregisters all connector userspace interfaces in sysfs. Should |
* be call when the device is disconnected, e.g. from an usb driver's |
* ->disconnect callback. |
*/ |
void drm_connector_unplug_all(struct drm_device *dev) |
{ |
struct drm_connector *connector; |
|
/* taking the mode config mutex ends up in a clash with sysfs */ |
// list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
// drm_sysfs_connector_remove(connector); |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
drm_connector_unregister(connector); |
|
} |
EXPORT_SYMBOL(drm_connector_unplug_all); |
|
/** |
* drm_bridge_init - initialize a drm transcoder/bridge |
* @dev: drm device |
* @bridge: transcoder/bridge to set up |
* @funcs: bridge function table |
* |
* Initialises a preallocated bridge. Bridges should be |
* subclassed as part of driver connector objects. |
* |
* Returns: |
* Zero on success, error code on failure. |
*/ |
int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, |
const struct drm_bridge_funcs *funcs) |
{ |
843,6 → 1037,12 |
} |
EXPORT_SYMBOL(drm_bridge_init); |
|
/** |
* drm_bridge_cleanup - cleans up an initialised bridge |
* @bridge: bridge to cleanup |
* |
* Cleans up the bridge but doesn't free the object. |
*/ |
void drm_bridge_cleanup(struct drm_bridge *bridge) |
{ |
struct drm_device *dev = bridge->dev; |
855,6 → 1055,19 |
} |
EXPORT_SYMBOL(drm_bridge_cleanup); |
|
/** |
* drm_encoder_init - Init a preallocated encoder |
* @dev: drm device |
* @encoder: the encoder to init |
* @funcs: callbacks for this encoder |
* @encoder_type: user visible type of the encoder |
* |
* Initialises a preallocated encoder. Encoder should be |
* subclassed as part of driver encoder objects. |
* |
* Returns: |
* Zero on success, error code on failure. |
*/ |
int drm_encoder_init(struct drm_device *dev, |
struct drm_encoder *encoder, |
const struct drm_encoder_funcs *funcs, |
866,16 → 1079,27 |
|
ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); |
if (ret) |
goto out; |
goto out_unlock; |
|
encoder->dev = dev; |
encoder->encoder_type = encoder_type; |
encoder->funcs = funcs; |
encoder->name = kasprintf(GFP_KERNEL, "%s-%d", |
drm_encoder_enum_list[encoder_type].name, |
encoder->base.id); |
if (!encoder->name) { |
ret = -ENOMEM; |
goto out_put; |
} |
|
list_add_tail(&encoder->head, &dev->mode_config.encoder_list); |
dev->mode_config.num_encoder++; |
|
out: |
out_put: |
if (ret) |
drm_mode_object_put(dev, &encoder->base); |
|
out_unlock: |
drm_modeset_unlock_all(dev); |
|
return ret; |
882,11 → 1106,19 |
} |
EXPORT_SYMBOL(drm_encoder_init); |
|
/** |
* drm_encoder_cleanup - cleans up an initialised encoder |
* @encoder: encoder to cleanup |
* |
* Cleans up the encoder but doesn't free the object. |
*/ |
void drm_encoder_cleanup(struct drm_encoder *encoder) |
{ |
struct drm_device *dev = encoder->dev; |
drm_modeset_lock_all(dev); |
drm_mode_object_put(dev, &encoder->base); |
kfree(encoder->name); |
encoder->name = NULL; |
list_del(&encoder->head); |
dev->mode_config.num_encoder--; |
drm_modeset_unlock_all(dev); |
894,7 → 1126,7 |
EXPORT_SYMBOL(drm_encoder_cleanup); |
|
/** |
* drm_plane_init - Initialise a new plane object |
* drm_universal_plane_init - Initialize a new universal plane object |
* @dev: DRM device |
* @plane: plane object to init |
* @possible_crtcs: bitmask of possible CRTCs |
901,18 → 1133,18 |
* @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)? |
* @type: type of plane (overlay, primary, cursor) |
* |
* Inits a new object created as base part of a driver plane object. |
* Initializes a plane object of type @type. |
* |
* RETURNS: |
* Returns: |
* Zero on success, error code on failure. |
*/ |
int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, |
int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, |
unsigned long possible_crtcs, |
const struct drm_plane_funcs *funcs, |
const uint32_t *formats, uint32_t format_count, |
bool priv) |
enum drm_plane_type type) |
{ |
int ret; |
|
937,23 → 1169,53 |
memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); |
plane->format_count = format_count; |
plane->possible_crtcs = possible_crtcs; |
plane->type = type; |
|
/* 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) { |
list_add_tail(&plane->head, &dev->mode_config.plane_list); |
dev->mode_config.num_plane++; |
} else { |
INIT_LIST_HEAD(&plane->head); |
} |
dev->mode_config.num_total_plane++; |
if (plane->type == DRM_PLANE_TYPE_OVERLAY) |
dev->mode_config.num_overlay_plane++; |
|
drm_object_attach_property(&plane->base, |
dev->mode_config.plane_type_property, |
plane->type); |
|
out: |
drm_modeset_unlock_all(dev); |
|
return ret; |
} |
EXPORT_SYMBOL(drm_universal_plane_init); |
|
/** |
* drm_plane_init - Initialize a legacy plane |
* @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 |
* @is_primary: plane type (primary vs overlay) |
* |
* Legacy API to initialize a DRM plane. |
* |
* New drivers should call drm_universal_plane_init() instead. |
* |
* Returns: |
* Zero on success, error code on failure. |
*/ |
int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, |
unsigned long possible_crtcs, |
const struct drm_plane_funcs *funcs, |
const uint32_t *formats, uint32_t format_count, |
bool is_primary) |
{ |
enum drm_plane_type type; |
|
type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; |
return drm_universal_plane_init(dev, plane, possible_crtcs, funcs, |
formats, format_count, type); |
} |
EXPORT_SYMBOL(drm_plane_init); |
|
/** |
971,11 → 1233,13 |
drm_modeset_lock_all(dev); |
kfree(plane->format_types); |
drm_mode_object_put(dev, &plane->base); |
/* if not added to a list, it must be a private plane */ |
if (!list_empty(&plane->head)) { |
|
BUG_ON(list_empty(&plane->head)); |
|
list_del(&plane->head); |
dev->mode_config.num_plane--; |
} |
dev->mode_config.num_total_plane--; |
if (plane->type == DRM_PLANE_TYPE_OVERLAY) |
dev->mode_config.num_overlay_plane--; |
drm_modeset_unlock_all(dev); |
} |
EXPORT_SYMBOL(drm_plane_cleanup); |
991,69 → 1255,29 |
*/ |
void drm_plane_force_disable(struct drm_plane *plane) |
{ |
struct drm_framebuffer *old_fb = plane->fb; |
int ret; |
|
if (!plane->fb) |
if (!old_fb) |
return; |
|
ret = plane->funcs->disable_plane(plane); |
if (ret) |
if (ret) { |
DRM_ERROR("failed to disable plane with busy fb\n"); |
return; |
} |
/* disconnect the plane from the fb and crtc: */ |
__drm_framebuffer_unreference(plane->fb); |
__drm_framebuffer_unreference(old_fb); |
plane->fb = NULL; |
plane->crtc = NULL; |
} |
EXPORT_SYMBOL(drm_plane_force_disable); |
|
/** |
* 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. |
*/ |
struct drm_display_mode *drm_mode_create(struct drm_device *dev) |
{ |
struct drm_display_mode *nmode; |
|
nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); |
if (!nmode) |
return NULL; |
|
if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) { |
kfree(nmode); |
return NULL; |
} |
|
return nmode; |
} |
EXPORT_SYMBOL(drm_mode_create); |
|
/** |
* drm_mode_destroy - remove a mode |
* @dev: DRM device |
* @mode: mode to remove |
* |
* Free @mode's unique identifier, then free it. |
*/ |
void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) |
{ |
if (!mode) |
return; |
|
drm_mode_object_put(dev, &mode->base); |
|
kfree(mode); |
} |
EXPORT_SYMBOL(drm_mode_destroy); |
|
static int drm_mode_create_standard_connector_properties(struct drm_device *dev) |
{ |
struct drm_property *edid; |
struct drm_property *dpms; |
struct drm_property *dev_path; |
|
/* |
* Standard properties (apply to all connectors) |
1068,9 → 1292,30 |
ARRAY_SIZE(drm_dpms_enum_list)); |
dev->mode_config.dpms_property = dpms; |
|
dev_path = drm_property_create(dev, |
DRM_MODE_PROP_BLOB | |
DRM_MODE_PROP_IMMUTABLE, |
"PATH", 0); |
dev->mode_config.path_property = dev_path; |
|
return 0; |
} |
|
static int drm_mode_create_standard_plane_properties(struct drm_device *dev) |
{ |
struct drm_property *type; |
|
/* |
* Standard properties (apply to all planes) |
*/ |
type = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, |
"type", drm_plane_type_enum_list, |
ARRAY_SIZE(drm_plane_type_enum_list)); |
dev->mode_config.plane_type_property = type; |
|
return 0; |
} |
|
/** |
* drm_mode_create_dvi_i_properties - create DVI-I specific connector properties |
* @dev: DRM device |
1209,6 → 1454,33 |
EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); |
|
/** |
* drm_mode_create_aspect_ratio_property - create aspect ratio property |
* @dev: DRM device |
* |
* Called by a driver the first time it's needed, must be attached to desired |
* connectors. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_create_aspect_ratio_property(struct drm_device *dev) |
{ |
if (dev->mode_config.aspect_ratio_property) |
return 0; |
|
dev->mode_config.aspect_ratio_property = |
drm_property_create_enum(dev, 0, "aspect ratio", |
drm_aspect_ratio_enum_list, |
ARRAY_SIZE(drm_aspect_ratio_enum_list)); |
|
if (dev->mode_config.aspect_ratio_property == NULL) |
return -ENOMEM; |
|
return 0; |
} |
EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property); |
|
/** |
* drm_mode_create_dirty_property - create dirty property |
* @dev: DRM device |
* |
1253,6 → 1525,16 |
return 0; |
} |
|
void drm_mode_group_destroy(struct drm_mode_group *group) |
{ |
kfree(group->id_list); |
group->id_list = NULL; |
} |
|
/* |
* NOTE: Driver's shouldn't ever call drm_mode_group_init_legacy_group - it is |
* the drm core's responsibility to set up mode control groups. |
*/ |
int drm_mode_group_init_legacy_group(struct drm_device *dev, |
struct drm_mode_group *group) |
{ |
1285,6 → 1567,15 |
} |
EXPORT_SYMBOL(drm_mode_group_init_legacy_group); |
|
void drm_reinit_primary_mode_group(struct drm_device *dev) |
{ |
drm_modeset_lock_all(dev); |
drm_mode_group_destroy(&dev->primary->mode_group); |
drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group); |
drm_modeset_unlock_all(dev); |
} |
EXPORT_SYMBOL(drm_reinit_primary_mode_group); |
|
/** |
* drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo |
* @out: drm_mode_modeinfo struct to return to the user |
1329,7 → 1620,7 |
* Convert a drm_mode_modeinfo into a drm_display_mode structure to return to |
* the caller. |
* |
* RETURNS: |
* Returns: |
* Zero on success, errno on failure. |
*/ |
static int drm_crtc_convert_umode(struct drm_display_mode *out, |
1374,7 → 1665,7 |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_getresources(struct drm_device *dev, void *data, |
1427,9 → 1718,9 |
mutex_unlock(&file_priv->fbs_lock); |
|
drm_modeset_lock_all(dev); |
mode_group = &file_priv->master->minor->mode_group; |
if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { |
if (!drm_is_primary_client(file_priv)) { |
|
mode_group = NULL; |
list_for_each(lh, &dev->mode_config.crtc_list) |
crtc_count++; |
|
1440,6 → 1731,7 |
encoder_count++; |
} else { |
|
mode_group = &file_priv->master->minor->mode_group; |
crtc_count = mode_group->num_crtcs; |
connector_count = mode_group->num_connectors; |
encoder_count = mode_group->num_encoders; |
1454,7 → 1746,7 |
if (card_res->count_crtcs >= crtc_count) { |
copied = 0; |
crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr; |
if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { |
if (!mode_group) { |
list_for_each_entry(crtc, &dev->mode_config.crtc_list, |
head) { |
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); |
1481,12 → 1773,12 |
if (card_res->count_encoders >= encoder_count) { |
copied = 0; |
encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr; |
if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { |
if (!mode_group) { |
list_for_each_entry(encoder, |
&dev->mode_config.encoder_list, |
head) { |
DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id, |
drm_get_encoder_name(encoder)); |
encoder->name); |
if (put_user(encoder->base.id, encoder_id + |
copied)) { |
ret = -EFAULT; |
1512,13 → 1804,13 |
if (card_res->count_connectors >= connector_count) { |
copied = 0; |
connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr; |
if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { |
if (!mode_group) { |
list_for_each_entry(connector, |
&dev->mode_config.connector_list, |
head) { |
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", |
connector->base.id, |
drm_get_connector_name(connector)); |
connector->name); |
if (put_user(connector->base.id, |
connector_id + copied)) { |
ret = -EFAULT; |
1559,7 → 1851,7 |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_getcrtc(struct drm_device *dev, |
1567,7 → 1859,6 |
{ |
struct drm_mode_crtc *crtc_resp = data; |
struct drm_crtc *crtc; |
struct drm_mode_object *obj; |
int ret = 0; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
1575,19 → 1866,17 |
|
drm_modeset_lock_all(dev); |
|
obj = drm_mode_object_find(dev, crtc_resp->crtc_id, |
DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
crtc = drm_crtc_find(dev, crtc_resp->crtc_id); |
if (!crtc) { |
ret = -ENOENT; |
goto out; |
} |
crtc = obj_to_crtc(obj); |
|
crtc_resp->x = crtc->x; |
crtc_resp->y = crtc->y; |
crtc_resp->gamma_size = crtc->gamma_size; |
if (crtc->fb) |
crtc_resp->fb_id = crtc->fb->base.id; |
if (crtc->primary->fb) |
crtc_resp->fb_id = crtc->primary->fb->base.id; |
else |
crtc_resp->fb_id = 0; |
|
1628,7 → 1917,7 |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_getconnector(struct drm_device *dev, void *data, |
1635,7 → 1924,6 |
struct drm_file *file_priv) |
{ |
struct drm_mode_get_connector *out_resp = data; |
struct drm_mode_object *obj; |
struct drm_connector *connector; |
struct drm_display_mode *mode; |
int mode_count = 0; |
1659,13 → 1947,11 |
|
mutex_lock(&dev->mode_config.mutex); |
|
obj = drm_mode_object_find(dev, out_resp->connector_id, |
DRM_MODE_OBJECT_CONNECTOR); |
if (!obj) { |
connector = drm_connector_find(dev, out_resp->connector_id); |
if (!connector) { |
ret = -ENOENT; |
goto out; |
} |
connector = obj_to_connector(obj); |
|
props_count = connector->properties.count; |
|
1693,10 → 1979,12 |
out_resp->mm_height = connector->display_info.height_mm; |
out_resp->subpixel = connector->display_info.subpixel_order; |
out_resp->connection = connector->status; |
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); |
if (connector->encoder) |
out_resp->encoder_id = connector->encoder->base.id; |
else |
out_resp->encoder_id = 0; |
drm_modeset_unlock(&dev->mode_config.connection_mutex); |
|
/* |
* This ioctl is called twice, once to determine how much space is |
1763,11 → 2051,23 |
return ret; |
} |
|
/** |
* drm_mode_getencoder - get encoder configuration |
* @dev: drm device for the ioctl |
* @data: data pointer for the ioctl |
* @file_priv: drm file for the ioctl call |
* |
* Construct a encoder configuration structure to return to the user. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_getencoder(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
struct drm_mode_get_encoder *enc_resp = data; |
struct drm_mode_object *obj; |
struct drm_encoder *encoder; |
int ret = 0; |
|
1775,13 → 2075,11 |
return -EINVAL; |
|
drm_modeset_lock_all(dev); |
obj = drm_mode_object_find(dev, enc_resp->encoder_id, |
DRM_MODE_OBJECT_ENCODER); |
if (!obj) { |
encoder = drm_encoder_find(dev, enc_resp->encoder_id); |
if (!encoder) { |
ret = -ENOENT; |
goto out; |
} |
encoder = obj_to_encoder(obj); |
|
if (encoder->crtc) |
enc_resp->crtc_id = encoder->crtc->base.id; |
1798,12 → 2096,17 |
} |
|
/** |
* drm_mode_getplane_res - get plane info |
* drm_mode_getplane_res - enumerate all plane resources |
* @dev: DRM device |
* @data: ioctl data |
* @file_priv: DRM file info |
* |
* Return an plane count and set of IDs. |
* Construct a list of plane ids to return to the user. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_getplane_res(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
1813,6 → 2116,7 |
struct drm_plane *plane; |
uint32_t __user *plane_ptr; |
int copied = 0, ret = 0; |
unsigned num_planes; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
1820,15 → 2124,28 |
drm_modeset_lock_all(dev); |
config = &dev->mode_config; |
|
if (file_priv->universal_planes) |
num_planes = config->num_total_plane; |
else |
num_planes = config->num_overlay_plane; |
|
/* |
* This ioctl is called twice, once to determine how much space is |
* needed, and the 2nd time to fill it. |
*/ |
if (config->num_plane && |
(plane_resp->count_planes >= config->num_plane)) { |
if (num_planes && |
(plane_resp->count_planes >= num_planes)) { |
plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; |
|
list_for_each_entry(plane, &config->plane_list, head) { |
/* |
* Unless userspace set the 'universal planes' |
* capability bit, only advertise overlays. |
*/ |
if (plane->type != DRM_PLANE_TYPE_OVERLAY && |
!file_priv->universal_planes) |
continue; |
|
if (put_user(plane->base.id, plane_ptr + copied)) { |
ret = -EFAULT; |
goto out; |
1836,7 → 2153,7 |
copied++; |
} |
} |
plane_resp->count_planes = config->num_plane; |
plane_resp->count_planes = num_planes; |
|
out: |
drm_modeset_unlock_all(dev); |
1844,19 → 2161,22 |
} |
|
/** |
* drm_mode_getplane - get plane info |
* drm_mode_getplane - get plane configuration |
* @dev: DRM device |
* @data: ioctl data |
* @file_priv: DRM file info |
* |
* Return plane info, including formats supported, gamma size, any |
* current fb, etc. |
* Construct a plane configuration structure to return to the user. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_getplane(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
struct drm_mode_get_plane *plane_resp = data; |
struct drm_mode_object *obj; |
struct drm_plane *plane; |
uint32_t __user *format_ptr; |
int ret = 0; |
1865,13 → 2185,11 |
return -EINVAL; |
|
drm_modeset_lock_all(dev); |
obj = drm_mode_object_find(dev, plane_resp->plane_id, |
DRM_MODE_OBJECT_PLANE); |
if (!obj) { |
plane = drm_plane_find(dev, plane_resp->plane_id); |
if (!plane) { |
ret = -ENOENT; |
goto out; |
} |
plane = obj_to_plane(obj); |
|
if (plane->crtc) |
plane_resp->crtc_id = plane->crtc->base.id; |
1908,72 → 2226,52 |
return ret; |
} |
|
/** |
* drm_mode_setplane - set up or tear down an plane |
* @dev: DRM device |
* @data: ioctl data* |
* @file_priv: DRM file info |
/* |
* setplane_internal - setplane handler for internal callers |
* |
* Set plane info, including placement, fb, scaling, and other factors. |
* Or pass a NULL fb to disable. |
* Note that we assume an extra reference has already been taken on fb. If the |
* update fails, this reference will be dropped before return; if it succeeds, |
* the previous framebuffer (if any) will be unreferenced instead. |
* |
* src_{x,y,w,h} are provided in 16.16 fixed point format |
*/ |
int drm_mode_setplane(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
static int setplane_internal(struct drm_plane *plane, |
struct drm_crtc *crtc, |
struct drm_framebuffer *fb, |
int32_t crtc_x, int32_t crtc_y, |
uint32_t crtc_w, uint32_t crtc_h, |
/* src_{x,y,w,h} values are 16.16 fixed point */ |
uint32_t src_x, uint32_t src_y, |
uint32_t src_w, uint32_t src_h) |
{ |
struct drm_mode_set_plane *plane_req = data; |
struct drm_mode_object *obj; |
struct drm_plane *plane; |
struct drm_crtc *crtc; |
struct drm_framebuffer *fb = NULL, *old_fb = NULL; |
struct drm_device *dev = plane->dev; |
struct drm_framebuffer *old_fb = NULL; |
int ret = 0; |
unsigned int fb_width, fb_height; |
int i; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
/* |
* First, find the plane, crtc, and fb objects. If not available, |
* we don't bother to call the driver. |
*/ |
obj = drm_mode_object_find(dev, plane_req->plane_id, |
DRM_MODE_OBJECT_PLANE); |
if (!obj) { |
DRM_DEBUG_KMS("Unknown plane ID %d\n", |
plane_req->plane_id); |
return -ENOENT; |
} |
plane = obj_to_plane(obj); |
|
/* No fb means shut it down */ |
if (!plane_req->fb_id) { |
if (!fb) { |
drm_modeset_lock_all(dev); |
old_fb = plane->fb; |
plane->funcs->disable_plane(plane); |
ret = plane->funcs->disable_plane(plane); |
if (!ret) { |
plane->crtc = NULL; |
plane->fb = NULL; |
} else { |
old_fb = NULL; |
} |
drm_modeset_unlock_all(dev); |
goto out; |
} |
|
obj = drm_mode_object_find(dev, plane_req->crtc_id, |
DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
DRM_DEBUG_KMS("Unknown crtc ID %d\n", |
plane_req->crtc_id); |
ret = -ENOENT; |
/* Check whether this plane is usable on this CRTC */ |
if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) { |
DRM_DEBUG_KMS("Invalid crtc for plane\n"); |
ret = -EINVAL; |
goto out; |
} |
crtc = obj_to_crtc(obj); |
|
fb = drm_framebuffer_lookup(dev, plane_req->fb_id); |
if (!fb) { |
DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", |
plane_req->fb_id); |
ret = -ENOENT; |
goto out; |
} |
|
/* Check whether this plane supports the fb pixel format. */ |
for (i = 0; i < plane->format_count; i++) |
if (fb->pixel_format == plane->format_types[i]) |
1989,47 → 2287,31 |
fb_height = fb->height << 16; |
|
/* Make sure source coordinates are inside the fb. */ |
if (plane_req->src_w > fb_width || |
plane_req->src_x > fb_width - plane_req->src_w || |
plane_req->src_h > fb_height || |
plane_req->src_y > fb_height - plane_req->src_h) { |
if (src_w > fb_width || |
src_x > fb_width - src_w || |
src_h > fb_height || |
src_y > fb_height - src_h) { |
DRM_DEBUG_KMS("Invalid source coordinates " |
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", |
plane_req->src_w >> 16, |
((plane_req->src_w & 0xffff) * 15625) >> 10, |
plane_req->src_h >> 16, |
((plane_req->src_h & 0xffff) * 15625) >> 10, |
plane_req->src_x >> 16, |
((plane_req->src_x & 0xffff) * 15625) >> 10, |
plane_req->src_y >> 16, |
((plane_req->src_y & 0xffff) * 15625) >> 10); |
src_w >> 16, ((src_w & 0xffff) * 15625) >> 10, |
src_h >> 16, ((src_h & 0xffff) * 15625) >> 10, |
src_x >> 16, ((src_x & 0xffff) * 15625) >> 10, |
src_y >> 16, ((src_y & 0xffff) * 15625) >> 10); |
ret = -ENOSPC; |
goto out; |
} |
|
/* Give drivers some help against integer overflows */ |
if (plane_req->crtc_w > INT_MAX || |
plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || |
plane_req->crtc_h > INT_MAX || |
plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { |
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", |
plane_req->crtc_w, plane_req->crtc_h, |
plane_req->crtc_x, plane_req->crtc_y); |
ret = -ERANGE; |
goto out; |
} |
|
drm_modeset_lock_all(dev); |
old_fb = plane->fb; |
ret = plane->funcs->update_plane(plane, crtc, fb, |
plane_req->crtc_x, plane_req->crtc_y, |
plane_req->crtc_w, plane_req->crtc_h, |
plane_req->src_x, plane_req->src_y, |
plane_req->src_w, plane_req->src_h); |
crtc_x, crtc_y, crtc_w, crtc_h, |
src_x, src_y, src_w, src_h); |
if (!ret) { |
old_fb = plane->fb; |
plane->crtc = crtc; |
plane->fb = fb; |
fb = NULL; |
} else { |
old_fb = NULL; |
} |
drm_modeset_unlock_all(dev); |
|
2040,7 → 2322,86 |
drm_framebuffer_unreference(old_fb); |
|
return ret; |
|
} |
|
/** |
* drm_mode_setplane - configure a plane's configuration |
* @dev: DRM device |
* @data: ioctl data* |
* @file_priv: DRM file info |
* |
* Set plane configuration, including placement, fb, scaling, and other factors. |
* Or pass a NULL fb to disable (planes may be disabled without providing a |
* valid crtc). |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_setplane(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
struct drm_mode_set_plane *plane_req = data; |
struct drm_mode_object *obj; |
struct drm_plane *plane; |
struct drm_crtc *crtc = NULL; |
struct drm_framebuffer *fb = NULL; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
/* Give drivers some help against integer overflows */ |
if (plane_req->crtc_w > INT_MAX || |
plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || |
plane_req->crtc_h > INT_MAX || |
plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { |
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", |
plane_req->crtc_w, plane_req->crtc_h, |
plane_req->crtc_x, plane_req->crtc_y); |
return -ERANGE; |
} |
|
/* |
* First, find the plane, crtc, and fb objects. If not available, |
* we don't bother to call the driver. |
*/ |
obj = drm_mode_object_find(dev, plane_req->plane_id, |
DRM_MODE_OBJECT_PLANE); |
if (!obj) { |
DRM_DEBUG_KMS("Unknown plane ID %d\n", |
plane_req->plane_id); |
return -ENOENT; |
} |
plane = obj_to_plane(obj); |
|
if (plane_req->fb_id) { |
fb = drm_framebuffer_lookup(dev, plane_req->fb_id); |
if (!fb) { |
DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", |
plane_req->fb_id); |
return -ENOENT; |
} |
|
obj = drm_mode_object_find(dev, plane_req->crtc_id, |
DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
DRM_DEBUG_KMS("Unknown crtc ID %d\n", |
plane_req->crtc_id); |
return -ENOENT; |
} |
crtc = obj_to_crtc(obj); |
} |
|
/* |
* setplane_internal will take care of deref'ing either the old or new |
* framebuffer depending on success. |
*/ |
return setplane_internal(plane, crtc, fb, |
plane_req->crtc_x, plane_req->crtc_y, |
plane_req->crtc_w, plane_req->crtc_h, |
plane_req->src_x, plane_req->src_y, |
plane_req->src_w, plane_req->src_h); |
} |
#endif |
|
/** |
2049,6 → 2410,9 |
* |
* This is a little helper to wrap internal calls to the ->set_config driver |
* interface. The only thing it adds is correct refcounting dance. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_set_config_internal(struct drm_mode_set *set) |
{ |
2063,19 → 2427,19 |
* crtcs. Atomic modeset will have saner semantics ... |
*/ |
list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) |
tmp->old_fb = tmp->fb; |
tmp->old_fb = tmp->primary->fb; |
|
fb = set->fb; |
|
ret = crtc->funcs->set_config(set); |
if (ret == 0) { |
/* crtc->fb must be updated by ->set_config, enforces this. */ |
WARN_ON(fb != crtc->fb); |
crtc->primary->crtc = crtc; |
crtc->primary->fb = fb; |
} |
|
list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { |
if (tmp->fb) |
drm_framebuffer_reference(tmp->fb); |
if (tmp->primary->fb) |
drm_framebuffer_reference(tmp->primary->fb); |
// if (tmp->old_fb) |
// drm_framebuffer_unreference(tmp->old_fb); |
} |
2085,11 → 2449,16 |
EXPORT_SYMBOL(drm_mode_set_config_internal); |
|
#if 0 |
/* |
* Checks that the framebuffer is big enough for the CRTC viewport |
* (x, y, hdisplay, vdisplay) |
/** |
* drm_crtc_check_viewport - Checks that a framebuffer is big enough for the |
* CRTC viewport |
* @crtc: CRTC that framebuffer will be displayed on |
* @x: x panning |
* @y: y panning |
* @mode: mode that framebuffer will be displayed under |
* @fb: framebuffer to check size of |
*/ |
static int drm_crtc_check_viewport(const struct drm_crtc *crtc, |
int drm_crtc_check_viewport(const struct drm_crtc *crtc, |
int x, int y, |
const struct drm_display_mode *mode, |
const struct drm_framebuffer *fb) |
2123,6 → 2492,7 |
|
return 0; |
} |
EXPORT_SYMBOL(drm_crtc_check_viewport); |
|
/** |
* drm_mode_setcrtc - set CRTC configuration |
2134,7 → 2504,7 |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_setcrtc(struct drm_device *dev, void *data, |
2142,7 → 2512,6 |
{ |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_mode_crtc *crtc_req = data; |
struct drm_mode_object *obj; |
struct drm_crtc *crtc; |
struct drm_connector **connector_set = NULL, *connector; |
struct drm_framebuffer *fb = NULL; |
2160,14 → 2529,12 |
return -ERANGE; |
|
drm_modeset_lock_all(dev); |
obj = drm_mode_object_find(dev, crtc_req->crtc_id, |
DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
crtc = drm_crtc_find(dev, crtc_req->crtc_id); |
if (!crtc) { |
DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); |
ret = -ENOENT; |
goto out; |
} |
crtc = obj_to_crtc(obj); |
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); |
|
if (crtc_req->mode_valid) { |
2174,12 → 2541,12 |
/* If we have a mode we need a framebuffer. */ |
/* If we pass -1, set the mode with the currently bound fb */ |
if (crtc_req->fb_id == -1) { |
if (!crtc->fb) { |
if (!crtc->primary->fb) { |
DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); |
ret = -EINVAL; |
goto out; |
} |
fb = crtc->fb; |
fb = crtc->primary->fb; |
/* Make refcounting symmetric with the lookup path. */ |
drm_framebuffer_reference(fb); |
} else { |
2250,18 → 2617,16 |
goto out; |
} |
|
obj = drm_mode_object_find(dev, out_id, |
DRM_MODE_OBJECT_CONNECTOR); |
if (!obj) { |
connector = drm_connector_find(dev, out_id); |
if (!connector) { |
DRM_DEBUG_KMS("Connector id %d unknown\n", |
out_id); |
ret = -ENOENT; |
goto out; |
} |
connector = obj_to_connector(obj); |
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", |
connector->base.id, |
drm_get_connector_name(connector)); |
connector->name); |
|
connector_set[i] = connector; |
} |
2290,7 → 2655,6 |
struct drm_mode_cursor2 *req, |
struct drm_file *file_priv) |
{ |
struct drm_mode_object *obj; |
struct drm_crtc *crtc; |
int ret = 0; |
|
2300,14 → 2664,20 |
if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) |
return -EINVAL; |
|
obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
crtc = drm_crtc_find(dev, req->crtc_id); |
if (!crtc) { |
DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); |
return -ENOENT; |
} |
crtc = obj_to_crtc(obj); |
|
mutex_lock(&crtc->mutex); |
/* |
* If this crtc has a universal cursor plane, call that plane's update |
* handler rather than using legacy cursor handlers. |
*/ |
if (crtc->cursor) |
return drm_mode_cursor_universal(crtc, req, file_priv); |
|
drm_modeset_lock(&crtc->mutex, NULL); |
if (req->flags & DRM_MODE_CURSOR_BO) { |
if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { |
ret = -ENXIO; |
2331,11 → 2701,26 |
} |
} |
out: |
mutex_unlock(&crtc->mutex); |
drm_modeset_unlock(&crtc->mutex); |
|
return ret; |
|
} |
|
|
/** |
* drm_mode_cursor_ioctl - set CRTC's cursor configuration |
* @dev: drm device for the ioctl |
* @data: data pointer for the ioctl |
* @file_priv: drm file for the ioctl call |
* |
* Set the cursor configuration based on user request. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_cursor_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
2348,6 → 2733,21 |
return drm_mode_cursor_common(dev, &new_req, file_priv); |
} |
|
/** |
* drm_mode_cursor2_ioctl - set CRTC's cursor configuration |
* @dev: drm device for the ioctl |
* @data: data pointer for the ioctl |
* @file_priv: drm file for the ioctl call |
* |
* Set the cursor configuration based on user request. This implements the 2nd |
* version of the cursor ioctl, which allows userspace to additionally specify |
* the hotspot of the pointer. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_cursor2_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
2356,7 → 2756,14 |
} |
#endif |
|
/* Original addfb only supported RGB formats, so figure out which one */ |
/** |
* drm_mode_legacy_fb_format - compute drm fourcc code from legacy description |
* @bpp: bits per pixels |
* @depth: bit depth per pixel |
* |
* Computes a drm fourcc pixel format code for the given @bpp/@depth values. |
* Useful in fbdev emulation code, since that deals in those values. |
*/ |
uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) |
{ |
uint32_t fmt; |
2398,11 → 2805,12 |
* @data: data pointer for the ioctl |
* @file_priv: drm file for the ioctl call |
* |
* Add a new FB to the specified CRTC, given a user request. |
* Add a new FB to the specified CRTC, given a user request. This is the |
* original addfb ioclt which only supported RGB formats. |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_addfb(struct drm_device *dev, |
2569,54 → 2977,38 |
return 0; |
} |
|
/** |
* drm_mode_addfb2 - add an FB to the graphics configuration |
* @dev: drm device for the ioctl |
* @data: data pointer for the ioctl |
* @file_priv: drm file for the ioctl call |
* |
* 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. |
*/ |
int drm_mode_addfb2(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, |
struct drm_mode_fb_cmd2 *r, |
struct drm_file *file_priv) |
{ |
struct drm_mode_fb_cmd2 *r = data; |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_framebuffer *fb; |
int ret; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
if (r->flags & ~DRM_MODE_FB_INTERLACED) { |
DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); |
return -EINVAL; |
return ERR_PTR(-EINVAL); |
} |
|
if ((config->min_width > r->width) || (r->width > config->max_width)) { |
DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n", |
r->width, config->min_width, config->max_width); |
return -EINVAL; |
return ERR_PTR(-EINVAL); |
} |
if ((config->min_height > r->height) || (r->height > config->max_height)) { |
DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n", |
r->height, config->min_height, config->max_height); |
return -EINVAL; |
return ERR_PTR(-EINVAL); |
} |
|
ret = framebuffer_check(r); |
if (ret) |
return ret; |
return ERR_PTR(ret); |
|
fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); |
if (IS_ERR(fb)) { |
DRM_DEBUG_KMS("could not create framebuffer\n"); |
return PTR_ERR(fb); |
return fb; |
} |
|
mutex_lock(&file_priv->fbs_lock); |
2625,8 → 3017,37 |
DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); |
mutex_unlock(&file_priv->fbs_lock); |
|
return fb; |
} |
|
return ret; |
/** |
* drm_mode_addfb2 - add an FB to the graphics configuration |
* @dev: drm device for the ioctl |
* @data: data pointer for the ioctl |
* @file_priv: drm file for the ioctl call |
* |
* Add a new FB to the specified CRTC, given a user request with format. This is |
* the 2nd version of the addfb ioctl, which supports multi-planar framebuffers |
* and uses fourcc codes as pixel format specifiers. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_addfb2(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_framebuffer *fb; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
fb = add_framebuffer_internal(dev, data, file_priv); |
if (IS_ERR(fb)) |
return PTR_ERR(fb); |
|
return 0; |
} |
|
/** |
2639,7 → 3060,7 |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_rmfb(struct drm_device *dev, |
2693,7 → 3114,7 |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_getfb(struct drm_device *dev, |
2737,6 → 3158,25 |
return ret; |
} |
|
/** |
* drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB |
* @dev: drm device for the ioctl |
* @data: data pointer for the ioctl |
* @file_priv: drm file for the ioctl call |
* |
* Lookup the FB and flush out the damaged area supplied by userspace as a clip |
* rectangle list. Generic userspace which does frontbuffer rendering must call |
* this ioctl to flush out the changes on manual-update display outputs, e.g. |
* usb display-link, mipi manual update panels or edp panel self refresh modes. |
* |
* Modesetting drivers which always update the frontbuffer do not need to |
* implement the corresponding ->dirty framebuffer callback. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_dirtyfb_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
2814,7 → 3254,7 |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Returns: |
* Zero on success, errno on failure. |
*/ |
void drm_fb_release(struct drm_file *priv) |
2840,6 → 3280,20 |
#endif |
|
|
/** |
* drm_property_create - create a new property type |
* @dev: drm device |
* @flags: flags specifying the property type |
* @name: name of the property |
* @num_values: number of pre-defined values |
* |
* This creates a new generic drm property which can then be attached to a drm |
* object with drm_object_attach_property. The returned property object must be |
* freed with drm_property_destroy. |
* |
* Returns: |
* A pointer to the newly created property on success, NULL on failure. |
*/ |
struct drm_property *drm_property_create(struct drm_device *dev, int flags, |
const char *name, int num_values) |
{ |
2850,6 → 3304,8 |
if (!property) |
return NULL; |
|
property->dev = dev; |
|
if (num_values) { |
property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL); |
if (!property->values) |
2870,6 → 3326,9 |
} |
|
list_add_tail(&property->head, &dev->mode_config.property_list); |
|
WARN_ON(!drm_property_type_valid(property)); |
|
return property; |
fail: |
kfree(property->values); |
2878,6 → 3337,24 |
} |
EXPORT_SYMBOL(drm_property_create); |
|
/** |
* drm_property_create_enum - create a new enumeration property type |
* @dev: drm device |
* @flags: flags specifying the property type |
* @name: name of the property |
* @props: enumeration lists with property values |
* @num_values: number of pre-defined values |
* |
* This creates a new generic drm property which can then be attached to a drm |
* object with drm_object_attach_property. The returned property object must be |
* freed with drm_property_destroy. |
* |
* Userspace is only allowed to set one of the predefined values for enumeration |
* properties. |
* |
* Returns: |
* A pointer to the newly created property on success, NULL on failure. |
*/ |
struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, |
const char *name, |
const struct drm_prop_enum_list *props, |
2906,13 → 3383,33 |
} |
EXPORT_SYMBOL(drm_property_create_enum); |
|
/** |
* drm_property_create_bitmask - create a new bitmask property type |
* @dev: drm device |
* @flags: flags specifying the property type |
* @name: name of the property |
* @props: enumeration lists with property bitflags |
* @num_values: number of pre-defined values |
* |
* This creates a new generic drm property which can then be attached to a drm |
* object with drm_object_attach_property. The returned property object must be |
* freed with drm_property_destroy. |
* |
* Compared to plain enumeration properties userspace is allowed to set any |
* or'ed together combination of the predefined property bitflag values |
* |
* Returns: |
* A pointer to the newly created property on success, NULL on failure. |
*/ |
struct drm_property *drm_property_create_bitmask(struct drm_device *dev, |
int flags, const char *name, |
const struct drm_prop_enum_list *props, |
int num_values) |
int num_props, |
uint64_t supported_bits) |
{ |
struct drm_property *property; |
int i, ret; |
int i, ret, index = 0; |
int num_values = hweight64(supported_bits); |
|
flags |= DRM_MODE_PROP_BITMASK; |
|
2919,9 → 3416,16 |
property = drm_property_create(dev, flags, name, num_values); |
if (!property) |
return NULL; |
for (i = 0; i < num_props; i++) { |
if (!(supported_bits & (1ULL << props[i].type))) |
continue; |
|
for (i = 0; i < num_values; i++) { |
ret = drm_property_add_enum(property, i, |
if (WARN_ON(index >= num_values)) { |
drm_property_destroy(dev, property); |
return NULL; |
} |
|
ret = drm_property_add_enum(property, index++, |
props[i].type, |
props[i].name); |
if (ret) { |
2934,14 → 3438,12 |
} |
EXPORT_SYMBOL(drm_property_create_bitmask); |
|
struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, |
const char *name, |
static struct drm_property *property_create_range(struct drm_device *dev, |
int flags, const char *name, |
uint64_t min, uint64_t max) |
{ |
struct drm_property *property; |
|
flags |= DRM_MODE_PROP_RANGE; |
|
property = drm_property_create(dev, flags, name, 2); |
if (!property) |
return NULL; |
2951,14 → 3453,82 |
|
return property; |
} |
|
/** |
* drm_property_create_range - create a new ranged property type |
* @dev: drm device |
* @flags: flags specifying the property type |
* @name: name of the property |
* @min: minimum value of the property |
* @max: maximum value of the property |
* |
* This creates a new generic drm property which can then be attached to a drm |
* object with drm_object_attach_property. The returned property object must be |
* freed with drm_property_destroy. |
* |
* Userspace is allowed to set any interger value in the (min, max) range |
* inclusive. |
* |
* Returns: |
* A pointer to the newly created property on success, NULL on failure. |
*/ |
struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, |
const char *name, |
uint64_t min, uint64_t max) |
{ |
return property_create_range(dev, DRM_MODE_PROP_RANGE | flags, |
name, min, max); |
} |
EXPORT_SYMBOL(drm_property_create_range); |
|
struct drm_property *drm_property_create_signed_range(struct drm_device *dev, |
int flags, const char *name, |
int64_t min, int64_t max) |
{ |
return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags, |
name, I642U64(min), I642U64(max)); |
} |
EXPORT_SYMBOL(drm_property_create_signed_range); |
|
struct drm_property *drm_property_create_object(struct drm_device *dev, |
int flags, const char *name, uint32_t type) |
{ |
struct drm_property *property; |
|
flags |= DRM_MODE_PROP_OBJECT; |
|
property = drm_property_create(dev, flags, name, 1); |
if (!property) |
return NULL; |
|
property->values[0] = type; |
|
return property; |
} |
EXPORT_SYMBOL(drm_property_create_object); |
|
/** |
* drm_property_add_enum - add a possible value to an enumeration property |
* @property: enumeration property to change |
* @index: index of the new enumeration |
* @value: value of the new enumeration |
* @name: symbolic name of the new enumeration |
* |
* This functions adds enumerations to a property. |
* |
* It's use is deprecated, drivers should use one of the more specific helpers |
* to directly create the property with all enumerations already attached. |
* |
* Returns: |
* Zero on success, error code on failure. |
*/ |
int drm_property_add_enum(struct drm_property *property, int index, |
uint64_t value, const char *name) |
{ |
struct drm_property_enum *prop_enum; |
|
if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK))) |
if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) || |
drm_property_type_is(property, DRM_MODE_PROP_BITMASK))) |
return -EINVAL; |
|
/* |
2965,7 → 3535,8 |
* Bitmask enum properties have the additional constraint of values |
* from 0 to 63 |
*/ |
if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63)) |
if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) && |
(value > 63)) |
return -EINVAL; |
|
if (!list_empty(&property->enum_blob_list)) { |
2992,6 → 3563,14 |
} |
EXPORT_SYMBOL(drm_property_add_enum); |
|
/** |
* drm_property_destroy - destroy a drm property |
* @dev: drm device |
* @property: property to destry |
* |
* This function frees a property including any attached resources like |
* enumeration values. |
*/ |
void drm_property_destroy(struct drm_device *dev, struct drm_property *property) |
{ |
struct drm_property_enum *prop_enum, *pt; |
3009,6 → 3588,16 |
} |
EXPORT_SYMBOL(drm_property_destroy); |
|
/** |
* drm_object_attach_property - attach a property to a modeset object |
* @obj: drm modeset object |
* @property: property to attach |
* @init_val: initial value of the property |
* |
* This attaches the given property to the modeset object with the given initial |
* value. Currently this function cannot fail since the properties are stored in |
* a statically sized array. |
*/ |
void drm_object_attach_property(struct drm_mode_object *obj, |
struct drm_property *property, |
uint64_t init_val) |
3029,6 → 3618,19 |
} |
EXPORT_SYMBOL(drm_object_attach_property); |
|
/** |
* drm_object_property_set_value - set the value of a property |
* @obj: drm mode object to set property value for |
* @property: property to set |
* @val: value the property should be set to |
* |
* This functions sets a given property on a given object. This function only |
* changes the software state of the property, it does not call into the |
* driver's ->set_property callback. |
* |
* Returns: |
* Zero on success, error code on failure. |
*/ |
int drm_object_property_set_value(struct drm_mode_object *obj, |
struct drm_property *property, uint64_t val) |
{ |
3045,6 → 3647,20 |
} |
EXPORT_SYMBOL(drm_object_property_set_value); |
|
/** |
* drm_object_property_get_value - retrieve the value of a property |
* @obj: drm mode object to get property value from |
* @property: property to retrieve |
* @val: storage for the property value |
* |
* This function retrieves the softare state of the given property for the given |
* property. Since there is no driver callback to retrieve the current property |
* value this might be out of sync with the hardware, depending upon the driver |
* and property. |
* |
* Returns: |
* Zero on success, error code on failure. |
*/ |
int drm_object_property_get_value(struct drm_mode_object *obj, |
struct drm_property *property, uint64_t *val) |
{ |
3062,10 → 3678,22 |
EXPORT_SYMBOL(drm_object_property_get_value); |
|
#if 0 |
/** |
* drm_mode_getproperty_ioctl - get the current value of a connector's property |
* @dev: DRM device |
* @data: ioctl data |
* @file_priv: DRM file info |
* |
* This function retrieves the current value for an connectors's property. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_getproperty_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_object *obj; |
struct drm_mode_get_property *out_resp = data; |
struct drm_property *property; |
int enum_count = 0; |
3084,17 → 3712,17 |
return -EINVAL; |
|
drm_modeset_lock_all(dev); |
obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); |
if (!obj) { |
property = drm_property_find(dev, out_resp->prop_id); |
if (!property) { |
ret = -ENOENT; |
goto done; |
} |
property = obj_to_property(obj); |
|
if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { |
if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || |
drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { |
list_for_each_entry(prop_enum, &property->enum_blob_list, head) |
enum_count++; |
} else if (property->flags & DRM_MODE_PROP_BLOB) { |
} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { |
list_for_each_entry(prop_blob, &property->enum_blob_list, head) |
blob_count++; |
} |
3116,7 → 3744,8 |
} |
out_resp->count_values = value_count; |
|
if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { |
if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || |
drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { |
if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { |
copied = 0; |
enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; |
3138,7 → 3767,7 |
out_resp->count_enum_blobs = enum_count; |
} |
|
if (property->flags & DRM_MODE_PROP_BLOB) { |
if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { |
if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { |
copied = 0; |
blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr; |
3202,10 → 3831,23 |
} |
|
#if 0 |
/** |
* drm_mode_getblob_ioctl - get the contents of a blob property value |
* @dev: DRM device |
* @data: ioctl data |
* @file_priv: DRM file info |
* |
* This function retrieves the contents of a blob property. The value stored in |
* an object's blob property is just a normal modeset object id. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_getblob_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_object *obj; |
struct drm_mode_get_blob *out_resp = data; |
struct drm_property_blob *blob; |
int ret = 0; |
3215,12 → 3857,11 |
return -EINVAL; |
|
drm_modeset_lock_all(dev); |
obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); |
if (!obj) { |
blob = drm_property_blob_find(dev, out_resp->blob_id); |
if (!blob) { |
ret = -ENOENT; |
goto done; |
} |
blob = obj_to_blob(obj); |
|
if (out_resp->length == blob->length) { |
blob_ptr = (void __user *)(unsigned long)out_resp->data; |
3237,6 → 3878,36 |
} |
#endif |
|
int drm_mode_connector_set_path_property(struct drm_connector *connector, |
char *path) |
{ |
struct drm_device *dev = connector->dev; |
int ret, size; |
size = strlen(path) + 1; |
|
connector->path_blob_ptr = drm_property_create_blob(connector->dev, |
size, path); |
if (!connector->path_blob_ptr) |
return -EINVAL; |
|
ret = drm_object_property_set_value(&connector->base, |
dev->mode_config.path_property, |
connector->path_blob_ptr->base.id); |
return ret; |
} |
EXPORT_SYMBOL(drm_mode_connector_set_path_property); |
|
/** |
* drm_mode_connector_update_edid_property - update the edid property of a connector |
* @connector: drm connector |
* @edid: new value of the edid property |
* |
* This function creates a new blob modeset object and assigns its id to the |
* connector's edid property. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_connector_update_edid_property(struct drm_connector *connector, |
struct edid *edid) |
{ |
3243,6 → 3914,10 |
struct drm_device *dev = connector->dev; |
int ret, size; |
|
/* ignore requests to set edid when overridden */ |
if (connector->override_edid) |
return 0; |
|
if (connector->edid_blob_ptr) |
drm_property_destroy_blob(dev, connector->edid_blob_ptr); |
|
3320,6 → 3995,21 |
return ret; |
} |
|
/** |
* drm_mode_getproperty_ioctl - get the current value of a object's property |
* @dev: DRM device |
* @data: ioctl data |
* @file_priv: DRM file info |
* |
* This function retrieves the current value for an object's property. Compared |
* to the connector specific ioctl this one is extended to also work on crtc and |
* plane objects. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
3376,6 → 4066,22 |
return ret; |
} |
|
/** |
* drm_mode_obj_set_property_ioctl - set the current value of an object's property |
* @dev: DRM device |
* @data: ioctl data |
* @file_priv: DRM file info |
* |
* This function sets the current value for an object's property. It also calls |
* into a driver's ->set_property callback to update the hardware state. |
* Compared to the connector specific ioctl this one is extended to also work on |
* crtc and plane objects. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
3436,6 → 4142,18 |
} |
#endif |
|
/** |
* drm_mode_connector_attach_encoder - attach a connector to an encoder |
* @connector: connector to attach |
* @encoder: encoder to attach @connector to |
* |
* This function links up a connector to an encoder. Note that the routing |
* restrictions between encoders and crtcs are exposed to userspace through the |
* possible_clones and possible_crtcs bitmasks. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_connector_attach_encoder(struct drm_connector *connector, |
struct drm_encoder *encoder) |
{ |
3451,21 → 4169,18 |
} |
EXPORT_SYMBOL(drm_mode_connector_attach_encoder); |
|
void drm_mode_connector_detach_encoder(struct drm_connector *connector, |
struct drm_encoder *encoder) |
{ |
int i; |
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { |
if (connector->encoder_ids[i] == encoder->base.id) { |
connector->encoder_ids[i] = 0; |
if (connector->encoder == encoder) |
connector->encoder = NULL; |
break; |
} |
} |
} |
EXPORT_SYMBOL(drm_mode_connector_detach_encoder); |
|
/** |
* drm_mode_crtc_set_gamma_size - set the gamma table size |
* @crtc: CRTC to set the gamma table size for |
* @gamma_size: size of the gamma table |
* |
* Drivers which support gamma tables should set this to the supported gamma |
* table size when initializing the CRTC. Currently the drm core only supports a |
* fixed gamma table size. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, |
int gamma_size) |
{ |
3482,11 → 4197,24 |
EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); |
|
#if 0 |
/** |
* drm_mode_gamma_set_ioctl - set the gamma table |
* @dev: DRM device |
* @data: ioctl data |
* @file_priv: DRM file info |
* |
* Set the gamma table of a CRTC to the one passed in by the user. Userspace can |
* inquire the required gamma table size through drm_mode_gamma_get_ioctl. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_gamma_set_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_crtc_lut *crtc_lut = data; |
struct drm_mode_object *obj; |
struct drm_crtc *crtc; |
void *r_base, *g_base, *b_base; |
int size; |
3496,12 → 4224,11 |
return -EINVAL; |
|
drm_modeset_lock_all(dev); |
obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
crtc = drm_crtc_find(dev, crtc_lut->crtc_id); |
if (!crtc) { |
ret = -ENOENT; |
goto out; |
} |
crtc = obj_to_crtc(obj); |
|
if (crtc->funcs->gamma_set == NULL) { |
ret = -ENOSYS; |
3541,11 → 4268,25 |
|
} |
|
/** |
* drm_mode_gamma_get_ioctl - get the gamma table |
* @dev: DRM device |
* @data: ioctl data |
* @file_priv: DRM file info |
* |
* Copy the current gamma table into the storage provided. This also provides |
* the gamma table size the driver expects, which can be used to size the |
* allocated storage. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_gamma_get_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_crtc_lut *crtc_lut = data; |
struct drm_mode_object *obj; |
struct drm_crtc *crtc; |
void *r_base, *g_base, *b_base; |
int size; |
3555,12 → 4296,11 |
return -EINVAL; |
|
drm_modeset_lock_all(dev); |
obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
crtc = drm_crtc_find(dev, crtc_lut->crtc_id); |
if (!crtc) { |
ret = -ENOENT; |
goto out; |
} |
crtc = obj_to_crtc(obj); |
|
/* memcpy into gamma store */ |
if (crtc_lut->gamma_size != crtc->gamma_size) { |
3594,6 → 4334,14 |
#endif |
|
|
/** |
* drm_mode_config_reset - call ->reset callbacks |
* @dev: drm device |
* |
* This functions calls all the crtc's, encoder's and connector's ->reset |
* callback. Drivers can use this in e.g. their driver load or resume code to |
* reset hardware and software state. |
*/ |
void drm_mode_config_reset(struct drm_device *dev) |
{ |
struct drm_crtc *crtc; |
3690,7 → 4438,7 |
* drm_format_num_planes - get the number of planes for format |
* @format: pixel format (DRM_FORMAT_*) |
* |
* RETURNS: |
* Returns: |
* The number of planes used by the specified pixel format. |
*/ |
int drm_format_num_planes(uint32_t format) |
3725,7 → 4473,7 |
* @format: pixel format (DRM_FORMAT_*) |
* @plane: plane index |
* |
* RETURNS: |
* Returns: |
* The bytes per pixel value for the specified plane. |
*/ |
int drm_format_plane_cpp(uint32_t format, int plane) |
3771,7 → 4519,7 |
* drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor |
* @format: pixel format (DRM_FORMAT_*) |
* |
* RETURNS: |
* Returns: |
* The horizontal chroma subsampling factor for the |
* specified pixel format. |
*/ |
3806,7 → 4554,7 |
* drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor |
* @format: pixel format (DRM_FORMAT_*) |
* |
* RETURNS: |
* Returns: |
* The vertical chroma subsampling factor for the |
* specified pixel format. |
*/ |
3842,6 → 4590,7 |
void drm_mode_config_init(struct drm_device *dev) |
{ |
mutex_init(&dev->mode_config.mutex); |
drm_modeset_lock_init(&dev->mode_config.connection_mutex); |
mutex_init(&dev->mode_config.idr_mutex); |
mutex_init(&dev->mode_config.fb_lock); |
INIT_LIST_HEAD(&dev->mode_config.fb_list); |
3856,6 → 4605,7 |
|
drm_modeset_lock_all(dev); |
drm_mode_create_standard_connector_properties(dev); |
drm_mode_create_standard_plane_properties(dev); |
drm_modeset_unlock_all(dev); |
|
/* Just to be sure */ |
3863,6 → 4613,21 |
dev->mode_config.num_connector = 0; |
dev->mode_config.num_crtc = 0; |
dev->mode_config.num_encoder = 0; |
dev->mode_config.num_overlay_plane = 0; |
dev->mode_config.num_total_plane = 0; |
} |
EXPORT_SYMBOL(drm_mode_config_init); |
|
/** |
* 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. |
* |
* Note that since this /should/ happen single-threaded at driver/device |
* 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 |
*/ |