diff options
Diffstat (limited to 'drivers/gpu/drm/drm_atomic.c')
-rw-r--r-- | drivers/gpu/drm/drm_atomic.c | 198 |
1 files changed, 127 insertions, 71 deletions
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 8ee1db866e80..8d2f111fa113 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -31,6 +31,22 @@ #include <drm/drm_mode.h> #include <drm/drm_plane_helper.h> +#include "drm_crtc_internal.h" + +static void crtc_commit_free(struct kref *kref) +{ + struct drm_crtc_commit *commit = + container_of(kref, struct drm_crtc_commit, ref); + + kfree(commit); +} + +void drm_crtc_commit_put(struct drm_crtc_commit *commit) +{ + kref_put(&commit->ref, crtc_commit_free); +} +EXPORT_SYMBOL(drm_crtc_commit_put); + /** * drm_atomic_state_default_release - * release memory initialized by drm_atomic_state_init @@ -42,11 +58,8 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state) { kfree(state->connectors); - kfree(state->connector_states); kfree(state->crtcs); - kfree(state->crtc_states); kfree(state->planes); - kfree(state->plane_states); } EXPORT_SYMBOL(drm_atomic_state_default_release); @@ -70,18 +83,10 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state) sizeof(*state->crtcs), GFP_KERNEL); if (!state->crtcs) goto fail; - state->crtc_states = kcalloc(dev->mode_config.num_crtc, - sizeof(*state->crtc_states), GFP_KERNEL); - if (!state->crtc_states) - goto fail; state->planes = kcalloc(dev->mode_config.num_total_plane, sizeof(*state->planes), GFP_KERNEL); if (!state->planes) goto fail; - state->plane_states = kcalloc(dev->mode_config.num_total_plane, - sizeof(*state->plane_states), GFP_KERNEL); - if (!state->plane_states) - goto fail; state->dev = dev; @@ -137,47 +142,48 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state); for (i = 0; i < state->num_connector; i++) { - struct drm_connector *connector = state->connectors[i]; + struct drm_connector *connector = state->connectors[i].ptr; 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, - state->connector_states[i]); - state->connectors[i] = NULL; - state->connector_states[i] = NULL; + connector->funcs->atomic_destroy_state(connector, + state->connectors[i].state); + state->connectors[i].ptr = NULL; + state->connectors[i].state = NULL; + drm_connector_unreference(connector); } for (i = 0; i < config->num_crtc; i++) { - struct drm_crtc *crtc = state->crtcs[i]; + struct drm_crtc *crtc = state->crtcs[i].ptr; if (!crtc) continue; crtc->funcs->atomic_destroy_state(crtc, - state->crtc_states[i]); - state->crtcs[i] = NULL; - state->crtc_states[i] = NULL; + state->crtcs[i].state); + + if (state->crtcs[i].commit) { + kfree(state->crtcs[i].commit->event); + state->crtcs[i].commit->event = NULL; + drm_crtc_commit_put(state->crtcs[i].commit); + } + + state->crtcs[i].commit = NULL; + state->crtcs[i].ptr = NULL; + state->crtcs[i].state = NULL; } for (i = 0; i < config->num_total_plane; i++) { - struct drm_plane *plane = state->planes[i]; + struct drm_plane *plane = state->planes[i].ptr; if (!plane) continue; plane->funcs->atomic_destroy_state(plane, - state->plane_states[i]); - state->planes[i] = NULL; - state->plane_states[i] = NULL; + state->planes[i].state); + state->planes[i].ptr = NULL; + state->planes[i].state = NULL; } } EXPORT_SYMBOL(drm_atomic_state_default_clear); @@ -261,6 +267,8 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, int ret, index = drm_crtc_index(crtc); struct drm_crtc_state *crtc_state; + WARN_ON(!state->acquire_ctx); + crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); if (crtc_state) return crtc_state; @@ -273,8 +281,8 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, if (!crtc_state) return ERR_PTR(-ENOMEM); - state->crtc_states[index] = crtc_state; - state->crtcs[index] = crtc; + state->crtcs[index].state = crtc_state; + state->crtcs[index].ptr = crtc; crtc_state->state = state; DRM_DEBUG_ATOMIC("Added [CRTC:%d:%s] %p state to %p\n", @@ -354,6 +362,8 @@ int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state, drm_property_unreference_blob(state->mode_blob); state->mode_blob = NULL; + memset(&state->mode, 0, sizeof(state->mode)); + if (blob) { if (blob->length != sizeof(struct drm_mode_modeinfo) || drm_mode_convert_umode(&state->mode, @@ -366,7 +376,6 @@ int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state, 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); @@ -395,8 +404,7 @@ drm_atomic_replace_property_blob(struct drm_property_blob **blob, if (old_blob == new_blob) return; - if (old_blob) - drm_property_unreference_blob(old_blob); + drm_property_unreference_blob(old_blob); if (new_blob) drm_property_reference_blob(new_blob); *blob = new_blob; @@ -620,6 +628,8 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, int ret, index = drm_plane_index(plane); struct drm_plane_state *plane_state; + WARN_ON(!state->acquire_ctx); + plane_state = drm_atomic_get_existing_plane_state(state, plane); if (plane_state) return plane_state; @@ -632,8 +642,8 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, if (!plane_state) return ERR_PTR(-ENOMEM); - state->plane_states[index] = plane_state; - state->planes[index] = plane; + state->planes[index].state = plane_state; + state->planes[index].ptr = plane; plane_state->state = state; DRM_DEBUG_ATOMIC("Added [PLANE:%d:%s] %p state to %p\n", @@ -888,6 +898,8 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, struct drm_mode_config *config = &connector->dev->mode_config; struct drm_connector_state *connector_state; + WARN_ON(!state->acquire_ctx); + ret = drm_modeset_lock(&config->connection_mutex, state->acquire_ctx); if (ret) return ERR_PTR(ret); @@ -895,8 +907,7 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, index = drm_connector_index(connector); if (index >= state->num_connector) { - struct drm_connector **c; - struct drm_connector_state **cs; + struct __drm_connnectors_state *c; int alloc = max(index + 1, config->num_connector); c = krealloc(state->connectors, alloc * sizeof(*state->connectors), GFP_KERNEL); @@ -907,25 +918,19 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, memset(&state->connectors[state->num_connector], 0, sizeof(*state->connectors) * (alloc - state->num_connector)); - cs = krealloc(state->connector_states, alloc * sizeof(*state->connector_states), GFP_KERNEL); - if (!cs) - return ERR_PTR(-ENOMEM); - - state->connector_states = cs; - memset(&state->connector_states[state->num_connector], 0, - sizeof(*state->connector_states) * (alloc - state->num_connector)); state->num_connector = alloc; } - if (state->connector_states[index]) - return state->connector_states[index]; + if (state->connectors[index].state) + return state->connectors[index].state; connector_state = connector->funcs->atomic_duplicate_state(connector); if (!connector_state) return ERR_PTR(-ENOMEM); - state->connector_states[index] = connector_state; - state->connectors[index] = connector; + drm_connector_reference(connector); + state->connectors[index].state = connector_state; + state->connectors[index].ptr = connector; connector_state->state = state; DRM_DEBUG_ATOMIC("Added [CONNECTOR:%d] %p state to %p\n", @@ -1158,12 +1163,18 @@ drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, { struct drm_crtc_state *crtc_state; - if (conn_state->crtc && conn_state->crtc != crtc) { + if (conn_state->crtc == crtc) + return 0; + + if (conn_state->crtc) { crtc_state = drm_atomic_get_existing_crtc_state(conn_state->state, conn_state->crtc); crtc_state->connector_mask &= ~(1 << drm_connector_index(conn_state->connector)); + + drm_connector_unreference(conn_state->connector); + conn_state->crtc = NULL; } if (crtc) { @@ -1173,16 +1184,16 @@ drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, crtc_state->connector_mask |= 1 << drm_connector_index(conn_state->connector); - } - conn_state->crtc = crtc; + drm_connector_reference(conn_state->connector); + conn_state->crtc = crtc; - if (crtc) DRM_DEBUG_ATOMIC("Link connector state %p to [CRTC:%d:%s]\n", conn_state, crtc->base.id, crtc->name); - else + } else { DRM_DEBUG_ATOMIC("Link connector state %p to [NOCRTC]\n", conn_state); + } return 0; } @@ -1287,14 +1298,39 @@ EXPORT_SYMBOL(drm_atomic_add_affected_planes); */ void drm_atomic_legacy_backoff(struct drm_atomic_state *state) { + struct drm_device *dev = state->dev; + unsigned crtc_mask = 0; + struct drm_crtc *crtc; int ret; + bool global = false; + + drm_for_each_crtc(crtc, dev) { + if (crtc->acquire_ctx != state->acquire_ctx) + continue; + + crtc_mask |= drm_crtc_mask(crtc); + crtc->acquire_ctx = NULL; + } + + if (WARN_ON(dev->mode_config.acquire_ctx == state->acquire_ctx)) { + global = true; + + dev->mode_config.acquire_ctx = NULL; + } retry: drm_modeset_backoff(state->acquire_ctx); - ret = drm_modeset_lock_all_ctx(state->dev, state->acquire_ctx); + ret = drm_modeset_lock_all_ctx(dev, state->acquire_ctx); if (ret) goto retry; + + drm_for_each_crtc(crtc, dev) + if (drm_crtc_mask(crtc) & crtc_mask) + crtc->acquire_ctx = state->acquire_ctx; + + if (global) + dev->mode_config.acquire_ctx = state->acquire_ctx; } EXPORT_SYMBOL(drm_atomic_legacy_backoff); @@ -1388,7 +1424,7 @@ int drm_atomic_commit(struct drm_atomic_state *state) EXPORT_SYMBOL(drm_atomic_commit); /** - * drm_atomic_async_commit - atomic&async configuration commit + * drm_atomic_nonblocking_commit - atomic&nonblocking configuration commit * @state: atomic configuration to check * * Note that this function can return -EDEADLK if the driver needed to acquire @@ -1403,7 +1439,7 @@ EXPORT_SYMBOL(drm_atomic_commit); * Returns: * 0 on success, negative error code on failure. */ -int drm_atomic_async_commit(struct drm_atomic_state *state) +int drm_atomic_nonblocking_commit(struct drm_atomic_state *state) { struct drm_mode_config *config = &state->dev->mode_config; int ret; @@ -1412,18 +1448,19 @@ int drm_atomic_async_commit(struct drm_atomic_state *state) if (ret) return ret; - DRM_DEBUG_ATOMIC("commiting %p asynchronously\n", state); + DRM_DEBUG_ATOMIC("commiting %p nonblocking\n", state); return config->funcs->atomic_commit(state->dev, state, true); } -EXPORT_SYMBOL(drm_atomic_async_commit); +EXPORT_SYMBOL(drm_atomic_nonblocking_commit); /* * The big monstor ioctl */ static struct drm_pending_vblank_event *create_vblank_event( - struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data) + struct drm_device *dev, struct drm_file *file_priv, + struct fence *fence, uint64_t user_data) { struct drm_pending_vblank_event *e = NULL; int ret; @@ -1436,12 +1473,17 @@ static struct drm_pending_vblank_event *create_vblank_event( e->event.base.length = sizeof(e->event); e->event.user_data = user_data; - ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base); - if (ret) { - kfree(e); - return NULL; + if (file_priv) { + ret = drm_event_reserve_init(dev, file_priv, &e->base, + &e->event.base); + if (ret) { + kfree(e); + return NULL; + } } + e->base.fence = fence; + return e; } @@ -1614,12 +1656,19 @@ retry: } obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY); - if (!obj || !obj->properties) { + if (!obj) { + ret = -ENOENT; + goto out; + } + + if (!obj->properties) { + drm_mode_object_unreference(obj); ret = -ENOENT; goto out; } if (get_user(count_props, count_props_ptr + copied_objs)) { + drm_mode_object_unreference(obj); ret = -EFAULT; goto out; } @@ -1632,12 +1681,14 @@ retry: struct drm_property *prop; if (get_user(prop_id, props_ptr + copied_props)) { + drm_mode_object_unreference(obj); ret = -EFAULT; goto out; } prop = drm_property_find(dev, prop_id); if (!prop) { + drm_mode_object_unreference(obj); ret = -ENOENT; goto out; } @@ -1645,13 +1696,16 @@ retry: if (copy_from_user(&prop_value, prop_values_ptr + copied_props, sizeof(prop_value))) { + drm_mode_object_unreference(obj); ret = -EFAULT; goto out; } ret = atomic_set_prop(state, obj, prop, prop_value); - if (ret) + if (ret) { + drm_mode_object_unreference(obj); goto out; + } copied_props++; } @@ -1662,13 +1716,15 @@ retry: plane_mask |= (1 << drm_plane_index(plane)); plane->old_fb = plane->fb; } + drm_mode_object_unreference(obj); } if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) { for_each_crtc_in_state(state, crtc, crtc_state, i) { struct drm_pending_vblank_event *e; - e = create_vblank_event(dev, file_priv, arg->user_data); + e = create_vblank_event(dev, file_priv, NULL, + arg->user_data); if (!e) { ret = -ENOMEM; goto out; @@ -1685,7 +1741,7 @@ retry: */ ret = drm_atomic_check_only(state); } else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) { - ret = drm_atomic_async_commit(state); + ret = drm_atomic_nonblocking_commit(state); } else { ret = drm_atomic_commit(state); } |