30,15 → 30,7 |
#include <drm/drm_atomic.h> |
#include <drm/drm_plane_helper.h> |
|
/** |
* drm_atomic_state_default_release - |
* release memory initialized by drm_atomic_state_init |
* @state: atomic state |
* |
* Free all the memory allocated by drm_atomic_state_init. |
* This is useful for drivers that subclass the atomic state. |
*/ |
void drm_atomic_state_default_release(struct drm_atomic_state *state) |
static void kfree_state(struct drm_atomic_state *state) |
{ |
kfree(state->connectors); |
kfree(state->connector_states); |
46,25 → 38,24 |
kfree(state->crtc_states); |
kfree(state->planes); |
kfree(state->plane_states); |
kfree(state); |
} |
EXPORT_SYMBOL(drm_atomic_state_default_release); |
|
/** |
* drm_atomic_state_init - init new atomic state |
* drm_atomic_state_alloc - allocate atomic state |
* @dev: DRM device |
* @state: atomic state |
* |
* Default implementation for filling in a new atomic state. |
* This is useful for drivers that subclass the atomic state. |
* This allocates an empty atomic state to track updates. |
*/ |
int |
drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state) |
struct drm_atomic_state * |
drm_atomic_state_alloc(struct drm_device *dev) |
{ |
/* TODO legacy paths should maybe do a better job about |
* setting this appropriately? |
*/ |
state->allow_modeset = true; |
struct drm_atomic_state *state; |
|
state = kzalloc(sizeof(*state), GFP_KERNEL); |
if (!state) |
return NULL; |
|
state->num_connector = ACCESS_ONCE(dev->mode_config.num_connector); |
|
state->crtcs = kcalloc(dev->mode_config.num_crtc, |
96,56 → 87,37 |
|
state->dev = dev; |
|
DRM_DEBUG_ATOMIC("Allocated atomic state %p\n", state); |
DRM_DEBUG_KMS("Allocate atomic state %p\n", state); |
|
return 0; |
return state; |
fail: |
drm_atomic_state_default_release(state); |
return -ENOMEM; |
} |
EXPORT_SYMBOL(drm_atomic_state_init); |
kfree_state(state); |
|
/** |
* drm_atomic_state_alloc - allocate atomic state |
* @dev: DRM device |
* |
* This allocates an empty atomic state to track updates. |
*/ |
struct drm_atomic_state * |
drm_atomic_state_alloc(struct drm_device *dev) |
{ |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_atomic_state *state; |
|
if (!config->funcs->atomic_state_alloc) { |
state = kzalloc(sizeof(*state), GFP_KERNEL); |
if (!state) |
return NULL; |
if (drm_atomic_state_init(dev, state) < 0) { |
kfree(state); |
return NULL; |
} |
return state; |
} |
|
return config->funcs->atomic_state_alloc(dev); |
} |
EXPORT_SYMBOL(drm_atomic_state_alloc); |
|
/** |
* drm_atomic_state_default_clear - clear base atomic state |
* drm_atomic_state_clear - clear state object |
* @state: atomic state |
* |
* Default implementation for clearing atomic state. |
* This is useful for drivers that subclass the atomic state. |
* When the w/w mutex algorithm detects a deadlock we need to back off and drop |
* all locks. So someone else could sneak in and change the current modeset |
* configuration. Which means that all the state assembled in @state is no |
* longer an atomic update to the current state, but to some arbitrary earlier |
* state. Which could break assumptions the driver's ->atomic_check likely |
* relies on. |
* |
* Hence we must clear all cached state and completely start over, using this |
* function. |
*/ |
void drm_atomic_state_default_clear(struct drm_atomic_state *state) |
void drm_atomic_state_clear(struct drm_atomic_state *state) |
{ |
struct drm_device *dev = state->dev; |
struct drm_mode_config *config = &dev->mode_config; |
int i; |
|
DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state); |
DRM_DEBUG_KMS("Clearing atomic state %p\n", state); |
|
for (i = 0; i < state->num_connector; i++) { |
struct drm_connector *connector = state->connectors[i]; |
153,18 → 125,10 |
if (!connector) |
continue; |
|
/* |
* FIXME: Async commits can race with connector unplugging and |
* there's currently nothing that prevents cleanup up state for |
* deleted connectors. As long as the callback doesn't look at |
* the connector we'll be fine though, so make sure that's the |
* case by setting all connector pointers to NULL. |
*/ |
state->connector_states[i]->connector = NULL; |
connector->funcs->atomic_destroy_state(NULL, |
WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); |
|
connector->funcs->atomic_destroy_state(connector, |
state->connector_states[i]); |
state->connectors[i] = NULL; |
state->connector_states[i] = NULL; |
} |
|
for (i = 0; i < config->num_crtc; i++) { |
175,8 → 139,6 |
|
crtc->funcs->atomic_destroy_state(crtc, |
state->crtc_states[i]); |
state->crtcs[i] = NULL; |
state->crtc_states[i] = NULL; |
} |
|
for (i = 0; i < config->num_total_plane; i++) { |
187,36 → 149,8 |
|
plane->funcs->atomic_destroy_state(plane, |
state->plane_states[i]); |
state->planes[i] = NULL; |
state->plane_states[i] = NULL; |
} |
} |
EXPORT_SYMBOL(drm_atomic_state_default_clear); |
|
/** |
* drm_atomic_state_clear - clear state object |
* @state: atomic state |
* |
* When the w/w mutex algorithm detects a deadlock we need to back off and drop |
* all locks. So someone else could sneak in and change the current modeset |
* configuration. Which means that all the state assembled in @state is no |
* longer an atomic update to the current state, but to some arbitrary earlier |
* state. Which could break assumptions the driver's ->atomic_check likely |
* relies on. |
* |
* Hence we must clear all cached state and completely start over, using this |
* function. |
*/ |
void drm_atomic_state_clear(struct drm_atomic_state *state) |
{ |
struct drm_device *dev = state->dev; |
struct drm_mode_config *config = &dev->mode_config; |
|
if (config->funcs->atomic_state_clear) |
config->funcs->atomic_state_clear(state); |
else |
drm_atomic_state_default_clear(state); |
} |
EXPORT_SYMBOL(drm_atomic_state_clear); |
|
/** |
228,26 → 162,12 |
*/ |
void drm_atomic_state_free(struct drm_atomic_state *state) |
{ |
struct drm_device *dev; |
struct drm_mode_config *config; |
|
if (!state) |
return; |
|
dev = state->dev; |
config = &dev->mode_config; |
|
drm_atomic_state_clear(state); |
|
DRM_DEBUG_ATOMIC("Freeing atomic state %p\n", state); |
DRM_DEBUG_KMS("Freeing atomic state %p\n", state); |
|
if (config->funcs->atomic_state_free) { |
config->funcs->atomic_state_free(state); |
} else { |
drm_atomic_state_default_release(state); |
kfree(state); |
kfree_state(state); |
} |
} |
EXPORT_SYMBOL(drm_atomic_state_free); |
|
/** |
269,13 → 189,14 |
drm_atomic_get_crtc_state(struct drm_atomic_state *state, |
struct drm_crtc *crtc) |
{ |
int ret, index = drm_crtc_index(crtc); |
int ret, index; |
struct drm_crtc_state *crtc_state; |
|
crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); |
if (crtc_state) |
return crtc_state; |
index = drm_crtc_index(crtc); |
|
if (state->crtc_states[index]) |
return state->crtc_states[index]; |
|
ret = drm_modeset_lock(&crtc->mutex, state->acquire_ctx); |
if (ret) |
return ERR_PTR(ret); |
288,7 → 209,7 |
state->crtcs[index] = crtc; |
crtc_state->state = state; |
|
DRM_DEBUG_ATOMIC("Added [CRTC:%d] %p state to %p\n", |
DRM_DEBUG_KMS("Added [CRTC:%d] %p state to %p\n", |
crtc->base.id, crtc_state, state); |
|
return crtc_state; |
296,216 → 217,6 |
EXPORT_SYMBOL(drm_atomic_get_crtc_state); |
|
/** |
* drm_atomic_set_mode_for_crtc - set mode for CRTC |
* @state: the CRTC whose incoming state to update |
* @mode: kernel-internal mode to use for the CRTC, or NULL to disable |
* |
* Set a mode (originating from the kernel) on the desired CRTC state. Does |
* not change any other state properties, including enable, active, or |
* mode_changed. |
* |
* RETURNS: |
* Zero on success, error code on failure. Cannot return -EDEADLK. |
*/ |
int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, |
struct drm_display_mode *mode) |
{ |
struct drm_mode_modeinfo umode; |
|
/* Early return for no change. */ |
if (mode && memcmp(&state->mode, mode, sizeof(*mode)) == 0) |
return 0; |
|
if (state->mode_blob) |
drm_property_unreference_blob(state->mode_blob); |
state->mode_blob = NULL; |
|
if (mode) { |
drm_mode_convert_to_umode(&umode, mode); |
state->mode_blob = |
drm_property_create_blob(state->crtc->dev, |
sizeof(umode), |
&umode); |
if (IS_ERR(state->mode_blob)) |
return PTR_ERR(state->mode_blob); |
|
drm_mode_copy(&state->mode, mode); |
state->enable = true; |
DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n", |
mode->name, state); |
} else { |
memset(&state->mode, 0, sizeof(state->mode)); |
state->enable = false; |
DRM_DEBUG_ATOMIC("Set [NOMODE] for CRTC state %p\n", |
state); |
} |
|
return 0; |
} |
EXPORT_SYMBOL(drm_atomic_set_mode_for_crtc); |
|
/** |
* drm_atomic_set_mode_prop_for_crtc - set mode for CRTC |
* @state: the CRTC whose incoming state to update |
* @blob: pointer to blob property to use for mode |
* |
* Set a mode (originating from a blob property) on the desired CRTC state. |
* This function will take a reference on the blob property for the CRTC state, |
* and release the reference held on the state's existing mode property, if any |
* was set. |
* |
* RETURNS: |
* Zero on success, error code on failure. Cannot return -EDEADLK. |
*/ |
int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state, |
struct drm_property_blob *blob) |
{ |
if (blob == state->mode_blob) |
return 0; |
|
if (state->mode_blob) |
drm_property_unreference_blob(state->mode_blob); |
state->mode_blob = NULL; |
|
if (blob) { |
if (blob->length != sizeof(struct drm_mode_modeinfo) || |
drm_mode_convert_umode(&state->mode, |
(const struct drm_mode_modeinfo *) |
blob->data)) |
return -EINVAL; |
|
state->mode_blob = drm_property_reference_blob(blob); |
state->enable = true; |
DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n", |
state->mode.name, state); |
} else { |
memset(&state->mode, 0, sizeof(state->mode)); |
state->enable = false; |
DRM_DEBUG_ATOMIC("Set [NOMODE] for CRTC state %p\n", |
state); |
} |
|
return 0; |
} |
EXPORT_SYMBOL(drm_atomic_set_mode_prop_for_crtc); |
|
/** |
* drm_atomic_crtc_set_property - set property on CRTC |
* @crtc: the drm CRTC to set a property on |
* @state: the state object to update with the new property value |
* @property: the property to set |
* @val: the new property value |
* |
* Use this instead of calling crtc->atomic_set_property directly. |
* This function handles generic/core properties and calls out to |
* driver's ->atomic_set_property() for driver properties. To ensure |
* consistent behavior you must call this function rather than the |
* driver hook directly. |
* |
* RETURNS: |
* Zero on success, error code on failure |
*/ |
int drm_atomic_crtc_set_property(struct drm_crtc *crtc, |
struct drm_crtc_state *state, struct drm_property *property, |
uint64_t val) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_mode_config *config = &dev->mode_config; |
int ret; |
|
if (property == config->prop_active) |
state->active = val; |
else if (property == config->prop_mode_id) { |
struct drm_property_blob *mode = |
drm_property_lookup_blob(dev, val); |
ret = drm_atomic_set_mode_prop_for_crtc(state, mode); |
if (mode) |
drm_property_unreference_blob(mode); |
return ret; |
} |
else if (crtc->funcs->atomic_set_property) |
return crtc->funcs->atomic_set_property(crtc, state, property, val); |
else |
return -EINVAL; |
|
return 0; |
} |
EXPORT_SYMBOL(drm_atomic_crtc_set_property); |
|
/* |
* This function handles generic/core properties and calls out to |
* driver's ->atomic_get_property() for driver properties. To ensure |
* consistent behavior you must call this function rather than the |
* driver hook directly. |
*/ |
static int |
drm_atomic_crtc_get_property(struct drm_crtc *crtc, |
const struct drm_crtc_state *state, |
struct drm_property *property, uint64_t *val) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_mode_config *config = &dev->mode_config; |
|
if (property == config->prop_active) |
*val = state->active; |
else if (property == config->prop_mode_id) |
*val = (state->mode_blob) ? state->mode_blob->base.id : 0; |
else if (crtc->funcs->atomic_get_property) |
return crtc->funcs->atomic_get_property(crtc, state, property, val); |
else |
return -EINVAL; |
|
return 0; |
} |
|
/** |
* drm_atomic_crtc_check - check crtc state |
* @crtc: crtc to check |
* @state: crtc state to check |
* |
* Provides core sanity checks for crtc state. |
* |
* RETURNS: |
* Zero on success, error code on failure |
*/ |
static int drm_atomic_crtc_check(struct drm_crtc *crtc, |
struct drm_crtc_state *state) |
{ |
/* NOTE: we explicitly don't enforce constraints such as primary |
* layer covering entire screen, since that is something we want |
* to allow (on hw that supports it). For hw that does not, it |
* should be checked in driver's crtc->atomic_check() vfunc. |
* |
* TODO: Add generic modeset state checks once we support those. |
*/ |
|
if (state->active && !state->enable) { |
DRM_DEBUG_ATOMIC("[CRTC:%d] active without enabled\n", |
crtc->base.id); |
return -EINVAL; |
} |
|
/* The state->enable vs. state->mode_blob checks can be WARN_ON, |
* as this is a kernel-internal detail that userspace should never |
* be able to trigger. */ |
if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) && |
WARN_ON(state->enable && !state->mode_blob)) { |
DRM_DEBUG_ATOMIC("[CRTC:%d] enabled without mode blob\n", |
crtc->base.id); |
return -EINVAL; |
} |
|
if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) && |
WARN_ON(!state->enable && state->mode_blob)) { |
DRM_DEBUG_ATOMIC("[CRTC:%d] disabled with mode blob\n", |
crtc->base.id); |
return -EINVAL; |
} |
|
return 0; |
} |
|
/** |
* drm_atomic_get_plane_state - get plane state |
* @state: global atomic state object |
* @plane: plane to get state object for |
524,13 → 235,14 |
drm_atomic_get_plane_state(struct drm_atomic_state *state, |
struct drm_plane *plane) |
{ |
int ret, index = drm_plane_index(plane); |
int ret, index; |
struct drm_plane_state *plane_state; |
|
plane_state = drm_atomic_get_existing_plane_state(state, plane); |
if (plane_state) |
return plane_state; |
index = drm_plane_index(plane); |
|
if (state->plane_states[index]) |
return state->plane_states[index]; |
|
ret = drm_modeset_lock(&plane->mutex, state->acquire_ctx); |
if (ret) |
return ERR_PTR(ret); |
543,7 → 255,7 |
state->planes[index] = plane; |
plane_state->state = state; |
|
DRM_DEBUG_ATOMIC("Added [PLANE:%d] %p state to %p\n", |
DRM_DEBUG_KMS("Added [PLANE:%d] %p state to %p\n", |
plane->base.id, plane_state, state); |
|
if (plane_state->crtc) { |
560,210 → 272,6 |
EXPORT_SYMBOL(drm_atomic_get_plane_state); |
|
/** |
* drm_atomic_plane_set_property - set property on plane |
* @plane: the drm plane to set a property on |
* @state: the state object to update with the new property value |
* @property: the property to set |
* @val: the new property value |
* |
* Use this instead of calling plane->atomic_set_property directly. |
* This function handles generic/core properties and calls out to |
* driver's ->atomic_set_property() for driver properties. To ensure |
* consistent behavior you must call this function rather than the |
* driver hook directly. |
* |
* RETURNS: |
* Zero on success, error code on failure |
*/ |
int drm_atomic_plane_set_property(struct drm_plane *plane, |
struct drm_plane_state *state, struct drm_property *property, |
uint64_t val) |
{ |
struct drm_device *dev = plane->dev; |
struct drm_mode_config *config = &dev->mode_config; |
|
if (property == config->prop_fb_id) { |
struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, val); |
drm_atomic_set_fb_for_plane(state, fb); |
if (fb) |
drm_framebuffer_unreference(fb); |
} else if (property == config->prop_crtc_id) { |
struct drm_crtc *crtc = drm_crtc_find(dev, val); |
return drm_atomic_set_crtc_for_plane(state, crtc); |
} else if (property == config->prop_crtc_x) { |
state->crtc_x = U642I64(val); |
} else if (property == config->prop_crtc_y) { |
state->crtc_y = U642I64(val); |
} else if (property == config->prop_crtc_w) { |
state->crtc_w = val; |
} else if (property == config->prop_crtc_h) { |
state->crtc_h = val; |
} else if (property == config->prop_src_x) { |
state->src_x = val; |
} else if (property == config->prop_src_y) { |
state->src_y = val; |
} else if (property == config->prop_src_w) { |
state->src_w = val; |
} else if (property == config->prop_src_h) { |
state->src_h = val; |
} else if (property == config->rotation_property) { |
state->rotation = val; |
} else if (plane->funcs->atomic_set_property) { |
return plane->funcs->atomic_set_property(plane, state, |
property, val); |
} else { |
return -EINVAL; |
} |
|
return 0; |
} |
EXPORT_SYMBOL(drm_atomic_plane_set_property); |
|
/* |
* This function handles generic/core properties and calls out to |
* driver's ->atomic_get_property() for driver properties. To ensure |
* consistent behavior you must call this function rather than the |
* driver hook directly. |
*/ |
static int |
drm_atomic_plane_get_property(struct drm_plane *plane, |
const struct drm_plane_state *state, |
struct drm_property *property, uint64_t *val) |
{ |
struct drm_device *dev = plane->dev; |
struct drm_mode_config *config = &dev->mode_config; |
|
if (property == config->prop_fb_id) { |
*val = (state->fb) ? state->fb->base.id : 0; |
} else if (property == config->prop_crtc_id) { |
*val = (state->crtc) ? state->crtc->base.id : 0; |
} else if (property == config->prop_crtc_x) { |
*val = I642U64(state->crtc_x); |
} else if (property == config->prop_crtc_y) { |
*val = I642U64(state->crtc_y); |
} else if (property == config->prop_crtc_w) { |
*val = state->crtc_w; |
} else if (property == config->prop_crtc_h) { |
*val = state->crtc_h; |
} else if (property == config->prop_src_x) { |
*val = state->src_x; |
} else if (property == config->prop_src_y) { |
*val = state->src_y; |
} else if (property == config->prop_src_w) { |
*val = state->src_w; |
} else if (property == config->prop_src_h) { |
*val = state->src_h; |
} else if (property == config->rotation_property) { |
*val = state->rotation; |
} else if (plane->funcs->atomic_get_property) { |
return plane->funcs->atomic_get_property(plane, state, property, val); |
} else { |
return -EINVAL; |
} |
|
return 0; |
} |
|
static bool |
plane_switching_crtc(struct drm_atomic_state *state, |
struct drm_plane *plane, |
struct drm_plane_state *plane_state) |
{ |
if (!plane->state->crtc || !plane_state->crtc) |
return false; |
|
if (plane->state->crtc == plane_state->crtc) |
return false; |
|
/* This could be refined, but currently there's no helper or driver code |
* to implement direct switching of active planes nor userspace to take |
* advantage of more direct plane switching without the intermediate |
* full OFF state. |
*/ |
return true; |
} |
|
/** |
* drm_atomic_plane_check - check plane state |
* @plane: plane to check |
* @state: plane state to check |
* |
* Provides core sanity checks for plane state. |
* |
* RETURNS: |
* Zero on success, error code on failure |
*/ |
static int drm_atomic_plane_check(struct drm_plane *plane, |
struct drm_plane_state *state) |
{ |
unsigned int fb_width, fb_height; |
int ret; |
|
/* either *both* CRTC and FB must be set, or neither */ |
if (WARN_ON(state->crtc && !state->fb)) { |
DRM_DEBUG_ATOMIC("CRTC set but no FB\n"); |
return -EINVAL; |
} else if (WARN_ON(state->fb && !state->crtc)) { |
DRM_DEBUG_ATOMIC("FB set but no CRTC\n"); |
return -EINVAL; |
} |
|
/* if disabled, we don't care about the rest of the state: */ |
if (!state->crtc) |
return 0; |
|
/* Check whether this plane is usable on this CRTC */ |
if (!(plane->possible_crtcs & drm_crtc_mask(state->crtc))) { |
DRM_DEBUG_ATOMIC("Invalid crtc for plane\n"); |
return -EINVAL; |
} |
|
/* Check whether this plane supports the fb pixel format. */ |
ret = drm_plane_check_pixel_format(plane, state->fb->pixel_format); |
if (ret) { |
DRM_DEBUG_ATOMIC("Invalid pixel format %s\n", |
drm_get_format_name(state->fb->pixel_format)); |
return ret; |
} |
|
/* Give drivers some help against integer overflows */ |
if (state->crtc_w > INT_MAX || |
state->crtc_x > INT_MAX - (int32_t) state->crtc_w || |
state->crtc_h > INT_MAX || |
state->crtc_y > INT_MAX - (int32_t) state->crtc_h) { |
DRM_DEBUG_ATOMIC("Invalid CRTC coordinates %ux%u+%d+%d\n", |
state->crtc_w, state->crtc_h, |
state->crtc_x, state->crtc_y); |
return -ERANGE; |
} |
|
fb_width = state->fb->width << 16; |
fb_height = state->fb->height << 16; |
|
/* Make sure source coordinates are inside the fb. */ |
if (state->src_w > fb_width || |
state->src_x > fb_width - state->src_w || |
state->src_h > fb_height || |
state->src_y > fb_height - state->src_h) { |
DRM_DEBUG_ATOMIC("Invalid source coordinates " |
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", |
state->src_w >> 16, ((state->src_w & 0xffff) * 15625) >> 10, |
state->src_h >> 16, ((state->src_h & 0xffff) * 15625) >> 10, |
state->src_x >> 16, ((state->src_x & 0xffff) * 15625) >> 10, |
state->src_y >> 16, ((state->src_y & 0xffff) * 15625) >> 10); |
return -ENOSPC; |
} |
|
if (plane_switching_crtc(state->state, plane, state)) { |
DRM_DEBUG_ATOMIC("[PLANE:%d] switching CRTC directly\n", |
plane->base.id); |
return -EINVAL; |
} |
|
return 0; |
} |
|
/** |
* drm_atomic_get_connector_state - get connector state |
* @state: global atomic state object |
* @connector: connector to get state object for |
803,7 → 311,7 |
* at most the array is a bit too large. |
*/ |
if (index >= state->num_connector) { |
DRM_DEBUG_ATOMIC("Hot-added connector would overflow state array, restarting\n"); |
DRM_DEBUG_KMS("Hot-added connector would overflow state array, restarting\n"); |
return ERR_PTR(-EAGAIN); |
} |
|
818,7 → 326,7 |
state->connectors[index] = connector; |
connector_state->state = state; |
|
DRM_DEBUG_ATOMIC("Added [CONNECTOR:%d] %p state to %p\n", |
DRM_DEBUG_KMS("Added [CONNECTOR:%d] %p state to %p\n", |
connector->base.id, connector_state, state); |
|
if (connector_state->crtc) { |
835,113 → 343,9 |
EXPORT_SYMBOL(drm_atomic_get_connector_state); |
|
/** |
* drm_atomic_connector_set_property - set property on connector. |
* @connector: the drm connector to set a property on |
* @state: the state object to update with the new property value |
* @property: the property to set |
* @val: the new property value |
* |
* Use this instead of calling connector->atomic_set_property directly. |
* This function handles generic/core properties and calls out to |
* driver's ->atomic_set_property() for driver properties. To ensure |
* consistent behavior you must call this function rather than the |
* driver hook directly. |
* |
* RETURNS: |
* Zero on success, error code on failure |
*/ |
int drm_atomic_connector_set_property(struct drm_connector *connector, |
struct drm_connector_state *state, struct drm_property *property, |
uint64_t val) |
{ |
struct drm_device *dev = connector->dev; |
struct drm_mode_config *config = &dev->mode_config; |
|
if (property == config->prop_crtc_id) { |
struct drm_crtc *crtc = drm_crtc_find(dev, val); |
return drm_atomic_set_crtc_for_connector(state, crtc); |
} else if (property == config->dpms_property) { |
/* setting DPMS property requires special handling, which |
* is done in legacy setprop path for us. Disallow (for |
* now?) atomic writes to DPMS property: |
*/ |
return -EINVAL; |
} else if (connector->funcs->atomic_set_property) { |
return connector->funcs->atomic_set_property(connector, |
state, property, val); |
} else { |
return -EINVAL; |
} |
} |
EXPORT_SYMBOL(drm_atomic_connector_set_property); |
|
/* |
* This function handles generic/core properties and calls out to |
* driver's ->atomic_get_property() for driver properties. To ensure |
* consistent behavior you must call this function rather than the |
* driver hook directly. |
*/ |
static int |
drm_atomic_connector_get_property(struct drm_connector *connector, |
const struct drm_connector_state *state, |
struct drm_property *property, uint64_t *val) |
{ |
struct drm_device *dev = connector->dev; |
struct drm_mode_config *config = &dev->mode_config; |
|
if (property == config->prop_crtc_id) { |
*val = (state->crtc) ? state->crtc->base.id : 0; |
} else if (property == config->dpms_property) { |
*val = connector->dpms; |
} else if (connector->funcs->atomic_get_property) { |
return connector->funcs->atomic_get_property(connector, |
state, property, val); |
} else { |
return -EINVAL; |
} |
|
return 0; |
} |
|
int drm_atomic_get_property(struct drm_mode_object *obj, |
struct drm_property *property, uint64_t *val) |
{ |
struct drm_device *dev = property->dev; |
int ret; |
|
switch (obj->type) { |
case DRM_MODE_OBJECT_CONNECTOR: { |
struct drm_connector *connector = obj_to_connector(obj); |
WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); |
ret = drm_atomic_connector_get_property(connector, |
connector->state, property, val); |
break; |
} |
case DRM_MODE_OBJECT_CRTC: { |
struct drm_crtc *crtc = obj_to_crtc(obj); |
WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); |
ret = drm_atomic_crtc_get_property(crtc, |
crtc->state, property, val); |
break; |
} |
case DRM_MODE_OBJECT_PLANE: { |
struct drm_plane *plane = obj_to_plane(obj); |
WARN_ON(!drm_modeset_is_locked(&plane->mutex)); |
ret = drm_atomic_plane_get_property(plane, |
plane->state, property, val); |
break; |
} |
default: |
ret = -EINVAL; |
break; |
} |
|
return ret; |
} |
|
/** |
* drm_atomic_set_crtc_for_plane - set crtc for plane |
* @plane_state: the plane whose incoming state to update |
* @state: the incoming atomic state |
* @plane: the plane whose incoming state to update |
* @crtc: crtc to use for the plane |
* |
* Changing the assigned crtc for a plane requires us to grab the lock and state |
954,12 → 358,16 |
* sequence must be restarted. All other errors are fatal. |
*/ |
int |
drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, |
struct drm_crtc *crtc) |
drm_atomic_set_crtc_for_plane(struct drm_atomic_state *state, |
struct drm_plane *plane, struct drm_crtc *crtc) |
{ |
struct drm_plane *plane = plane_state->plane; |
struct drm_plane_state *plane_state = |
drm_atomic_get_plane_state(state, plane); |
struct drm_crtc_state *crtc_state; |
|
if (WARN_ON(IS_ERR(plane_state))) |
return PTR_ERR(plane_state); |
|
if (plane_state->crtc) { |
crtc_state = drm_atomic_get_crtc_state(plane_state->state, |
plane_state->crtc); |
980,11 → 388,10 |
} |
|
if (crtc) |
DRM_DEBUG_ATOMIC("Link plane state %p to [CRTC:%d]\n", |
DRM_DEBUG_KMS("Link plane state %p to [CRTC:%d]\n", |
plane_state, crtc->base.id); |
else |
DRM_DEBUG_ATOMIC("Link plane state %p to [NOCRTC]\n", |
plane_state); |
DRM_DEBUG_KMS("Link plane state %p to [NOCRTC]\n", plane_state); |
|
return 0; |
} |
991,7 → 398,7 |
EXPORT_SYMBOL(drm_atomic_set_crtc_for_plane); |
|
/** |
* drm_atomic_set_fb_for_plane - set framebuffer for plane |
* drm_atomic_set_fb_for_plane - set crtc for plane |
* @plane_state: atomic state object for the plane |
* @fb: fb to use for the plane |
* |
1011,11 → 418,10 |
plane_state->fb = fb; |
|
if (fb) |
DRM_DEBUG_ATOMIC("Set [FB:%d] for plane state %p\n", |
DRM_DEBUG_KMS("Set [FB:%d] for plane state %p\n", |
fb->base.id, plane_state); |
else |
DRM_DEBUG_ATOMIC("Set [NOFB] for plane state %p\n", |
plane_state); |
DRM_DEBUG_KMS("Set [NOFB] for plane state %p\n", plane_state); |
} |
EXPORT_SYMBOL(drm_atomic_set_fb_for_plane); |
|
1048,10 → 454,10 |
conn_state->crtc = crtc; |
|
if (crtc) |
DRM_DEBUG_ATOMIC("Link connector state %p to [CRTC:%d]\n", |
DRM_DEBUG_KMS("Link connector state %p to [CRTC:%d]\n", |
conn_state, crtc->base.id); |
else |
DRM_DEBUG_ATOMIC("Link connector state %p to [NOCRTC]\n", |
DRM_DEBUG_KMS("Link connector state %p to [NOCRTC]\n", |
conn_state); |
|
return 0; |
1088,7 → 494,7 |
if (ret) |
return ret; |
|
DRM_DEBUG_ATOMIC("Adding all current connectors for [CRTC:%d] to %p\n", |
DRM_DEBUG_KMS("Adding all current connectors for [CRTC:%d] to %p\n", |
crtc->base.id, state); |
|
/* |
1095,7 → 501,7 |
* Changed connectors are already in @state, so only need to look at the |
* current configuration. |
*/ |
drm_for_each_connector(connector, state->dev) { |
list_for_each_entry(connector, &config->connector_list, head) { |
if (connector->state->crtc != crtc) |
continue; |
|
1109,45 → 515,6 |
EXPORT_SYMBOL(drm_atomic_add_affected_connectors); |
|
/** |
* drm_atomic_add_affected_planes - add planes for crtc |
* @state: atomic state |
* @crtc: DRM crtc |
* |
* This function walks the current configuration and adds all planes |
* currently used by @crtc to the atomic configuration @state. This is useful |
* when an atomic commit also needs to check all currently enabled plane on |
* @crtc, e.g. when changing the mode. It's also useful when re-enabling a CRTC |
* to avoid special code to force-enable all planes. |
* |
* Since acquiring a plane state will always also acquire the w/w mutex of the |
* current CRTC for that plane (if there is any) adding all the plane states for |
* a CRTC will not reduce parallism of atomic updates. |
* |
* Returns: |
* 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK |
* then the w/w mutex code has detected a deadlock and the entire atomic |
* sequence must be restarted. All other errors are fatal. |
*/ |
int |
drm_atomic_add_affected_planes(struct drm_atomic_state *state, |
struct drm_crtc *crtc) |
{ |
struct drm_plane *plane; |
|
WARN_ON(!drm_atomic_get_existing_crtc_state(state, crtc)); |
|
drm_for_each_plane_mask(plane, state->dev, crtc->state->plane_mask) { |
struct drm_plane_state *plane_state = |
drm_atomic_get_plane_state(state, plane); |
|
if (IS_ERR(plane_state)) |
return PTR_ERR(plane_state); |
} |
return 0; |
} |
EXPORT_SYMBOL(drm_atomic_add_affected_planes); |
|
/** |
* drm_atomic_connectors_for_crtc - count number of connected outputs |
* @state: atomic state |
* @crtc: DRM crtc |
1159,17 → 526,18 |
drm_atomic_connectors_for_crtc(struct drm_atomic_state *state, |
struct drm_crtc *crtc) |
{ |
struct drm_connector *connector; |
int i, num_connected_connectors = 0; |
|
for (i = 0; i < state->num_connector; i++) { |
struct drm_connector_state *conn_state; |
|
int i, num_connected_connectors = 0; |
conn_state = state->connector_states[i]; |
|
for_each_connector_in_state(state, connector, conn_state, i) { |
if (conn_state->crtc == crtc) |
if (conn_state && conn_state->crtc == crtc) |
num_connected_connectors++; |
} |
|
DRM_DEBUG_ATOMIC("State %p has %i connectors for [CRTC:%d]\n", |
DRM_DEBUG_KMS("State %p has %i connectors for [CRTC:%d]\n", |
state, num_connected_connectors, crtc->base.id); |
|
return num_connected_connectors; |
1215,49 → 583,15 |
*/ |
int drm_atomic_check_only(struct drm_atomic_state *state) |
{ |
struct drm_device *dev = state->dev; |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_plane *plane; |
struct drm_plane_state *plane_state; |
struct drm_crtc *crtc; |
struct drm_crtc_state *crtc_state; |
int i, ret = 0; |
struct drm_mode_config *config = &state->dev->mode_config; |
|
DRM_DEBUG_ATOMIC("checking %p\n", state); |
DRM_DEBUG_KMS("checking %p\n", state); |
|
for_each_plane_in_state(state, plane, plane_state, i) { |
ret = drm_atomic_plane_check(plane, plane_state); |
if (ret) { |
DRM_DEBUG_ATOMIC("[PLANE:%d] atomic core check failed\n", |
plane->base.id); |
return ret; |
} |
} |
|
for_each_crtc_in_state(state, crtc, crtc_state, i) { |
ret = drm_atomic_crtc_check(crtc, crtc_state); |
if (ret) { |
DRM_DEBUG_ATOMIC("[CRTC:%d] atomic core check failed\n", |
crtc->base.id); |
return ret; |
} |
} |
|
if (config->funcs->atomic_check) |
ret = config->funcs->atomic_check(state->dev, state); |
|
if (!state->allow_modeset) { |
for_each_crtc_in_state(state, crtc, crtc_state, i) { |
if (drm_atomic_crtc_needs_modeset(crtc_state)) { |
DRM_DEBUG_ATOMIC("[CRTC:%d] requires full modeset\n", |
crtc->base.id); |
return -EINVAL; |
return config->funcs->atomic_check(state->dev, state); |
else |
return 0; |
} |
} |
} |
|
return ret; |
} |
EXPORT_SYMBOL(drm_atomic_check_only); |
|
/** |
1285,7 → 619,7 |
if (ret) |
return ret; |
|
DRM_DEBUG_ATOMIC("commiting %p\n", state); |
DRM_DEBUG_KMS("commiting %p\n", state); |
|
return config->funcs->atomic_commit(state->dev, state, false); |
} |
1316,157 → 650,66 |
if (ret) |
return ret; |
|
DRM_DEBUG_ATOMIC("commiting %p asynchronously\n", state); |
DRM_DEBUG_KMS("commiting %p asynchronously\n", state); |
|
return config->funcs->atomic_commit(state->dev, state, true); |
} |
EXPORT_SYMBOL(drm_atomic_async_commit); |
|
/* |
* The big monstor ioctl |
/** |
* drm_atomic_helper_plane_duplicate_state - default state duplicate hook |
* @plane: drm plane |
* |
* Default plane state duplicate hook for drivers which don't have their own |
* subclassed plane state structure. |
*/ |
|
static struct drm_pending_vblank_event *create_vblank_event( |
struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data) |
struct drm_plane_state * |
drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane) |
{ |
struct drm_pending_vblank_event *e = NULL; |
unsigned long flags; |
struct drm_plane_state *state; |
|
spin_lock_irqsave(&dev->event_lock, flags); |
if (file_priv->event_space < sizeof e->event) { |
spin_unlock_irqrestore(&dev->event_lock, flags); |
goto out; |
} |
file_priv->event_space -= sizeof e->event; |
spin_unlock_irqrestore(&dev->event_lock, flags); |
if (WARN_ON(!plane->state)) |
return NULL; |
|
e = kzalloc(sizeof *e, GFP_KERNEL); |
if (e == NULL) { |
spin_lock_irqsave(&dev->event_lock, flags); |
file_priv->event_space += sizeof e->event; |
spin_unlock_irqrestore(&dev->event_lock, flags); |
goto out; |
} |
state = kmemdup(plane->state, sizeof(*plane->state), GFP_KERNEL); |
|
e->event.base.type = DRM_EVENT_FLIP_COMPLETE; |
e->event.base.length = sizeof e->event; |
e->event.user_data = user_data; |
e->base.event = &e->event.base; |
e->base.file_priv = file_priv; |
e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; |
if (state && state->fb) |
drm_framebuffer_reference(state->fb); |
|
out: |
return e; |
return state; |
} |
EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state); |
|
static void destroy_vblank_event(struct drm_device *dev, |
struct drm_file *file_priv, struct drm_pending_vblank_event *e) |
{ |
unsigned long flags; |
|
spin_lock_irqsave(&dev->event_lock, flags); |
file_priv->event_space += sizeof e->event; |
spin_unlock_irqrestore(&dev->event_lock, flags); |
kfree(e); |
} |
|
static int atomic_set_prop(struct drm_atomic_state *state, |
struct drm_mode_object *obj, struct drm_property *prop, |
uint64_t prop_value) |
/** |
* drm_atomic_helper_crtc_destroy_state - default state destroy hook |
* @crtc: drm CRTC |
* @state: CRTC state object to release |
* |
* Default CRTC state destroy hook for drivers which don't have their own |
* subclassed CRTC state structure. |
*/ |
void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, |
struct drm_crtc_state *state) |
{ |
struct drm_mode_object *ref; |
int ret; |
|
if (!drm_property_change_valid_get(prop, prop_value, &ref)) |
return -EINVAL; |
|
switch (obj->type) { |
case DRM_MODE_OBJECT_CONNECTOR: { |
struct drm_connector *connector = obj_to_connector(obj); |
struct drm_connector_state *connector_state; |
|
connector_state = drm_atomic_get_connector_state(state, connector); |
if (IS_ERR(connector_state)) { |
ret = PTR_ERR(connector_state); |
break; |
kfree(state); |
} |
EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); |
|
ret = drm_atomic_connector_set_property(connector, |
connector_state, prop, prop_value); |
break; |
} |
case DRM_MODE_OBJECT_CRTC: { |
struct drm_crtc *crtc = obj_to_crtc(obj); |
struct drm_crtc_state *crtc_state; |
|
crtc_state = drm_atomic_get_crtc_state(state, crtc); |
if (IS_ERR(crtc_state)) { |
ret = PTR_ERR(crtc_state); |
break; |
} |
|
ret = drm_atomic_crtc_set_property(crtc, |
crtc_state, prop, prop_value); |
break; |
} |
case DRM_MODE_OBJECT_PLANE: { |
struct drm_plane *plane = obj_to_plane(obj); |
struct drm_plane_state *plane_state; |
|
plane_state = drm_atomic_get_plane_state(state, plane); |
if (IS_ERR(plane_state)) { |
ret = PTR_ERR(plane_state); |
break; |
} |
|
ret = drm_atomic_plane_set_property(plane, |
plane_state, prop, prop_value); |
break; |
} |
default: |
ret = -EINVAL; |
break; |
} |
|
drm_property_change_valid_put(prop, ref); |
return ret; |
} |
|
/** |
* drm_atomic_update_old_fb -- Unset old_fb pointers and set plane->fb pointers. |
* drm_atomic_helper_plane_destroy_state - default state destroy hook |
* @plane: drm plane |
* @state: plane state object to release |
* |
* @dev: drm device to check. |
* @plane_mask: plane mask for planes that were updated. |
* @ret: return value, can be -EDEADLK for a retry. |
* |
* Before doing an update plane->old_fb is set to plane->fb, |
* but before dropping the locks old_fb needs to be set to NULL |
* and plane->fb updated. This is a common operation for each |
* atomic update, so this call is split off as a helper. |
* Default plane state destroy hook for drivers which don't have their own |
* subclassed plane state structure. |
*/ |
void drm_atomic_clean_old_fb(struct drm_device *dev, |
unsigned plane_mask, |
int ret) |
void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, |
struct drm_plane_state *state) |
{ |
struct drm_plane *plane; |
if (state->fb) |
drm_framebuffer_unreference(state->fb); |
|
/* if succeeded, fixup legacy plane crtc/fb ptrs before dropping |
* locks (ie. while it is still safe to deref plane->state). We |
* need to do this here because the driver entry points cannot |
* distinguish between legacy and atomic ioctls. |
*/ |
drm_for_each_plane_mask(plane, dev, plane_mask) { |
if (ret == 0) { |
struct drm_framebuffer *new_fb = plane->state->fb; |
if (new_fb) |
drm_framebuffer_reference(new_fb); |
plane->fb = new_fb; |
plane->crtc = plane->state->crtc; |
|
if (plane->old_fb) |
drm_framebuffer_unreference(plane->old_fb); |
kfree(state); |
} |
plane->old_fb = NULL; |
} |
} |
EXPORT_SYMBOL(drm_atomic_clean_old_fb); |
EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); |