[RFCv4 10/14] drm: convert crtc to properties/state
On Tue, Mar 4, 2014 at 5:04 PM, Rob Clark wrote: > On Tue, Mar 4, 2014 at 4:29 PM, Sean Paul wrote: >> On Mon, Nov 25, 2013 at 9:47 AM, 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. >>> --- > > > >>> +static int check_connectors(struct drm_crtc *crtc, void *state, bool fix, >>> + uint32_t *connector_ids, uint32_t num_connector_ids) >>> +{ >>> + struct drm_mode_config *config = >dev->mode_config; >>> + struct drm_crtc *ocrtc; /* other connector */ >>> + >>> + list_for_each_entry(ocrtc, >crtc_list, head) { >>> + struct drm_crtc_state *ostate; /* other state */ >>> + unsigned i; >>> + >>> + if (ocrtc == crtc) >>> + continue; >>> + >>> + ostate = drm_atomic_get_crtc_state(crtc, state); >> >> Hi Rob, >> This will populate state's placeholder for ocrtc, which will have the >> unintended consequence of committing ocrtc's state and thus >> unreferencing ocrtc's current fb in >> drm_atomic_helper_commit_crtc_state. >> >> Maybe a new transient state bit in drm_crtc_state which avoids the >> commit_crtc_state call is in order? > > probably not a bad idea to avoid unnecessary commit, but need to > check if that is just masking a refcnt'ing problem. Ie. userspace > *should* be allowed to set properties on the crtc (for example, set > color correction properties, etc) without causing an unintended > finalizing of the fb.. > Right, agreed. Whenever I do a setcrtc on crtc A, I lose a reference to crtc B's current fb (and vice versa). If I short-circuit this code, we don't populate crtc B's cstate in state and the fb is not unreferenced. The alternative would be to take a reference on crtc B's fb here, but that will cause problems if crtc B is meant to be involved in the commit. I pasted what I have locally below, everything seems well-behaved now: diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 2386ab1..9fe1276 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -485,6 +485,7 @@ void drm_atomic_helper_init_crtc_state(struct drm_crtc *crtc, /* this should never happen.. but make sure! */ WARN_ON(cstate->event); cstate->event = NULL; + cstate->commit_state = false; } EXPORT_SYMBOL(drm_atomic_helper_init_crtc_state); @@ -631,6 +632,9 @@ drm_atomic_helper_commit_crtc_state(struct drm_crtc *crtc, struct drm_atomic_helper_state *a = cstate->state; int ret = -EINVAL; + if (!cstate->commit_state) + return 0; + if (cstate->set_config) return set_config(crtc, cstate); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 4834dd7..49eda31 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -959,6 +959,7 @@ int drm_crtc_set_property(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_mode_config *config = >mode_config; + state->commit_state = true; drm_object_property_set_value(>base, >propvals, property, value, blob_data); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index b81a64c..043ca1e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -482,6 +482,7 @@ struct drm_crtc_state { bool set_config: 1; bool new_fb: 1; bool connectors_change : 1; + bool commit_state : 1; uint8_t num_connector_ids; uint32_t *connector_ids; > > >>> @@ -3946,92 +4163,41 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, >>> if (!crtc) >>> return -ENOENT; >>> >>> - drm_modeset_lock(>mutex, NULL); >>> - if (crtc->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, >mode, >>> fb); >>> - if (ret) >>> - goto out; >>> - >>> - if (crtc->fb->pixel_format !=
[RFCv4 10/14] drm: convert crtc to properties/state
On Tue, Mar 4, 2014 at 4:29 PM, Sean Paul wrote: > On Mon, Nov 25, 2013 at 9:47 AM, 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. >> --- >> +static int check_connectors(struct drm_crtc *crtc, void *state, bool fix, >> + uint32_t *connector_ids, uint32_t num_connector_ids) >> +{ >> + struct drm_mode_config *config = >dev->mode_config; >> + struct drm_crtc *ocrtc; /* other connector */ >> + >> + list_for_each_entry(ocrtc, >crtc_list, head) { >> + struct drm_crtc_state *ostate; /* other state */ >> + unsigned i; >> + >> + if (ocrtc == crtc) >> + continue; >> + >> + ostate = drm_atomic_get_crtc_state(crtc, state); > > Hi Rob, > This will populate state's placeholder for ocrtc, which will have the > unintended consequence of committing ocrtc's state and thus > unreferencing ocrtc's current fb in > drm_atomic_helper_commit_crtc_state. > > Maybe a new transient state bit in drm_crtc_state which avoids the > commit_crtc_state call is in order? probably not a bad idea to avoid unnecessary commit, but need to check if that is just masking a refcnt'ing problem. Ie. userspace *should* be allowed to set properties on the crtc (for example, set color correction properties, etc) without causing an unintended finalizing of the fb.. >> @@ -3946,92 +4163,41 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, >> if (!crtc) >> return -ENOENT; >> >> - drm_modeset_lock(>mutex, NULL); >> - if (crtc->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, >mode, >> fb); >> - if (ret) >> - goto out; >> - >> - if (crtc->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(>event_lock, flags); >> - if (file_priv->event_space < sizeof e->event) { >> - spin_unlock_irqrestore(>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(>event_lock, flags); >> - >> - e = kzalloc(sizeof *e, GFP_KERNEL); >> - if (e == NULL) { >> - spin_lock_irqsave(>event_lock, flags); >> - file_priv->event_space += sizeof e->event; >> - spin_unlock_irqrestore(>event_lock, flags); >> + ret = dev->driver->atomic_set_event(dev, state, >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 = >event.base; >> - e->base.file_priv = file_priv; >> - e->base.destroy = >> - (void (*) (struct drm_pending_event *)) kfree; >> } >> >> - old_fb = crtc->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(>event_lock, flags); >> - file_priv->event_space += sizeof e->event; >> -
[RFCv4 10/14] drm: convert crtc to properties/state
On Mon, Nov 25, 2013 at 9:47 AM, 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. > --- > + > +static int remove_connector(struct drm_crtc *ocrtc, > + struct drm_crtc_state *ostate, void *state, int idx) > +{ > + struct drm_mode_config *config = >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(_connector_ids[idx], > + >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, void *state, bool fix, > + uint32_t *connector_ids, uint32_t num_connector_ids) > +{ > + struct drm_mode_config *config = >dev->mode_config; > + struct drm_crtc *ocrtc; /* other connector */ > + > + list_for_each_entry(ocrtc, >crtc_list, head) { > + struct drm_crtc_state *ostate; /* other state */ > + unsigned i; > + > + if (ocrtc == crtc) > + continue; > + > + ostate = drm_atomic_get_crtc_state(crtc, state); Hi Rob, This will populate state's placeholder for ocrtc, which will have the unintended consequence of committing ocrtc's state and thus unreferencing ocrtc's current fb in drm_atomic_helper_commit_crtc_state. Maybe a new transient state bit in drm_crtc_state which avoids the commit_crtc_state call is in order? > + 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_framebuffer *fb = state->fb; > + int hdisplay, vdisplay; > + struct drm_display_mode *mode = get_mode(crtc, state); > + > + 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(, CRTC_STEREO_DOUBLE); > + hdisplay = adjusted.crtc_hdisplay; > + vdisplay = adjusted.crtc_vdisplay; > + } > + > + if (state->invert_dimensions) > + swap(hdisplay, vdisplay); > + > + /* For some reason crtc x/y offsets are signed internally. */ > + if (state->x > INT_MAX || state->y > INT_MAX) > + return -ERANGE; > + > + if (hdisplay > fb->width || > + vdisplay > fb->height || > + state->x > fb->width - hdisplay || > + state->y > fb->height - vdisplay) { > + DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport > %ux%u+%d+%d%s.\n", > +
[RFCv4 10/14] drm: convert crtc to properties/state
On Wed, Dec 11, 2013 at 4:48 PM, Matt Roper wrote: > On Mon, Nov 25, 2013 at 09:47:34AM -0500, Rob Clark wrote: > ... >> +static struct drm_crtc_state * >> +drm_atomic_helper_get_crtc_state(struct drm_crtc *crtc, void *state) >> +{ >> + struct drm_atomic_helper_state *a = state; >> + struct drm_crtc_state *cstate; >> + int ret; >> + >> + ret = drm_modeset_lock(>mutex, state); >> + if (ret) >> + return ERR_PTR(ret); >> + >> + cstate = a->cstates[crtc->id]; > > The id field of struct drm_crtc never seems to be initialized in the > current DRM codebase, so this will always wind up looking up cstates[0]; > we probably want to initialize crtc->id in drm_crtc_init() at the same > place we increment dev->mode_config.num_crtc. > yeah, it is supposed to be initialized in drm_crtc_init().. I was juggling the patches around a bit, possibly I screwed up and that ended up in one of the later patches? I'll go back and check this > ... >> +static int check_connectors(struct drm_crtc *crtc, void *state, bool fix, >> + uint32_t *connector_ids, uint32_t num_connector_ids) >> +{ >> + struct drm_mode_config *config = >dev->mode_config; >> + struct drm_crtc *ocrtc; /* other connector */ >> + >> + list_for_each_entry(ocrtc, >crtc_list, head) { >> + struct drm_crtc_state *ostate; /* other state */ >> + unsigned i; >> + >> + if (ocrtc == crtc) >> + continue; >> + >> + ostate = drm_atomic_get_crtc_state(crtc, state); > > I think you meant to use ocrtc here rather than crtc, right? > yes, good catch. I need a driver with more than one connector to test with, apparently ;-) > > I think you might also want to move patch 11 up above 9 & 10, otherwise > you'll run into the lastclose deadlock while bisecting through this > patchset. > good point.. that should be pretty easy to re-order BR, -R > > Matt > > -- > Matt Roper > Intel Corporation
[RFCv4 10/14] drm: convert crtc to properties/state
On Mon, Nov 25, 2013 at 09:47:34AM -0500, Rob Clark wrote: ... > +static struct drm_crtc_state * > +drm_atomic_helper_get_crtc_state(struct drm_crtc *crtc, void *state) > +{ > + struct drm_atomic_helper_state *a = state; > + struct drm_crtc_state *cstate; > + int ret; > + > + ret = drm_modeset_lock(>mutex, state); > + if (ret) > + return ERR_PTR(ret); > + > + cstate = a->cstates[crtc->id]; The id field of struct drm_crtc never seems to be initialized in the current DRM codebase, so this will always wind up looking up cstates[0]; we probably want to initialize crtc->id in drm_crtc_init() at the same place we increment dev->mode_config.num_crtc. ... > +static int check_connectors(struct drm_crtc *crtc, void *state, bool fix, > + uint32_t *connector_ids, uint32_t num_connector_ids) > +{ > + struct drm_mode_config *config = >dev->mode_config; > + struct drm_crtc *ocrtc; /* other connector */ > + > + list_for_each_entry(ocrtc, >crtc_list, head) { > + struct drm_crtc_state *ostate; /* other state */ > + unsigned i; > + > + if (ocrtc == crtc) > + continue; > + > + ostate = drm_atomic_get_crtc_state(crtc, state); I think you meant to use ocrtc here rather than crtc, right? I think you might also want to move patch 11 up above 9 & 10, otherwise you'll run into the lastclose deadlock while bisecting through this patchset. Matt -- Matt Roper Intel Corporation
[RFCv4 10/14] drm: convert crtc to properties/state
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. --- drivers/gpu/drm/ast/ast_mode.c | 1 + drivers/gpu/drm/cirrus/cirrus_mode.c | 1 + drivers/gpu/drm/drm_atomic_helper.c| 277 - drivers/gpu/drm/drm_crtc.c | 622 ++--- 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/mdp4/mdp4_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_helper.h| 39 ++ include/drm/drm_crtc.h | 82 +++- 24 files changed, 818 insertions(+), 250 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 7fc9f72..13f6943 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -619,6 +619,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_helper_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 adabc3d..9e0b713 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -363,6 +363,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_helper_crtc_set_property, .destroy = cirrus_crtc_destroy, }; diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 14e0571..9b60536 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -40,11 +40,13 @@ void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags) { struct drm_atomic_helper_state *state; int nplanes = dev->mode_config.num_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); @@ -65,6 +67,12 @@ void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags) state->pstates = ptr; ptr = >pstates[nplanes]; + state->crtcs = ptr; + ptr = >crtcs[ncrtcs]; + + state->cstates = ptr; + ptr = >cstates[ncrtcs]; + return state; } EXPORT_SYMBOL(drm_atomic_helper_begin); @@ -83,7 +91,18 @@ int drm_atomic_helper_set_event(struct drm_device *dev, void *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_helper_set_event); @@ -102,6 +121,7 @@ int drm_atomic_helper_check(struct drm_device *dev, void *state) { struct drm_atomic_helper_state *a = state; int nplanes = dev->mode_config.num_plane; + int ncrtcs = dev->mode_config.num_crtc; int i, ret = 0; for (i = 0; i < nplanes; i++) { @@ -112,6 +132,14 @@ int drm_atomic_helper_check(struct drm_device *dev, void *state)