40,102 → 40,12 |
#include <drm/drm_modeset_lock.h> |
|
#include "drm_crtc_internal.h" |
#include "drm_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. Locks must be dropped with |
* drm_modeset_unlock_all. |
*/ |
void drm_modeset_lock_all(struct drm_device *dev) |
{ |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_modeset_acquire_ctx *ctx; |
int ret; |
|
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); |
if (WARN_ON(!ctx)) |
return; |
|
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_mode_config *config = &dev->mode_config; |
struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; |
|
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); |
|
/** |
* 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) |
{ |
struct drm_crtc *crtc; |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) |
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); |
|
/* Avoid boilerplate. I'm tired of typing. */ |
#define DRM_ENUM_NAME_FN(fnname, list) \ |
const char *fnname(int val) \ |
511,9 → 421,6 |
if (ret) |
goto out; |
|
/* Grab the idr reference. */ |
drm_framebuffer_reference(fb); |
|
dev->mode_config.num_fb++; |
list_add(&fb->head, &dev->mode_config.fb_list); |
out: |
523,10 → 430,34 |
} |
EXPORT_SYMBOL(drm_framebuffer_init); |
|
/* dev->mode_config.fb_lock must be held! */ |
static void __drm_framebuffer_unregister(struct drm_device *dev, |
struct drm_framebuffer *fb) |
{ |
mutex_lock(&dev->mode_config.idr_mutex); |
idr_remove(&dev->mode_config.crtc_idr, fb->base.id); |
mutex_unlock(&dev->mode_config.idr_mutex); |
|
fb->base.id = 0; |
} |
|
static void drm_framebuffer_free(struct kref *kref) |
{ |
struct drm_framebuffer *fb = |
container_of(kref, struct drm_framebuffer, refcount); |
struct drm_device *dev = fb->dev; |
|
/* |
* The lookup idr holds a weak reference, which has not necessarily been |
* removed at this point. Check for that. |
*/ |
mutex_lock(&dev->mode_config.fb_lock); |
if (fb->base.id) { |
/* Mark fb as reaped and drop idr ref. */ |
__drm_framebuffer_unregister(dev, fb); |
} |
mutex_unlock(&dev->mode_config.fb_lock); |
|
fb->funcs->destroy(fb); |
} |
|
608,19 → 539,6 |
kref_put(&fb->refcount, drm_framebuffer_free_bug); |
} |
|
/* dev->mode_config.fb_lock must be held! */ |
static void __drm_framebuffer_unregister(struct drm_device *dev, |
struct drm_framebuffer *fb) |
{ |
mutex_lock(&dev->mode_config.idr_mutex); |
idr_remove(&dev->mode_config.crtc_idr, fb->base.id); |
mutex_unlock(&dev->mode_config.idr_mutex); |
|
fb->base.id = 0; |
|
__drm_framebuffer_unreference(fb); |
} |
|
/** |
* drm_framebuffer_unregister_private - unregister a private fb from the lookup idr |
* @fb: fb to unregister |
760,14 → 678,10 |
crtc->funcs = funcs; |
crtc->invert_dimensions = false; |
|
drm_modeset_lock_all(dev); |
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) |
goto out; |
return ret; |
|
crtc->base.properties = &crtc->properties; |
|
781,10 → 695,7 |
if (cursor) |
cursor->possible_crtcs = 1 << drm_crtc_index(crtc); |
|
out: |
drm_modeset_unlock_all(dev); |
|
return ret; |
return 0; |
} |
EXPORT_SYMBOL(drm_crtc_init_with_planes); |
|
808,6 → 719,12 |
drm_mode_object_put(dev, &crtc->base); |
list_del(&crtc->head); |
dev->mode_config.num_crtc--; |
|
WARN_ON(crtc->state && !crtc->funcs->atomic_destroy_state); |
if (crtc->state && crtc->funcs->atomic_destroy_state) |
crtc->funcs->atomic_destroy_state(crtc, crtc->state); |
|
memset(crtc, 0, sizeof(*crtc)); |
} |
EXPORT_SYMBOL(drm_crtc_cleanup); |
|
949,10 → 866,43 |
connector->name = NULL; |
list_del(&connector->head); |
dev->mode_config.num_connector--; |
|
WARN_ON(connector->state && !connector->funcs->atomic_destroy_state); |
if (connector->state && connector->funcs->atomic_destroy_state) |
connector->funcs->atomic_destroy_state(connector, |
connector->state); |
|
memset(connector, 0, sizeof(*connector)); |
} |
EXPORT_SYMBOL(drm_connector_cleanup); |
|
/** |
* drm_connector_index - find the index of a registered connector |
* @connector: connector to find index for |
* |
* Given a registered connector, return the index of that connector within a DRM |
* device's list of connectors. |
*/ |
unsigned int drm_connector_index(struct drm_connector *connector) |
{ |
unsigned int index = 0; |
struct drm_connector *tmp; |
struct drm_mode_config *config = &connector->dev->mode_config; |
|
WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); |
|
list_for_each_entry(tmp, &connector->dev->mode_config.connector_list, head) { |
if (tmp == connector) |
return index; |
|
index++; |
} |
|
BUG(); |
} |
EXPORT_SYMBOL(drm_connector_index); |
|
/** |
* drm_connector_register - register a connector |
* @connector: the connector to register |
* |
1052,6 → 1002,8 |
list_del(&bridge->head); |
dev->mode_config.num_bridge--; |
drm_modeset_unlock_all(dev); |
|
memset(bridge, 0, sizeof(*bridge)); |
} |
EXPORT_SYMBOL(drm_bridge_cleanup); |
|
1118,10 → 1070,11 |
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); |
|
memset(encoder, 0, sizeof(*encoder)); |
} |
EXPORT_SYMBOL(drm_encoder_cleanup); |
|
1148,12 → 1101,12 |
{ |
int ret; |
|
drm_modeset_lock_all(dev); |
|
ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); |
if (ret) |
goto out; |
return ret; |
|
drm_modeset_lock_init(&plane->mutex); |
|
plane->base.properties = &plane->properties; |
plane->dev = dev; |
plane->funcs = funcs; |
1162,8 → 1115,7 |
if (!plane->format_types) { |
DRM_DEBUG_KMS("out of memory when allocating plane\n"); |
drm_mode_object_put(dev, &plane->base); |
ret = -ENOMEM; |
goto out; |
return -ENOMEM; |
} |
|
memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); |
1180,10 → 1132,7 |
dev->mode_config.plane_type_property, |
plane->type); |
|
out: |
drm_modeset_unlock_all(dev); |
|
return ret; |
return 0; |
} |
EXPORT_SYMBOL(drm_universal_plane_init); |
|
1241,10 → 1190,39 |
if (plane->type == DRM_PLANE_TYPE_OVERLAY) |
dev->mode_config.num_overlay_plane--; |
drm_modeset_unlock_all(dev); |
|
WARN_ON(plane->state && !plane->funcs->atomic_destroy_state); |
if (plane->state && plane->funcs->atomic_destroy_state) |
plane->funcs->atomic_destroy_state(plane, plane->state); |
|
memset(plane, 0, sizeof(*plane)); |
} |
EXPORT_SYMBOL(drm_plane_cleanup); |
|
/** |
* drm_plane_index - find the index of a registered plane |
* @plane: plane to find index for |
* |
* Given a registered plane, return the index of that CRTC within a DRM |
* device's list of planes. |
*/ |
unsigned int drm_plane_index(struct drm_plane *plane) |
{ |
unsigned int index = 0; |
struct drm_plane *tmp; |
|
list_for_each_entry(tmp, &plane->dev->mode_config.plane_list, head) { |
if (tmp == plane) |
return index; |
|
index++; |
} |
|
BUG(); |
} |
EXPORT_SYMBOL(drm_plane_index); |
|
/** |
* drm_plane_force_disable - Forcibly disable a plane |
* @plane: plane to disable |
* |
1255,19 → 1233,21 |
*/ |
void drm_plane_force_disable(struct drm_plane *plane) |
{ |
struct drm_framebuffer *old_fb = plane->fb; |
int ret; |
|
if (!old_fb) |
if (!plane->fb) |
return; |
|
plane->old_fb = plane->fb; |
ret = plane->funcs->disable_plane(plane); |
if (ret) { |
DRM_ERROR("failed to disable plane with busy fb\n"); |
plane->old_fb = NULL; |
return; |
} |
/* disconnect the plane from the fb and crtc: */ |
__drm_framebuffer_unreference(old_fb); |
__drm_framebuffer_unreference(plane->old_fb); |
plane->old_fb = NULL; |
plane->fb = NULL; |
plane->crtc = NULL; |
} |
1298,6 → 1278,11 |
"PATH", 0); |
dev->mode_config.path_property = dev_path; |
|
dev->mode_config.tile_property = drm_property_create(dev, |
DRM_MODE_PROP_BLOB | |
DRM_MODE_PROP_IMMUTABLE, |
"TILE", 0); |
|
return 0; |
} |
|
1358,12 → 1343,13 |
* responsible for allocating a list of format names and passing them to |
* this routine. |
*/ |
int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes, |
int drm_mode_create_tv_properties(struct drm_device *dev, |
unsigned int num_modes, |
char *modes[]) |
{ |
struct drm_property *tv_selector; |
struct drm_property *tv_subconnector; |
int i; |
unsigned int i; |
|
if (dev->mode_config.tv_select_subconnector_property) |
return 0; |
1461,7 → 1447,7 |
* connectors. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_create_aspect_ratio_property(struct drm_device *dev) |
{ |
1505,6 → 1491,30 |
} |
EXPORT_SYMBOL(drm_mode_create_dirty_info_property); |
|
/** |
* drm_mode_create_suggested_offset_properties - create suggests offset properties |
* @dev: DRM device |
* |
* Create the the suggested x/y offset property for connectors. |
*/ |
int drm_mode_create_suggested_offset_properties(struct drm_device *dev) |
{ |
if (dev->mode_config.suggested_x_property && dev->mode_config.suggested_y_property) |
return 0; |
|
dev->mode_config.suggested_x_property = |
drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested X", 0, 0xffffffff); |
|
dev->mode_config.suggested_y_property = |
drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested Y", 0, 0xffffffff); |
|
if (dev->mode_config.suggested_x_property == NULL || |
dev->mode_config.suggested_y_property == NULL) |
return -ENOMEM; |
return 0; |
} |
EXPORT_SYMBOL(drm_mode_create_suggested_offset_properties); |
|
static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group) |
{ |
uint32_t total_objects = 0; |
1621,7 → 1631,7 |
* the caller. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
static int drm_crtc_convert_umode(struct drm_display_mode *out, |
const struct drm_mode_modeinfo *in) |
1666,7 → 1676,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_getresources(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
1717,7 → 1727,9 |
card_res->count_fbs = fb_count; |
mutex_unlock(&file_priv->fbs_lock); |
|
drm_modeset_lock_all(dev); |
/* mode_config.mutex protects the connector list against e.g. DP MST |
* connector hot-adding. CRTC/Plane lists are invariant. */ |
mutex_lock(&dev->mode_config.mutex); |
if (!drm_is_primary_client(file_priv)) { |
|
mode_group = NULL; |
1837,7 → 1849,7 |
card_res->count_connectors, card_res->count_encoders); |
|
out: |
drm_modeset_unlock_all(dev); |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
|
1852,7 → 1864,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_getcrtc(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
1859,19 → 1871,15 |
{ |
struct drm_mode_crtc *crtc_resp = data; |
struct drm_crtc *crtc; |
int ret = 0; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
drm_modeset_lock_all(dev); |
|
crtc = drm_crtc_find(dev, crtc_resp->crtc_id); |
if (!crtc) { |
ret = -ENOENT; |
goto out; |
} |
if (!crtc) |
return -ENOENT; |
|
drm_modeset_lock_crtc(crtc, crtc->primary); |
crtc_resp->x = crtc->x; |
crtc_resp->y = crtc->y; |
crtc_resp->gamma_size = crtc->gamma_size; |
1888,10 → 1896,9 |
} else { |
crtc_resp->mode_valid = 0; |
} |
drm_modeset_unlock_crtc(crtc); |
|
out: |
drm_modeset_unlock_all(dev); |
return ret; |
return 0; |
} |
|
static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, |
1907,6 → 1914,15 |
return true; |
} |
|
static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector) |
{ |
/* For atomic drivers only state objects are synchronously updated and |
* protected by modeset locks, so check those first. */ |
if (connector->state) |
return connector->state->best_encoder; |
return connector->encoder; |
} |
|
/** |
* drm_mode_getconnector - get connector configuration |
* @dev: drm device for the ioctl |
1918,7 → 1934,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_getconnector(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
1925,6 → 1941,7 |
{ |
struct drm_mode_get_connector *out_resp = data; |
struct drm_connector *connector; |
struct drm_encoder *encoder; |
struct drm_display_mode *mode; |
int mode_count = 0; |
int props_count = 0; |
1980,8 → 1997,10 |
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; |
|
encoder = drm_connector_get_encoder(connector); |
if (encoder) |
out_resp->encoder_id = encoder->base.id; |
else |
out_resp->encoder_id = 0; |
drm_modeset_unlock(&dev->mode_config.connection_mutex); |
2051,6 → 2070,33 |
return ret; |
} |
|
static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) |
{ |
struct drm_connector *connector; |
struct drm_device *dev = encoder->dev; |
bool uses_atomic = false; |
|
/* For atomic drivers only state objects are synchronously updated and |
* protected by modeset locks, so check those first. */ |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
if (!connector->state) |
continue; |
|
uses_atomic = true; |
|
if (connector->state->best_encoder != encoder) |
continue; |
|
return connector->state->crtc; |
} |
|
/* Don't return stale data (e.g. pending async disable). */ |
if (uses_atomic) |
return NULL; |
|
return encoder->crtc; |
} |
|
/** |
* drm_mode_getencoder - get encoder configuration |
* @dev: drm device for the ioctl |
2062,7 → 2108,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_getencoder(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
2069,30 → 2115,31 |
{ |
struct drm_mode_get_encoder *enc_resp = data; |
struct drm_encoder *encoder; |
int ret = 0; |
struct drm_crtc *crtc; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
drm_modeset_lock_all(dev); |
encoder = drm_encoder_find(dev, enc_resp->encoder_id); |
if (!encoder) { |
ret = -ENOENT; |
goto out; |
} |
if (!encoder) |
return -ENOENT; |
|
if (encoder->crtc) |
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); |
crtc = drm_encoder_get_crtc(encoder); |
if (crtc) |
enc_resp->crtc_id = crtc->base.id; |
else if (encoder->crtc) |
enc_resp->crtc_id = encoder->crtc->base.id; |
else |
enc_resp->crtc_id = 0; |
drm_modeset_unlock(&dev->mode_config.connection_mutex); |
|
enc_resp->encoder_type = encoder->encoder_type; |
enc_resp->encoder_id = encoder->base.id; |
enc_resp->possible_crtcs = encoder->possible_crtcs; |
enc_resp->possible_clones = encoder->possible_clones; |
|
out: |
drm_modeset_unlock_all(dev); |
return ret; |
return 0; |
} |
|
/** |
2106,7 → 2153,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_getplane_res(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
2115,13 → 2162,12 |
struct drm_mode_config *config; |
struct drm_plane *plane; |
uint32_t __user *plane_ptr; |
int copied = 0, ret = 0; |
int copied = 0; |
unsigned num_planes; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
drm_modeset_lock_all(dev); |
config = &dev->mode_config; |
|
if (file_priv->universal_planes) |
2137,6 → 2183,7 |
(plane_resp->count_planes >= num_planes)) { |
plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; |
|
/* Plane lists are invariant, no locking needed. */ |
list_for_each_entry(plane, &config->plane_list, head) { |
/* |
* Unless userspace set the 'universal planes' |
2146,18 → 2193,14 |
!file_priv->universal_planes) |
continue; |
|
if (put_user(plane->base.id, plane_ptr + copied)) { |
ret = -EFAULT; |
goto out; |
} |
if (put_user(plane->base.id, plane_ptr + copied)) |
return -EFAULT; |
copied++; |
} |
} |
plane_resp->count_planes = num_planes; |
|
out: |
drm_modeset_unlock_all(dev); |
return ret; |
return 0; |
} |
|
/** |
2171,7 → 2214,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_getplane(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
2179,18 → 2222,15 |
struct drm_mode_get_plane *plane_resp = data; |
struct drm_plane *plane; |
uint32_t __user *format_ptr; |
int ret = 0; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
|
drm_modeset_lock_all(dev); |
plane = drm_plane_find(dev, plane_resp->plane_id); |
if (!plane) { |
ret = -ENOENT; |
goto out; |
} |
if (!plane) |
return -ENOENT; |
|
drm_modeset_lock(&plane->mutex, NULL); |
if (plane->crtc) |
plane_resp->crtc_id = plane->crtc->base.id; |
else |
2200,6 → 2240,7 |
plane_resp->fb_id = plane->fb->base.id; |
else |
plane_resp->fb_id = 0; |
drm_modeset_unlock(&plane->mutex); |
|
plane_resp->plane_id = plane->base.id; |
plane_resp->possible_crtcs = plane->possible_crtcs; |
2215,15 → 2256,12 |
if (copy_to_user(format_ptr, |
plane->format_types, |
sizeof(uint32_t) * plane->format_count)) { |
ret = -EFAULT; |
goto out; |
return -EFAULT; |
} |
} |
plane_resp->count_format_types = plane->format_count; |
|
out: |
drm_modeset_unlock_all(dev); |
return ret; |
return 0; |
} |
|
/* |
2235,7 → 2273,7 |
* |
* src_{x,y,w,h} are provided in 16.16 fixed point format |
*/ |
static int setplane_internal(struct drm_plane *plane, |
static int __setplane_internal(struct drm_plane *plane, |
struct drm_crtc *crtc, |
struct drm_framebuffer *fb, |
int32_t crtc_x, int32_t crtc_y, |
2244,24 → 2282,20 |
uint32_t src_x, uint32_t src_y, |
uint32_t src_w, uint32_t src_h) |
{ |
struct drm_device *dev = plane->dev; |
struct drm_framebuffer *old_fb = NULL; |
int ret = 0; |
unsigned int fb_width, fb_height; |
int i; |
unsigned int i; |
|
/* No fb means shut it down */ |
if (!fb) { |
drm_modeset_lock_all(dev); |
old_fb = plane->fb; |
plane->old_fb = plane->fb; |
ret = plane->funcs->disable_plane(plane); |
if (!ret) { |
plane->crtc = NULL; |
plane->fb = NULL; |
} else { |
old_fb = NULL; |
plane->old_fb = NULL; |
} |
drm_modeset_unlock_all(dev); |
goto out; |
} |
|
2301,8 → 2335,7 |
goto out; |
} |
|
drm_modeset_lock_all(dev); |
old_fb = plane->fb; |
plane->old_fb = plane->fb; |
ret = plane->funcs->update_plane(plane, crtc, fb, |
crtc_x, crtc_y, crtc_w, crtc_h, |
src_x, src_y, src_w, src_h); |
2311,18 → 2344,37 |
plane->fb = fb; |
fb = NULL; |
} else { |
old_fb = NULL; |
plane->old_fb = NULL; |
} |
drm_modeset_unlock_all(dev); |
|
out: |
if (fb) |
drm_framebuffer_unreference(fb); |
if (old_fb) |
drm_framebuffer_unreference(old_fb); |
if (plane->old_fb) |
drm_framebuffer_unreference(plane->old_fb); |
plane->old_fb = NULL; |
|
return ret; |
} |
|
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) |
{ |
int ret; |
|
drm_modeset_lock_all(plane->dev); |
ret = __setplane_internal(plane, crtc, fb, |
crtc_x, crtc_y, crtc_w, crtc_h, |
src_x, src_y, src_w, src_h); |
drm_modeset_unlock_all(plane->dev); |
|
return ret; |
} |
|
/** |
2336,13 → 2388,12 |
* valid crtc). |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative 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; |
2365,14 → 2416,12 |
* 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) { |
plane = drm_plane_find(dev, plane_req->plane_id); |
if (!plane) { |
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); |
2382,14 → 2431,12 |
return -ENOENT; |
} |
|
obj = drm_mode_object_find(dev, plane_req->crtc_id, |
DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
crtc = drm_crtc_find(dev, plane_req->crtc_id); |
if (!crtc) { |
DRM_DEBUG_KMS("Unknown crtc ID %d\n", |
plane_req->crtc_id); |
return -ENOENT; |
} |
crtc = obj_to_crtc(obj); |
} |
|
/* |
2412,7 → 2459,7 |
* interface. The only thing it adds is correct refcounting dance. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_set_config_internal(struct drm_mode_set *set) |
{ |
2427,7 → 2474,7 |
* crtcs. Atomic modeset will have saner semantics ... |
*/ |
list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) |
tmp->old_fb = tmp->primary->fb; |
tmp->primary->old_fb = tmp->primary->fb; |
|
fb = set->fb; |
|
2505,7 → 2552,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_setcrtc(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
2674,10 → 2721,12 |
* 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(crtc, crtc->cursor); |
if (crtc->cursor) { |
ret = drm_mode_cursor_universal(crtc, req, file_priv); |
goto out; |
} |
|
drm_modeset_lock(&crtc->mutex, NULL); |
if (req->flags & DRM_MODE_CURSOR_BO) { |
if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { |
ret = -ENXIO; |
2701,7 → 2750,7 |
} |
} |
out: |
drm_modeset_unlock(&crtc->mutex); |
drm_modeset_unlock_crtc(crtc); |
|
return ret; |
|
2719,7 → 2768,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_cursor_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
2746,7 → 2795,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_cursor2_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
2806,12 → 2855,12 |
* @file_priv: drm file for the ioctl call |
* |
* Add a new FB to the specified CRTC, given a user request. This is the |
* original addfb ioclt which only supported RGB formats. |
* original addfb ioctl which only supported RGB formats. |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_addfb(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
2818,11 → 2867,9 |
{ |
struct drm_mode_fb_cmd *or = data; |
struct drm_mode_fb_cmd2 r = {}; |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_framebuffer *fb; |
int ret = 0; |
int ret; |
|
/* Use new struct with format internally */ |
/* convert to new format and call new ioctl */ |
r.fb_id = or->fb_id; |
r.width = or->width; |
r.height = or->height; |
2830,30 → 2877,15 |
r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); |
r.handles[0] = or->handle; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
ret = drm_mode_addfb2(dev, &r, file_priv); |
if (ret) |
return ret; |
|
if ((config->min_width > r.width) || (r.width > config->max_width)) |
return -EINVAL; |
or->fb_id = r.fb_id; |
|
if ((config->min_height > r.height) || (r.height > config->max_height)) |
return -EINVAL; |
|
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 0; |
} |
|
mutex_lock(&file_priv->fbs_lock); |
or->fb_id = fb->base.id; |
list_add(&fb->filp_head, &file_priv->fbs); |
DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); |
mutex_unlock(&file_priv->fbs_lock); |
|
return ret; |
} |
|
static int format_check(const struct drm_mode_fb_cmd2 *r) |
{ |
uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN; |
2943,7 → 2975,7 |
num_planes = drm_format_num_planes(r->pixel_format); |
|
if (r->width == 0 || r->width % hsub) { |
DRM_DEBUG_KMS("bad framebuffer width %u\n", r->height); |
DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width); |
return -EINVAL; |
} |
|
3033,7 → 3065,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_addfb2(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
3061,7 → 3093,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_rmfb(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
3115,7 → 3147,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_getfb(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
3137,7 → 3169,8 |
r->bpp = fb->bits_per_pixel; |
r->pitch = fb->pitches[0]; |
if (fb->funcs->create_handle) { |
if (file_priv->is_master || capable(CAP_SYS_ADMIN)) { |
if (file_priv->is_master || capable(CAP_SYS_ADMIN) || |
drm_is_control_client(file_priv)) { |
ret = fb->funcs->create_handle(fb, file_priv, |
&r->handle); |
} else { |
3175,7 → 3208,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_dirtyfb_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
3255,7 → 3288,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
void drm_fb_release(struct drm_file *priv) |
{ |
3262,7 → 3295,16 |
struct drm_device *dev = priv->minor->dev; |
struct drm_framebuffer *fb, *tfb; |
|
mutex_lock(&priv->fbs_lock); |
/* |
* When the file gets released that means no one else can access the fb |
* list any more, so no need to grab fpriv->fbs_lock. And we need to |
* avoid upsetting lockdep since the universal cursor code adds a |
* framebuffer while holding mutex locks. |
* |
* Note that a real deadlock between fpriv->fbs_lock and the modeset |
* locks is impossible here since no one else but this function can get |
* at it any more. |
*/ |
list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { |
|
mutex_lock(&dev->mode_config.fb_lock); |
3275,7 → 3317,6 |
/* This will also drop the fpriv->fbs reference. */ |
drm_framebuffer_remove(fb); |
} |
mutex_unlock(&priv->fbs_lock); |
} |
#endif |
|
3291,6 → 3332,10 |
* object with drm_object_attach_property. The returned property object must be |
* freed with drm_property_destroy. |
* |
* Note that the DRM core keeps a per-device list of properties and that, if |
* drm_mode_config_cleanup() is called, it will destroy all properties created |
* by the driver. |
* |
* Returns: |
* A pointer to the newly created property on success, NULL on failure. |
*/ |
3318,7 → 3363,7 |
|
property->flags = flags; |
property->num_values = num_values; |
INIT_LIST_HEAD(&property->enum_blob_list); |
INIT_LIST_HEAD(&property->enum_list); |
|
if (name) { |
strncpy(property->name, name, DRM_PROP_NAME_LEN); |
3389,9 → 3434,10 |
* @flags: flags specifying the property type |
* @name: name of the property |
* @props: enumeration lists with property bitflags |
* @num_values: number of pre-defined values |
* @num_props: size of the @props array |
* @supported_bits: bitmask of all supported enumeration values |
* |
* This creates a new generic drm property which can then be attached to a drm |
* This creates a new bitmask 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. |
* |
3466,7 → 3512,7 |
* 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 |
* Userspace is allowed to set any integer value in the (min, max) range |
* inclusive. |
* |
* Returns: |
3539,8 → 3585,8 |
(value > 63)) |
return -EINVAL; |
|
if (!list_empty(&property->enum_blob_list)) { |
list_for_each_entry(prop_enum, &property->enum_blob_list, head) { |
if (!list_empty(&property->enum_list)) { |
list_for_each_entry(prop_enum, &property->enum_list, head) { |
if (prop_enum->value == value) { |
strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); |
prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; |
3558,7 → 3604,7 |
prop_enum->value = value; |
|
property->values[index] = value; |
list_add_tail(&prop_enum->head, &property->enum_blob_list); |
list_add_tail(&prop_enum->head, &property->enum_list); |
return 0; |
} |
EXPORT_SYMBOL(drm_property_add_enum); |
3575,7 → 3621,7 |
{ |
struct drm_property_enum *prop_enum, *pt; |
|
list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) { |
list_for_each_entry_safe(prop_enum, pt, &property->enum_list, head) { |
list_del(&prop_enum->head); |
kfree(prop_enum); |
} |
3679,17 → 3725,20 |
|
#if 0 |
/** |
* drm_mode_getproperty_ioctl - get the current value of a connector's property |
* drm_mode_getproperty_ioctl - get the property metadata |
* @dev: DRM device |
* @data: ioctl data |
* @file_priv: DRM file info |
* |
* This function retrieves the current value for an connectors's property. |
* This function retrieves the metadata for a given property, like the different |
* possible values for an enum property or the limits for a range property. |
* |
* Blob properties are special |
* |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_getproperty_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
3697,16 → 3746,12 |
struct drm_mode_get_property *out_resp = data; |
struct drm_property *property; |
int enum_count = 0; |
int blob_count = 0; |
int value_count = 0; |
int ret = 0, i; |
int copied; |
struct drm_property_enum *prop_enum; |
struct drm_mode_property_enum __user *enum_ptr; |
struct drm_property_blob *prop_blob; |
uint32_t __user *blob_id_ptr; |
uint64_t __user *values_ptr; |
uint32_t __user *blob_length_ptr; |
|
if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
return -EINVAL; |
3720,11 → 3765,8 |
|
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) |
list_for_each_entry(prop_enum, &property->enum_list, head) |
enum_count++; |
} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { |
list_for_each_entry(prop_blob, &property->enum_blob_list, head) |
blob_count++; |
} |
|
value_count = property->num_values; |
3749,7 → 3791,7 |
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; |
list_for_each_entry(prop_enum, &property->enum_blob_list, head) { |
list_for_each_entry(prop_enum, &property->enum_list, head) { |
|
if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { |
ret = -EFAULT; |
3767,28 → 3809,16 |
out_resp->count_enum_blobs = enum_count; |
} |
|
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; |
blob_length_ptr = (uint32_t __user *)(unsigned long)out_resp->values_ptr; |
|
list_for_each_entry(prop_blob, &property->enum_blob_list, head) { |
if (put_user(prop_blob->base.id, blob_id_ptr + copied)) { |
ret = -EFAULT; |
goto done; |
} |
|
if (put_user(prop_blob->length, blob_length_ptr + copied)) { |
ret = -EFAULT; |
goto done; |
} |
|
copied++; |
} |
} |
out_resp->count_enum_blobs = blob_count; |
} |
/* |
* NOTE: The idea seems to have been to use this to read all the blob |
* property values. But nothing ever added them to the corresponding |
* list, userspace always used the special-purpose get_blob ioctl to |
* read the value for a blob property. It also doesn't make a lot of |
* sense to return values here when everything else is just metadata for |
* the property itself. |
*/ |
if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) |
out_resp->count_enum_blobs = 0; |
done: |
drm_modeset_unlock_all(dev); |
return ret; |
3795,8 → 3825,9 |
} |
#endif |
|
static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, |
void *data) |
static struct drm_property_blob * |
drm_property_create_blob(struct drm_device *dev, size_t length, |
const void *data) |
{ |
struct drm_property_blob *blob; |
int ret; |
3843,7 → 3874,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_getblob_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
3878,12 → 3909,25 |
} |
#endif |
|
/** |
* drm_mode_connector_set_path_property - set tile property on connector |
* @connector: connector to set property on. |
* @path: path to use for property. |
* |
* This creates a property to expose to userspace to specify a |
* connector path. This is mainly used for DisplayPort MST where |
* connectors have a topology and we want to allow userspace to give |
* them more meaningful names. |
* |
* Returns: |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_connector_set_path_property(struct drm_connector *connector, |
char *path) |
const char *path) |
{ |
struct drm_device *dev = connector->dev; |
int ret, size; |
size = strlen(path) + 1; |
size_t size = strlen(path) + 1; |
int ret; |
|
connector->path_blob_ptr = drm_property_create_blob(connector->dev, |
size, path); |
3898,6 → 3942,52 |
EXPORT_SYMBOL(drm_mode_connector_set_path_property); |
|
/** |
* drm_mode_connector_set_tile_property - set tile property on connector |
* @connector: connector to set property on. |
* |
* This looks up the tile information for a connector, and creates a |
* property for userspace to parse if it exists. The property is of |
* the form of 8 integers using ':' as a separator. |
* |
* Returns: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_connector_set_tile_property(struct drm_connector *connector) |
{ |
struct drm_device *dev = connector->dev; |
int ret, size; |
char tile[256]; |
|
if (connector->tile_blob_ptr) |
drm_property_destroy_blob(dev, connector->tile_blob_ptr); |
|
if (!connector->has_tile) { |
connector->tile_blob_ptr = NULL; |
ret = drm_object_property_set_value(&connector->base, |
dev->mode_config.tile_property, 0); |
return ret; |
} |
|
snprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d", |
connector->tile_group->id, connector->tile_is_single_monitor, |
connector->num_h_tile, connector->num_v_tile, |
connector->tile_h_loc, connector->tile_v_loc, |
connector->tile_h_size, connector->tile_v_size); |
size = strlen(tile) + 1; |
|
connector->tile_blob_ptr = drm_property_create_blob(connector->dev, |
size, tile); |
if (!connector->tile_blob_ptr) |
return -EINVAL; |
|
ret = drm_object_property_set_value(&connector->base, |
dev->mode_config.tile_property, |
connector->tile_blob_ptr->base.id); |
return ret; |
} |
EXPORT_SYMBOL(drm_mode_connector_set_tile_property); |
|
/** |
* drm_mode_connector_update_edid_property - update the edid property of a connector |
* @connector: drm connector |
* @edid: new value of the edid property |
3909,10 → 3999,11 |
* Zero on success, errno on failure. |
*/ |
int drm_mode_connector_update_edid_property(struct drm_connector *connector, |
struct edid *edid) |
const struct edid *edid) |
{ |
struct drm_device *dev = connector->dev; |
int ret, size; |
size_t size; |
int ret; |
|
/* ignore requests to set edid when overridden */ |
if (connector->override_edid) |
3942,8 → 4033,8 |
} |
EXPORT_SYMBOL(drm_mode_connector_update_edid_property); |
|
#if 0 |
|
|
static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, |
struct drm_property *property, |
uint64_t value) |
3980,12 → 4071,25 |
return ret; |
} |
|
static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj, |
/** |
* drm_mode_plane_set_obj_prop - set the value of a property |
* @plane: drm plane object to set property value for |
* @property: property to set |
* @value: value the property should be set to |
* |
* This functions sets a given property on a given plane object. This function |
* calls the driver's ->set_property callback and changes the software state of |
* the property if the callback succeeds. |
* |
* Returns: |
* Zero on success, error code on failure. |
*/ |
int drm_mode_plane_set_obj_prop(struct drm_plane *plane, |
struct drm_property *property, |
uint64_t value) |
{ |
int ret = -EINVAL; |
struct drm_plane *plane = obj_to_plane(obj); |
struct drm_mode_object *obj = &plane->base; |
|
if (plane->funcs->set_property) |
ret = plane->funcs->set_property(plane, property, value); |
3994,9 → 4098,11 |
|
return ret; |
} |
EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); |
|
#if 0 |
/** |
* drm_mode_getproperty_ioctl - get the current value of a object's property |
* drm_mode_obj_get_properties_ioctl - get the current value of a object's property |
* @dev: DRM device |
* @data: ioctl data |
* @file_priv: DRM file info |
4008,7 → 4114,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
4080,7 → 4186,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
4132,7 → 4238,8 |
ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); |
break; |
case DRM_MODE_OBJECT_PLANE: |
ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value); |
ret = drm_mode_plane_set_obj_prop(obj_to_plane(arg_obj), |
property, arg->value); |
break; |
} |
|
4152,7 → 4259,7 |
* possible_clones and possible_crtcs bitmasks. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_connector_attach_encoder(struct drm_connector *connector, |
struct drm_encoder *encoder) |
4179,7 → 4286,7 |
* fixed gamma table size. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, |
int gamma_size) |
4209,7 → 4316,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_gamma_set_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
4281,7 → 4388,7 |
* Called by the user via ioctl. |
* |
* Returns: |
* Zero on success, errno on failure. |
* Zero on success, negative errno on failure. |
*/ |
int drm_mode_gamma_get_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
4345,9 → 4452,14 |
void drm_mode_config_reset(struct drm_device *dev) |
{ |
struct drm_crtc *crtc; |
struct drm_plane *plane; |
struct drm_encoder *encoder; |
struct drm_connector *connector; |
|
list_for_each_entry(plane, &dev->mode_config.plane_list, head) |
if (plane->funcs->reset) |
plane->funcs->reset(plane); |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) |
if (crtc->funcs->reset) |
crtc->funcs->reset(crtc); |
4576,6 → 4688,36 |
EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); |
|
/** |
* drm_rotation_simplify() - Try to simplify the rotation |
* @rotation: Rotation to be simplified |
* @supported_rotations: Supported rotations |
* |
* Attempt to simplify the rotation to a form that is supported. |
* Eg. if the hardware supports everything except DRM_REFLECT_X |
* one could call this function like this: |
* |
* drm_rotation_simplify(rotation, BIT(DRM_ROTATE_0) | |
* BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_180) | |
* BIT(DRM_ROTATE_270) | BIT(DRM_REFLECT_Y)); |
* |
* to eliminate the DRM_ROTATE_X flag. Depending on what kind of |
* transforms the hardware supports, this function may not |
* be able to produce a supported transform, so the caller should |
* check the result afterwards. |
*/ |
unsigned int drm_rotation_simplify(unsigned int rotation, |
unsigned int supported_rotations) |
{ |
if (rotation & ~supported_rotations) { |
rotation ^= BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y); |
rotation = (rotation & ~0xf) | BIT((ffs(rotation & 0xf) + 1) % 4); |
} |
|
return rotation; |
} |
EXPORT_SYMBOL(drm_rotation_simplify); |
|
/** |
* drm_mode_config_init - initialize DRM mode_configuration structure |
* @dev: DRM device |
* |
4602,6 → 4744,7 |
INIT_LIST_HEAD(&dev->mode_config.property_blob_list); |
INIT_LIST_HEAD(&dev->mode_config.plane_list); |
idr_init(&dev->mode_config.crtc_idr); |
idr_init(&dev->mode_config.tile_idr); |
|
drm_modeset_lock_all(dev); |
drm_mode_create_standard_connector_properties(dev); |
4631,3 → 4774,181 |
* |
* FIXME: cleanup any dangling user buffer objects too |
*/ |
void drm_mode_config_cleanup(struct drm_device *dev) |
{ |
struct drm_connector *connector, *ot; |
struct drm_crtc *crtc, *ct; |
struct drm_encoder *encoder, *enct; |
struct drm_bridge *bridge, *brt; |
struct drm_framebuffer *fb, *fbt; |
struct drm_property *property, *pt; |
struct drm_property_blob *blob, *bt; |
struct drm_plane *plane, *plt; |
|
list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, |
head) { |
encoder->funcs->destroy(encoder); |
} |
|
list_for_each_entry_safe(bridge, brt, |
&dev->mode_config.bridge_list, head) { |
bridge->funcs->destroy(bridge); |
} |
|
list_for_each_entry_safe(connector, ot, |
&dev->mode_config.connector_list, head) { |
connector->funcs->destroy(connector); |
} |
|
list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, |
head) { |
drm_property_destroy(dev, property); |
} |
|
list_for_each_entry_safe(blob, bt, &dev->mode_config.property_blob_list, |
head) { |
drm_property_destroy_blob(dev, blob); |
} |
|
/* |
* 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. |
*/ |
WARN_ON(!list_empty(&dev->mode_config.fb_list)); |
list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { |
drm_framebuffer_remove(fb); |
} |
|
list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, |
head) { |
plane->funcs->destroy(plane); |
} |
|
list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { |
crtc->funcs->destroy(crtc); |
} |
|
idr_destroy(&dev->mode_config.tile_idr); |
idr_destroy(&dev->mode_config.crtc_idr); |
drm_modeset_lock_fini(&dev->mode_config.connection_mutex); |
} |
EXPORT_SYMBOL(drm_mode_config_cleanup); |
|
struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, |
unsigned int supported_rotations) |
{ |
static const struct drm_prop_enum_list props[] = { |
{ DRM_ROTATE_0, "rotate-0" }, |
{ DRM_ROTATE_90, "rotate-90" }, |
{ DRM_ROTATE_180, "rotate-180" }, |
{ DRM_ROTATE_270, "rotate-270" }, |
{ DRM_REFLECT_X, "reflect-x" }, |
{ DRM_REFLECT_Y, "reflect-y" }, |
}; |
|
return drm_property_create_bitmask(dev, 0, "rotation", |
props, ARRAY_SIZE(props), |
supported_rotations); |
} |
EXPORT_SYMBOL(drm_mode_create_rotation_property); |
|
/** |
* DOC: Tile group |
* |
* Tile groups are used to represent tiled monitors with a unique |
* integer identifier. Tiled monitors using DisplayID v1.3 have |
* a unique 8-byte handle, we store this in a tile group, so we |
* have a common identifier for all tiles in a monitor group. |
*/ |
static void drm_tile_group_free(struct kref *kref) |
{ |
struct drm_tile_group *tg = container_of(kref, struct drm_tile_group, refcount); |
struct drm_device *dev = tg->dev; |
mutex_lock(&dev->mode_config.idr_mutex); |
idr_remove(&dev->mode_config.tile_idr, tg->id); |
mutex_unlock(&dev->mode_config.idr_mutex); |
kfree(tg); |
} |
|
/** |
* drm_mode_put_tile_group - drop a reference to a tile group. |
* @dev: DRM device |
* @tg: tile group to drop reference to. |
* |
* drop reference to tile group and free if 0. |
*/ |
void drm_mode_put_tile_group(struct drm_device *dev, |
struct drm_tile_group *tg) |
{ |
kref_put(&tg->refcount, drm_tile_group_free); |
} |
|
/** |
* drm_mode_get_tile_group - get a reference to an existing tile group |
* @dev: DRM device |
* @topology: 8-bytes unique per monitor. |
* |
* Use the unique bytes to get a reference to an existing tile group. |
* |
* RETURNS: |
* tile group or NULL if not found. |
*/ |
struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, |
char topology[8]) |
{ |
struct drm_tile_group *tg; |
int id; |
mutex_lock(&dev->mode_config.idr_mutex); |
idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) { |
if (!memcmp(tg->group_data, topology, 8)) { |
// if (!kref_get_unless_zero(&tg->refcount)) |
// tg = NULL; |
mutex_unlock(&dev->mode_config.idr_mutex); |
return tg; |
} |
} |
mutex_unlock(&dev->mode_config.idr_mutex); |
return NULL; |
} |
|
/** |
* drm_mode_create_tile_group - create a tile group from a displayid description |
* @dev: DRM device |
* @topology: 8-bytes unique per monitor. |
* |
* Create a tile group for the unique monitor, and get a unique |
* identifier for the tile group. |
* |
* RETURNS: |
* new tile group or error. |
*/ |
struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, |
char topology[8]) |
{ |
struct drm_tile_group *tg; |
int ret; |
|
tg = kzalloc(sizeof(*tg), GFP_KERNEL); |
if (!tg) |
return ERR_PTR(-ENOMEM); |
|
kref_init(&tg->refcount); |
memcpy(tg->group_data, topology, 8); |
tg->dev = dev; |
|
mutex_lock(&dev->mode_config.idr_mutex); |
ret = idr_alloc(&dev->mode_config.tile_idr, tg, 1, 0, GFP_KERNEL); |
if (ret >= 0) { |
tg->id = ret; |
} else { |
kfree(tg); |
tg = ERR_PTR(ret); |
} |
|
mutex_unlock(&dev->mode_config.idr_mutex); |
return tg; |
} |