On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark wrote: > 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> Same comments about interface design as for the plane patch apply here. One additional comment below. > --- > 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 | 598 ++++++++++++++++++----------- > drivers/gpu/drm/drm_fb_helper.c | 2 +- > 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, 785 insertions(+), 243 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 403ffc5..863a0fe 100644 > --- a/drivers/gpu/drm/drm_atomic.c > +++ b/drivers/gpu/drm/drm_atomic.c > @@ -48,11 +48,13 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, > { > struct drm_atomic_state *state; > 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); > > @@ -74,6 +76,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); > @@ -92,7 +100,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; > + } Hm, I think if we only want completion events on crtcs (which I agree on) then we should make the set_event interface more specific by passing struct drm_crtc * and only call it for crtcs. > } > EXPORT_SYMBOL(drm_atomic_set_event); > > @@ -111,6 +130,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++) { > @@ -120,6 +140,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; > > @@ -203,6 +230,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++) { > @@ -213,6 +241,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); > @@ -223,8 +259,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) { > @@ -403,6 +449,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; > @@ -425,8 +472,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, > @@ -443,7 +493,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 > @@ -473,9 +523,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) > @@ -484,8 +535,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); I haven't figured out why. With primary planes I don't really see a need for this. If we need it to implement the legacy setcrtc interface, then that should be done there, not here. > + } > + 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 b556a31..e14d517 100644 > --- a/drivers/gpu/drm/drm_crtc.c > +++ b/drivers/gpu/drm/drm_crtc.c > @@ -689,10 +689,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)); > > @@ -712,7 +709,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) > * in this manner. > */ > if (atomic_read(&fb->refcount.refcount) > 1) { > - void *state; > + struct drm_atomic_state *state; > > state = dev->driver->atomic_begin(dev, 0); > if (IS_ERR(state)) { > @@ -720,24 +717,7 @@ 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); > - } > - } > - > + /* 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); > @@ -750,8 +730,6 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) > dev->driver->atomic_commit(dev, state); > > dev->driver->atomic_end(dev, state); > - > - drm_modeset_unlock_all(dev); > } > > drm_framebuffer_unreference(fb); > @@ -782,9 +760,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); > @@ -796,14 +778,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, &dev->mode_config.crtc_list); > dev->mode_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); > @@ -832,31 +817,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); > } > -EXPORT_SYMBOL(drm_crtc_index); > + > +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_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 > @@ -1239,6 +1438,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; > > @@ -1465,6 +1668,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; > } > > @@ -1754,7 +1967,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) > @@ -2001,8 +2214,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; > @@ -2495,7 +2708,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 || > @@ -2504,7 +2717,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; > } > > @@ -2531,22 +2744,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); > @@ -2564,55 +2770,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; > + fb_id = crtc_req->fb_id; > } > - > - ret = drm_crtc_convert_umode(mode, &crtc_req->mode); > - if (ret) { > - DRM_DEBUG_KMS("Invalid mode\n"); > - goto out; > - } > - > - 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) { > @@ -2620,52 +2786,65 @@ 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); > +retry: > + state = dev->driver->atomic_begin(dev, 0); > + if (IS_ERR(state)) > + return PTR_ERR(state); > > -out: > - if (fb) > - drm_framebuffer_unreference(fb); > + /* 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; > > - kfree(connector_set); > - drm_mode_destroy(dev, mode); > - drm_modeset_unlock_all(dev); > + 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) > + dev->driver->atomic_end(dev, state); > + if (ret == -EDEADLK) > + goto retry; > return ret; > } > > @@ -4028,9 +4207,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; > } > @@ -4424,6 +4600,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 > @@ -4446,10 +4667,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 || > @@ -4463,92 +4684,41 @@ 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; > - } > +retry: > + state = dev->driver->atomic_begin(dev, > + page_flip->flags | DRM_MODE_ATOMIC_NONBLOCK); > + if (IS_ERR(state)) > + return PTR_ERR(state); > > 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); > + dev->driver->atomic_end(dev, state); > + if (ret == -EDEADLK) > + goto retry; > return ret; > } > > diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c > index b73d3b0..4669e69 100644 > --- a/drivers/gpu/drm/drm_fb_helper.c > +++ b/drivers/gpu/drm/drm_fb_helper.c > @@ -286,7 +286,7 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) > struct drm_device *dev = fb_helper->dev; > struct drm_plane *plane; > bool error = false; > - void *state; > + struct drm_atomic_state *state; > int i; > > drm_warn_on_modeset_not_all_locked(dev); > 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 e9f6eb7..53b996f 100644 > --- a/drivers/gpu/drm/i915/intel_display.c > +++ b/drivers/gpu/drm/i915/intel_display.c > @@ -10430,6 +10430,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 7cf0f78..d0d8befd 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 771390b..7f4ee99 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 da80bdc..3f64c47 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 92839ba..b07f116 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 58309cc..2fbf13a 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 > @@ -875,6 +914,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; > @@ -935,7 +976,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 > @@ -946,9 +988,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, > @@ -1024,6 +1075,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); > @@ -1251,6 +1303,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.0 > -- Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel