Break the mutable state of a crtc out into a separate structure and use atomic properties mechanism to set crtc attributes. This makes it easier to have some helpers for crtc->set_property() and for checking for invalid params. The idea is that individual drivers can wrap the state struct in their own struct which adds driver specific parameters, for easy build-up of state across multiple set_property() calls and for easy atomic commit or roll- back. Signed-off-by: Rob Clark <robdclark@xxxxxxxxx> --- drivers/gpu/drm/armada/armada_crtc.c | 11 +- drivers/gpu/drm/ast/ast_mode.c | 1 + drivers/gpu/drm/cirrus/cirrus_mode.c | 1 + drivers/gpu/drm/drm_atomic.c | 231 ++++++++++- drivers/gpu/drm/drm_crtc.c | 631 +++++++++++++++++++---------- drivers/gpu/drm/drm_fb_helper.c | 24 +- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 7 +- drivers/gpu/drm/gma500/cdv_intel_display.c | 1 + drivers/gpu/drm/gma500/psb_intel_display.c | 1 + drivers/gpu/drm/i915/intel_display.c | 1 + drivers/gpu/drm/mgag200/mgag200_mode.c | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 6 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 6 +- drivers/gpu/drm/nouveau/dispnv04/crtc.c | 1 + drivers/gpu/drm/nouveau/nv50_display.c | 1 + drivers/gpu/drm/omapdrm/omap_crtc.c | 12 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/qxl/qxl_display.c | 2 + drivers/gpu/drm/radeon/radeon_display.c | 2 + drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 2 + drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 2 + drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 1 + drivers/gpu/drm/udl/udl_modeset.c | 2 + drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 1 + include/drm/drm_atomic.h | 29 ++ include/drm/drm_crtc.h | 103 ++++- 27 files changed, 837 insertions(+), 246 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 7d3c649..6237af4 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -9,6 +9,7 @@ #include <linux/clk.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include "armada_crtc.h" #include "armada_drm.h" #include "armada_fb.h" @@ -966,7 +967,12 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc, { struct armada_private *priv = crtc->dev->dev_private; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); + struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); bool update_csc = false; + int ret = 0; + + if (IS_ERR(cstate)) + return PTR_ERR(cstate); if (property == priv->csc_yuv_prop) { dcrtc->csc_yuv_mode = val; @@ -974,6 +980,9 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc, } else if (property == priv->csc_rgb_prop) { dcrtc->csc_rgb_mode = val; update_csc = true; + } else { + ret = drm_crtc_set_property(crtc, cstate, property, + val, blob_data); } if (update_csc) { @@ -984,7 +993,7 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc, writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL); } - return 0; + return ret; } static struct drm_crtc_funcs armada_crtc_funcs = { diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 114aee9..c08e0e1 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -632,6 +632,7 @@ static const struct drm_crtc_funcs ast_crtc_funcs = { .cursor_move = ast_cursor_move, .reset = ast_crtc_reset, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .gamma_set = ast_crtc_gamma_set, .destroy = ast_crtc_destroy, }; diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index 49332c5..3c4428c 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -366,6 +366,7 @@ static void cirrus_crtc_destroy(struct drm_crtc *crtc) static const struct drm_crtc_funcs cirrus_crtc_funcs = { .gamma_set = cirrus_crtc_gamma_set, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = cirrus_crtc_destroy, }; diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index ac12d9b..00b1c13 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -49,11 +49,13 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, struct drm_atomic_state *state; uint32_t acquire_flags = 0; int nplanes = dev->mode_config.num_total_plane; + int ncrtcs = dev->mode_config.num_crtc; int sz; void *ptr; sz = sizeof(*state); sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes; + sz += (sizeof(state->crtcs) + sizeof(state->cstates)) * ncrtcs; ptr = kzalloc(sz, GFP_KERNEL); @@ -78,6 +80,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, state->pstates = ptr; ptr = &state->pstates[nplanes]; + state->crtcs = ptr; + ptr = &state->crtcs[ncrtcs]; + + state->cstates = ptr; + ptr = &state->cstates[ncrtcs]; + return state; } EXPORT_SYMBOL(drm_atomic_begin); @@ -96,7 +104,18 @@ int drm_atomic_set_event(struct drm_device *dev, struct drm_atomic_state *state, struct drm_mode_object *obj, struct drm_pending_vblank_event *event) { - return -EINVAL; /* for now */ + switch (obj->type) { + case DRM_MODE_OBJECT_CRTC: { + struct drm_crtc_state *cstate = + drm_atomic_get_crtc_state(obj_to_crtc(obj), state); + if (IS_ERR(cstate)) + return PTR_ERR(cstate); + cstate->event = event; + return 0; + } + default: + return -EINVAL; + } } EXPORT_SYMBOL(drm_atomic_set_event); @@ -115,6 +134,7 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_atomic_state *a = state; int nplanes = dev->mode_config.num_total_plane; + int ncrtcs = dev->mode_config.num_crtc; int i, ret = 0; for (i = 0; i < nplanes; i++) { @@ -124,6 +144,13 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) break; } } + for (i = 0; i < ncrtcs; i++) { + if (a->crtcs[i]) { + ret = drm_atomic_check_crtc_state(a->crtcs[i], a->cstates[i]); + if (ret) + break; + } + } a->acquire_ctx.frozen = true; @@ -207,6 +234,7 @@ static void commit_locks(struct drm_atomic_state *a, { struct drm_device *dev = a->dev; int nplanes = dev->mode_config.num_total_plane; + int ncrtcs = dev->mode_config.num_crtc; int i; for (i = 0; i < nplanes; i++) { @@ -217,6 +245,14 @@ static void commit_locks(struct drm_atomic_state *a, } } + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc *crtc = a->crtcs[i]; + if (crtc) { + crtc->state->state = NULL; + drm_crtc_destroy_state(crtc, a->cstates[i]); + } + } + /* and properly release them (clear in_atomic, remove from list): */ drm_modeset_drop_locks(&a->acquire_ctx); ww_acquire_fini(ww_ctx); @@ -227,8 +263,18 @@ static int atomic_commit(struct drm_atomic_state *a, struct ww_acquire_ctx *ww_ctx) { int nplanes = a->dev->mode_config.num_total_plane; + int ncrtcs = a->dev->mode_config.num_crtc; int i, ret = 0; + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc *crtc = a->crtcs[i]; + if (crtc) { + ret = drm_atomic_commit_crtc_state(crtc, a->cstates[i]); + if (ret) + break; + } + } + for (i = 0; i < nplanes; i++) { struct drm_plane *plane = a->planes[i]; if (plane) { @@ -407,6 +453,7 @@ static int commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) { struct drm_atomic_state *a = pstate->state; + struct drm_crtc_state *cstate = NULL; struct drm_framebuffer *old_fb = plane->fb; struct drm_framebuffer *fb = pstate->fb; bool enabled = pstate->crtc && fb; @@ -429,8 +476,11 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) } } else { struct drm_crtc *crtc = pstate->crtc; + cstate = drm_atomic_get_crtc_state(crtc, pstate->state); if (pstate->update_plane || (pstate->new_fb && !can_flip(plane, pstate))) { +/* TODO pass event to update_plane().. */ +WARN_ON(cstate->event); ret = plane->funcs->update_plane(plane, crtc, pstate->fb, pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h, @@ -447,7 +497,7 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) } } else if (pstate->new_fb) { - ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags); + ret = crtc->funcs->page_flip(crtc, fb, cstate->event, a->flags); if (ret == 0) { /* * Warn if the driver hasn't properly updated the plane->fb @@ -477,9 +527,10 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) * original code. */ swap_plane_state(plane, pstate->state); + if (cstate) + cstate->event = NULL; } - if (fb) drm_framebuffer_unreference(fb); if (old_fb) @@ -488,8 +539,182 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) return ret; } +int drm_atomic_crtc_set_property(struct drm_crtc *crtc, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) +{ + struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); + if (IS_ERR(cstate)) + return PTR_ERR(cstate); + return drm_crtc_set_property(crtc, cstate, property, val, blob_data); +} +EXPORT_SYMBOL(drm_atomic_crtc_set_property); + +static void init_crtc_state(struct drm_crtc *crtc, + struct drm_crtc_state *cstate, struct drm_atomic_state *state) +{ + /* snapshot current state: */ + *cstate = *crtc->state; + cstate->state = state; + + if (cstate->connector_ids) { + int sz = cstate->num_connector_ids * sizeof(cstate->connector_ids[0]); + cstate->connector_ids = kmemdup(cstate->connector_ids, sz, GFP_KERNEL); + } + + /* this should never happen.. but make sure! */ + WARN_ON(cstate->event); + cstate->event = NULL; +} + +struct drm_crtc_state * +drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a) +{ + struct drm_crtc_state *cstate; + int ret; + + cstate = a->cstates[crtc->id]; + + if (!cstate) { + ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx); + if (ret) + return ERR_PTR(ret); + + cstate = drm_crtc_create_state(crtc); + if (!cstate) + return ERR_PTR(-ENOMEM); + init_crtc_state(crtc, cstate, a); + a->crtcs[crtc->id] = crtc; + a->cstates[crtc->id] = cstate; + + /* we'll need it later, so make sure we have state + * for primary plane too: + */ + drm_atomic_get_plane_state(crtc->primary, a); + } + return cstate; +} +EXPORT_SYMBOL(drm_atomic_get_crtc_state); + +static void +swap_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a) +{ + struct drm_crtc_state *cstate = a->cstates[crtc->id]; + struct drm_device *dev = crtc->dev; + struct drm_pending_vblank_event *event = cstate->event; + + if (event) { + /* hrm, need to sort out a better way to send events for + * other-than-pageflip.. but modeset is not async, so: + */ + unsigned long flags; + spin_lock_irqsave(&dev->event_lock, flags); + drm_send_vblank_event(dev, crtc->id, event); + cstate->event = NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); + } + + /* clear transient state (only valid during atomic update): */ + cstate->set_config = false; + cstate->connectors_change = false; + + swap(crtc->state, a->cstates[crtc->id]); + crtc->base.propvals = &crtc->state->propvals; +} + +static struct drm_connector **get_connector_set(struct drm_device *dev, + uint32_t *connector_ids, uint32_t num_connector_ids) +{ + struct drm_connector **connector_set = NULL; + int i; + + connector_set = kmalloc(num_connector_ids * + sizeof(struct drm_connector *), + GFP_KERNEL); + if (!connector_set) + return NULL; + + for (i = 0; i < num_connector_ids; i++) + connector_set[i] = drm_connector_find(dev, connector_ids[i]); + + return connector_set; +} + +static int set_config(struct drm_crtc *crtc, struct drm_crtc_state *cstate) +{ + struct drm_device *dev = crtc->dev; + struct drm_plane_state *pstate = + drm_atomic_get_plane_state(crtc->primary, cstate->state); + struct drm_framebuffer *fb = pstate->fb; + struct drm_connector **connector_set = get_connector_set(crtc->dev, + cstate->connector_ids, cstate->num_connector_ids); + struct drm_display_mode *mode = drm_crtc_get_mode(crtc, cstate); + struct drm_mode_set set = { + .crtc = crtc, + .x = pstate->src_x >> 16, + .y = pstate->src_y >> 16, + .mode = mode, + .num_connectors = cstate->num_connector_ids, + .connectors = connector_set, + .fb = fb, + }; + int ret; + + if (IS_ERR(mode)) { + ret = PTR_ERR(mode); + return ret; + } + + if (fb) + drm_framebuffer_reference(fb); + + ret = drm_mode_set_config_internal(&set); + if (!ret) { + swap_crtc_state(crtc, cstate->state); + pstate->new_fb = pstate->update_plane = false; + } + + if (fb) + drm_framebuffer_unreference(fb); + + kfree(connector_set); + if (mode) + drm_mode_destroy(dev, mode); + return ret; +} + +static int +commit_crtc_state(struct drm_crtc *crtc, + struct drm_crtc_state *cstate) +{ + struct drm_plane_state *pstate = + drm_atomic_get_plane_state(crtc->primary, cstate->state); + int ret = -EINVAL; + + if (cstate->set_config) + return set_config(crtc, cstate); + + if (!pstate->fb) { + /* disable */ + struct drm_mode_set set = { + .crtc = crtc, + .fb = NULL, + }; + + ret = drm_mode_set_config_internal(&set); + if (!ret) { + swap_crtc_state(crtc, cstate->state); + } + } + + return ret; +} + const struct drm_atomic_funcs drm_atomic_funcs = { .check_plane_state = drm_plane_check_state, .commit_plane_state = commit_plane_state, + + .check_crtc_state = drm_crtc_check_state, + .commit_crtc_state = commit_crtc_state, }; EXPORT_SYMBOL(drm_atomic_funcs); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index a9ecf52..8024add 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -685,10 +685,7 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); void drm_framebuffer_remove(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev; - struct drm_crtc *crtc; struct drm_plane *plane; - struct drm_mode_set set; - int ret; WARN_ON(!list_empty(&fb->filp_head)); @@ -708,7 +705,9 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) * in this manner. */ if (atomic_read(&fb->refcount.refcount) > 1) { - void *state; + struct drm_mode_config *config = &dev->mode_config; + struct drm_atomic_state *state; + int ret; state = dev->driver->atomic_begin(dev, 0); if (IS_ERR(state)) { @@ -716,24 +715,15 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) return; } - /* TODO once CRTC is converted to state/properties, we can push the - * locking down into drm_atomic_commit(), since that is where - * the actual changes take place.. - */ - drm_modeset_lock_all(dev); - /* remove from any CRTC */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->primary->fb == fb) { - /* should turn off the crtc */ - memset(&set, 0, sizeof(struct drm_mode_set)); - set.crtc = crtc; - set.fb = NULL; - ret = drm_mode_set_config_internal(&set); - if (ret) - DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); - } - } +retry: + ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx); + if (ret) + goto out; + ret = drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx); + if (ret) + goto out; + /* remove from any plane */ list_for_each_entry(plane, &dev->mode_config.plane_list, head) { if (plane->fb == fb) drm_plane_force_disable(plane, state); @@ -745,9 +735,12 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) else dev->driver->atomic_commit(dev, state); +out: + if (ret == -EDEADLK) { + drm_modeset_backoff(&state->acquire_ctx); + goto retry; + } dev->driver->atomic_end(dev, state); - - drm_modeset_unlock_all(dev); } drm_framebuffer_unreference(fb); @@ -778,9 +771,13 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, struct drm_mode_config *config = &dev->mode_config; int ret; + /* this is now required: */ + WARN_ON(!funcs->set_property); + crtc->dev = dev; crtc->funcs = funcs; - crtc->invert_dimensions = false; + crtc->state = drm_crtc_create_state(crtc); + crtc->state->invert_dimensions = false; drm_modeset_lock_all(dev); drm_modeset_lock_init(&crtc->mutex); @@ -792,14 +789,17 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, goto out; crtc->base.properties = &crtc->properties; - crtc->base.propvals = &crtc->propvals; + crtc->base.propvals = &crtc->state->propvals; list_add_tail(&crtc->head, &config->crtc_list); config->num_crtc++; crtc->primary = primary; if (primary) - primary->possible_crtcs = 1 << drm_crtc_index(crtc); + primary->possible_crtcs = 1 << crtc->id; + + drm_object_attach_property(&crtc->base, config->prop_mode, 0); + drm_object_attach_property(&crtc->base, config->prop_connector_ids, 0); out: drm_modeset_unlock_all(dev); @@ -828,31 +828,245 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) drm_mode_object_put(dev, &crtc->base); list_del(&crtc->head); dev->mode_config.num_crtc--; + + drm_crtc_destroy_state(crtc, crtc->state); } EXPORT_SYMBOL(drm_crtc_cleanup); -/** - * drm_crtc_index - find the index of a registered CRTC - * @crtc: CRTC to find index for - * - * Given a registered CRTC, return the index of that CRTC within a DRM - * device's list of CRTCs. - */ -unsigned int drm_crtc_index(struct drm_crtc *crtc) +/* get display-mode from user-mode */ +struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc, + struct drm_crtc_state *cstate) { - unsigned int index = 0; - struct drm_crtc *tmp; + struct drm_display_mode *mode = NULL; + if (cstate->mode_valid) { + struct drm_device *dev = crtc->dev; + int ret; - list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { - if (tmp == crtc) - return index; + mode = drm_mode_create(dev); + if (!mode) + return ERR_PTR(-ENOMEM); + + ret = drm_crtc_convert_umode(mode, &cstate->mode); + if (ret) { + DRM_DEBUG_KMS("Invalid mode\n"); + drm_mode_destroy(dev, mode); + return ERR_PTR(ret); + } - index++; + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); } + return mode; +} - BUG(); +static int connector_idx(struct drm_crtc_state *state, + uint32_t connector_id) +{ + int i; + for (i = 0; i < state->num_connector_ids; i++) + if (state->connector_ids[i] == connector_id) + return i; + return -1; +} + +static int remove_connector(struct drm_crtc *ocrtc, + struct drm_crtc_state *ostate, struct drm_atomic_state *state, + int idx) +{ + struct drm_mode_config *config = &ocrtc->dev->mode_config; + uint32_t *new_connector_ids; + int a, b; + + /* before deletion point: */ + a = idx * sizeof(ostate->connector_ids[0]); + + /* after deletion point: */ + b = (ostate->num_connector_ids - 1 - idx) * + sizeof(ostate->connector_ids[0]); + + new_connector_ids = kmalloc(a+b, GFP_KERNEL); + if (!new_connector_ids) + return -ENOMEM; + + memcpy(new_connector_ids, ostate->connector_ids, a); + memcpy(&new_connector_ids[idx], + &ostate->connector_ids[idx + 1], b); + + return drm_mode_crtc_set_obj_prop(ocrtc, state, + config->prop_connector_ids, a + b, + new_connector_ids); +} + +static int check_connectors(struct drm_crtc *crtc, + struct drm_atomic_state *state, bool fix, + uint32_t *connector_ids, uint32_t num_connector_ids) +{ + struct drm_mode_config *config = &crtc->dev->mode_config; + struct drm_crtc *ocrtc; /* other connector */ + + list_for_each_entry(ocrtc, &config->crtc_list, head) { + struct drm_crtc_state *ostate; /* other state */ + unsigned i; + + if (ocrtc == crtc) + continue; + + ostate = drm_atomic_get_crtc_state(crtc, state); + if (IS_ERR(ostate)) + return PTR_ERR(ostate); + + for (i = 0; i < num_connector_ids; i++) { + struct drm_connector *connector; + uint32_t cid = connector_ids[i]; + int idx; + +retry: + idx = connector_idx(ostate, cid); + if (idx < 0) + continue; + + if (fix) { + int ret = remove_connector(ocrtc, + ostate, state, idx); + if (ret) + return ret; + goto retry; + } + + connector = drm_connector_find(crtc->dev, cid); + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] already in use\n", + connector->base.id, + drm_get_connector_name(connector)); + return -EINVAL; + } + } + + return 0; +} + +int drm_crtc_check_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct drm_plane *primary = crtc->primary; + struct drm_plane_state *pstate = + drm_atomic_get_plane_state(primary, state->state); + struct drm_framebuffer *fb = pstate->fb; + int hdisplay, vdisplay; + struct drm_display_mode *mode = drm_crtc_get_mode(crtc, state); + unsigned x, y; + + if (IS_ERR(mode)) + return PTR_ERR(mode); + + /* disabling the crtc is allowed: */ + if (!(fb && state->mode_valid)) + return 0; + + hdisplay = state->mode.hdisplay; + vdisplay = state->mode.vdisplay; + + if (mode && drm_mode_is_stereo(mode)) { + struct drm_display_mode adjusted = *mode; + + drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE); + hdisplay = adjusted.crtc_hdisplay; + vdisplay = adjusted.crtc_vdisplay; + } + + if (state->invert_dimensions) + swap(hdisplay, vdisplay); + + x = pstate->src_x >> 16; + y = pstate->src_y >> 16; + + if (hdisplay > fb->width || + vdisplay > fb->height || + x > fb->width - hdisplay || + y > fb->height - vdisplay) { + DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", + fb->width, fb->height, hdisplay, vdisplay, + x, y, state->invert_dimensions ? " (inverted)" : ""); + return -ENOSPC; + } + + if (crtc->enabled && !state->set_config) { + if (primary->state->fb->pixel_format != fb->pixel_format) { + DRM_DEBUG_KMS("Page flip is not allowed to " + "change frame buffer format.\n"); + return -EINVAL; + } + } + + if (state->num_connector_ids == 0) { + DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); + return -EINVAL; + } + + if (state->connectors_change) { + int ret = check_connectors(crtc, state->state, false, + state->connector_ids, state->num_connector_ids); + if (ret) + return ret; + } + + if (mode) + drm_mode_destroy(crtc->dev, mode); + + return 0; } -EXPORT_SYMBOL(drm_crtc_index); +EXPORT_SYMBOL(drm_crtc_check_state); + +void drm_crtc_commit_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + crtc->state = state; + crtc->base.propvals = &state->propvals; +} +EXPORT_SYMBOL(drm_crtc_commit_state); + +int drm_crtc_set_property(struct drm_crtc *crtc, + struct drm_crtc_state *state, + struct drm_property *property, + uint64_t value, void *blob_data) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_config *config = &dev->mode_config; + + /* grab primary plane state now, to ensure locks are held, etc. */ + drm_atomic_get_plane_state(crtc->primary, state->state); + + drm_object_property_set_value(&crtc->base, + &state->propvals, property, value, blob_data); + + if (property == config->prop_mode) { + if (!blob_data) { + memset(&state->mode, 0, sizeof(state->mode)); + state->mode_valid = false; + } else { + /* check size: */ + if (value < sizeof(struct drm_mode_modeinfo)) + return -EINVAL; + state->mode = *(struct drm_mode_modeinfo *)blob_data; + state->mode_valid = true; + } + state->set_config = true; + } else if (property == config->prop_connector_ids) { + /* if connector-id's changing, we need to have all the locks: */ + struct drm_atomic_state *a = state->state; + int ret = drm_modeset_lock_all_crtcs(crtc->dev, &a->acquire_ctx); + if (ret) + return ret; + state->connectors_change = true; + state->num_connector_ids = value / sizeof(state->connector_ids[0]); + kfree(state->connector_ids); + state->connector_ids = blob_data; + state->set_config = true; + } else { + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(drm_crtc_set_property); /* * drm_mode_remove - remove and free a mode @@ -1262,6 +1476,10 @@ int drm_plane_check_state(struct drm_plane *plane, if (!fb) return 0; + /* we'll need this later during commit: */ + if (state->crtc) + drm_atomic_get_crtc_state(state->crtc, state->state); + fb_width = fb->width << 16; fb_height = fb->height << 16; @@ -1488,6 +1706,16 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev) return -ENOMEM; dev->mode_config.prop_crtc_id = prop; + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_connector_ids = prop; + + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_mode = prop; + return 0; } @@ -1777,7 +2005,7 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, * Returns: * Zero on success, errno on failure. */ -static int drm_crtc_convert_umode(struct drm_display_mode *out, +int drm_crtc_convert_umode(struct drm_display_mode *out, const struct drm_mode_modeinfo *in) { if (in->clock > INT_MAX || in->vrefresh > INT_MAX) @@ -2024,8 +2252,8 @@ int drm_mode_getcrtc(struct drm_device *dev, goto out; } - crtc_resp->x = crtc->x; - crtc_resp->y = crtc->y; + crtc_resp->x = crtc->primary->state->src_x >> 16; + crtc_resp->y = crtc->primary->state->src_y >> 16; crtc_resp->gamma_size = crtc->gamma_size; if (crtc->primary->fb) crtc_resp->fb_id = crtc->primary->fb->base.id; @@ -2528,7 +2756,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc, vdisplay = adjusted.crtc_vdisplay; } - if (crtc->invert_dimensions) + if (crtc->state->invert_dimensions) swap(hdisplay, vdisplay); if (hdisplay > fb->width || @@ -2537,7 +2765,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc, y > fb->height - vdisplay) { DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", fb->width, fb->height, hdisplay, vdisplay, x, y, - crtc->invert_dimensions ? " (inverted)" : ""); + crtc->state->invert_dimensions ? " (inverted)" : ""); return -ENOSPC; } @@ -2564,22 +2792,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_mode_config *config = &dev->mode_config; struct drm_mode_crtc *crtc_req = data; struct drm_crtc *crtc; - struct drm_connector **connector_set = NULL, *connector; - struct drm_framebuffer *fb = NULL; - struct drm_display_mode *mode = NULL; - struct drm_mode_set set; - uint32_t __user *set_connectors_ptr; + uint32_t fb_id = -1; + uint32_t *connector_ids = NULL; + struct drm_atomic_state *state = NULL; int ret; int i; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - /* For some reason crtc x/y offsets are signed internally. */ - if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX) - return -ERANGE; - - drm_modeset_lock_all(dev); crtc = drm_crtc_find(dev, crtc_req->crtc_id); if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); @@ -2597,55 +2818,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ret = -EINVAL; goto out; } - fb = crtc->primary->fb; - /* Make refcounting symmetric with the lookup path. */ - drm_framebuffer_reference(fb); + fb_id = crtc->primary->fb->base.id; } else { - fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); - if (!fb) { - DRM_DEBUG_KMS("Unknown FB ID%d\n", - crtc_req->fb_id); - ret = -ENOENT; - goto out; - } - } - - mode = drm_mode_create(dev); - if (!mode) { - ret = -ENOMEM; - goto out; - } - - ret = drm_crtc_convert_umode(mode, &crtc_req->mode); - if (ret) { - DRM_DEBUG_KMS("Invalid mode\n"); - goto out; + fb_id = crtc_req->fb_id; } - - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); - - ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y, - mode, fb); - if (ret) - goto out; - - } - - if (crtc_req->count_connectors == 0 && mode) { - DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); - ret = -EINVAL; - goto out; - } - - if (crtc_req->count_connectors > 0 && (!mode || !fb)) { - DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n", - crtc_req->count_connectors); - ret = -EINVAL; - goto out; } if (crtc_req->count_connectors > 0) { - u32 out_id; + uint32_t __user *set_connectors_ptr = + (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr; /* Avoid unbounded kernel memory allocation */ if (crtc_req->count_connectors > config->num_connector) { @@ -2653,52 +2834,75 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; } - connector_set = kmalloc(crtc_req->count_connectors * - sizeof(struct drm_connector *), + connector_ids = kmalloc(crtc_req->count_connectors * + sizeof(connector_ids[0]), GFP_KERNEL); - if (!connector_set) { + if (!connector_ids) { ret = -ENOMEM; goto out; } for (i = 0; i < crtc_req->count_connectors; i++) { - set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr; + u32 out_id; + if (get_user(out_id, &set_connectors_ptr[i])) { ret = -EFAULT; goto out; } - - connector = drm_connector_find(dev, out_id); - if (!connector) { - DRM_DEBUG_KMS("Connector id %d unknown\n", - out_id); - ret = -ENOENT; - goto out; - } - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", - connector->base.id, - drm_get_connector_name(connector)); - - connector_set[i] = connector; + connector_ids[i] = out_id; } } - set.crtc = crtc; - set.x = crtc_req->x; - set.y = crtc_req->y; - set.mode = mode; - set.connectors = connector_set; - set.num_connectors = crtc_req->count_connectors; - set.fb = fb; - ret = drm_mode_set_config_internal(&set); + state = dev->driver->atomic_begin(dev, 0); + if (IS_ERR(state)) + return PTR_ERR(state); -out: - if (fb) - drm_framebuffer_unreference(fb); +retry: + ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx); + if (ret) + goto out; + ret = drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx); + if (ret) + goto out; - kfree(connector_set); - drm_mode_destroy(dev, mode); - drm_modeset_unlock_all(dev); + /* If connectors change, we need to check if we need to steal one + * from another CRTC.. setcrtc makes this implicit, but atomic + * treats it as an error so we need to handle here: + */ + ret = check_connectors(crtc, state, true, + connector_ids, crtc_req->count_connectors); + if (ret) + goto out; + + ret = + drm_mode_crtc_set_obj_prop(crtc, state, + config->prop_mode, sizeof(crtc_req->mode), &crtc_req->mode) || + drm_mode_crtc_set_obj_prop(crtc, state, + config->prop_connector_ids, + crtc_req->count_connectors * sizeof(connector_ids[0]), + connector_ids) || + drm_mode_plane_set_obj_prop(crtc->primary, state, + config->prop_crtc_id, crtc->base.id, NULL) || + drm_mode_plane_set_obj_prop(crtc->primary, state, + config->prop_fb_id, fb_id, NULL) || + drm_mode_plane_set_obj_prop(crtc->primary, state, + config->prop_src_x, crtc_req->x << 16, NULL) || + drm_mode_plane_set_obj_prop(crtc->primary, state, + config->prop_src_y, crtc_req->y << 16, NULL) || + dev->driver->atomic_check(dev, state); + if (ret) + goto out; + + ret = dev->driver->atomic_commit(dev, state); + +out: + if (state) { + if (ret == -EDEADLK) { + drm_modeset_backoff(&state->acquire_ctx); + goto retry; + } + dev->driver->atomic_end(dev, state); + } return ret; } @@ -4070,9 +4274,6 @@ int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, if (crtc->funcs->set_property) ret = crtc->funcs->set_property(crtc, state, property, value, blob_data); - if (!ret) - drm_object_property_set_value(&crtc->base, &crtc->propvals, - property, value, NULL); return ret; } @@ -4245,11 +4446,11 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; -retry: state = dev->driver->atomic_begin(dev, 0); if (IS_ERR(state)) return PTR_ERR(state); +retry: ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx); if (ret) goto out; @@ -4270,9 +4471,11 @@ retry: ret = dev->driver->atomic_commit(dev, state); out: - dev->driver->atomic_end(dev, state); - if (ret == -EDEADLK) + if (ret == -EDEADLK) { + drm_modeset_backoff(&state->acquire_ctx); goto retry; + } + dev->driver->atomic_end(dev, state); return ret; } @@ -4464,6 +4667,51 @@ out: return ret; } +static struct drm_pending_vblank_event *create_vblank_event( + struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data) +{ + struct drm_pending_vblank_event *e = NULL; + unsigned long flags; + + 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); + + 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; + } + + 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; + +out: + return e; +} + +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); +} + /** * drm_mode_page_flip_ioctl - schedule an asynchronous fb update * @dev: DRM device @@ -4486,10 +4734,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_page_flip *page_flip = data; + struct drm_mode_config *config = &dev->mode_config; struct drm_crtc *crtc; - struct drm_framebuffer *fb = NULL, *old_fb = NULL; struct drm_pending_vblank_event *e = NULL; - unsigned long flags; + struct drm_atomic_state *state; int ret = -EINVAL; if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS || @@ -4503,92 +4751,43 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (!crtc) return -ENOENT; - drm_modeset_lock(&crtc->mutex, NULL); - if (crtc->primary->fb == NULL) { - /* The framebuffer is currently unbound, presumably - * due to a hotplug event, that userspace has not - * yet discovered. - */ - ret = -EBUSY; - goto out; - } - - if (crtc->funcs->page_flip == NULL) - goto out; - - fb = drm_framebuffer_lookup(dev, page_flip->fb_id); - if (!fb) { - ret = -ENOENT; - goto out; - } - - ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); - if (ret) - goto out; - - if (crtc->primary->fb->pixel_format != fb->pixel_format) { - DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); - ret = -EINVAL; - goto out; - } + state = dev->driver->atomic_begin(dev, + page_flip->flags | DRM_MODE_ATOMIC_NONBLOCK); + if (IS_ERR(state)) + return PTR_ERR(state); +retry: if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { - ret = -ENOMEM; - spin_lock_irqsave(&dev->event_lock, flags); - if (file_priv->event_space < sizeof e->event) { - spin_unlock_irqrestore(&dev->event_lock, flags); + e = create_vblank_event(dev, file_priv, page_flip->user_data); + if (!e) { + ret = -ENOMEM; goto out; } - file_priv->event_space -= sizeof e->event; - spin_unlock_irqrestore(&dev->event_lock, flags); - - 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); + ret = dev->driver->atomic_set_event(dev, state, &crtc->base, e); + if (ret) { goto out; } - - e->event.base.type = DRM_EVENT_FLIP_COMPLETE; - e->event.base.length = sizeof e->event; - e->event.user_data = page_flip->user_data; - e->base.event = &e->event.base; - e->base.file_priv = file_priv; - e->base.destroy = - (void (*) (struct drm_pending_event *)) kfree; } - old_fb = crtc->primary->fb; - ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags); - if (ret) { - if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { - spin_lock_irqsave(&dev->event_lock, flags); - file_priv->event_space += sizeof e->event; - spin_unlock_irqrestore(&dev->event_lock, flags); - kfree(e); - } - /* Keep the old fb, don't unref it. */ - old_fb = NULL; - } else { - /* - * Warn if the driver hasn't properly updated the crtc->fb - * field to reflect that the new framebuffer is now used. - * Failing to do so will screw with the reference counting - * on framebuffers. - */ - WARN_ON(crtc->primary->fb != fb); - /* Unref only the old framebuffer. */ - fb = NULL; - } + ret = drm_mode_plane_set_obj_prop(crtc->primary, state, + config->prop_fb_id, page_flip->fb_id, NULL); + if (ret) + goto out; -out: - if (fb) - drm_framebuffer_unreference(fb); - if (old_fb) - drm_framebuffer_unreference(old_fb); - drm_modeset_unlock(&crtc->mutex); + ret = dev->driver->atomic_check(dev, state); + if (ret) + goto out; + ret = dev->driver->atomic_commit(dev, state); + +out: + if (ret && e) + destroy_vblank_event(dev, file_priv, e); + if (ret == -EDEADLK) { + drm_modeset_backoff(&state->acquire_ctx); + goto retry; + } + dev->driver->atomic_end(dev, state); return ret; } diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index d88a196..969c495 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -36,6 +36,7 @@ #include <linux/module.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> +#include <drm/drm_atomic.h> #include <drm/drm_fb_helper.h> #include <drm/drm_crtc_helper.h> @@ -277,10 +278,11 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper, bool lockless) { struct drm_device *dev = fb_helper->dev; + struct drm_mode_config *config = &dev->mode_config; struct drm_plane *plane; bool error = false; - void *state; - int i; + struct drm_atomic_state *state; + int ret, i; state = dev->driver->atomic_begin(dev, lockless ? DRM_MODE_ATOMIC_NOLOCK : 0); @@ -289,6 +291,14 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper, return true; } +retry: + ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx); + if (ret) + goto out; + ret = drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx); + if (ret) + goto out; + list_for_each_entry(plane, &dev->mode_config.plane_list, head) if (plane->type != DRM_PLANE_TYPE_PRIMARY) drm_plane_force_disable(plane, state); @@ -299,8 +309,15 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper, else dev->driver->atomic_commit(dev, state); +out: + if (ret == -EDEADLK) { + drm_modeset_backoff(&state->acquire_ctx); + goto retry; + } dev->driver->atomic_end(dev, state); + drm_modeset_lock_all(dev); + for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; struct drm_crtc *crtc = mode_set->crtc; @@ -316,6 +333,9 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper, if (ret) error = true; } + + drm_modeset_unlock_all(dev); + return error; } /** diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 2a56973..f3c7e77 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -14,6 +14,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include "exynos_drm_crtc.h" #include "exynos_drm_drv.h" @@ -289,6 +290,10 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct exynos_drm_private *dev_priv = dev->dev_private; struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); + + if (IS_ERR(cstate)) + return PTR_ERR(cstate); if (property == dev_priv->crtc_mode_property) { enum exynos_crtc_mode mode = val; @@ -313,7 +318,7 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, return 0; } - return -EINVAL; + return drm_crtc_set_property(crtc, cstate, property, val, blob_data); } static struct drm_crtc_funcs exynos_crtc_funcs = { diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 6672732..5b6eee9 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -989,6 +989,7 @@ const struct drm_crtc_funcs cdv_intel_crtc_funcs = { .cursor_move = gma_crtc_cursor_move, .gamma_set = gma_crtc_gamma_set, .set_config = gma_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = gma_crtc_destroy, }; diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index 87b50ba..79b5692 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c @@ -444,6 +444,7 @@ const struct drm_crtc_funcs psb_intel_crtc_funcs = { .cursor_move = gma_crtc_cursor_move, .gamma_set = gma_crtc_gamma_set, .set_config = gma_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = gma_crtc_destroy, }; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index fdc58fa..ea9307d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -10454,6 +10454,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { .cursor_move = intel_crtc_cursor_move, .gamma_set = intel_crtc_gamma_set, .set_config = intel_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = intel_crtc_destroy, .page_flip = intel_crtc_page_flip, }; diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index a034ed4..ba9bd91 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -1296,6 +1296,7 @@ static const struct drm_crtc_funcs mga_crtc_funcs = { .cursor_move = mga_crtc_cursor_move, .gamma_set = mga_crtc_gamma_set, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = mga_crtc_destroy, }; diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index efa19c8..8d9b604 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -471,8 +471,10 @@ static int mdp4_crtc_set_property(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) { - // XXX - return -EINVAL; + struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); + if (IS_ERR(cstate)) + return PTR_ERR(cstate); + return drm_crtc_set_property(crtc, cstate, property, val, blob_data); } #define CURSOR_WIDTH 64 diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index ff48944..57b67b4 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -389,8 +389,10 @@ static int mdp5_crtc_set_property(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) { - // XXX - return -EINVAL; + struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); + if (IS_ERR(cstate)) + return PTR_ERR(cstate); + return drm_crtc_set_property(crtc, cstate, property, val, blob_data); } static const struct drm_crtc_funcs mdp5_crtc_funcs = { diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index 41be342..9e24632 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -1086,6 +1086,7 @@ static const struct drm_crtc_funcs nv04_crtc_funcs = { .cursor_move = nv04_crtc_cursor_move, .gamma_set = nv_crtc_gamma_set, .set_config = nouveau_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .page_flip = nouveau_crtc_page_flip, .destroy = nv_crtc_destroy, }; diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 58af547..ecbffeb 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -1329,6 +1329,7 @@ static const struct drm_crtc_funcs nv50_crtc_func = { .cursor_move = nv50_crtc_cursor_move, .gamma_set = nv50_crtc_gamma_set, .set_config = nouveau_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = nv50_crtc_destroy, .page_flip = nouveau_crtc_page_flip, }; diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index a75934d..772687b 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -387,14 +387,22 @@ static int omap_crtc_set_property(struct drm_crtc *crtc, { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct omap_drm_private *priv = crtc->dev->dev_private; + struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); + int ret; + + if (IS_ERR(cstate)) + return PTR_ERR(cstate); if (property == priv->rotation_prop) { - crtc->invert_dimensions = + cstate->invert_dimensions = !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270))); } - return omap_plane_set_property(omap_crtc->plane, state, + ret = omap_plane_set_property(omap_crtc->plane, state, property, val, blob_data); + if (ret) + ret = drm_crtc_set_property(crtc, cstate, property, val, blob_data); + return ret; } static const struct drm_crtc_funcs omap_crtc_funcs = { diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 9630a32..4b4bc42 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -579,7 +579,7 @@ static void dev_lastclose(struct drm_device *dev) */ for (i = 0; i < priv->num_crtcs; i++) { drm_object_property_set_value(&priv->crtcs[i]->base, - &priv->crtcs[i]->propvals, + &priv->crtcs[i]->state->propvals, priv->rotation_prop, 0, NULL); } diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index b54c970..25896a9 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -29,6 +29,7 @@ #include "qxl_drv.h" #include "qxl_object.h" #include "drm_crtc_helper.h" +#include "drm_atomic.h" static bool qxl_head_enabled(struct qxl_head *head) { @@ -373,6 +374,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = { .cursor_set2 = qxl_crtc_cursor_set2, .cursor_move = qxl_crtc_cursor_move, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = qxl_crtc_destroy, }; diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 8d99d5e..cc86aac 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -32,6 +32,7 @@ #include <linux/pm_runtime.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include <drm/drm_edid.h> #include <linux/gcd.h> @@ -546,6 +547,7 @@ static const struct drm_crtc_funcs radeon_crtc_funcs = { .cursor_move = radeon_crtc_cursor_move, .gamma_set = radeon_crtc_gamma_set, .set_config = radeon_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = radeon_crtc_destroy, .page_flip = radeon_crtc_page_flip, }; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 299267d..f5a3d55 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -17,6 +17,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> @@ -527,6 +528,7 @@ static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, static const struct drm_crtc_funcs crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .page_flip = rcar_du_crtc_page_flip, }; diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index 90e023a..0a5280c 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -17,6 +17,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> @@ -506,6 +507,7 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc, static const struct drm_crtc_funcs crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .page_flip = shmob_drm_crtc_page_flip, }; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index d642d4a0..95ffae7 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -411,6 +411,7 @@ static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, static const struct drm_crtc_funcs tilcdc_crtc_funcs = { .destroy = tilcdc_crtc_destroy, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .page_flip = tilcdc_crtc_page_flip, }; diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index cddc4fc..36d0116 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -14,6 +14,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include "udl_drv.h" /* @@ -383,6 +384,7 @@ static struct drm_crtc_helper_funcs udl_helper_funcs = { static const struct drm_crtc_funcs udl_crtc_funcs = { .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = udl_crtc_destroy, }; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index b2b9bd2..0313b00 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -300,6 +300,7 @@ static struct drm_crtc_funcs vmw_legacy_crtc_funcs = { .cursor_move = vmw_du_crtc_cursor_move, .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_ldu_crtc_destroy, + .set_property = drm_atomic_crtc_set_property, .set_config = vmw_ldu_crtc_set_config, }; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index a95d3a0..b723e09 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -397,6 +397,7 @@ static struct drm_crtc_funcs vmw_screen_object_crtc_funcs = { .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_sou_crtc_destroy, .set_config = vmw_sou_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .page_flip = vmw_du_page_flip, }; diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 78e93ec..7946b7f 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -70,6 +70,9 @@ struct drm_atomic_funcs { int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate); int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate); + + int (*check_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate); + int (*commit_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate); }; const extern struct drm_atomic_funcs drm_atomic_funcs; @@ -109,6 +112,30 @@ drm_atomic_commit_plane_state(struct drm_plane *plane, return funcs->commit_plane_state(plane, pstate); } +int drm_atomic_crtc_set_property(struct drm_crtc *crtc, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data); +struct drm_crtc_state *drm_atomic_get_crtc_state(struct drm_crtc *crtc, + struct drm_atomic_state *state); + +static inline int +drm_atomic_check_crtc_state(struct drm_crtc *crtc, + struct drm_crtc_state *cstate) +{ + const struct drm_atomic_funcs *funcs = + crtc->dev->driver->atomic_funcs; + return funcs->check_crtc_state(crtc, cstate); +} + +static inline int +drm_atomic_commit_crtc_state(struct drm_crtc *crtc, + struct drm_crtc_state *cstate) +{ + const struct drm_atomic_funcs *funcs = + crtc->dev->driver->atomic_funcs; + return funcs->commit_crtc_state(crtc, cstate); +} + /** * struct drm_atomic_state - the state object used by atomic helpers */ @@ -118,6 +145,8 @@ struct drm_atomic_state { uint32_t flags; struct drm_plane **planes; struct drm_plane_state **pstates; + struct drm_crtc **crtcs; + struct drm_crtc_state **cstates; bool committed; bool checked; /* just for debugging */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 784eb62..5f4e786 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -284,6 +284,10 @@ struct drm_crtc_funcs { struct drm_pending_vblank_event *event, uint32_t flags); + struct drm_crtc_state *(*create_state)(struct drm_crtc *crtc); + void (*destroy_state)(struct drm_crtc *crtc, + struct drm_crtc_state *cstate); + int (*set_property)(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, @@ -291,21 +295,52 @@ struct drm_crtc_funcs { }; /** + * drm_crtc_state - mutable crtc state + * @invert_dimensions: for purposes of error checking crtc vs fb sizes, + * invert the width/height of the crtc. This is used if the driver + * is performing 90 or 270 degree rotated scanout + * @mode_valid: a valid mode has been set + * @set_config: needs modeset (crtc->set_config()) + * @connectors_change: the connector-ids array has changed + * @num_connector_ids: the number of connector-ids + * @connector_ids: array of connector ids + * @mode: current mode timings + * @event: pending pageflip event + * @propvals: property values + * @state: current global/toplevel state object (for atomic) while an + * update is in progress, NULL otherwise. + */ +struct drm_crtc_state { + bool invert_dimensions : 1; + bool mode_valid : 1; + + /* transient state, only valid during atomic operation: */ + bool set_config : 1; + bool connectors_change : 1; + + uint8_t num_connector_ids; + uint32_t *connector_ids; + struct drm_mode_modeinfo mode; + + struct drm_pending_vblank_event *event; + + struct drm_object_property_values propvals; + + struct drm_atomic_state *state; +}; + +/** * drm_crtc - central CRTC control structure * @dev: parent DRM device * @head: list management + * @id: CRTC number, 0..n * @mutex: per-CRTC locking * @base: base KMS object for ID tracking etc. * @primary: primary plane for this CRTC * @cursor: cursor plane for this CRTC + * @state: the mutable state * @enabled: is this CRTC enabled? - * @mode: current mode timings * @hwmode: mode timings as programmed to hw regs - * @invert_dimensions: for purposes of error checking crtc vs fb sizes, - * invert the width/height of the crtc. This is used if the driver - * is performing 90 or 270 degree rotated scanout - * @x: x position on screen - * @y: y position on screen * @funcs: CRTC control functions * @gamma_size: size of gamma ramp * @gamma_store: gamma ramp values @@ -322,6 +357,8 @@ struct drm_crtc { struct drm_device *dev; struct list_head head; + int id; + /** * crtc mutex * @@ -337,23 +374,19 @@ struct drm_crtc { struct drm_plane *primary; struct drm_plane *cursor; + struct drm_crtc_state *state; + /* Temporary tracking of the old fb while a modeset is ongoing. Used * by drm_mode_set_config_internal to implement correct refcounting. */ struct drm_framebuffer *old_fb; bool enabled; - /* Requested mode from modesetting. */ - struct drm_display_mode mode; - /* Programmed mode in hw, after adjustments for encoders, * crtc, panel scaling etc. Needed for timestamping etc. */ struct drm_display_mode hwmode; - bool invert_dimensions; - - int x, y; const struct drm_crtc_funcs *funcs; /* CRTC gamma size for reporting to userspace */ @@ -367,9 +400,15 @@ struct drm_crtc { void *helper_private; struct drm_object_properties properties; - struct drm_object_property_values propvals; -}; + /* These are (temporary) duplicate information from what is in the + * drm_crtc_state struct.. keeping duplicate copy here makes the + * switch to atomic far less intrusive. Once all the drivers and + * the crtc/fb helpers are updated, then we can remove these: + */ + int x, y; + struct drm_display_mode mode; +}; /** * drm_connector_funcs - control connectors on a given device @@ -876,6 +915,8 @@ struct drm_mode_config { struct drm_property *prop_crtc_h; struct drm_property *prop_fb_id; struct drm_property *prop_crtc_id; + struct drm_property *prop_connector_ids; + struct drm_property *prop_mode; struct drm_property *edid_property; struct drm_property *dpms_property; struct drm_property *plane_type_property; @@ -936,7 +977,8 @@ extern int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, const struct drm_crtc_funcs *funcs); extern void drm_crtc_cleanup(struct drm_crtc *crtc); -extern unsigned int drm_crtc_index(struct drm_crtc *crtc); +struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc, + struct drm_crtc_state *cstate); /** * drm_crtc_mask - find the mask of a registered CRTC @@ -947,9 +989,18 @@ extern unsigned int drm_crtc_index(struct drm_crtc *crtc); */ static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc) { - return 1 << drm_crtc_index(crtc); + return 1 << crtc->id; } +extern int drm_crtc_check_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); +extern void drm_crtc_commit_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); +extern int drm_crtc_set_property(struct drm_crtc *crtc, + struct drm_crtc_state *state, + struct drm_property *property, + uint64_t value, void *blob_data); + extern void drm_connector_ida_init(void); extern void drm_connector_ida_destroy(void); extern int drm_connector_init(struct drm_device *dev, @@ -1025,6 +1076,7 @@ extern const char *drm_get_tv_select_name(int val); extern void drm_fb_release(struct drm_file *file_priv); extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); extern void drm_mode_group_destroy(struct drm_mode_group *group); +extern int drm_crtc_convert_umode(struct drm_display_mode *out, const struct drm_mode_modeinfo *in); extern bool drm_probe_ddc(struct i2c_adapter *adapter); extern struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter); @@ -1252,6 +1304,25 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id) return mo ? obj_to_blob(mo) : NULL; } +static inline struct drm_crtc_state * +drm_crtc_create_state(struct drm_crtc *crtc) +{ + if (crtc->funcs->create_state) + return crtc->funcs->create_state(crtc); + return kzalloc(sizeof(struct drm_crtc_state), GFP_KERNEL); +} + +static inline void +drm_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *cstate) +{ + kfree(cstate->connector_ids); + if (crtc->funcs->destroy_state) + crtc->funcs->destroy_state(crtc, cstate); + else + kfree(cstate); +} + static inline struct drm_plane_state * drm_plane_create_state(struct drm_plane *plane) { -- 1.9.3 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel