diff options
Diffstat (limited to 'drivers/gpu/drm/drm_atomic.c')
| -rw-r--r-- | drivers/gpu/drm/drm_atomic.c | 552 |
1 files changed, 416 insertions, 136 deletions
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 9ea2611770f4..080aec5a9774 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -26,7 +26,7 @@ * Daniel Vetter <daniel.vetter@ffwll.ch> */ - +#include <linux/export.h> #include <linux/sync_file.h> #include <drm/drm_atomic.h> @@ -42,6 +42,7 @@ #include <drm/drm_mode.h> #include <drm/drm_print.h> #include <drm/drm_writeback.h> +#include <drm/drm_colorop.h> #include "drm_crtc_internal.h" #include "drm_internal.h" @@ -94,34 +95,35 @@ int drm_crtc_commit_wait(struct drm_crtc_commit *commit) EXPORT_SYMBOL(drm_crtc_commit_wait); /** - * drm_atomic_state_default_release - - * release memory initialized by drm_atomic_state_init + * drm_atomic_commit_default_release - + * release memory initialized by drm_atomic_commit_init * @state: atomic state * - * Free all the memory allocated by drm_atomic_state_init. + * Free all the memory allocated by drm_atomic_commit_init. * This should only be used by drivers which are still subclassing - * &drm_atomic_state and haven't switched to &drm_private_state yet. + * &drm_atomic_commit and haven't switched to &drm_private_state yet. */ -void drm_atomic_state_default_release(struct drm_atomic_state *state) +void drm_atomic_commit_default_release(struct drm_atomic_commit *state) { kfree(state->connectors); kfree(state->crtcs); kfree(state->planes); + kfree(state->colorops); kfree(state->private_objs); } -EXPORT_SYMBOL(drm_atomic_state_default_release); +EXPORT_SYMBOL(drm_atomic_commit_default_release); /** - * drm_atomic_state_init - init new atomic state + * drm_atomic_commit_init - init new atomic state * @dev: DRM device * @state: atomic state * * Default implementation for filling in a new atomic state. * This should only be used by drivers which are still subclassing - * &drm_atomic_state and haven't switched to &drm_private_state yet. + * &drm_atomic_commit and haven't switched to &drm_private_state yet. */ int -drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state) +drm_atomic_commit_init(struct drm_device *dev, struct drm_atomic_commit *state) { kref_init(&state->ref); @@ -130,17 +132,20 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state) */ state->allow_modeset = true; - state->crtcs = kcalloc(dev->mode_config.num_crtc, - sizeof(*state->crtcs), GFP_KERNEL); + state->crtcs = kzalloc_objs(*state->crtcs, dev->mode_config.num_crtc); if (!state->crtcs) goto fail; - state->planes = kcalloc(dev->mode_config.num_total_plane, - sizeof(*state->planes), GFP_KERNEL); + state->planes = kzalloc_objs(*state->planes, + dev->mode_config.num_total_plane); if (!state->planes) goto fail; + state->colorops = kzalloc_objs(*state->colorops, + dev->mode_config.num_colorop); + if (!state->colorops) + goto fail; /* - * Because drm_atomic_state can be committed asynchronously we need our + * Because drm_atomic_commit can be committed asynchronously we need our * own reference and cannot rely on the on implied by drm_file in the * ioctl call. */ @@ -151,29 +156,29 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state) return 0; fail: - drm_atomic_state_default_release(state); + drm_atomic_commit_default_release(state); return -ENOMEM; } -EXPORT_SYMBOL(drm_atomic_state_init); +EXPORT_SYMBOL(drm_atomic_commit_init); /** - * drm_atomic_state_alloc - allocate atomic state + * drm_atomic_commit_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_atomic_commit * +drm_atomic_commit_alloc(struct drm_device *dev) { struct drm_mode_config *config = &dev->mode_config; if (!config->funcs->atomic_state_alloc) { - struct drm_atomic_state *state; + struct drm_atomic_commit *state; - state = kzalloc(sizeof(*state), GFP_KERNEL); + state = kzalloc_obj(*state); if (!state) return NULL; - if (drm_atomic_state_init(dev, state) < 0) { + if (drm_atomic_commit_init(dev, state) < 0) { kfree(state); return NULL; } @@ -182,17 +187,17 @@ drm_atomic_state_alloc(struct drm_device *dev) return config->funcs->atomic_state_alloc(dev); } -EXPORT_SYMBOL(drm_atomic_state_alloc); +EXPORT_SYMBOL(drm_atomic_commit_alloc); /** - * drm_atomic_state_default_clear - clear base atomic state + * drm_atomic_commit_default_clear - clear base atomic state * @state: atomic state * * Default implementation for clearing atomic state. * This should only be used by drivers which are still subclassing - * &drm_atomic_state and haven't switched to &drm_private_state yet. + * &drm_atomic_commit and haven't switched to &drm_private_state yet. */ -void drm_atomic_state_default_clear(struct drm_atomic_state *state) +void drm_atomic_commit_default_clear(struct drm_atomic_commit *state) { struct drm_device *dev = state->dev; struct drm_mode_config *config = &dev->mode_config; @@ -200,6 +205,8 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) drm_dbg_atomic(dev, "Clearing atomic state %p\n", state); + state->checked = false; + for (i = 0; i < state->num_connector; i++) { struct drm_connector *connector = state->connectors[i].ptr; @@ -207,9 +214,9 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) continue; connector->funcs->atomic_destroy_state(connector, - state->connectors[i].state); + state->connectors[i].state_to_destroy); state->connectors[i].ptr = NULL; - state->connectors[i].state = NULL; + state->connectors[i].state_to_destroy = NULL; state->connectors[i].old_state = NULL; state->connectors[i].new_state = NULL; drm_connector_put(connector); @@ -222,10 +229,10 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) continue; crtc->funcs->atomic_destroy_state(crtc, - state->crtcs[i].state); + state->crtcs[i].state_to_destroy); state->crtcs[i].ptr = NULL; - state->crtcs[i].state = NULL; + state->crtcs[i].state_to_destroy = NULL; state->crtcs[i].old_state = NULL; state->crtcs[i].new_state = NULL; @@ -242,20 +249,34 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) continue; plane->funcs->atomic_destroy_state(plane, - state->planes[i].state); + state->planes[i].state_to_destroy); state->planes[i].ptr = NULL; - state->planes[i].state = NULL; + state->planes[i].state_to_destroy = NULL; state->planes[i].old_state = NULL; state->planes[i].new_state = NULL; } + for (i = 0; i < config->num_colorop; i++) { + struct drm_colorop *colorop = state->colorops[i].ptr; + + if (!colorop) + continue; + + drm_colorop_atomic_destroy_state(colorop, + state->colorops[i].state); + state->colorops[i].ptr = NULL; + state->colorops[i].state = NULL; + state->colorops[i].old_state = NULL; + state->colorops[i].new_state = NULL; + } + for (i = 0; i < state->num_private_objs; i++) { struct drm_private_obj *obj = state->private_objs[i].ptr; obj->funcs->atomic_destroy_state(obj, - state->private_objs[i].state); + state->private_objs[i].state_to_destroy); state->private_objs[i].ptr = NULL; - state->private_objs[i].state = NULL; + state->private_objs[i].state_to_destroy = NULL; state->private_objs[i].old_state = NULL; state->private_objs[i].new_state = NULL; } @@ -266,10 +287,10 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) state->fake_commit = NULL; } } -EXPORT_SYMBOL(drm_atomic_state_default_clear); +EXPORT_SYMBOL(drm_atomic_commit_default_clear); /** - * drm_atomic_state_clear - clear state object + * drm_atomic_commit_clear - clear state object * @state: atomic state * * When the w/w mutex algorithm detects a deadlock we need to back off and drop @@ -282,7 +303,7 @@ EXPORT_SYMBOL(drm_atomic_state_default_clear); * Hence we must clear all cached state and completely start over, using this * function. */ -void drm_atomic_state_clear(struct drm_atomic_state *state) +void drm_atomic_commit_clear(struct drm_atomic_commit *state) { struct drm_device *dev = state->dev; struct drm_mode_config *config = &dev->mode_config; @@ -290,37 +311,37 @@ void drm_atomic_state_clear(struct drm_atomic_state *state) if (config->funcs->atomic_state_clear) config->funcs->atomic_state_clear(state); else - drm_atomic_state_default_clear(state); + drm_atomic_commit_default_clear(state); } -EXPORT_SYMBOL(drm_atomic_state_clear); +EXPORT_SYMBOL(drm_atomic_commit_clear); /** - * __drm_atomic_state_free - free all memory for an atomic state + * __drm_atomic_commit_free - free all memory for an atomic state * @ref: This atomic state to deallocate * * This frees all memory associated with an atomic state, including all the * per-object state for planes, CRTCs and connectors. */ -void __drm_atomic_state_free(struct kref *ref) +void __drm_atomic_commit_free(struct kref *ref) { - struct drm_atomic_state *state = container_of(ref, typeof(*state), ref); + struct drm_atomic_commit *state = container_of(ref, typeof(*state), ref); struct drm_device *dev = state->dev; struct drm_mode_config *config = &dev->mode_config; - drm_atomic_state_clear(state); + drm_atomic_commit_clear(state); drm_dbg_atomic(state->dev, "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); + drm_atomic_commit_default_release(state); kfree(state); } drm_dev_put(dev); } -EXPORT_SYMBOL(__drm_atomic_state_free); +EXPORT_SYMBOL(__drm_atomic_commit_free); /** * drm_atomic_get_crtc_state - get CRTC state @@ -332,7 +353,7 @@ EXPORT_SYMBOL(__drm_atomic_state_free); * is consistent. * * WARNING: Drivers may only add new CRTC states to a @state if - * drm_atomic_state.allow_modeset is set, or if it's a driver-internal commit + * drm_atomic_commit.allow_modeset is set, or if it's a driver-internal commit * not created by userspace through an IOCTL call. * * Returns: @@ -341,15 +362,16 @@ EXPORT_SYMBOL(__drm_atomic_state_free); * entire atomic sequence must be restarted. All other errors are fatal. */ struct drm_crtc_state * -drm_atomic_get_crtc_state(struct drm_atomic_state *state, +drm_atomic_get_crtc_state(struct drm_atomic_commit *state, struct drm_crtc *crtc) { int ret, index = drm_crtc_index(crtc); struct drm_crtc_state *crtc_state; WARN_ON(!state->acquire_ctx); + drm_WARN_ON(state->dev, state->checked); - crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); if (crtc_state) return crtc_state; @@ -361,7 +383,7 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, if (!crtc_state) return ERR_PTR(-ENOMEM); - state->crtcs[index].state = crtc_state; + state->crtcs[index].state_to_destroy = crtc_state; state->crtcs[index].old_state = crtc->state; state->crtcs[index].new_state = crtc_state; state->crtcs[index].ptr = crtc; @@ -441,18 +463,19 @@ static void drm_atomic_crtc_print_state(struct drm_printer *p, struct drm_crtc *crtc = state->crtc; drm_printf(p, "crtc[%u]: %s\n", crtc->base.id, crtc->name); - drm_printf(p, "\tenable=%d\n", state->enable); - drm_printf(p, "\tactive=%d\n", state->active); - drm_printf(p, "\tself_refresh_active=%d\n", state->self_refresh_active); - drm_printf(p, "\tplanes_changed=%d\n", state->planes_changed); - drm_printf(p, "\tmode_changed=%d\n", state->mode_changed); - drm_printf(p, "\tactive_changed=%d\n", state->active_changed); - drm_printf(p, "\tconnectors_changed=%d\n", state->connectors_changed); - drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed); - drm_printf(p, "\tplane_mask=%x\n", state->plane_mask); - drm_printf(p, "\tconnector_mask=%x\n", state->connector_mask); - drm_printf(p, "\tencoder_mask=%x\n", state->encoder_mask); - drm_printf(p, "\tmode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&state->mode)); + drm_printf_indent(p, 1, "enable=%d\n", state->enable); + drm_printf_indent(p, 1, "active=%d\n", state->active); + drm_printf_indent(p, 1, "self_refresh_active=%d\n", state->self_refresh_active); + drm_printf_indent(p, 1, "planes_changed=%d\n", state->planes_changed); + drm_printf_indent(p, 1, "mode_changed=%d\n", state->mode_changed); + drm_printf_indent(p, 1, "active_changed=%d\n", state->active_changed); + drm_printf_indent(p, 1, "connectors_changed=%d\n", state->connectors_changed); + drm_printf_indent(p, 1, "color_mgmt_changed=%d\n", state->color_mgmt_changed); + drm_printf_indent(p, 1, "plane_mask=%x\n", state->plane_mask); + drm_printf_indent(p, 1, "connector_mask=%x\n", state->connector_mask); + drm_printf_indent(p, 1, "encoder_mask=%x\n", state->encoder_mask); + drm_printf_indent(p, 1, "mode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&state->mode)); + drm_printf_indent(p, 1, "background_color=%llx\n", state->background_color); if (crtc->funcs->atomic_print_state) crtc->funcs->atomic_print_state(p, state); @@ -480,8 +503,8 @@ static int drm_atomic_connector_check(struct drm_connector *connector, } if (state->crtc) - crtc_state = drm_atomic_get_existing_crtc_state(state->state, - state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state->state, + state->crtc); if (writeback_job->fb && !crtc_state->active) { drm_dbg_atomic(connector->dev, @@ -521,20 +544,21 @@ static int drm_atomic_connector_check(struct drm_connector *connector, * entire atomic sequence must be restarted. All other errors are fatal. */ struct drm_plane_state * -drm_atomic_get_plane_state(struct drm_atomic_state *state, +drm_atomic_get_plane_state(struct drm_atomic_commit *state, struct drm_plane *plane) { int ret, index = drm_plane_index(plane); struct drm_plane_state *plane_state; WARN_ON(!state->acquire_ctx); + drm_WARN_ON(state->dev, state->checked); /* the legacy pointers should never be set */ WARN_ON(plane->fb); WARN_ON(plane->old_fb); WARN_ON(plane->crtc); - plane_state = drm_atomic_get_existing_plane_state(state, plane); + plane_state = drm_atomic_get_new_plane_state(state, plane); if (plane_state) return plane_state; @@ -546,7 +570,7 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, if (!plane_state) return ERR_PTR(-ENOMEM); - state->planes[index].state = plane_state; + state->planes[index].state_to_destroy = plane_state; state->planes[index].ptr = plane; state->planes[index].old_state = plane->state; state->planes[index].new_state = plane_state; @@ -568,6 +592,87 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_get_plane_state); +/** + * drm_atomic_get_colorop_state - get colorop state + * @state: global atomic state object + * @colorop: colorop to get state object for + * + * This function returns the colorop state for the given colorop, allocating it + * if needed. It will also grab the relevant plane lock to make sure that the + * state is consistent. + * + * Returns: + * + * Either the allocated state or the error code encoded into the pointer. 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. + */ +struct drm_colorop_state * +drm_atomic_get_colorop_state(struct drm_atomic_commit *state, + struct drm_colorop *colorop) +{ + int ret, index = drm_colorop_index(colorop); + struct drm_colorop_state *colorop_state; + + WARN_ON(!state->acquire_ctx); + + colorop_state = drm_atomic_get_new_colorop_state(state, colorop); + if (colorop_state) + return colorop_state; + + ret = drm_modeset_lock(&colorop->plane->mutex, state->acquire_ctx); + if (ret) + return ERR_PTR(ret); + + colorop_state = drm_atomic_helper_colorop_duplicate_state(colorop); + if (!colorop_state) + return ERR_PTR(-ENOMEM); + + state->colorops[index].state = colorop_state; + state->colorops[index].ptr = colorop; + state->colorops[index].old_state = colorop->state; + state->colorops[index].new_state = colorop_state; + colorop_state->state = state; + + drm_dbg_atomic(colorop->dev, "Added [COLOROP:%d:%d] %p state to %p\n", + colorop->base.id, colorop->type, colorop_state, state); + + return colorop_state; +} +EXPORT_SYMBOL(drm_atomic_get_colorop_state); + +/** + * drm_atomic_get_old_colorop_state - get colorop state, if it exists + * @state: global atomic state object + * @colorop: colorop to grab + * + * This function returns the old colorop state for the given colorop, or + * NULL if the colorop is not part of the global atomic state. + */ +struct drm_colorop_state * +drm_atomic_get_old_colorop_state(struct drm_atomic_commit *state, + struct drm_colorop *colorop) +{ + return state->colorops[drm_colorop_index(colorop)].old_state; +} +EXPORT_SYMBOL(drm_atomic_get_old_colorop_state); + +/** + * drm_atomic_get_new_colorop_state - get colorop state, if it exists + * @state: global atomic state object + * @colorop: colorop to grab + * + * This function returns the new colorop state for the given colorop, or + * NULL if the colorop is not part of the global atomic state. + */ +struct drm_colorop_state * +drm_atomic_get_new_colorop_state(struct drm_atomic_commit *state, + struct drm_colorop *colorop) +{ + return state->colorops[drm_colorop_index(colorop)].new_state; +} +EXPORT_SYMBOL(drm_atomic_get_new_colorop_state); + static bool plane_switching_crtc(const struct drm_plane_state *old_plane_state, const struct drm_plane_state *new_plane_state) @@ -707,6 +812,46 @@ static int drm_atomic_plane_check(const struct drm_plane_state *old_plane_state, return 0; } +static void drm_atomic_colorop_print_state(struct drm_printer *p, + const struct drm_colorop_state *state) +{ + struct drm_colorop *colorop = state->colorop; + + drm_printf(p, "colorop[%u]:\n", colorop->base.id); + drm_printf_indent(p, 1, "type=%s\n", drm_get_colorop_type_name(colorop->type)); + if (colorop->bypass_property) + drm_printf_indent(p, 1, "bypass=%u\n", state->bypass); + + switch (colorop->type) { + case DRM_COLOROP_1D_CURVE: + drm_printf_indent(p, 1, "curve_1d_type=%s\n", + drm_get_colorop_curve_1d_type_name(state->curve_1d_type)); + break; + case DRM_COLOROP_1D_LUT: + drm_printf_indent(p, 1, "size=%d\n", colorop->size); + drm_printf_indent(p, 1, "interpolation=%s\n", + drm_get_colorop_lut1d_interpolation_name(state->lut1d_interpolation)); + drm_printf_indent(p, 1, "data blob id=%d\n", state->data ? state->data->base.id : 0); + break; + case DRM_COLOROP_CTM_3X4: + drm_printf_indent(p, 1, "data blob id=%d\n", state->data ? state->data->base.id : 0); + break; + case DRM_COLOROP_MULTIPLIER: + drm_printf_indent(p, 1, "multiplier=%llu\n", state->multiplier); + break; + case DRM_COLOROP_3D_LUT: + drm_printf_indent(p, 1, "size=%d\n", colorop->size); + drm_printf_indent(p, 1, "interpolation=%s\n", + drm_get_colorop_lut3d_interpolation_name(state->lut3d_interpolation)); + drm_printf_indent(p, 1, "data blob id=%d\n", state->data ? state->data->base.id : 0); + break; + default: + break; + } + + drm_printf_indent(p, 1, "next=%d\n", colorop->next ? colorop->next->base.id : 0); +} + static void drm_atomic_plane_print_state(struct drm_printer *p, const struct drm_plane_state *state) { @@ -715,20 +860,21 @@ static void drm_atomic_plane_print_state(struct drm_printer *p, struct drm_rect dest = drm_plane_state_dest(state); drm_printf(p, "plane[%u]: %s\n", plane->base.id, plane->name); - drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)"); - drm_printf(p, "\tfb=%u\n", state->fb ? state->fb->base.id : 0); + drm_printf_indent(p, 1, "crtc=%s\n", state->crtc ? state->crtc->name : "(null)"); + drm_printf_indent(p, 1, "fb=%u\n", state->fb ? state->fb->base.id : 0); if (state->fb) drm_framebuffer_print_info(p, 2, state->fb); - drm_printf(p, "\tcrtc-pos=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&dest)); - drm_printf(p, "\tsrc-pos=" DRM_RECT_FP_FMT "\n", DRM_RECT_FP_ARG(&src)); - drm_printf(p, "\trotation=%x\n", state->rotation); - drm_printf(p, "\tnormalized-zpos=%x\n", state->normalized_zpos); - drm_printf(p, "\tcolor-encoding=%s\n", - drm_get_color_encoding_name(state->color_encoding)); - drm_printf(p, "\tcolor-range=%s\n", - drm_get_color_range_name(state->color_range)); - drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed); - + drm_printf_indent(p, 1, "crtc-pos=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&dest)); + drm_printf_indent(p, 1, "src-pos=" DRM_RECT_FP_FMT "\n", DRM_RECT_FP_ARG(&src)); + drm_printf_indent(p, 1, "rotation=%x\n", state->rotation); + drm_printf_indent(p, 1, "normalized-zpos=%x\n", state->normalized_zpos); + drm_printf_indent(p, 1, "color-encoding=%s\n", + drm_get_color_encoding_name(state->color_encoding)); + drm_printf_indent(p, 1, "color-range=%s\n", + drm_get_color_range_name(state->color_range)); + drm_printf_indent(p, 1, "color_mgmt_changed=%d\n", state->color_mgmt_changed); + drm_printf_indent(p, 1, "color-pipeline=%d\n", + state->color_pipeline ? state->color_pipeline->base.id : 0); if (plane->funcs->atomic_print_state) plane->funcs->atomic_print_state(p, state); } @@ -755,9 +901,9 @@ static void drm_atomic_plane_print_state(struct drm_printer *p, * directly. Sequence of the actual hardware state commit is not handled, * drivers might need to keep track of struct drm_crtc_commit within subclassed * structure of &drm_private_state as necessary, e.g. similar to - * &drm_plane_state.commit. See also &drm_atomic_state.fake_commit. + * &drm_plane_state.commit. See also &drm_atomic_commit.fake_commit. * - * All private state structures contained in a &drm_atomic_state update can be + * All private state structures contained in a &drm_atomic_commit update can be * iterated using for_each_oldnew_private_obj_in_state(), * for_each_new_private_obj_in_state() and for_each_old_private_obj_in_state(). * Drivers are recommended to wrap these for each type of driver private state @@ -765,7 +911,7 @@ static void drm_atomic_plane_print_state(struct drm_printer *p, * least if they want to iterate over all objects of a given type. * * An earlier way to handle driver private state was by subclassing struct - * &drm_atomic_state. But since that encourages non-standard ways to implement + * &drm_atomic_commit. But since that encourages non-standard ways to implement * the check/commit split atomic requires (by using e.g. "check and rollback or * commit instead" of "duplicate state, check, then either commit or release * duplicated state) it is deprecated in favour of using &drm_private_state. @@ -775,28 +921,35 @@ static void drm_atomic_plane_print_state(struct drm_printer *p, * drm_atomic_private_obj_init - initialize private object * @dev: DRM device this object will be attached to * @obj: private object - * @state: initial private object state * @funcs: pointer to the struct of function pointers that identify the object * type * * Initialize the private object, which can be embedded into any * driver private object that needs its own atomic state. + * + * RETURNS: + * Zero on success, error code on failure */ -void -drm_atomic_private_obj_init(struct drm_device *dev, - struct drm_private_obj *obj, - struct drm_private_state *state, - const struct drm_private_state_funcs *funcs) +int drm_atomic_private_obj_init(struct drm_device *dev, + struct drm_private_obj *obj, + const struct drm_private_state_funcs *funcs) { + struct drm_private_state *state; memset(obj, 0, sizeof(*obj)); drm_modeset_lock_init(&obj->lock); - obj->state = state; + obj->dev = dev; obj->funcs = funcs; list_add_tail(&obj->head, &dev->mode_config.privobj_list); - state->obj = obj; + state = obj->funcs->atomic_create_state(obj); + if (IS_ERR(state)) + return PTR_ERR(state); + + obj->state = state; + + return 0; } EXPORT_SYMBOL(drm_atomic_private_obj_init); @@ -828,17 +981,20 @@ EXPORT_SYMBOL(drm_atomic_private_obj_fini); * Either the allocated state or the error code encoded into a pointer. */ struct drm_private_state * -drm_atomic_get_private_obj_state(struct drm_atomic_state *state, +drm_atomic_get_private_obj_state(struct drm_atomic_commit *state, struct drm_private_obj *obj) { - int index, num_objs, i, ret; + int index, num_objs, ret; size_t size; struct __drm_private_objs_state *arr; struct drm_private_state *obj_state; - for (i = 0; i < state->num_private_objs; i++) - if (obj == state->private_objs[i].ptr) - return state->private_objs[i].state; + WARN_ON(!state->acquire_ctx); + drm_WARN_ON(state->dev, state->checked); + + obj_state = drm_atomic_get_new_private_obj_state(state, obj); + if (obj_state) + return obj_state; ret = drm_modeset_lock(&obj->lock, state->acquire_ctx); if (ret) @@ -858,7 +1014,7 @@ drm_atomic_get_private_obj_state(struct drm_atomic_state *state, if (!obj_state) return ERR_PTR(-ENOMEM); - state->private_objs[index].state = obj_state; + state->private_objs[index].state_to_destroy = obj_state; state->private_objs[index].old_state = obj->state; state->private_objs[index].new_state = obj_state; state->private_objs[index].ptr = obj; @@ -883,7 +1039,7 @@ EXPORT_SYMBOL(drm_atomic_get_private_obj_state); * or NULL if the private_obj is not part of the global atomic state. */ struct drm_private_state * -drm_atomic_get_old_private_obj_state(const struct drm_atomic_state *state, +drm_atomic_get_old_private_obj_state(const struct drm_atomic_commit *state, struct drm_private_obj *obj) { int i; @@ -905,7 +1061,7 @@ EXPORT_SYMBOL(drm_atomic_get_old_private_obj_state); * or NULL if the private_obj is not part of the global atomic state. */ struct drm_private_state * -drm_atomic_get_new_private_obj_state(const struct drm_atomic_state *state, +drm_atomic_get_new_private_obj_state(const struct drm_atomic_commit *state, struct drm_private_obj *obj) { int i; @@ -933,11 +1089,14 @@ EXPORT_SYMBOL(drm_atomic_get_new_private_obj_state); * state). This is especially true in enable hooks because the pipeline has * changed. * + * If you don't have access to the atomic state, see + * drm_atomic_get_connector_for_encoder(). + * * Returns: The old connector connected to @encoder, or NULL if the encoder is * not connected. */ struct drm_connector * -drm_atomic_get_old_connector_for_encoder(const struct drm_atomic_state *state, +drm_atomic_get_old_connector_for_encoder(const struct drm_atomic_commit *state, struct drm_encoder *encoder) { struct drm_connector_state *conn_state; @@ -967,11 +1126,14 @@ EXPORT_SYMBOL(drm_atomic_get_old_connector_for_encoder); * attached to @encoder vs ones that do (and to inspect their state). This is * especially true in disable hooks because the pipeline will change. * + * If you don't have access to the atomic state, see + * drm_atomic_get_connector_for_encoder(). + * * Returns: The new connector connected to @encoder, or NULL if the encoder is * not connected. */ struct drm_connector * -drm_atomic_get_new_connector_for_encoder(const struct drm_atomic_state *state, +drm_atomic_get_new_connector_for_encoder(const struct drm_atomic_commit *state, struct drm_encoder *encoder) { struct drm_connector_state *conn_state; @@ -988,6 +1150,59 @@ drm_atomic_get_new_connector_for_encoder(const struct drm_atomic_state *state, EXPORT_SYMBOL(drm_atomic_get_new_connector_for_encoder); /** + * drm_atomic_get_connector_for_encoder - Get connector currently assigned to an encoder + * @encoder: The encoder to find the connector of + * @ctx: Modeset locking context + * + * This function finds and returns the connector currently assigned to + * an @encoder. + * + * It is similar to the drm_atomic_get_old_connector_for_encoder() and + * drm_atomic_get_new_connector_for_encoder() helpers, but doesn't + * require access to the atomic state. If you have access to it, prefer + * using these. This helper is typically useful in situations where you + * don't have access to the atomic state, like detect, link repair, + * threaded interrupt handlers, or hooks from other frameworks (ALSA, + * CEC, etc.). + * + * Returns: + * The connector connected to @encoder, or an error pointer otherwise. + * When the error is EDEADLK, a deadlock has been detected and the + * sequence must be restarted. + */ +struct drm_connector * +drm_atomic_get_connector_for_encoder(const struct drm_encoder *encoder, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_connector_list_iter conn_iter; + struct drm_connector *out_connector = ERR_PTR(-EINVAL); + struct drm_connector *connector; + struct drm_device *dev = encoder->dev; + int ret; + + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, ctx); + if (ret) + return ERR_PTR(ret); + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (!connector->state) + continue; + + if (encoder == connector->state->best_encoder) { + out_connector = connector; + break; + } + } + drm_connector_list_iter_end(&conn_iter); + drm_modeset_unlock(&dev->mode_config.connection_mutex); + + return out_connector; +} +EXPORT_SYMBOL(drm_atomic_get_connector_for_encoder); + + +/** * drm_atomic_get_old_crtc_for_encoder - Get old crtc for an encoder * @state: Atomic state * @encoder: The encoder to fetch the crtc state for @@ -999,7 +1214,7 @@ EXPORT_SYMBOL(drm_atomic_get_new_connector_for_encoder); * not connected. */ struct drm_crtc * -drm_atomic_get_old_crtc_for_encoder(struct drm_atomic_state *state, +drm_atomic_get_old_crtc_for_encoder(struct drm_atomic_commit *state, struct drm_encoder *encoder) { struct drm_connector *connector; @@ -1029,7 +1244,7 @@ EXPORT_SYMBOL(drm_atomic_get_old_crtc_for_encoder); * not connected. */ struct drm_crtc * -drm_atomic_get_new_crtc_for_encoder(struct drm_atomic_state *state, +drm_atomic_get_new_crtc_for_encoder(struct drm_atomic_commit *state, struct drm_encoder *encoder) { struct drm_connector *connector; @@ -1062,7 +1277,7 @@ EXPORT_SYMBOL(drm_atomic_get_new_crtc_for_encoder); * entire atomic sequence must be restarted. All other errors are fatal. */ struct drm_connector_state * -drm_atomic_get_connector_state(struct drm_atomic_state *state, +drm_atomic_get_connector_state(struct drm_atomic_commit *state, struct drm_connector *connector) { int ret, index; @@ -1070,6 +1285,7 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, struct drm_connector_state *connector_state; WARN_ON(!state->acquire_ctx); + drm_WARN_ON(state->dev, state->checked); ret = drm_modeset_lock(&config->connection_mutex, state->acquire_ctx); if (ret) @@ -1093,15 +1309,16 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, state->num_connector = alloc; } - if (state->connectors[index].state) - return state->connectors[index].state; + connector_state = drm_atomic_get_new_connector_state(state, connector); + if (connector_state) + return connector_state; connector_state = connector->funcs->atomic_duplicate_state(connector); if (!connector_state) return ERR_PTR(-ENOMEM); drm_connector_get(connector); - state->connectors[index].state = connector_state; + state->connectors[index].state_to_destroy = connector_state; state->connectors[index].old_state = connector->state; state->connectors[index].new_state = connector_state; state->connectors[index].ptr = connector; @@ -1130,27 +1347,27 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, struct drm_connector *connector = state->connector; drm_printf(p, "connector[%u]: %s\n", connector->base.id, connector->name); - drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)"); - drm_printf(p, "\tself_refresh_aware=%d\n", state->self_refresh_aware); - drm_printf(p, "\tinterlace_allowed=%d\n", connector->interlace_allowed); - drm_printf(p, "\tycbcr_420_allowed=%d\n", connector->ycbcr_420_allowed); - drm_printf(p, "\tmax_requested_bpc=%d\n", state->max_requested_bpc); - drm_printf(p, "\tcolorspace=%s\n", drm_get_colorspace_name(state->colorspace)); + drm_printf_indent(p, 1, "crtc=%s\n", state->crtc ? state->crtc->name : "(null)"); + drm_printf_indent(p, 1, "self_refresh_aware=%d\n", state->self_refresh_aware); + drm_printf_indent(p, 1, "interlace_allowed=%d\n", connector->interlace_allowed); + drm_printf_indent(p, 1, "ycbcr_420_allowed=%d\n", connector->ycbcr_420_allowed); + drm_printf_indent(p, 1, "max_requested_bpc=%d\n", state->max_requested_bpc); + drm_printf_indent(p, 1, "colorspace=%s\n", drm_get_colorspace_name(state->colorspace)); if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) { - drm_printf(p, "\tbroadcast_rgb=%s\n", - drm_hdmi_connector_get_broadcast_rgb_name(state->hdmi.broadcast_rgb)); - drm_printf(p, "\tis_limited_range=%c\n", state->hdmi.is_limited_range ? 'y' : 'n'); - drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc); - drm_printf(p, "\toutput_format=%s\n", - drm_hdmi_connector_get_output_format_name(state->hdmi.output_format)); - drm_printf(p, "\ttmds_char_rate=%llu\n", state->hdmi.tmds_char_rate); + drm_printf_indent(p, 1, "broadcast_rgb=%s\n", + drm_hdmi_connector_get_broadcast_rgb_name(state->hdmi.broadcast_rgb)); + drm_printf_indent(p, 1, "is_limited_range=%c\n", state->hdmi.is_limited_range ? 'y' : 'n'); + drm_printf_indent(p, 1, "output_bpc=%u\n", state->hdmi.output_bpc); + drm_printf_indent(p, 1, "output_format=%s\n", + drm_hdmi_connector_get_output_format_name(state->hdmi.output_format)); + drm_printf_indent(p, 1, "tmds_char_rate=%llu\n", state->hdmi.tmds_char_rate); } if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) if (state->writeback_job && state->writeback_job->fb) - drm_printf(p, "\tfb=%d\n", state->writeback_job->fb->base.id); + drm_printf_indent(p, 1, "fb=%d\n", state->writeback_job->fb->base.id); if (connector->funcs->atomic_print_state) connector->funcs->atomic_print_state(p, state); @@ -1171,7 +1388,7 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, * entire atomic sequence must be restarted. */ struct drm_bridge_state * -drm_atomic_get_bridge_state(struct drm_atomic_state *state, +drm_atomic_get_bridge_state(struct drm_atomic_commit *state, struct drm_bridge *bridge) { struct drm_private_state *obj_state; @@ -1193,7 +1410,7 @@ EXPORT_SYMBOL(drm_atomic_get_bridge_state); * the bridge is not part of the global atomic state. */ struct drm_bridge_state * -drm_atomic_get_old_bridge_state(const struct drm_atomic_state *state, +drm_atomic_get_old_bridge_state(const struct drm_atomic_commit *state, struct drm_bridge *bridge) { struct drm_private_state *obj_state; @@ -1215,7 +1432,7 @@ EXPORT_SYMBOL(drm_atomic_get_old_bridge_state); * the bridge is not part of the global atomic state. */ struct drm_bridge_state * -drm_atomic_get_new_bridge_state(const struct drm_atomic_state *state, +drm_atomic_get_new_bridge_state(const struct drm_atomic_commit *state, struct drm_bridge *bridge) { struct drm_private_state *obj_state; @@ -1245,11 +1462,10 @@ EXPORT_SYMBOL(drm_atomic_get_new_bridge_state); * sequence must be restarted. All other errors are fatal. */ int -drm_atomic_add_encoder_bridges(struct drm_atomic_state *state, +drm_atomic_add_encoder_bridges(struct drm_atomic_commit *state, struct drm_encoder *encoder) { struct drm_bridge_state *bridge_state; - struct drm_bridge *bridge; if (!encoder) return 0; @@ -1258,7 +1474,7 @@ drm_atomic_add_encoder_bridges(struct drm_atomic_state *state, "Adding all bridges for [encoder:%d:%s] to %p\n", encoder->base.id, encoder->name, state); - drm_for_each_bridge_in_chain(encoder, bridge) { + drm_for_each_bridge_in_chain_scoped(encoder, bridge) { /* Skip bridges that don't implement the atomic state hooks. */ if (!bridge->funcs->atomic_duplicate_state) continue; @@ -1290,7 +1506,7 @@ EXPORT_SYMBOL(drm_atomic_add_encoder_bridges); * sequence must be restarted. All other errors are fatal. */ int -drm_atomic_add_affected_connectors(struct drm_atomic_state *state, +drm_atomic_add_affected_connectors(struct drm_atomic_commit *state, struct drm_crtc *crtc) { struct drm_mode_config *config = &state->dev->mode_config; @@ -1354,12 +1570,13 @@ EXPORT_SYMBOL(drm_atomic_add_affected_connectors); * sequence must be restarted. All other errors are fatal. */ int -drm_atomic_add_affected_planes(struct drm_atomic_state *state, +drm_atomic_add_affected_planes(struct drm_atomic_commit *state, struct drm_crtc *crtc) { const struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); struct drm_plane *plane; + int ret; WARN_ON(!drm_atomic_get_new_crtc_state(state, crtc)); @@ -1373,12 +1590,64 @@ drm_atomic_add_affected_planes(struct drm_atomic_state *state, if (IS_ERR(plane_state)) return PTR_ERR(plane_state); + + if (plane_state->color_pipeline) { + ret = drm_atomic_add_affected_colorops(state, plane); + if (ret) + return ret; + } } return 0; } EXPORT_SYMBOL(drm_atomic_add_affected_planes); /** + * drm_atomic_add_affected_colorops - add colorops for plane + * @state: atomic state + * @plane: DRM plane + * + * This function walks the current configuration and adds all colorops + * currently used by @plane to the atomic configuration @state. This is useful + * when an atomic commit also needs to check all currently enabled colorop on + * @plane, e.g. when changing the mode. It's also useful when re-enabling a plane + * to avoid special code to force-enable all colorops. + * + * Since acquiring a colorop state will always also acquire the w/w mutex of the + * current plane for that colorop (if there is any) adding all the colorop states for + * a plane will not reduce parallelism 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_colorops(struct drm_atomic_commit *state, + struct drm_plane *plane) +{ + struct drm_colorop *colorop; + struct drm_colorop_state *colorop_state; + + WARN_ON(!drm_atomic_get_new_plane_state(state, plane)); + + drm_dbg_atomic(plane->dev, + "Adding all current colorops for [PLANE:%d:%s] to %p\n", + plane->base.id, plane->name, state); + + drm_for_each_colorop(colorop, plane->dev) { + if (colorop->plane != plane) + continue; + + colorop_state = drm_atomic_get_colorop_state(state, colorop); + if (IS_ERR(colorop_state)) + return PTR_ERR(colorop_state); + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_add_affected_colorops); + +/** * drm_atomic_check_only - check whether a given config would work * @state: atomic configuration to check * @@ -1389,7 +1658,7 @@ EXPORT_SYMBOL(drm_atomic_add_affected_planes); * Returns: * 0 on success, negative error code on failure. */ -int drm_atomic_check_only(struct drm_atomic_state *state) +int drm_atomic_check_only(struct drm_atomic_commit *state) { struct drm_device *dev = state->dev; struct drm_mode_config *config = &dev->mode_config; @@ -1482,6 +1751,8 @@ int drm_atomic_check_only(struct drm_atomic_state *state) requested_crtc, affected_crtc); } + state->checked = true; + return 0; } EXPORT_SYMBOL(drm_atomic_check_only); @@ -1495,12 +1766,12 @@ EXPORT_SYMBOL(drm_atomic_check_only); * backoff dance and restart. All other errors are fatal. * * This function will take its own reference on @state. - * Callers should always release their reference with drm_atomic_state_put(). + * Callers should always release their reference with drm_atomic_commit_put(). * * Returns: * 0 on success, negative error code on failure. */ -int drm_atomic_commit(struct drm_atomic_state *state) +int drm_atomic_commit(struct drm_atomic_commit *state) { struct drm_mode_config *config = &state->dev->mode_config; struct drm_printer p = drm_info_printer(state->dev->dev); @@ -1528,12 +1799,12 @@ EXPORT_SYMBOL(drm_atomic_commit); * backoff dance and restart. All other errors are fatal. * * This function will take its own reference on @state. - * Callers should always release their reference with drm_atomic_state_put(). + * Callers should always release their reference with drm_atomic_commit_put(). * * Returns: * 0 on success, negative error code on failure. */ -int drm_atomic_nonblocking_commit(struct drm_atomic_state *state) +int drm_atomic_nonblocking_commit(struct drm_atomic_commit *state) { struct drm_mode_config *config = &state->dev->mode_config; int ret; @@ -1572,7 +1843,7 @@ int __drm_atomic_helper_disable_plane(struct drm_plane *plane, } EXPORT_SYMBOL(__drm_atomic_helper_disable_plane); -static int update_output_state(struct drm_atomic_state *state, +static int update_output_state(struct drm_atomic_commit *state, struct drm_mode_set *set) { struct drm_device *dev = set->crtc->dev; @@ -1642,7 +1913,7 @@ static int update_output_state(struct drm_atomic_state *state, /* just used from drm-client and atomic-helper: */ int __drm_atomic_helper_set_config(struct drm_mode_set *set, - struct drm_atomic_state *state) + struct drm_atomic_commit *state) { struct drm_crtc_state *crtc_state; struct drm_plane_state *primary_state; @@ -1736,7 +2007,7 @@ static void drm_atomic_private_obj_print_state(struct drm_printer *p, * Note that this function looks into the new state objects and hence its not * safe to be used after the call to drm_atomic_helper_commit_hw_done(). */ -void drm_atomic_print_new_state(const struct drm_atomic_state *state, +void drm_atomic_print_new_state(const struct drm_atomic_commit *state, struct drm_printer *p) { struct drm_plane *plane; @@ -1774,6 +2045,7 @@ static void __drm_state_dump(struct drm_device *dev, struct drm_printer *p, bool take_locks) { struct drm_mode_config *config = &dev->mode_config; + struct drm_colorop *colorop; struct drm_plane *plane; struct drm_crtc *crtc; struct drm_connector *connector; @@ -1783,6 +2055,14 @@ static void __drm_state_dump(struct drm_device *dev, struct drm_printer *p, if (!drm_drv_uses_atomic_modeset(dev)) return; + list_for_each_entry(colorop, &config->colorop_list, head) { + if (take_locks) + drm_modeset_lock(&colorop->plane->mutex, NULL); + drm_atomic_colorop_print_state(p, colorop->state); + if (take_locks) + drm_modeset_unlock(&colorop->plane->mutex); + } + list_for_each_entry(plane, &config->plane_list, head) { if (take_locks) drm_modeset_lock(&plane->mutex, NULL); |
