| From d893f7f791c4844c66fafb0494987294451176de Mon Sep 17 00:00:00 2001 |
| From: Rob Clark <robdclark@gmail.com> |
| Date: Sat, 15 Mar 2014 12:39:01 -0400 |
| Subject: [PATCH 12/15] UPSTREAM: 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. |
| |
| Signed-off-by: Rob Clark <robdclark@gmail.com> |
| |
| BUG=chromium:336809 |
| TEST=Tested on snow & peppy |
| |
| (cherry picked from commit c7979e4d26489df8e5112691ae4a1a16286e3607) |
| Signed-off-by: Sean Paul <seanpaul@chromium.org> |
| |
| Conflicts: |
| drivers/gpu/drm/armada/armada_crtc.c |
| drivers/gpu/drm/drm_crtc.c |
| drivers/gpu/drm/gma500/cdv_intel_display.c |
| drivers/gpu/drm/gma500/psb_intel_display.c |
| drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c |
| drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c |
| drivers/gpu/drm/nouveau/dispnv04/crtc.c |
| drivers/gpu/drm/nouveau/nv50_display.c |
| drivers/gpu/drm/omapdrm/omap_crtc.c |
| drivers/gpu/drm/omapdrm/omap_drv.c |
| drivers/gpu/drm/qxl/qxl_display.c |
| drivers/gpu/drm/radeon/radeon_display.c |
| drivers/gpu/drm/rcar-du/rcar_du_crtc.c |
| drivers/gpu/drm/tilcdc/tilcdc_crtc.c |
| include/drm/drm_crtc.h |
| |
| Change-Id: I266c9bdc87f8c94bdec1ddae8b024f086bed0de1 |
| --- |
| 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 | 576 +++++++++++++++++++---------- |
| 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/nouveau/nv50_display.c | 1 + |
| drivers/gpu/drm/radeon/radeon_display.c | 2 + |
| drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 2 + |
| 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 | 28 ++ |
| include/drm/drm_crtc.h | 103 +++++- |
| 16 files changed, 733 insertions(+), 220 deletions(-) |
| |
| diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c |
| index 9296cfe..0de5d53 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_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 2553f05..d37e484 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_crtc_set_property, |
| .destroy = cirrus_crtc_destroy, |
| }; |
| |
| diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c |
| index ac12b3b..71a5493 100644 |
| --- a/drivers/gpu/drm/drm_atomic.c |
| +++ b/drivers/gpu/drm/drm_atomic.c |
| @@ -40,11 +40,13 @@ void *drm_atomic_begin(struct drm_device *dev, uint32_t flags) |
| { |
| 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); |
| |
| @@ -65,6 +67,12 @@ void *drm_atomic_begin(struct drm_device *dev, uint32_t flags) |
| 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); |
| @@ -83,7 +91,18 @@ int drm_atomic_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_set_event); |
| |
| @@ -102,6 +121,7 @@ int drm_atomic_check(struct drm_device *dev, void *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++) { |
| @@ -111,6 +131,13 @@ int drm_atomic_check(struct drm_device *dev, void *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->checked = true; |
| |
| return ret; |
| @@ -191,6 +218,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++) { |
| @@ -201,6 +229,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): */ |
| mutex_lock(&a->mutex); |
| while (!list_empty(&a->locked)) { |
| @@ -220,8 +256,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) { |
| @@ -398,6 +444,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; |
| @@ -411,8 +458,11 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) |
| reset_plane(plane, 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, |
| @@ -429,7 +479,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 |
| @@ -459,9 +509,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) |
| @@ -470,8 +521,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, void *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, void *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, void *state) |
| +{ |
| + struct drm_atomic_state *a = state; |
| + struct drm_crtc_state *cstate; |
| + int ret; |
| + |
| + cstate = a->cstates[crtc->id]; |
| + |
| + if (!cstate) { |
| + ret = drm_modeset_lock(&crtc->mutex, state); |
| + if (ret) |
| + return ERR_PTR(ret); |
| + |
| + cstate = drm_crtc_create_state(crtc); |
| + if (!cstate) |
| + return ERR_PTR(-ENOMEM); |
| + init_crtc_state(crtc, cstate, state); |
| + 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, state); |
| + } |
| + 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 f404b54..8738926 100644 |
| --- a/drivers/gpu/drm/drm_crtc.c |
| +++ b/drivers/gpu/drm/drm_crtc.c |
| @@ -660,10 +660,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)); |
| |
| @@ -691,24 +688,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); |
| @@ -721,8 +701,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); |
| @@ -750,11 +728,16 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, |
| void *cursor, |
| const struct drm_crtc_funcs *funcs) |
| { |
| + 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); |
| @@ -765,14 +748,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); |
| @@ -800,31 +786,234 @@ 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, void *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, void *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 (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: */ |
| + int ret = drm_modeset_lock_all_crtcs(crtc->dev, state->state); |
| + 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_probed_add - add a mode to a connector's probed mode list |
| @@ -1165,6 +1354,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; |
| |
| @@ -1358,6 +1551,9 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev) |
| return -ENOMEM; |
| dev->mode_config.dpms_property = prop; |
| |
| + prop = drm_property_create_enum(dev, 0, "Content Protection", |
| + drm_cp_enum_list, ARRAY_SIZE(drm_cp_enum_list)); |
| + dev->mode_config.content_protection_property = prop; |
| |
| prop = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX); |
| if (!prop) |
| @@ -1412,9 +1608,15 @@ 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_enum(dev, 0, "Content Protection", |
| - drm_cp_enum_list, ARRAY_SIZE(drm_cp_enum_list)); |
| - dev->mode_config.content_protection_property = 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; |
| } |
| @@ -1719,7 +1921,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) |
| @@ -1962,8 +2164,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; |
| @@ -2405,7 +2607,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc, |
| hdisplay = mode->hdisplay; |
| vdisplay = mode->vdisplay; |
| |
| - if (crtc->invert_dimensions) |
| + if (crtc->state->invert_dimensions) |
| swap(hdisplay, vdisplay); |
| |
| if (hdisplay > fb->width || |
| @@ -2414,7 +2616,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; |
| } |
| |
| @@ -2441,22 +2643,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; |
| + void *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); |
| @@ -2474,55 +2669,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 = -EINVAL; |
| - goto out; |
| - } |
| + fb_id = crtc_req->fb_id; |
| } |
| - |
| - mode = drm_mode_create(dev); |
| - if (!mode) { |
| - ret = -ENOMEM; |
| - goto out; |
| - } |
| - |
| - ret = drm_crtc_convert_umode(mode, &crtc_req->mode); |
| - if (ret) { |
| - DRM_DEBUG_KMS("Invalid mode\n"); |
| - goto out; |
| - } |
| - |
| - 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) { |
| @@ -2530,52 +2685,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 = -EINVAL; |
| - 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; |
| } |
| |
| @@ -3850,9 +4018,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; |
| } |
| @@ -4206,14 +4371,59 @@ 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); |
| +} |
| + |
| 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; |
| + void *state; |
| int ret = -EINVAL; |
| |
| if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS || |
| @@ -4227,77 +4437,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) |
| - goto out; |
| - |
| - ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); |
| - if (ret) |
| - 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 { |
| - /* 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/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c |
| index 9d4f6e7..6cb9dc0 100644 |
| --- a/drivers/gpu/drm/gma500/cdv_intel_display.c |
| +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c |
| @@ -1766,5 +1766,6 @@ const struct drm_crtc_funcs cdv_intel_crtc_funcs = { |
| .cursor_move = cdv_intel_crtc_cursor_move, |
| .gamma_set = cdv_intel_crtc_gamma_set, |
| .set_config = cdv_crtc_set_config, |
| + .set_property = drm_atomic_crtc_set_property, |
| .destroy = cdv_intel_crtc_destroy, |
| }; |
| diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c |
| index bf2b5cb..5820e02 100644 |
| --- a/drivers/gpu/drm/gma500/psb_intel_display.c |
| +++ b/drivers/gpu/drm/gma500/psb_intel_display.c |
| @@ -1262,6 +1262,7 @@ const struct drm_crtc_funcs psb_intel_crtc_funcs = { |
| .cursor_move = psb_intel_crtc_cursor_move, |
| .gamma_set = psb_intel_crtc_gamma_set, |
| .set_config = psb_crtc_set_config, |
| + .set_property = drm_atomic_crtc_set_property, |
| .destroy = psb_intel_crtc_destroy, |
| }; |
| |
| diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c |
| index 5ca3347..b67f6f0 100644 |
| --- a/drivers/gpu/drm/i915/intel_display.c |
| +++ b/drivers/gpu/drm/i915/intel_display.c |
| @@ -8315,6 +8315,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 d9fac3c..7c1298b 100644 |
| --- a/drivers/gpu/drm/mgag200/mgag200_mode.c |
| +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c |
| @@ -1240,6 +1240,7 @@ static void mga_crtc_destroy(struct drm_crtc *crtc) |
| static const struct drm_crtc_funcs mga_crtc_funcs = { |
| .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/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c |
| index 3851f43..911deef 100644 |
| --- a/drivers/gpu/drm/nouveau/nv50_display.c |
| +++ b/drivers/gpu/drm/nouveau/nv50_display.c |
| @@ -1265,6 +1265,7 @@ static const struct drm_crtc_funcs nv50_crtc_func = { |
| .cursor_move = nv50_crtc_cursor_move, |
| .gamma_set = nv50_crtc_gamma_set, |
| .set_config = drm_crtc_helper_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/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c |
| index 52beff0..4783bf7 100644 |
| --- a/drivers/gpu/drm/radeon/radeon_display.c |
| +++ b/drivers/gpu/drm/radeon/radeon_display.c |
| @@ -31,6 +31,7 @@ |
| #include <asm/div64.h> |
| |
| #include <drm/drm_crtc_helper.h> |
| +#include <drm/drm_atomic.h> |
| #include <drm/drm_edid.h> |
| |
| static void avivo_crtc_load_lut(struct drm_crtc *crtc) |
| @@ -500,6 +501,7 @@ static const struct drm_crtc_funcs radeon_crtc_funcs = { |
| .cursor_move = radeon_crtc_cursor_move, |
| .gamma_set = radeon_crtc_gamma_set, |
| .set_config = drm_crtc_helper_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/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c |
| index a7b0207..0e5b5b8 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> |
| |
| @@ -507,6 +508,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/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c |
| index 20018c2..edc75860 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" |
| |
| /* |
| @@ -408,6 +409,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, |
| .page_flip = udl_crtc_page_flip, |
| }; |
| diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c |
| index 7d83c99..1e26f8b 100644 |
| --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c |
| +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c |
| @@ -298,6 +298,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 37a8e67..cd1a1ea 100644 |
| --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c |
| +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c |
| @@ -394,6 +394,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 7b7ba33..2287dbe 100644 |
| --- a/include/drm/drm_atomic.h |
| +++ b/include/drm/drm_atomic.h |
| @@ -64,6 +64,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; |
| @@ -100,6 +103,29 @@ 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, void *state, |
| + struct drm_property *property, uint64_t val, void *blob_data); |
| +struct drm_crtc_state *drm_atomic_get_crtc_state(struct drm_crtc *crtc, |
| + void *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 |
| */ |
| @@ -109,6 +135,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 a9698bf..39cd72c 100644 |
| --- a/include/drm/drm_crtc.h |
| +++ b/include/drm/drm_crtc.h |
| @@ -449,27 +449,62 @@ 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, void *state, |
| struct drm_property *property, uint64_t val, |
| void *blob_data); |
| }; |
| |
| /** |
| + * 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; |
| + |
| + void *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 |
| @@ -486,6 +521,8 @@ struct drm_crtc { |
| struct drm_device *dev; |
| struct list_head head; |
| |
| + int id; |
| + |
| /** |
| * crtc mutex |
| * |
| @@ -501,23 +538,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 */ |
| @@ -531,9 +564,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 |
| @@ -1059,6 +1098,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 *content_protection_property; |
| @@ -1118,7 +1159,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 |
| @@ -1129,9 +1171,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 int drm_connector_init(struct drm_device *dev, |
| struct drm_connector *connector, |
| const struct drm_connector_funcs *funcs, |
| @@ -1189,6 +1240,7 @@ extern char *drm_get_tv_subconnector_name(int val); |
| extern 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 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); |
| @@ -1475,6 +1527,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) |
| { |
| -- |
| 2.0.0.526.g5318336 |
| |