diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 8c54f9a393cf..3789f2db3c21 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2343,6 +2343,7 @@ void intel_crt_init(struct drm_device *dev) Atomic State Reset and Initialization !Pdrivers/gpu/drm/drm_atomic_helper.c atomic state reset and initialization +!Iinclude/drm/drm_atomic_helper.h !Edrivers/gpu/drm/drm_atomic_helper.c diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index d3b46746b611..ff5f034cc405 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -243,12 +243,6 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, if (state->plane_states[index]) return state->plane_states[index]; - /* - * TODO: We currently don't have per-plane mutexes. So instead of trying - * crazy tricks with deferring plane->crtc and hoping for the best just - * grab all crtc locks. Once we have per-plane locks we must update this - * to only take the plane mutex. - */ ret = drm_modeset_lock(&plane->mutex, state->acquire_ctx); if (ret) return ERR_PTR(ret); @@ -350,7 +344,8 @@ EXPORT_SYMBOL(drm_atomic_get_connector_state); /** * drm_atomic_set_crtc_for_plane - set crtc for plane - * @plane_state: atomic state object for the plane + * @state: the incoming atomic state + * @plane: the plane whose incoming state to update * @crtc: crtc to use for the plane * * Changing the assigned crtc for a plane requires us to grab the lock and state @@ -363,20 +358,35 @@ EXPORT_SYMBOL(drm_atomic_get_connector_state); * sequence must be restarted. All other errors are fatal. */ int -drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, - struct drm_crtc *crtc) +drm_atomic_set_crtc_for_plane(struct drm_atomic_state *state, + struct drm_plane *plane, struct drm_crtc *crtc) { + struct drm_plane_state *plane_state = + drm_atomic_get_plane_state(state, plane); struct drm_crtc_state *crtc_state; + if (WARN_ON(IS_ERR(plane_state))) + return PTR_ERR(plane_state); + + if (plane_state->crtc) { + crtc_state = drm_atomic_get_crtc_state(plane_state->state, + plane_state->crtc); + if (WARN_ON(IS_ERR(crtc_state))) + return PTR_ERR(crtc_state); + + crtc_state->plane_mask &= ~(1 << drm_plane_index(plane)); + } + + plane_state->crtc = crtc; + if (crtc) { crtc_state = drm_atomic_get_crtc_state(plane_state->state, crtc); if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state); + crtc_state->plane_mask |= (1 << drm_plane_index(plane)); } - plane_state->crtc = crtc; - if (crtc) DRM_DEBUG_KMS("Link plane state %p to [CRTC:%d]\n", plane_state, crtc->base.id); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index a17b8e9c0a81..4a78a773151c 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -751,6 +751,33 @@ static void wait_for_fences(struct drm_device *dev, } } +static bool framebuffer_changed(struct drm_device *dev, + struct drm_atomic_state *old_state, + struct drm_crtc *crtc) +{ + struct drm_plane *plane; + struct drm_plane_state *old_plane_state; + int nplanes = old_state->dev->mode_config.num_total_plane; + int i; + + for (i = 0; i < nplanes; i++) { + plane = old_state->planes[i]; + old_plane_state = old_state->plane_states[i]; + + if (!plane) + continue; + + if (plane->state->crtc != crtc && + old_plane_state->crtc != crtc) + continue; + + if (plane->state->fb != old_plane_state->fb) + return true; + } + + return false; +} + /** * drm_atomic_helper_wait_for_vblanks - wait for vblank on crtcs * @dev: DRM device @@ -758,7 +785,9 @@ static void wait_for_fences(struct drm_device *dev, * * Helper to, after atomic commit, wait for vblanks on all effected * crtcs (ie. before cleaning up old framebuffers using - * drm_atomic_helper_cleanup_planes()) + * drm_atomic_helper_cleanup_planes()). It will only wait on crtcs where the + * framebuffers have actually changed to optimize for the legacy cursor and + * plane update use-case. */ void drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, @@ -784,6 +813,9 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, if (!crtc->state->enable) continue; + if (!framebuffer_changed(dev, old_state, crtc)) + continue; + ret = drm_crtc_vblank_get(crtc); if (ret != 0) continue; @@ -1014,6 +1046,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, for (i = 0; i < nplanes; i++) { struct drm_plane_helper_funcs *funcs; struct drm_plane *plane = old_state->planes[i]; + struct drm_plane_state *old_plane_state; if (!plane) continue; @@ -1023,7 +1056,9 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, if (!funcs || !funcs->atomic_update) continue; - funcs->atomic_update(plane); + old_plane_state = old_state->plane_states[i]; + + funcs->atomic_update(plane, old_plane_state); } for (i = 0; i < ncrtcs; i++) { @@ -1187,7 +1222,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); + ret = drm_atomic_set_crtc_for_plane(state, plane, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, fb); @@ -1243,6 +1278,17 @@ int drm_atomic_helper_disable_plane(struct drm_plane *plane) struct drm_plane_state *plane_state; int ret = 0; + /* + * FIXME: Without plane->crtc set we can't get at the implicit legacy + * acquire context. The real fix will be to wire the acquire ctx through + * everywhere we need it, but meanwhile prevent chaos by just skipping + * this noop. The critical case is the cursor ioctls which a) only grab + * crtc/cursor-plane locks (so we need the crtc to get at the right + * acquire context) and b) can try to disable the plane multiple times. + */ + if (!plane->crtc) + return 0; + state = drm_atomic_state_alloc(plane->dev); if (!state) return -ENOMEM; @@ -1255,7 +1301,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); + ret = drm_atomic_set_crtc_for_plane(state, plane, NULL); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, NULL); @@ -1406,11 +1452,24 @@ retry: goto fail; } + primary_state = drm_atomic_get_plane_state(state, crtc->primary); + if (IS_ERR(primary_state)) { + ret = PTR_ERR(primary_state); + goto fail; + } + if (!set->mode) { WARN_ON(set->fb); WARN_ON(set->num_connectors); crtc_state->enable = false; + + ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, NULL); + if (ret != 0) + goto fail; + + drm_atomic_set_fb_for_plane(primary_state, NULL); + goto commit; } @@ -1420,13 +1479,7 @@ retry: crtc_state->enable = true; drm_mode_copy(&crtc_state->mode, set->mode); - primary_state = drm_atomic_get_plane_state(state, crtc->primary); - if (IS_ERR(primary_state)) { - ret = PTR_ERR(primary_state); - goto fail; - } - - ret = drm_atomic_set_crtc_for_plane(primary_state, crtc); + ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(primary_state, set->fb); @@ -1698,7 +1751,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); + ret = drm_atomic_set_crtc_for_plane(state, plane, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, fb); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 589a921d4313..de79283eaea7 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -721,6 +721,10 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) drm_mode_object_put(dev, &crtc->base); list_del(&crtc->head); dev->mode_config.num_crtc--; + + WARN_ON(crtc->state && !crtc->funcs->atomic_destroy_state); + if (crtc->state && crtc->funcs->atomic_destroy_state) + crtc->funcs->atomic_destroy_state(crtc, crtc->state); } EXPORT_SYMBOL(drm_crtc_cleanup); @@ -918,6 +922,11 @@ void drm_connector_cleanup(struct drm_connector *connector) connector->name = NULL; list_del(&connector->head); dev->mode_config.num_connector--; + + WARN_ON(connector->state && !connector->funcs->atomic_destroy_state); + if (connector->state && connector->funcs->atomic_destroy_state) + connector->funcs->atomic_destroy_state(connector, + connector->state); } EXPORT_SYMBOL(drm_connector_cleanup); @@ -1244,6 +1253,10 @@ void drm_plane_cleanup(struct drm_plane *plane) if (plane->type == DRM_PLANE_TYPE_OVERLAY) dev->mode_config.num_overlay_plane--; drm_modeset_unlock_all(dev); + + WARN_ON(plane->state && !plane->funcs->atomic_destroy_state); + if (plane->state && plane->funcs->atomic_destroy_state) + plane->funcs->atomic_destroy_state(plane, plane->state); } EXPORT_SYMBOL(drm_plane_cleanup); @@ -1955,6 +1968,15 @@ static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, return true; } +static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector) +{ + /* For atomic drivers only state objects are synchronously updated and + * protected by modeset locks, so check those first. */ + if (connector->state) + return connector->state->best_encoder; + return connector->encoder; +} + /** * drm_mode_getconnector - get connector configuration * @dev: drm device for the ioctl @@ -1973,6 +1995,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, { struct drm_mode_get_connector *out_resp = data; struct drm_connector *connector; + struct drm_encoder *encoder; struct drm_display_mode *mode; int mode_count = 0; int props_count = 0; @@ -2028,8 +2051,10 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->subpixel = connector->display_info.subpixel_order; out_resp->connection = connector->status; drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - if (connector->encoder) - out_resp->encoder_id = connector->encoder->base.id; + + encoder = drm_connector_get_encoder(connector); + if (encoder) + out_resp->encoder_id = encoder->base.id; else out_resp->encoder_id = 0; drm_modeset_unlock(&dev->mode_config.connection_mutex); @@ -2099,6 +2124,33 @@ out: return ret; } +static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) +{ + struct drm_connector *connector; + struct drm_device *dev = encoder->dev; + bool uses_atomic = false; + + /* For atomic drivers only state objects are synchronously updated and + * protected by modeset locks, so check those first. */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (!connector->state) + continue; + + uses_atomic = true; + + if (connector->state->best_encoder != encoder) + continue; + + return connector->state->crtc; + } + + /* Don't return stale data (e.g. pending async disable). */ + if (uses_atomic) + return NULL; + + return encoder->crtc; +} + /** * drm_mode_getencoder - get encoder configuration * @dev: drm device for the ioctl @@ -2117,6 +2169,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, { struct drm_mode_get_encoder *enc_resp = data; struct drm_encoder *encoder; + struct drm_crtc *crtc; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -2126,7 +2179,10 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, return -ENOENT; drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - if (encoder->crtc) + crtc = drm_encoder_get_crtc(encoder); + if (crtc) + enc_resp->crtc_id = crtc->base.id; + else if (encoder->crtc) enc_resp->crtc_id = encoder->crtc->base.id; else enc_resp->crtc_id = 0; @@ -2392,7 +2448,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_set_plane *plane_req = data; - struct drm_mode_object *obj; struct drm_plane *plane; struct drm_crtc *crtc = NULL; struct drm_framebuffer *fb = NULL; @@ -2415,14 +2470,12 @@ int drm_mode_setplane(struct drm_device *dev, void *data, * First, find the plane, crtc, and fb objects. If not available, * we don't bother to call the driver. */ - obj = drm_mode_object_find(dev, plane_req->plane_id, - DRM_MODE_OBJECT_PLANE); - if (!obj) { + plane = drm_plane_find(dev, plane_req->plane_id); + if (!plane) { DRM_DEBUG_KMS("Unknown plane ID %d\n", plane_req->plane_id); return -ENOENT; } - plane = obj_to_plane(obj); if (plane_req->fb_id) { fb = drm_framebuffer_lookup(dev, plane_req->fb_id); @@ -2432,14 +2485,12 @@ int drm_mode_setplane(struct drm_device *dev, void *data, return -ENOENT; } - obj = drm_mode_object_find(dev, plane_req->crtc_id, - DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(dev, plane_req->crtc_id); + if (!crtc) { DRM_DEBUG_KMS("Unknown crtc ID %d\n", plane_req->crtc_id); return -ENOENT; } - crtc = obj_to_crtc(obj); } /* diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 2e5c7d941313..4f41377b0b80 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -535,6 +535,8 @@ static void drm_fs_inode_free(struct inode *inode) * The initial ref-count of the object is 1. Use drm_dev_ref() and * drm_dev_unref() to take and drop further ref-counts. * + * Note that for purely virtual devices @parent can be NULL. + * * RETURNS: * Pointer to new DRM device, or NULL if out of memory. */ diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 93c6533c25da..18a1ac6ac22f 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -443,7 +443,7 @@ int drm_plane_helper_commit(struct drm_plane *plane, crtc_funcs[i]->atomic_begin(crtc[i]); } - plane_funcs->atomic_update(plane); + plane_funcs->atomic_update(plane, plane_state); for (i = 0; i < 2; i++) { if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush) diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index 6781aa994613..a7672e100d8b 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -84,7 +84,7 @@ static void crtc_flush(struct drm_crtc *crtc) struct drm_plane *plane; uint32_t flush = 0; - for_each_plane_on_crtc(crtc, plane) { + drm_atomic_crtc_for_each_plane(plane, crtc) { enum mdp4_pipe pipe_id = mdp4_plane_pipe(plane); flush |= pipe2flush(pipe_id); } @@ -197,7 +197,7 @@ static void setup_mixer(struct mdp4_kms *mdp4_kms) struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); struct drm_plane *plane; - for_each_plane_on_crtc(crtc, plane) { + drm_atomic_crtc_for_each_plane(plane, crtc) { enum mdp4_pipe pipe_id = mdp4_plane_pipe(plane); int idx = idxs[pipe_id]; mixer_cfg = mixercfg(mixer_cfg, mdp4_crtc->mixer, @@ -221,7 +221,7 @@ static void blend_setup(struct drm_crtc *crtc) mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH0(ovlp), 0); mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH1(ovlp), 0); - for_each_plane_on_crtc(crtc, plane) { + drm_atomic_crtc_for_each_plane(plane, crtc) { enum mdp4_pipe pipe_id = mdp4_plane_pipe(plane); int idx = idxs[pipe_id]; if (idx > 0) { diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 76d0a40c7138..1e5ebe83647d 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -107,7 +107,8 @@ static int mdp4_plane_atomic_check(struct drm_plane *plane, return 0; } -static void mdp4_plane_atomic_update(struct drm_plane *plane) +static void mdp4_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) { struct drm_plane_state *state = plane->state; int ret; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 0598bdea4ff4..0e9a2e3a82d7 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -91,7 +91,7 @@ static void crtc_flush_all(struct drm_crtc *crtc) if (!mdp5_crtc->ctl) return; - for_each_plane_on_crtc(crtc, plane) { + drm_atomic_crtc_for_each_plane(plane, crtc) { flush_mask |= mdp5_plane_get_flush(plane); } flush_mask |= mdp5_ctl_get_flush(mdp5_crtc->ctl); @@ -124,8 +124,9 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) } spin_unlock_irqrestore(&dev->event_lock, flags); - for_each_plane_on_crtc(crtc, plane) + drm_atomic_crtc_for_each_plane(plane, crtc) { mdp5_plane_complete_flip(plane); + } } static void mdp5_crtc_destroy(struct drm_crtc *crtc) @@ -195,7 +196,7 @@ static void blend_setup(struct drm_crtc *crtc) if (!mdp5_crtc->ctl) goto out; - for_each_plane_on_crtc(crtc, plane) { + drm_atomic_crtc_for_each_plane(plane, crtc) { enum mdp_mixer_stage_id stage = to_mdp5_plane_state(plane->state)->stage; @@ -317,7 +318,7 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, /* verify that there are not too many planes attached to crtc * and that we don't have conflicting mixer stages: */ - for_each_pending_plane_on_crtc(state->state, crtc, plane) { + drm_atomic_crtc_state_for_each_plane(plane, state) { struct drm_plane_state *pstate; if (cnt >= ARRAY_SIZE(pstates)) { diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 533df7caa310..26e5fdea6594 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -213,7 +213,8 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane, return 0; } -static void mdp5_plane_atomic_update(struct drm_plane *plane) +static void mdp5_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct drm_plane_state *state = plane->state; diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index 7fb4876388e7..06437745bc2c 100644 --- a/drivers/gpu/drm/msm/msm_kms.h +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -65,27 +65,4 @@ static inline void msm_kms_init(struct msm_kms *kms, struct msm_kms *mdp4_kms_init(struct drm_device *dev); struct msm_kms *mdp5_kms_init(struct drm_device *dev); -/* TODO move these helper iterator macro somewhere common: */ -#define for_each_plane_on_crtc(_crtc, _plane) \ - list_for_each_entry((_plane), &(_crtc)->dev->mode_config.plane_list, head) \ - if ((_plane)->state->crtc == (_crtc)) - -static inline bool -__plane_will_be_attached_to_crtc(struct drm_atomic_state *state, - struct drm_plane *plane, struct drm_crtc *crtc) -{ - int idx = drm_plane_index(plane); - - /* if plane is modified in incoming state, use the new state: */ - if (state->plane_states[idx]) - return state->plane_states[idx]->crtc == crtc; - - /* otherwise, current state: */ - return plane->state->crtc == crtc; -} - -#define for_each_pending_plane_on_crtc(_state, _crtc, _plane) \ - list_for_each_entry((_plane), &(_crtc)->dev->mode_config.plane_list, head) \ - if (__plane_will_be_attached_to_crtc((_state), (_plane), (_crtc))) - #endif /* __MSM_KMS_H__ */ diff --git a/include/drm/drmP.h b/include/drm/drmP.h index be776fb2db18..8ba35c622e22 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -809,7 +809,7 @@ struct drm_device { struct drm_local_map *agp_buffer_map; unsigned int agp_buffer_token; - struct drm_mode_config mode_config; /**< Current mode config */ + struct drm_mode_config mode_config; /**< Current mode config */ /** \name GEM information */ /*@{ */ diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 9d919168bc11..ad2229574dd9 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -28,6 +28,8 @@ #ifndef DRM_ATOMIC_H_ #define DRM_ATOMIC_H_ +#include + struct drm_atomic_state * __must_check drm_atomic_state_alloc(struct drm_device *dev); void drm_atomic_state_clear(struct drm_atomic_state *state); @@ -44,8 +46,8 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, struct drm_connector *connector); int __must_check -drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, - struct drm_crtc *crtc); +drm_atomic_set_crtc_for_plane(struct drm_atomic_state *state, + struct drm_plane *plane, struct drm_crtc *crtc); void drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state, struct drm_framebuffer *fb); int __must_check diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 64b4e91b93bc..f956b413311e 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -28,6 +28,8 @@ #ifndef DRM_ATOMIC_HELPER_H_ #define DRM_ATOMIC_HELPER_H_ +#include + int drm_atomic_helper_check(struct drm_device *dev, struct drm_atomic_state *state); int drm_atomic_helper_commit(struct drm_device *dev, @@ -96,5 +98,29 @@ drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector); void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, struct drm_connector_state *state); +/** + * drm_atomic_crtc_for_each_plane - iterate over planes currently attached to CRTC + * @plane: the loop cursor + * @crtc: the crtc whose planes are iterated + * + * This iterates over the current state, useful (for example) when applying + * atomic state after it has been checked and swapped. To iterate over the + * planes which *will* be attached (for ->atomic_check()) see + * drm_crtc_for_each_pending_plane() + */ +#define drm_atomic_crtc_for_each_plane(plane, crtc) \ + drm_for_each_plane_mask(plane, (crtc)->dev, (crtc)->state->plane_mask) + +/** + * drm_crtc_atomic_state_for_each_plane - iterate over attached planes in new state + * @plane: the loop cursor + * @crtc_state: the incoming crtc-state + * + * Similar to drm_crtc_for_each_plane(), but iterates the planes that will be + * attached if the specified state is applied. Useful during (for example) + * ->atomic_check() operations, to validate the incoming state + */ +#define drm_atomic_crtc_state_for_each_plane(plane, crtc_state) \ + drm_for_each_plane_mask(plane, (crtc_state)->state->dev, (crtc_state)->plane_mask) #endif /* DRM_ATOMIC_HELPER_H_ */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index b459e8fbbc25..dd2c16e43333 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -231,6 +231,7 @@ struct drm_atomic_state; * struct drm_crtc_state - mutable CRTC state * @enable: whether the CRTC should be enabled, gates all other state * @mode_changed: for use by helpers and drivers when computing state updates + * @plane_mask: bitmask of (1 << drm_plane_index(plane)) of attached planes * @last_vblank_count: for helpers and drivers to capture the vblank of the * update to ensure framebuffer cleanup isn't done too early * @planes_changed: for use by helpers and drivers when computing state updates @@ -247,6 +248,13 @@ struct drm_crtc_state { bool planes_changed : 1; bool mode_changed : 1; + /* attached planes bitmask: + * WARNING: transitional helpers do not maintain plane_mask so + * drivers not converted over to atomic helpers should not rely + * on plane_mask being accurate! + */ + u32 plane_mask; + /* last_vblank_count: for vblank waits before cleanup */ u32 last_vblank_count; @@ -438,7 +446,7 @@ struct drm_crtc { * @state: backpointer to global drm_atomic_state */ struct drm_connector_state { - struct drm_crtc *crtc; + struct drm_crtc *crtc; /* do not write directly, use drm_atomic_set_crtc_for_connector() */ struct drm_encoder *best_encoder; @@ -673,8 +681,8 @@ struct drm_connector { * @state: backpointer to global drm_atomic_state */ struct drm_plane_state { - struct drm_crtc *crtc; - struct drm_framebuffer *fb; + struct drm_crtc *crtc; /* do not write directly, use drm_atomic_set_crtc_for_plane() */ + struct drm_framebuffer *fb; /* do not write directly, use drm_atomic_set_fb_for_plane() */ struct fence *fence; /* Signed dest location allows it to be partially off screen */ @@ -1054,6 +1062,19 @@ struct drm_mode_config { uint32_t cursor_width, cursor_height; }; +/** + * drm_for_each_plane_mask - iterate over planes specified by bitmask + * @plane: the loop cursor + * @dev: the DRM device + * @plane_mask: bitmask of plane indices + * + * Iterate over all planes specified by bitmask. + */ +#define drm_for_each_plane_mask(plane, dev, plane_mask) \ + list_for_each_entry((plane), &(dev)->mode_config.plane_list, head) \ + if ((plane_mask) & (1 << drm_plane_index(plane))) + + #define obj_to_crtc(x) container_of(x, struct drm_crtc, base) #define obj_to_connector(x) container_of(x, struct drm_connector, base) #define obj_to_encoder(x) container_of(x, struct drm_encoder, base) diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h index c48f14d88690..a185392cafeb 100644 --- a/include/drm/drm_plane_helper.h +++ b/include/drm/drm_plane_helper.h @@ -49,6 +49,10 @@ extern int drm_crtc_init(struct drm_device *dev, /** * drm_plane_helper_funcs - helper operations for CRTCs + * @prepare_fb: prepare a framebuffer for use by the plane + * @cleanup_fb: cleanup a framebuffer when it's no longer used by the plane + * @atomic_check: check that a given atomic state is valid and can be applied + * @atomic_update: apply an atomic state to the plane * * The helper operations are called by the mid-layer CRTC helper. */ @@ -60,7 +64,8 @@ struct drm_plane_helper_funcs { int (*atomic_check)(struct drm_plane *plane, struct drm_plane_state *state); - void (*atomic_update)(struct drm_plane *plane); + void (*atomic_update)(struct drm_plane *plane, + struct drm_plane_state *old_state); }; static inline void drm_plane_helper_add(struct drm_plane *plane,