One more time, with feeling..
Previous revision of series: http://lists.freedesktop.org/archives/dri-devel/2014-March/055806.html
And if you prefer, in git form: http://cgit.freedesktop.org/~robclark/linux/log/?h=cold-fusion git://people.freedesktop.org/~robclark/linux cold-fusion
This series does not include the actual atomic ioctl, but it does include all the needed infrastructure changes.
Compared to previous revision, I've split out drm_modeset_acquire_ctx from drm_atomic_state, so that we can ww acquire mode_config and crtc locks outside of atomic transactions. (Which keeps lockdep happy wrt. drm_modeset_lock_all().) I also got to the point in juggling things around where I wanted to let the compiler type checking do it's job, so 's/void *state/struct drm_atomic_state *state/g'.
At this point, I've tested this on i915 (few different generation laptops), radeon, and msm. And of course w/ ww debug (deadlock in- jection) enabled. So I think all the locking related paths should be covered.
I believe Thierry has tested a slighly older revision on tegra. I would of course appreciate more testing on other drivers for which I don't have the hw. But I think it is pretty much ready to go. I do still owe some docs updates, I will send some patches for that in the near future, but didn't want to hold up giving others a chance to start banging on this.
Rob Clark (14): drm: fix typo drm: add atomic fxns drm: convert crtc and mode_config to ww_mutex drm: add object property type drm: add signed-range property type drm: helpers to find mode objects drm: split propvals out and blob property support drm: allow FB's in drm_mode_object_find drm: convert plane to properties/state drm: convert crtc to properties/state drm: push locking down into restore_fbdev_mode drm/msm: add atomic support drm: spiff out FB refcnting traces drm: more conservative locking
Sean Paul (1): drm: Fix up the atomic legacy paths so they work
Ville Syrjälä (2): drm: Allow drm_mode_object_find() to look up an object of any type drm: Refactor object property check code
drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/armada/armada_crtc.c | 14 +- drivers/gpu/drm/armada/armada_fbdev.c | 4 +- drivers/gpu/drm/armada/armada_output.c | 3 +- drivers/gpu/drm/armada/armada_overlay.c | 14 +- drivers/gpu/drm/ast/ast_drv.c | 6 + drivers/gpu/drm/ast/ast_drv.h | 1 + drivers/gpu/drm/ast/ast_mode.c | 1 + drivers/gpu/drm/cirrus/cirrus_drv.c | 6 + drivers/gpu/drm/cirrus/cirrus_drv.h | 1 + drivers/gpu/drm/cirrus/cirrus_mode.c | 1 + drivers/gpu/drm/drm_atomic.c | 720 +++++++++++++ drivers/gpu/drm/drm_crtc.c | 1534 ++++++++++++++++++--------- drivers/gpu/drm/drm_crtc_helper.c | 6 +- drivers/gpu/drm/drm_fb_cma_helper.c | 15 +- drivers/gpu/drm/drm_fb_helper.c | 69 +- drivers/gpu/drm/drm_modes.c | 4 +- drivers/gpu/drm/drm_modeset_lock.c | 159 +++ drivers/gpu/drm/drm_plane_helper.c | 2 + drivers/gpu/drm/drm_probe_helper.c | 10 +- drivers/gpu/drm/drm_sysfs.c | 4 +- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 11 +- drivers/gpu/drm/exynos/exynos_drm_drv.c | 7 + drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 8 +- drivers/gpu/drm/exynos/exynos_drm_plane.c | 11 +- drivers/gpu/drm/gma500/cdv_intel_crt.c | 4 +- drivers/gpu/drm/gma500/cdv_intel_display.c | 1 + drivers/gpu/drm/gma500/cdv_intel_dp.c | 7 +- drivers/gpu/drm/gma500/cdv_intel_hdmi.c | 7 +- drivers/gpu/drm/gma500/cdv_intel_lvds.c | 16 +- drivers/gpu/drm/gma500/mdfld_dsi_output.c | 12 +- drivers/gpu/drm/gma500/oaktrail_lvds.c | 6 +- drivers/gpu/drm/gma500/psb_drv.c | 11 +- drivers/gpu/drm/gma500/psb_drv.h | 1 + drivers/gpu/drm/gma500/psb_intel_display.c | 1 + drivers/gpu/drm/gma500/psb_intel_drv.h | 4 +- drivers/gpu/drm/gma500/psb_intel_lvds.c | 16 +- drivers/gpu/drm/gma500/psb_intel_sdvo.c | 23 +- drivers/gpu/drm/i2c/ch7006_drv.c | 4 +- drivers/gpu/drm/i915/i915_debugfs.c | 12 +- drivers/gpu/drm/i915/i915_drv.c | 16 +- drivers/gpu/drm/i915/i915_irq.c | 6 +- drivers/gpu/drm/i915/intel_crt.c | 4 +- drivers/gpu/drm/i915/intel_display.c | 24 +- drivers/gpu/drm/i915/intel_dp.c | 25 +- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_fbdev.c | 6 +- drivers/gpu/drm/i915/intel_hdmi.c | 7 +- drivers/gpu/drm/i915/intel_lvds.c | 10 +- drivers/gpu/drm/i915/intel_opregion.c | 4 +- drivers/gpu/drm/i915/intel_overlay.c | 4 +- drivers/gpu/drm/i915/intel_panel.c | 8 +- drivers/gpu/drm/i915/intel_sdvo.c | 23 +- drivers/gpu/drm/i915/intel_sprite.c | 3 +- drivers/gpu/drm/i915/intel_tv.c | 12 +- drivers/gpu/drm/mgag200/mgag200_drv.c | 7 + drivers/gpu/drm/mgag200/mgag200_drv.h | 1 + drivers/gpu/drm/mgag200/mgag200_mode.c | 1 + drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 70 +- drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c | 6 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c | 14 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 69 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 6 + drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 2 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 14 +- drivers/gpu/drm/msm/msm_atomic.c | 141 +++ drivers/gpu/drm/msm/msm_drv.c | 33 +- drivers/gpu/drm/msm/msm_drv.h | 8 + drivers/gpu/drm/msm/msm_gem.c | 24 +- drivers/gpu/drm/msm/msm_gem.h | 13 + drivers/gpu/drm/msm/msm_kms.h | 1 + drivers/gpu/drm/nouveau/dispnv04/crtc.c | 1 + drivers/gpu/drm/nouveau/dispnv04/overlay.c | 13 +- drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 3 +- drivers/gpu/drm/nouveau/nouveau_connector.c | 7 +- drivers/gpu/drm/nouveau/nouveau_drm.c | 7 + drivers/gpu/drm/nouveau/nouveau_drm.h | 1 + drivers/gpu/drm/nouveau/nv50_display.c | 1 + drivers/gpu/drm/omapdrm/omap_crtc.c | 26 +- drivers/gpu/drm/omapdrm/omap_drv.c | 16 +- drivers/gpu/drm/omapdrm/omap_drv.h | 4 +- drivers/gpu/drm/omapdrm/omap_plane.c | 14 +- drivers/gpu/drm/qxl/qxl_display.c | 6 +- drivers/gpu/drm/qxl/qxl_drv.c | 9 + drivers/gpu/drm/radeon/radeon_connectors.c | 9 +- drivers/gpu/drm/radeon/radeon_display.c | 2 + drivers/gpu/drm/radeon/radeon_drv.c | 9 + drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 2 + drivers/gpu/drm/rcar-du/rcar_du_drv.c | 7 + drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c | 3 +- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 12 +- drivers/gpu/drm/rcar-du/rcar_du_vgacon.c | 3 +- drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 6 +- drivers/gpu/drm/shmobile/shmob_drm_drv.c | 7 + drivers/gpu/drm/shmobile/shmob_drm_plane.c | 2 + drivers/gpu/drm/tegra/fb.c | 7 +- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 5 +- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 6 + drivers/gpu/drm/tilcdc/tilcdc_drv.h | 1 + drivers/gpu/drm/tilcdc/tilcdc_slave.c | 3 +- drivers/gpu/drm/udl/udl_connector.c | 6 +- drivers/gpu/drm/udl/udl_drv.c | 8 + drivers/gpu/drm/udl/udl_modeset.c | 2 + drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 7 + drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 26 +- drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 4 +- drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 1 + include/drm/drmP.h | 85 +- include/drm/drm_atomic.h | 170 +++ include/drm/drm_crtc.h | 326 +++++- include/drm/drm_fb_helper.h | 3 +- include/drm/drm_modeset_lock.h | 130 +++ include/uapi/drm/drm_mode.h | 18 + 117 files changed, 3401 insertions(+), 882 deletions(-) create mode 100644 drivers/gpu/drm/drm_atomic.c create mode 100644 drivers/gpu/drm/drm_modeset_lock.c create mode 100644 drivers/gpu/drm/msm/msm_atomic.c create mode 100644 include/drm/drm_atomic.h create mode 100644 include/drm/drm_modeset_lock.h
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/drm_crtc_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index a8b78e7..54e8fdb 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -147,7 +147,7 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev) list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (!drm_helper_encoder_in_use(encoder)) { drm_encoder_disable(encoder); - /* disconnector encoder from any connector */ + /* disconnect encoder from any connector */ encoder->crtc = NULL; } }
The 'atomic' mechanism allows for multiple properties to be updated, checked, and commited atomically. This will be the basis of atomic- modeset and nuclear-pageflip.
The basic flow is:
state = dev->atomic_begin(); for (... one or more ...) obj->set_property(obj, state, prop, value); if (dev->atomic_check(state)) dev->atomic_commit(state); dev->atomic_end(state);
The split of check and commit steps is to allow for ioctls with a test-only flag (which would skip the commit step).
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/armada/armada_crtc.c | 3 +- drivers/gpu/drm/armada/armada_output.c | 3 +- drivers/gpu/drm/armada/armada_overlay.c | 3 +- drivers/gpu/drm/ast/ast_drv.c | 6 ++ drivers/gpu/drm/ast/ast_drv.h | 1 + drivers/gpu/drm/cirrus/cirrus_drv.c | 6 ++ drivers/gpu/drm/cirrus/cirrus_drv.h | 1 + drivers/gpu/drm/drm_atomic.c | 142 +++++++++++++++++++++++++++ drivers/gpu/drm/drm_crtc.c | 144 +++++++++++++++++----------- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 4 +- drivers/gpu/drm/exynos/exynos_drm_drv.c | 7 ++ drivers/gpu/drm/exynos/exynos_drm_plane.c | 4 +- drivers/gpu/drm/gma500/cdv_intel_crt.c | 4 +- drivers/gpu/drm/gma500/cdv_intel_dp.c | 4 +- drivers/gpu/drm/gma500/cdv_intel_hdmi.c | 4 +- drivers/gpu/drm/gma500/cdv_intel_lvds.c | 4 +- drivers/gpu/drm/gma500/mdfld_dsi_output.c | 4 +- drivers/gpu/drm/gma500/psb_drv.c | 7 ++ drivers/gpu/drm/gma500/psb_drv.h | 1 + drivers/gpu/drm/gma500/psb_intel_drv.h | 4 +- drivers/gpu/drm/gma500/psb_intel_lvds.c | 4 +- drivers/gpu/drm/gma500/psb_intel_sdvo.c | 4 +- drivers/gpu/drm/i915/i915_drv.c | 8 ++ drivers/gpu/drm/i915/intel_crt.c | 4 +- drivers/gpu/drm/i915/intel_dp.c | 4 +- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_hdmi.c | 4 +- drivers/gpu/drm/i915/intel_lvds.c | 4 +- drivers/gpu/drm/i915/intel_sdvo.c | 4 +- drivers/gpu/drm/i915/intel_tv.c | 6 +- drivers/gpu/drm/mgag200/mgag200_drv.c | 7 ++ drivers/gpu/drm/mgag200/mgag200_drv.h | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 3 +- drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c | 3 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 3 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 3 +- drivers/gpu/drm/msm/msm_drv.c | 6 ++ drivers/gpu/drm/msm/msm_drv.h | 1 + drivers/gpu/drm/nouveau/dispnv04/overlay.c | 5 +- drivers/gpu/drm/nouveau/nouveau_connector.c | 3 +- drivers/gpu/drm/nouveau/nouveau_drm.c | 7 ++ drivers/gpu/drm/nouveau/nouveau_drm.h | 1 + drivers/gpu/drm/omapdrm/omap_crtc.c | 6 +- drivers/gpu/drm/omapdrm/omap_drv.c | 6 ++ drivers/gpu/drm/omapdrm/omap_drv.h | 4 +- drivers/gpu/drm/omapdrm/omap_plane.c | 3 +- drivers/gpu/drm/qxl/qxl_display.c | 4 +- drivers/gpu/drm/qxl/qxl_drv.c | 9 ++ drivers/gpu/drm/radeon/radeon_connectors.c | 9 +- drivers/gpu/drm/radeon/radeon_drv.c | 9 ++ drivers/gpu/drm/rcar-du/rcar_du_drv.c | 7 ++ drivers/gpu/drm/rcar-du/rcar_du_plane.c | 4 +- drivers/gpu/drm/shmobile/shmob_drm_drv.c | 7 ++ drivers/gpu/drm/tilcdc/tilcdc_drv.c | 6 ++ drivers/gpu/drm/tilcdc/tilcdc_drv.h | 1 + drivers/gpu/drm/tilcdc/tilcdc_slave.c | 3 +- drivers/gpu/drm/udl/udl_connector.c | 6 +- drivers/gpu/drm/udl/udl_drv.c | 8 ++ drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 7 ++ drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 4 +- drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 4 +- include/drm/drmP.h | 80 ++++++++++++++++ include/drm/drm_atomic.h | 107 +++++++++++++++++++++ include/drm/drm_crtc.h | 15 ++- 66 files changed, 652 insertions(+), 102 deletions(-) create mode 100644 drivers/gpu/drm/drm_atomic.c create mode 100644 include/drm/drm_atomic.h
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 48e38ba..ba2ed83 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -14,7 +14,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ drm_trace_points.o drm_global.o drm_prime.o \ drm_rect.o drm_vma_manager.o drm_flip_work.o \ - drm_plane_helper.o + drm_plane_helper.o drm_atomic.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 81c34f9..7d3c649 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -961,7 +961,8 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
static int armada_drm_crtc_set_property(struct drm_crtc *crtc, - struct drm_property *property, uint64_t val) + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) { struct armada_private *priv = crtc->dev->dev_private; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c index d685a54..9915306 100644 --- a/drivers/gpu/drm/armada/armada_output.c +++ b/drivers/gpu/drm/armada/armada_output.c @@ -54,7 +54,8 @@ static void armada_drm_connector_destroy(struct drm_connector *conn) }
static int armada_drm_connector_set_property(struct drm_connector *conn, - struct drm_property *property, uint64_t value) + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data) { struct armada_connector *dconn = drm_to_armada_conn(conn);
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c index c5b06fd..601ba9a 100644 --- a/drivers/gpu/drm/armada/armada_overlay.c +++ b/drivers/gpu/drm/armada/armada_overlay.c @@ -283,7 +283,8 @@ static void armada_plane_destroy(struct drm_plane *plane) }
static int armada_plane_set_property(struct drm_plane *plane, - struct drm_property *property, uint64_t val) + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) { struct armada_private *priv = plane->dev->dev_private; struct armada_plane *dplane = drm_to_armada_plane(plane); diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 2ba39ac..cd81d68 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -215,6 +215,12 @@ static struct drm_driver driver = { .dumb_map_offset = ast_dumb_mmap_offset, .dumb_destroy = drm_gem_dumb_destroy,
+ .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, };
static int __init ast_init(void) diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 5d6a875..0d21ff8 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -29,6 +29,7 @@ #define __AST_DRV_H__
#include <drm/drm_fb_helper.h> +#include <drm/drm_atomic.h>
#include <drm/ttm/ttm_bo_api.h> #include <drm/ttm/ttm_bo_driver.h> diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index 08ce520..023db21 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -137,6 +137,12 @@ static struct drm_driver driver = { .dumb_create = cirrus_dumb_create, .dumb_map_offset = cirrus_dumb_mmap_offset, .dumb_destroy = drm_gem_dumb_destroy, + .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, };
static const struct dev_pm_ops cirrus_pm_ops = { diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index 117d3ec..f929bc8 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -14,6 +14,7 @@ #include <video/vga.h>
#include <drm/drm_fb_helper.h> +#include <drm/drm_atomic.h>
#include <drm/ttm/ttm_bo_api.h> #include <drm/ttm/ttm_bo_driver.h> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c new file mode 100644 index 0000000..12fcbc0 --- /dev/null +++ b/drivers/gpu/drm/drm_atomic.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2014 Red Hat + * Author: Rob Clark robdclark@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include <drm/drmP.h> +#include <drm/drm_atomic.h> + +/** + * drm_atomic_begin - start a sequence of atomic updates + * @dev: DRM device + * @flags: the modifier flags that userspace has requested + * + * Begin a sequence of atomic property sets. Returns a driver + * private state object that is passed back into the various + * object's set_property() fxns, and into the remainder of the + * atomic funcs. The state object should accumulate the changes + * from one o more set_property()'s. At the end, the state can + * be checked, and optionally committed. + * + * RETURNS + * a driver state object, which is passed back in to the + * various other atomic fxns, or error (such as -EBUSY if + * there is still a pending async update) + */ +struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, + uint32_t flags) +{ + struct drm_atomic_state *state; + int sz; + void *ptr; + + sz = sizeof(*state); + + ptr = kzalloc(sz, GFP_KERNEL); + + state = ptr; + ptr = &state[1]; + + kref_init(&state->refcount); + state->dev = dev; + state->flags = flags; + return state; +} +EXPORT_SYMBOL(drm_atomic_begin); + +/** + * drm_atomic_set_event - set a pending event on mode object + * @dev: DRM device + * @state: the driver state object + * @obj: the object to set the event on + * @event: the event to send back + * + * Set pending event for an update on the specified object. The + * event is to be sent back to userspace after the update completes. + */ +int drm_atomic_set_event(struct drm_device *dev, + struct drm_atomic_state *state, struct drm_mode_object *obj, + struct drm_pending_vblank_event *event) +{ + return -EINVAL; /* for now */ +} +EXPORT_SYMBOL(drm_atomic_set_event); + +/** + * drm_atomic_check - validate state object + * @dev: DRM device + * @state: the driver state object + * + * Check the state object to see if the requested state is + * physically possible. + * + * RETURNS + * Zero for success or -errno + */ +int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) +{ + return 0; /* for now */ +} +EXPORT_SYMBOL(drm_atomic_check); + +/** + * drm_atomic_commit - commit state + * @dev: DRM device + * @state: the driver state object + * + * Commit the state. This will only be called if atomic_check() + * succeeds. + * + * RETURNS + * Zero for success or -errno + */ +int drm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state) +{ + return 0; /* for now */ +} +EXPORT_SYMBOL(drm_atomic_commit); + +/** + * drm_atomic_end - conclude the atomic update + * @dev: DRM device + * @state: the driver state object + * + * Release resources associated with the state object. + */ +void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state) +{ + drm_atomic_state_unreference(state); +} +EXPORT_SYMBOL(drm_atomic_end); + +void _drm_atomic_state_free(struct kref *kref) +{ + struct drm_atomic_state *state = + container_of(kref, struct drm_atomic_state, refcount); + kfree(state); +} +EXPORT_SYMBOL(_drm_atomic_state_free); + + +const struct drm_atomic_funcs drm_atomic_funcs = { +}; +EXPORT_SYMBOL(drm_atomic_funcs); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 34f0bf1..1876abb 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3731,20 +3731,21 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); }
-static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t value) +static int drm_mode_connector_set_obj_prop(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data) { int ret = -EINVAL; - struct drm_connector *connector = obj_to_connector(obj);
/* Do DPMS ourselves */ if (property == connector->dev->mode_config.dpms_property) { if (connector->funcs->dpms) (*connector->funcs->dpms)(connector, (int)value); ret = 0; - } else if (connector->funcs->set_property) - ret = connector->funcs->set_property(connector, property, value); + } else if (connector->funcs->set_property) { + ret = connector->funcs->set_property(connector, state, + property, value, blob_data); + }
/* store the property value if successful */ if (!ret) @@ -3752,38 +3753,93 @@ static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, return ret; }
-static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t value) +static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data) { int ret = -EINVAL; - struct drm_crtc *crtc = obj_to_crtc(obj);
if (crtc->funcs->set_property) - ret = crtc->funcs->set_property(crtc, property, value); + ret = crtc->funcs->set_property(crtc, state, property, + value, blob_data); if (!ret) - drm_object_property_set_value(obj, property, value); + drm_object_property_set_value(&crtc->base, property, value);
return ret; }
-static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t value) +static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data) { int ret = -EINVAL; - struct drm_plane *plane = obj_to_plane(obj);
if (plane->funcs->set_property) - ret = plane->funcs->set_property(plane, property, value); + ret = plane->funcs->set_property(plane, state, property, + value, blob_data); if (!ret) - drm_object_property_set_value(obj, property, value); + drm_object_property_set_value(&plane->base, property, value);
return ret; }
+static int drm_mode_set_obj_prop(struct drm_device *dev, + struct drm_mode_object *obj, struct drm_atomic_state *state, + struct drm_property *property, uint64_t value, void *blob_data) +{ + if (drm_property_change_is_valid(property, value)) { + switch (obj->type) { + case DRM_MODE_OBJECT_CONNECTOR: + return drm_mode_connector_set_obj_prop(obj_to_connector(obj), + state, property, value, blob_data); + case DRM_MODE_OBJECT_CRTC: + return drm_mode_crtc_set_obj_prop(obj_to_crtc(obj), + state, property, value, blob_data); + case DRM_MODE_OBJECT_PLANE: + return drm_mode_plane_set_obj_prop(obj_to_plane(obj), + state, property, value, blob_data); + } + } + + return -EINVAL; +} + +/* call with mode_config mutex held */ +static int drm_mode_set_obj_prop_id(struct drm_device *dev, + struct drm_atomic_state *state, + uint32_t obj_id, uint32_t obj_type, + uint32_t prop_id, uint64_t value, void *blob_data) +{ + struct drm_mode_object *arg_obj; + struct drm_mode_object *prop_obj; + struct drm_property *property; + int i; + + arg_obj = drm_mode_object_find(dev, obj_id, obj_type); + if (!arg_obj) + return -ENOENT; + if (!arg_obj->properties) + return -EINVAL; + + for (i = 0; i < arg_obj->properties->count; i++) + if (arg_obj->properties->ids[i] == prop_id) + break; + + if (i == arg_obj->properties->count) + return -EINVAL; + + prop_obj = drm_mode_object_find(dev, prop_id, + DRM_MODE_OBJECT_PROPERTY); + if (!prop_obj) + return -ENOENT; + property = obj_to_property(prop_obj); + + return drm_mode_set_obj_prop(dev, arg_obj, state, property, + value, blob_data); +} + /** - * drm_mode_getproperty_ioctl - get the current value of a object's property + * drm_mode_obj_get_properties_ioctl - get the current value of a object's property * @dev: DRM device * @data: ioctl data * @file_priv: DRM file info @@ -3873,57 +3929,35 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_obj_set_property *arg = data; - struct drm_mode_object *arg_obj; - struct drm_mode_object *prop_obj; - struct drm_property *property; + struct drm_atomic_state *state; int ret = -EINVAL; - int i;
if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL;
drm_modeset_lock_all(dev);
- arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); - if (!arg_obj) { - ret = -ENOENT; - goto out; + state = dev->driver->atomic_begin(dev, 0); + if (IS_ERR(state)) { + ret = PTR_ERR(state); + goto out_unlock; } - if (!arg_obj->properties) - goto out; - - for (i = 0; i < arg_obj->properties->count; i++) - if (arg_obj->properties->ids[i] == arg->prop_id) - break;
- if (i == arg_obj->properties->count) + ret = drm_mode_set_obj_prop_id(dev, state, + arg->obj_id, arg->obj_type, + arg->prop_id, arg->value, NULL); + if (ret) goto out;
- prop_obj = drm_mode_object_find(dev, arg->prop_id, - DRM_MODE_OBJECT_PROPERTY); - if (!prop_obj) { - ret = -ENOENT; - goto out; - } - property = obj_to_property(prop_obj); - - if (!drm_property_change_is_valid(property, arg->value)) + ret = dev->driver->atomic_check(dev, state); + if (ret) goto out;
- switch (arg_obj->type) { - case DRM_MODE_OBJECT_CONNECTOR: - ret = drm_mode_connector_set_obj_prop(arg_obj, property, - arg->value); - break; - case DRM_MODE_OBJECT_CRTC: - ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); - break; - case DRM_MODE_OBJECT_PLANE: - ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value); - break; - } + ret = dev->driver->atomic_commit(dev, state);
out: + dev->driver->atomic_end(dev, state); +out_unlock: drm_modeset_unlock_all(dev); return ret; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 1ef5ab9..2a56973 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -281,8 +281,10 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) }
static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t val) + uint64_t val, + void *blob_data) { struct drm_device *dev = crtc->dev; struct exynos_drm_private *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 2d27ba2..318969d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -14,6 +14,7 @@ #include <linux/pm_runtime.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h>
#include <linux/anon_inodes.h>
@@ -341,6 +342,12 @@ static struct drm_driver exynos_drm_driver = { .dumb_create = exynos_drm_gem_dumb_create, .dumb_map_offset = exynos_drm_gem_dumb_map_offset, .dumb_destroy = drm_gem_dumb_destroy, + .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_export = exynos_dmabuf_prime_export, diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 8371cbd..9da0935 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -212,8 +212,10 @@ static void exynos_plane_destroy(struct drm_plane *plane) }
static int exynos_plane_set_property(struct drm_plane *plane, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t val) + uint64_t val, + void *blob_data) { struct drm_device *dev = plane->dev; struct exynos_plane *exynos_plane = to_exynos_plane(plane); diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c index c18268c..6d637d3 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_crt.c +++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c @@ -205,8 +205,10 @@ static int cdv_intel_crt_get_modes(struct drm_connector *connector) }
static int cdv_intel_crt_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t value) + uint64_t value, + void *blob_data) { return 0; } diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 9ff30c2..31d872d 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -1645,8 +1645,10 @@ cdv_intel_dp_detect_audio(struct drm_connector *connector)
static int cdv_intel_dp_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t val) + uint64_t val, + void *blob_data) { struct drm_psb_private *dev_priv = connector->dev->dev_private; struct gma_encoder *encoder = gma_attached_encoder(connector); diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index b99084b..f0e4d78 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c @@ -150,8 +150,10 @@ static enum drm_connector_status cdv_hdmi_detect( }
static int cdv_hdmi_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t value) + uint64_t value, + void *blob_data) { struct drm_encoder *encoder = connector->encoder;
diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index 8ecc920..7ffaed4 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -452,8 +452,10 @@ static void cdv_intel_lvds_destroy(struct drm_connector *connector) }
static int cdv_intel_lvds_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t value) + uint64_t value, + void *blob_data) { struct drm_encoder *encoder = connector->encoder;
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c index 6e91b20..2318a46 100644 --- a/drivers/gpu/drm/gma500/mdfld_dsi_output.c +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c @@ -243,8 +243,10 @@ mdfld_dsi_connector_detect(struct drm_connector *connector, bool force) }
static int mdfld_dsi_connector_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t value) + uint64_t value, + void *blob_data) { struct drm_encoder *encoder = connector->encoder;
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 0a3101a..0180292 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -489,6 +489,13 @@ static struct drm_driver driver = { .disable_vblank = psb_disable_vblank, .get_vblank_counter = psb_get_vblank_counter,
+ .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, + .gem_free_object = psb_gem_free_object, .gem_vm_ops = &psb_gem_vm_ops,
diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 55ebe2b..413ea37 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -25,6 +25,7 @@ #include <drm/drmP.h> #include <drm/drm_global.h> #include <drm/gma_drm.h> +#include <drm/drm_atomic.h> #include "psb_reg.h" #include "psb_intel_drv.h" #include "gma_display.h" diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index 336bd3a..96e9759 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -254,8 +254,10 @@ extern bool psb_intel_lvds_mode_fixup(struct drm_encoder *encoder, extern int psb_intel_lvds_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode); extern int psb_intel_lvds_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t value); + uint64_t value, + void *blob_data); extern void psb_intel_lvds_destroy(struct drm_connector *connector); extern const struct drm_encoder_funcs psb_intel_lvds_enc_funcs;
diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index d7778d0..6b64d9d 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -569,8 +569,10 @@ void psb_intel_lvds_destroy(struct drm_connector *connector) }
int psb_intel_lvds_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t value) + uint64_t value, + void *blob_data) { struct drm_encoder *encoder = connector->encoder;
diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index deeb082..724b525 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -1705,8 +1705,10 @@ static bool psb_intel_sdvo_detect_hdmi_audio(struct drm_connector *connector)
static int psb_intel_sdvo_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t val) + uint64_t val, + void *blob_data) { struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector); struct psb_intel_sdvo_connector *psb_intel_sdvo_connector = to_psb_intel_sdvo_connector(connector); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 208e185..e3a2482 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1119,6 +1119,14 @@ static struct drm_driver driver = { .dumb_create = i915_gem_dumb_create, .dumb_map_offset = i915_gem_mmap_gtt, .dumb_destroy = drm_gem_dumb_destroy, + + .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, + .ioctls = i915_ioctls, .fops = &i915_driver_fops, .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 22d8347..d4198ff 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -724,8 +724,10 @@ out: }
static int intel_crt_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t value) + uint64_t value, + void *blob_data) { return 0; } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 34ed143..c63d210 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3270,8 +3270,10 @@ intel_dp_detect_audio(struct drm_connector *connector)
static int intel_dp_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t val) + uint64_t val, + void *blob_data) { struct drm_i915_private *dev_priv = connector->dev->dev_private; struct intel_connector *intel_connector = to_intel_connector(connector); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d8b540b..64aeae0 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -31,6 +31,7 @@ #include "i915_drv.h" #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include <drm/drm_fb_helper.h> #include <drm/drm_dp_helper.h>
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index b606162..8e160b0 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1044,8 +1044,10 @@ intel_hdmi_detect_audio(struct drm_connector *connector)
static int intel_hdmi_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t val) + uint64_t val, + void *blob_data) { struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); struct intel_digital_port *intel_dig_port = diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 1b1541d..4028237 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -486,8 +486,10 @@ static void intel_lvds_destroy(struct drm_connector *connector) }
static int intel_lvds_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t value) + uint64_t value, + void *blob_data) { struct intel_connector *intel_connector = to_intel_connector(connector); struct drm_device *dev = connector->dev; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 2bf09e8..4e36ee99 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2052,8 +2052,10 @@ static bool intel_sdvo_detect_hdmi_audio(struct drm_connector *connector)
static int intel_sdvo_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t val) + uint64_t val, + void *blob_data) { struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector); diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index e0193e8..05ee0d5 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1444,8 +1444,10 @@ intel_tv_destroy(struct drm_connector *connector)
static int -intel_tv_set_property(struct drm_connector *connector, struct drm_property *property, - uint64_t val) +intel_tv_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, + struct drm_property *property, + uint64_t val, void *blob_data) { struct drm_device *dev = connector->dev; struct intel_tv *intel_tv = intel_attached_tv(connector); diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index f15ea3c..0425bdd 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -103,6 +103,13 @@ static struct drm_driver driver = { .dumb_create = mgag200_dumb_create, .dumb_map_offset = mgag200_dumb_mmap_offset, .dumb_destroy = drm_gem_dumb_destroy, + + .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, };
static struct pci_driver mgag200_pci_driver = { diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index cf11ee6..c4d1600 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -16,6 +16,7 @@ #include <video/vga.h>
#include <drm/drm_fb_helper.h> +#include <drm/drm_atomic.h> #include <drm/ttm/ttm_bo_api.h> #include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_placement.h> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index ef9957d..efa19c8 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -468,7 +468,8 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc, }
static int mdp4_crtc_set_property(struct drm_crtc *crtc, - struct drm_property *property, uint64_t val) + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) { // XXX return -EINVAL; diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 66f33db..8c064dc 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -85,7 +85,8 @@ void mdp4_plane_install_properties(struct drm_plane *plane, }
int mdp4_plane_set_property(struct drm_plane *plane, - struct drm_property *property, uint64_t val) + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) { // XXX return -EINVAL; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 6ea10bd..ff48944 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -386,7 +386,8 @@ static int mdp5_crtc_page_flip(struct drm_crtc *crtc, }
static int mdp5_crtc_set_property(struct drm_crtc *crtc, - struct drm_property *property, uint64_t val) + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) { // XXX return -EINVAL; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 47f7bbb..5cbf226 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -100,7 +100,8 @@ void mdp5_plane_install_properties(struct drm_plane *plane, }
int mdp5_plane_set_property(struct drm_plane *plane, - struct drm_property *property, uint64_t val) + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) { // XXX return -EINVAL; diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 50ec1be..46e0f29 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -815,6 +815,12 @@ static struct drm_driver msm_driver = { .gem_prime_import_sg_table = msm_gem_prime_import_sg_table, .gem_prime_vmap = msm_gem_prime_vmap, .gem_prime_vunmap = msm_gem_prime_vunmap, + .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, #ifdef CONFIG_DEBUG_FS .debugfs_init = msm_debugfs_init, .debugfs_cleanup = msm_debugfs_cleanup, diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 9d10ee0..afc990e 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -50,6 +50,7 @@ static inline struct device *msm_iommu_get_ctx(const char *ctx_name) #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_atomic.h> #include <drm/msm_drm.h>
struct msm_kms; diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c index ab03f77..577e6aa 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c @@ -221,8 +221,9 @@ nv10_set_params(struct nouveau_plane *plane)
static int nv_set_property(struct drm_plane *plane, - struct drm_property *property, - uint64_t value) + struct drm_atomic_state *state, + struct drm_property *property, + uint64_t value, void *blob_data) { struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index d07ce02..3351e72 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -447,7 +447,8 @@ nouveau_connector_force(struct drm_connector *connector)
static int nouveau_connector_set_property(struct drm_connector *connector, - struct drm_property *property, uint64_t value) + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data) { struct nouveau_display *disp = nouveau_display(connector->dev); struct nouveau_connector *nv_connector = nouveau_connector(connector); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index ddd8375..9a96d6d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -858,6 +858,13 @@ driver = { .dumb_map_offset = nouveau_display_dumb_map_offset, .dumb_destroy = drm_gem_dumb_destroy,
+ .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, + .name = DRIVER_NAME, .desc = DRIVER_DESC, #ifdef GIT_REVISION diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h index 7efbafa..8941696 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.h +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h @@ -29,6 +29,7 @@ #include <subdev/vm.h>
#include <drmP.h> +#include <drm/drm_atomic.h> #include <drm/nouveau_drm.h>
#include <drm/ttm/ttm_bo_api.h> diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index e3c47a8..a7c0010 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -382,7 +382,8 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, }
static int omap_crtc_set_property(struct drm_crtc *crtc, - struct drm_property *property, uint64_t val) + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct omap_drm_private *priv = crtc->dev->dev_private; @@ -392,7 +393,8 @@ static int omap_crtc_set_property(struct drm_crtc *crtc, !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270))); }
- return omap_plane_set_property(omap_crtc->plane, property, val); + return omap_plane_set_property(omap_crtc->plane, state, + property, val, blob_data); }
static const struct drm_crtc_funcs omap_crtc_funcs = { diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index c8270e4..d3c9161 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -651,6 +651,12 @@ static struct drm_driver omap_drm_driver = { .dumb_create = omap_gem_dumb_create, .dumb_map_offset = omap_gem_dumb_map_offset, .dumb_destroy = drm_gem_dumb_destroy, + .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, .ioctls = ioctls, .num_ioctls = DRM_OMAP_NUM_IOCTLS, .fops = &omapdriver_fops, diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 284b80f..346fc8a 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -25,6 +25,7 @@ #include <linux/types.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include <drm/omap_drm.h> #include <linux/platform_data/omap_drm.h>
@@ -178,7 +179,8 @@ int omap_plane_mode_set(struct drm_plane *plane, void omap_plane_install_properties(struct drm_plane *plane, struct drm_mode_object *obj); int omap_plane_set_property(struct drm_plane *plane, - struct drm_property *property, uint64_t val); + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data);
struct drm_encoder *omap_encoder_init(struct drm_device *dev, struct omap_dss_device *dssdev); diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 3cf31ee..8ae5c49 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -336,7 +336,8 @@ void omap_plane_install_properties(struct drm_plane *plane, }
int omap_plane_set_property(struct drm_plane *plane, - struct drm_property *property, uint64_t val) + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) { struct omap_plane *omap_plane = to_omap_plane(plane); struct omap_drm_private *priv = plane->dev->dev_private; diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 3ab9072..b54c970 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -819,8 +819,10 @@ static enum drm_connector_status qxl_conn_detect( }
static int qxl_conn_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t value) + uint64_t value, + void *blob_data) { DRM_DEBUG("\n"); return 0; diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index 6e93663..1984c2c 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -34,6 +34,7 @@ #include "drmP.h" #include "drm/drm.h" #include "drm_crtc_helper.h" +#include "drm_atomic.h" #include "qxl_drv.h" #include "qxl_object.h"
@@ -220,6 +221,14 @@ static struct drm_driver qxl_driver = { .dumb_create = qxl_mode_dumb_create, .dumb_map_offset = qxl_mode_dumb_mmap, .dumb_destroy = drm_gem_dumb_destroy, + + .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, + #if defined(CONFIG_DEBUG_FS) .debugfs_init = qxl_debugfs_init, .debugfs_cleanup = qxl_debugfs_takedown, diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index ea50e0a..f7ecdfc 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -359,8 +359,9 @@ static void radeon_add_common_modes(struct drm_encoder *encoder, struct drm_conn } }
-static int radeon_connector_set_property(struct drm_connector *connector, struct drm_property *property, - uint64_t val) +static int radeon_connector_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) { struct drm_device *dev = connector->dev; struct radeon_device *rdev = dev->dev_private; @@ -682,8 +683,10 @@ static void radeon_connector_destroy(struct drm_connector *connector) }
static int radeon_lvds_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t value) + uint64_t value, + void *blob_data) { struct drm_device *dev = connector->dev; struct radeon_encoder *radeon_encoder; diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 15447a41..745791d 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -34,6 +34,7 @@ #include "radeon_drv.h"
#include <drm/drm_pciids.h> +#include <drm/drm_atomic.h> #include <linux/console.h> #include <linux/module.h> #include <linux/pm_runtime.h> @@ -545,6 +546,14 @@ static struct drm_driver kms_driver = { .dumb_create = radeon_mode_dumb_create, .dumb_map_offset = radeon_mode_dumb_mmap, .dumb_destroy = drm_gem_dumb_destroy, + + .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, + .fops = &radeon_driver_kms_fops,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 792fd1d..3f642a8 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -21,6 +21,7 @@
#include <drm/drmP.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>
@@ -175,6 +176,12 @@ static struct drm_driver rcar_du_driver = { .dumb_create = rcar_du_dumb_create, .dumb_map_offset = drm_gem_cma_dumb_map_offset, .dumb_destroy = drm_gem_dumb_destroy, + .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, .fops = &rcar_du_fops, .name = "rcar-du", .desc = "Renesas R-Car Display Unit", diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 3fb69d9..3a5d843 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -397,8 +397,10 @@ done: }
static int rcar_du_plane_set_property(struct drm_plane *plane, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t value) + uint64_t value, + void *blob_data) { struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_group *rgrp = rplane->group; diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index 82c84c7..7474ffb 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -21,6 +21,7 @@
#include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include <drm/drm_gem_cma_helper.h>
#include "shmob_drm_crtc.h" @@ -285,6 +286,12 @@ static struct drm_driver shmob_drm_driver = { .dumb_create = drm_gem_cma_dumb_create, .dumb_map_offset = drm_gem_cma_dumb_map_offset, .dumb_destroy = drm_gem_dumb_destroy, + .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, .fops = &shmob_drm_fops, .name = "shmob-drm", .desc = "Renesas SH Mobile DRM", diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index b20b694..3579b07 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -519,6 +519,12 @@ static struct drm_driver tilcdc_driver = { .dumb_create = drm_gem_cma_dumb_create, .dumb_map_offset = drm_gem_cma_dumb_map_offset, .dumb_destroy = drm_gem_dumb_destroy, + .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, #ifdef CONFIG_DEBUG_FS .debugfs_init = tilcdc_debugfs_init, .debugfs_cleanup = tilcdc_debugfs_cleanup, diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index 0938036..1280a65 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h @@ -31,6 +31,7 @@
#include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_fb_cma_helper.h>
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c index 595068b..693f3ec 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.c @@ -206,7 +206,8 @@ static struct drm_encoder *slave_connector_best_encoder( }
static int slave_connector_set_property(struct drm_connector *connector, - struct drm_property *property, uint64_t value) + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data) { struct drm_encoder *encoder = to_slave_connector(connector)->encoder; return get_slave_funcs(encoder)->set_property(encoder, diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index b44d548..936244d 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -115,9 +115,9 @@ udl_best_single_encoder(struct drm_connector *connector) return encoder; }
-static int udl_connector_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t val) +static int udl_connector_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) { return 0; } diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index 3ddd6cd..52ade41 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -9,6 +9,7 @@ #include <linux/module.h> #include <drm/drm_usb.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include "udl_drv.h"
static struct drm_driver driver; @@ -88,6 +89,13 @@ static struct drm_driver driver = { .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = udl_gem_prime_import,
+ .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, + .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 6bdd15e..3ec6792 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -1433,6 +1433,13 @@ static struct drm_driver driver = { .prime_fd_to_handle = vmw_prime_fd_to_handle, .prime_handle_to_fd = vmw_prime_handle_to_fd,
+ .atomic_begin = drm_atomic_begin, + .atomic_set_event = drm_atomic_set_event, + .atomic_check = drm_atomic_check, + .atomic_commit = drm_atomic_commit, + .atomic_end = drm_atomic_end, + .atomic_funcs = &drm_atomic_funcs, + .fops = &vmwgfx_driver_fops, .name = VMWGFX_DRIVER_NAME, .desc = VMWGFX_DRIVER_DESC, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 6b252a8..b0d6f3a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -30,6 +30,7 @@
#include "vmwgfx_reg.h" #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/vmwgfx_drm.h> #include <drm/drm_hashtab.h> #include <linux/suspend.h> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index e7199b4..6b9db5e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -2007,8 +2007,10 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector, }
int vmw_du_connector_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t val) + uint64_t val, + void *blob_data) { return 0; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index 8d038c3..6b02fdb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -141,8 +141,10 @@ vmw_du_connector_detect(struct drm_connector *connector, bool force); int vmw_du_connector_fill_modes(struct drm_connector *connector, uint32_t max_width, uint32_t max_height); int vmw_du_connector_set_property(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, - uint64_t val); + uint64_t val, + void *blob_data);
/* diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 12f10bc..e8d0d17 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -948,6 +948,86 @@ struct drm_driver { struct drm_device *dev, uint32_t handle);
+ /* + * Atomic functions: + */ + + /** + * atomic_begin - start a sequence of atomic updates + * @dev: DRM device + * @flags: the modifier flags that userspace has requested + * + * Begin a sequence of atomic property sets. Returns a driver + * private state object that is passed back into the various + * object's set_property() fxns, and into the remainder of the + * atomic funcs. The state object should accumulate the changes + * from one o more set_property()'s. At the end, the state can + * be checked, and optionally committed. + * + * RETURNS + * a driver state object, which is passed back in to the + * various other atomic fxns, or error (such as -EBUSY if + * there is still a pending async update) + */ + struct drm_atomic_state *(*atomic_begin)(struct drm_device *dev, uint32_t flags); + + /** + * atomic_set_event - set a pending event on mode object + * @dev: DRM device + * @state: the driver state object + * @obj: the object to set the event on + * @event: the event to send back + * + * Set pending event for an update on the specified object. The + * event is to be sent back to userspace after the update completes. + */ + int (*atomic_set_event)(struct drm_device *dev, + struct drm_atomic_state *state, struct drm_mode_object *obj, + struct drm_pending_vblank_event *event); + + /** + * atomic_check - validate state object + * @dev: DRM device + * @state: the driver state object + * + * Check the state object to see if the requested state is + * physically possible. + * + * RETURNS + * Zero for success or -errno + */ + int (*atomic_check)(struct drm_device *dev, + struct drm_atomic_state *state); + + /** + * atomic_commit - commit state + * @dev: DRM device + * @state: the driver state object + * + * Commit the state. This will only be called if atomic_check() + * succeeds. + * + * RETURNS + * Zero for success or -errno + */ + int (*atomic_commit)(struct drm_device *dev, + struct drm_atomic_state *state); + + /** + * atomic_end - conclude the atomic update + * @dev: DRM device + * @state: the driver state object + + * Release resources associated with the state object. + */ + void (*atomic_end)(struct drm_device *dev, + struct drm_atomic_state *state); + + /** + * Funcs used by drm-atomic-helpers + */ + const struct drm_atomic_funcs *atomic_funcs; + /* Driver private ops for this object */ const struct vm_operations_struct *gem_vm_ops;
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h new file mode 100644 index 0000000..5274212 --- /dev/null +++ b/include/drm/drm_atomic.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2014 Red Hat + * Author: Rob Clark robdclark@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef DRM_ATOMIC_HELPER_H_ +#define DRM_ATOMIC_HELPER_H_ + +/** + * DOC: atomic state helpers + * + * Base helper atomic state and functions. Drivers are free to either + * use these as-is, extend them, or completely replace them, in order + * to implement the atomic KMS API. + * + * A naive driver, with no special constraints or hw support for atomic + * updates may simply add the following to their driver struct: + * + * .atomic_begin = drm_atomic_begin, + * .atomic_set_event = drm_atomic_set_event, + * .atomic_check = drm_atomic_check, + * .atomic_commit = drm_atomic_commit, + * .atomic_end = drm_atomic_end, + * .atomics = &drm_atomic_funcs, + * + * In addition, if you're plane/crtc doesn't already have it's own custom + * properties, then add to your plane/crtc_funcs: + * + * .set_property = drm_atomic_{plane,crtc}_set_property, + * + * Unlike the crtc helpers, it is intended that the atomic helpers can be + * used piecemeal by the drivers, either using all or overriding parts as + * needed. + * + * A driver which can have (for example) conflicting modes across multiple + * crtcs (for example, bandwidth limitations or clock/pll configuration + * restrictions), can simply wrap drm_atomic_check() with their own + * driver specific .atomic_check() function. + * + * A driver which can support true atomic updates can wrap + * drm_atomic_commit(). + * + * A driver with custom properties should override the appropriate get_state(), + * check_state(), and commit_state() functions in .atomics if it uses + * the drm-atomic-helpers. Otherwise it is free to use &drm_atomic_funcs + * as-is. + */ + +/** + * struct drm_atomic_funcs - helper funcs used by the atomic helpers + */ +struct drm_atomic_funcs { + int dummy; /* for now */ +}; + +const extern struct drm_atomic_funcs drm_atomic_funcs; + +struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, + uint32_t flags); +int drm_atomic_set_event(struct drm_device *dev, + struct drm_atomic_state *state, struct drm_mode_object *obj, + struct drm_pending_vblank_event *event); +int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state); +int drm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state); +void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state); + +/** + * struct drm_atomic_state - the state object used by atomic helpers + */ +struct drm_atomic_state { + struct kref refcount; + struct drm_device *dev; + uint32_t flags; +}; + +static inline void +drm_atomic_state_reference(struct drm_atomic_state *state) +{ + kref_get(&state->refcount); +} + +static inline void +drm_atomic_state_unreference(struct drm_atomic_state *state) +{ + void _drm_atomic_state_free(struct kref *kref); + kref_put(&state->refcount, _drm_atomic_state_free); +} + +#endif /* DRM_ATOMIC_HELPER_H_ */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 698d54e..4790cad 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -200,6 +200,8 @@ struct drm_encoder; struct drm_pending_vblank_event; struct drm_plane; struct drm_bridge; +struct drm_atomic_state; +struct drm_atomic_funcs;
/** * drm_crtc_funcs - control CRTCs for a given device @@ -262,7 +264,9 @@ struct drm_crtc_funcs { uint32_t flags);
int (*set_property)(struct drm_crtc *crtc, - struct drm_property *property, uint64_t val); + struct drm_atomic_state *state, + struct drm_property *property, uint64_t val, + void *blob_data); };
/** @@ -375,8 +379,9 @@ struct drm_connector_funcs { enum drm_connector_status (*detect)(struct drm_connector *connector, bool force); int (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height); - int (*set_property)(struct drm_connector *connector, struct drm_property *property, - uint64_t val); + int (*set_property)(struct drm_connector *connector, + struct drm_atomic_state *state, + struct drm_property *property, uint64_t val, void *blob_data); void (*destroy)(struct drm_connector *connector); void (*force)(struct drm_connector *connector); }; @@ -541,7 +546,9 @@ struct drm_plane_funcs { void (*destroy)(struct drm_plane *plane);
int (*set_property)(struct drm_plane *plane, - struct drm_property *property, uint64_t val); + struct drm_atomic_state *state, + struct drm_property *property, uint64_t val, + void *blob_data); };
enum drm_plane_type {
For atomic, it will be quite convenient to not have to care so much about locking order. And 'state' gives us a convenient place to stash a ww_ctx for any sort of update that needs to grab multiple crtc locks.
Because we will want to eventually make locking even more fine grained (giving locks to planes, connectors, etc), split out drm_modeset_lock so that the atomic state won't eventually have to keep separate lists of locked planes/crtcs/etc.
The state keeps track of which locks it has aquired, and for the benefit of NONBLOCK operations, supports transfering "locked" resources to driver worker/thread to complete the asynchronous state change.
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_atomic.c | 140 ++++++++++++++++++++++++-- drivers/gpu/drm/drm_crtc.c | 88 ++++++++++++----- drivers/gpu/drm/drm_crtc_helper.c | 4 +- drivers/gpu/drm/drm_fb_cma_helper.c | 6 +- drivers/gpu/drm/drm_fb_helper.c | 18 ++-- drivers/gpu/drm/drm_modes.c | 4 +- drivers/gpu/drm/drm_modeset_lock.c | 159 ++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_probe_helper.c | 10 +- drivers/gpu/drm/drm_sysfs.c | 4 +- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 4 +- drivers/gpu/drm/gma500/cdv_intel_lvds.c | 6 +- drivers/gpu/drm/gma500/oaktrail_lvds.c | 6 +- drivers/gpu/drm/gma500/psb_intel_lvds.c | 6 +- drivers/gpu/drm/i915/i915_debugfs.c | 12 +-- drivers/gpu/drm/i915/i915_drv.c | 8 +- drivers/gpu/drm/i915/i915_irq.c | 6 +- drivers/gpu/drm/i915/intel_display.c | 20 ++-- drivers/gpu/drm/i915/intel_dp.c | 18 ++-- drivers/gpu/drm/i915/intel_lvds.c | 6 +- drivers/gpu/drm/i915/intel_opregion.c | 4 +- drivers/gpu/drm/i915/intel_overlay.c | 4 +- drivers/gpu/drm/i915/intel_panel.c | 8 +- drivers/gpu/drm/i915/intel_sprite.c | 2 +- drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 4 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 4 +- drivers/gpu/drm/omapdrm/omap_crtc.c | 10 +- drivers/gpu/drm/omapdrm/omap_plane.c | 4 +- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 4 +- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 22 ++--- include/drm/drmP.h | 5 - include/drm/drm_atomic.h | 7 ++ include/drm/drm_crtc.h | 19 ++-- include/drm/drm_modeset_lock.h | 130 ++++++++++++++++++++++++ include/uapi/drm/drm_mode.h | 3 + 35 files changed, 614 insertions(+), 143 deletions(-) create mode 100644 drivers/gpu/drm/drm_modeset_lock.c create mode 100644 include/drm/drm_modeset_lock.h
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index ba2ed83..7a6a9f5 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -14,7 +14,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ drm_trace_points.o drm_global.o drm_prime.o \ drm_rect.o drm_vma_manager.o drm_flip_work.o \ - drm_plane_helper.o drm_atomic.o + drm_plane_helper.o drm_atomic.o drm_modeset_lock.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 12fcbc0..45df5e5 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -57,8 +57,14 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, ptr = &state[1];
kref_init(&state->refcount); + + drm_modeset_acquire_init(&state->acquire_ctx, + !!(flags & DRM_MODE_ATOMIC_NOLOCK), + !!(flags & DRM_MODE_ATOMIC_NONBLOCK)); + state->dev = dev; state->flags = flags; + return state; } EXPORT_SYMBOL(drm_atomic_begin); @@ -94,10 +100,102 @@ EXPORT_SYMBOL(drm_atomic_set_event); */ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { + struct drm_atomic_state *a = state; + a->acquire_ctx.frozen = true; return 0; /* for now */ } EXPORT_SYMBOL(drm_atomic_check);
+/* Note that we drop and re-acquire the locks w/ ww_mutex directly, + * since we keep the crtc in our list with in_atomic == true. + */ + +static void drop_locks(struct drm_atomic_state *a, + struct ww_acquire_ctx *ww_ctx) +{ + struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx; + struct drm_modeset_lock *lock; + + mutex_lock(&ctx->mutex); + list_for_each_entry(lock, &ctx->locked, head) + ww_mutex_unlock(&lock->mutex); + mutex_unlock(&ctx->mutex); + + ww_acquire_fini(ww_ctx); +} + +static void grab_locks(struct drm_atomic_state *a, + struct ww_acquire_ctx *ww_ctx) +{ + struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx; + struct drm_modeset_lock *lock, *slow_locked, *contended; + int ret; + + lock = slow_locked = contended = NULL; + + + ww_acquire_init(ww_ctx, &crtc_ww_class); + + /* + * We need to do proper rain^Hww dance.. another context + * could sneak in a grab the lock in order to check + * crtc->in_atomic, and we get -EDEADLK. But the winner + * will realize the mistake when it sees crtc->in_atomic + * already set, and then drop lock and return -EBUSY. + * So we just need to keep dancing until we win. + */ +retry: + ret = 0; + list_for_each_entry(lock, &ctx->locked, head) { + if (lock == slow_locked) { + slow_locked = NULL; + continue; + } + contended = lock; + ret = ww_mutex_lock(&lock->mutex, ww_ctx); + if (ret) + goto fail; + } + +fail: + if (ret == -EDEADLK) { + /* we lost out in a seqno race, backoff, lock and retry.. */ + + list_for_each_entry(lock, &ctx->locked, head) { + if (lock == contended) + break; + ww_mutex_unlock(&lock->mutex); + } + + if (slow_locked) + ww_mutex_unlock(&slow_locked->mutex); + + ww_mutex_lock_slow(&contended->mutex, ww_ctx); + slow_locked = contended; + goto retry; + } + WARN_ON(ret); /* if we get EALREADY then something is fubar */ +} + +static void commit_locks(struct drm_atomic_state *a, + struct ww_acquire_ctx *ww_ctx) +{ + /* and properly release them (clear in_atomic, remove from list): */ + drm_modeset_drop_locks(&a->acquire_ctx); + ww_acquire_fini(ww_ctx); + a->committed = true; +} + +static int atomic_commit(struct drm_atomic_state *a, + struct ww_acquire_ctx *ww_ctx) +{ + int ret = 0; + + commit_locks(a, ww_ctx); + + return ret; +} + /** * drm_atomic_commit - commit state * @dev: DRM device @@ -109,30 +207,60 @@ EXPORT_SYMBOL(drm_atomic_check); * RETURNS * Zero for success or -errno */ -int drm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state) +int drm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *a) { - return 0; /* for now */ + return atomic_commit(a, &a->acquire_ctx.ww_ctx); } EXPORT_SYMBOL(drm_atomic_commit);
/** + * drm_atomic_commit_unlocked - like drm_atomic_commit + * but can be called back by driver in other thread. Manages the lock + * transfer from initiating thread. + */ +int drm_atomic_commit_unlocked(struct drm_device *dev, + struct drm_atomic_state *a) +{ + struct ww_acquire_ctx ww_ctx; + grab_locks(a, &ww_ctx); + return atomic_commit(a, &ww_ctx); +} +EXPORT_SYMBOL(drm_atomic_commit_unlocked); + +/** * drm_atomic_end - conclude the atomic update * @dev: DRM device * @state: the driver state object * * Release resources associated with the state object. */ -void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state) +void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *a) { - drm_atomic_state_unreference(state); + /* if commit is happening from another thread, it will + * block grabbing locks until we drop (and not set + * a->committed until after), so this is not a race: + */ + if (!a->committed) + drop_locks(a, &a->acquire_ctx.ww_ctx); + + drm_atomic_state_unreference(a); } EXPORT_SYMBOL(drm_atomic_end);
void _drm_atomic_state_free(struct kref *kref) { - struct drm_atomic_state *state = + struct drm_atomic_state *a = container_of(kref, struct drm_atomic_state, refcount); - kfree(state); + + /* in case we haven't already: */ + if (!a->committed) { + grab_locks(a, &a->acquire_ctx.ww_ctx); + commit_locks(a, &a->acquire_ctx.ww_ctx); + } + + drm_modeset_acquire_fini(&a->acquire_ctx); + + kfree(a); } EXPORT_SYMBOL(_drm_atomic_state_free);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 1876abb..dd10e4c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -37,8 +37,8 @@ #include <drm/drm_crtc.h> #include <drm/drm_edid.h> #include <drm/drm_fourcc.h> +#include <drm/drm_atomic.h>
-#include "drm_crtc_internal.h"
/** * drm_modeset_lock_all - take all modeset locks @@ -50,12 +50,32 @@ */ void drm_modeset_lock_all(struct drm_device *dev) { - struct drm_crtc *crtc; + struct drm_mode_config *config = &dev->mode_config; + struct drm_modeset_acquire_ctx *ctx; + int ret;
- mutex_lock(&dev->mode_config.mutex); + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (WARN_ON(!ctx)) + return;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); +retry: + drm_modeset_acquire_init(ctx, false, false); + + ret = drm_modeset_lock(&config->mutex, ctx) || + drm_modeset_lock_all_crtcs(dev, ctx); + if (ret == -EDEADLK) { + drm_modeset_drop_locks(ctx); + ww_acquire_fini(&ctx->ww_ctx); + drm_modeset_acquire_fini(ctx); + goto retry; + } + + WARN_ON(config->acquire_ctx); + + /* now we hold the locks, so now that it is safe, stash the + * ctx for drm_modeset_unlock_all(): + */ + config->acquire_ctx = ctx; } EXPORT_SYMBOL(drm_modeset_lock_all);
@@ -67,12 +87,18 @@ EXPORT_SYMBOL(drm_modeset_lock_all); */ void drm_modeset_unlock_all(struct drm_device *dev) { - struct drm_crtc *crtc; + struct drm_mode_config *config = &dev->mode_config; + struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - mutex_unlock(&crtc->mutex); + if (WARN_ON(!ctx)) + return; + + config->acquire_ctx = NULL; + drm_modeset_drop_locks(ctx); + ww_acquire_fini(&ctx->ww_ctx); + drm_modeset_acquire_fini(ctx);
- mutex_unlock(&dev->mode_config.mutex); + kfree(ctx); } EXPORT_SYMBOL(drm_modeset_unlock_all);
@@ -91,9 +117,9 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) return;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - WARN_ON(!mutex_is_locked(&crtc->mutex)); + WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
- WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.mutex)); } EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
@@ -691,6 +717,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) } EXPORT_SYMBOL(drm_framebuffer_remove);
+DEFINE_WW_CLASS(crtc_ww_class); + /** * drm_crtc_init_with_planes - Initialise a new CRTC object with * specified primary and cursor planes. @@ -710,6 +738,7 @@ 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;
crtc->dev = dev; @@ -717,8 +746,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, crtc->invert_dimensions = false;
drm_modeset_lock_all(dev); - mutex_init(&crtc->mutex); - mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); + drm_modeset_lock_init(&crtc->mutex); + /* dropped by _unlock_all(): */ + drm_modeset_lock(&crtc->mutex, config->acquire_ctx);
ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) @@ -755,6 +785,8 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) kfree(crtc->gamma_store); crtc->gamma_store = NULL;
+ drm_modeset_lock_fini(&crtc->mutex); + drm_mode_object_put(dev, &crtc->base); list_del(&crtc->head); dev->mode_config.num_crtc--; @@ -1794,7 +1826,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL);
obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); @@ -1895,7 +1927,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->count_encoders = encoders_count;
out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
return ret; } @@ -2496,7 +2528,7 @@ static int drm_mode_cursor_common(struct drm_device *dev, } crtc = obj_to_crtc(obj);
- mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL); if (req->flags & DRM_MODE_CURSOR_BO) { if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { ret = -ENXIO; @@ -2520,7 +2552,7 @@ static int drm_mode_cursor_common(struct drm_device *dev, } } out: - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex);
return ret;
@@ -3929,20 +3961,25 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_obj_set_property *arg = data; + struct drm_mode_config *config = &dev->mode_config; struct drm_atomic_state *state; int ret = -EINVAL;
if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL;
- drm_modeset_lock_all(dev); - +retry: state = dev->driver->atomic_begin(dev, 0); if (IS_ERR(state)) { ret = PTR_ERR(state); - goto out_unlock; + goto out; }
+ ret = drm_modeset_lock(&config->mutex, &state->acquire_ctx) || + drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx); + if (ret) + goto out; + ret = drm_mode_set_obj_prop_id(dev, state, arg->obj_id, arg->obj_type, arg->prop_id, arg->value, NULL); @@ -3957,8 +3994,8 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
out: dev->driver->atomic_end(dev, state); -out_unlock: - drm_modeset_unlock_all(dev); + if (ret == -EDEADLK) + goto retry; return ret; }
@@ -4195,7 +4232,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, return -ENOENT; crtc = obj_to_crtc(obj);
- mutex_lock(&crtc->mutex); + 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 @@ -4279,7 +4316,7 @@ out: drm_framebuffer_unreference(fb); if (old_fb) drm_framebuffer_unreference(old_fb); - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex);
return ret; } @@ -4643,7 +4680,7 @@ EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); */ void drm_mode_config_init(struct drm_device *dev) { - mutex_init(&dev->mode_config.mutex); + drm_modeset_lock_init(&dev->mode_config.mutex); mutex_init(&dev->mode_config.idr_mutex); mutex_init(&dev->mode_config.fb_lock); INIT_LIST_HEAD(&dev->mode_config.fb_list); @@ -4743,5 +4780,6 @@ void drm_mode_config_cleanup(struct drm_device *dev) }
idr_destroy(&dev->mode_config.crtc_idr); + drm_modeset_lock_fini(&dev->mode_config.mutex); } EXPORT_SYMBOL(drm_mode_config_cleanup); diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 54e8fdb..8c30259 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -88,7 +88,7 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder) struct drm_connector *connector; struct drm_device *dev = encoder->dev;
- WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.mutex)); list_for_each_entry(connector, &dev->mode_config.connector_list, head) if (connector->encoder == encoder) return true; @@ -112,7 +112,7 @@ bool drm_helper_crtc_in_use(struct drm_crtc *crtc) struct drm_encoder *encoder; struct drm_device *dev = crtc->dev;
- WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.mutex)); list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder)) return true; diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 61b5a47..65540b7 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -211,13 +211,13 @@ int drm_fb_cma_debugfs_show(struct seq_file *m, void *arg) struct drm_framebuffer *fb; int ret;
- ret = mutex_lock_interruptible(&dev->mode_config.mutex); + ret = drm_modeset_lock_interruptible(&dev->mode_config.mutex, NULL); if (ret) return ret;
ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) { - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); return ret; }
@@ -225,7 +225,7 @@ int drm_fb_cma_debugfs_show(struct seq_file *m, void *arg) drm_fb_cma_describe(fb, m);
mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
return 0; } diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index e95ed58..142f6bd 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -331,7 +331,11 @@ static bool drm_fb_helper_force_kernel_mode(void) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) continue;
- if (!mutex_trylock(&dev->mode_config.mutex)) { + /* NOTE: we use lockless flag below to avoid grabbing other + * modeset locks. So just trylock the underlying mutex + * directly: + */ + if (!mutex_trylock(&dev->mode_config.mutex.mutex.base)) { error = true; continue; } @@ -340,7 +344,7 @@ static bool drm_fb_helper_force_kernel_mode(void) if (ret) error = true;
- mutex_unlock(&dev->mode_config.mutex); + mutex_unlock(&dev->mode_config.mutex.mutex.base); } return error; } @@ -1557,11 +1561,11 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
drm_fb_helper_parse_command_line(fb_helper);
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); count = drm_fb_helper_probe_connector_modes(fb_helper, dev->mode_config.max_width, dev->mode_config.max_height); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); /* * we shouldn't end up with no modes here. */ @@ -1601,10 +1605,10 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) if (!fb_helper->fb) return 0;
- mutex_lock(&fb_helper->dev->mode_config.mutex); + drm_modeset_lock(&fb_helper->dev->mode_config.mutex, NULL); if (!drm_fb_helper_is_bound(fb_helper)) { fb_helper->delayed_hotplug = true; - mutex_unlock(&fb_helper->dev->mode_config.mutex); + drm_modeset_unlock(&fb_helper->dev->mode_config.mutex); return 0; } DRM_DEBUG_KMS("\n"); @@ -1613,7 +1617,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) max_height = fb_helper->fb->height;
drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height); - mutex_unlock(&fb_helper->dev->mode_config.mutex); + drm_modeset_unlock(&fb_helper->dev->mode_config.mutex);
drm_modeset_lock_all(dev); drm_setup_crtcs(fb_helper); diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index bedf189..d8a6b16 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -116,7 +116,7 @@ EXPORT_SYMBOL(drm_mode_destroy); void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode) { - WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex)); + WARN_ON(!drm_modeset_is_locked(&connector->dev->mode_config.mutex));
list_add_tail(&mode->head, &connector->probed_modes); } @@ -1029,7 +1029,7 @@ void drm_mode_connector_list_update(struct drm_connector *connector, struct drm_display_mode *pmode, *pt; int found_it;
- WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex)); + WARN_ON(!drm_modeset_is_locked(&connector->dev->mode_config.mutex));
list_for_each_entry_safe(pmode, pt, &connector->probed_modes, head) { diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c new file mode 100644 index 0000000..49f1afc --- /dev/null +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2014 Red Hat + * Author: Rob Clark robdclark@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_modeset_lock.h> + + +void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, + bool nolock, bool nonblock) +{ + ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class); + INIT_LIST_HEAD(&ctx->locked); + mutex_init(&ctx->mutex); + ctx->nolock = nolock; + ctx->nonblock = nonblock; +} + +void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx) +{ + /* + * NOTE: it is intentional that ww_acquire_fini() is not called + * here.. due to the way lock handover works in drm_atomic + */ + mutex_destroy(&ctx->mutex); +} + +void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx) +{ + mutex_lock(&ctx->mutex); + while (!list_empty(&ctx->locked)) { + struct drm_modeset_lock *lock; + + lock = list_first_entry(&ctx->locked, + struct drm_modeset_lock, head); + + drm_modeset_unlock(lock); + } + mutex_unlock(&ctx->mutex); +} + +static int modeset_lock(struct drm_modeset_lock *lock, + struct drm_modeset_acquire_ctx *ctx, bool interruptible) +{ + int ret; + + if (ctx->nolock) + return 0; + + WARN_ON(ctx->frozen); /* all locks should be held by now! */ + +retry: + if (interruptible) + ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx); + else + ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx); + if (!ret) { + if (lock->atomic_pending) { + /* some other pending update with dropped locks */ + ww_mutex_unlock(&lock->mutex); + if (ctx->nonblock) + return -EBUSY; + wait_event(lock->event, !lock->atomic_pending); + goto retry; + } + lock->atomic_pending = true; + WARN_ON(!list_empty(&lock->head)); + list_add(&lock->head, &ctx->locked); + } else if (ret == -EALREADY) { + /* we already hold the lock.. this is fine */ + ret = 0; + } + + return ret; +} + +/** + * drm_modeset_lock - take modeset lock + * @lock: lock to take + * @ctx: acquire ctx + * + * If ctx is not NULL, then then it's acquire context is used + * and the lock does not need to be explicitly unlocked, it + * will be automatically unlocked when the atomic update is + * complete + */ +int drm_modeset_lock(struct drm_modeset_lock *lock, + struct drm_modeset_acquire_ctx *ctx) +{ + if (ctx) + return modeset_lock(lock, ctx, false); + + ww_mutex_lock(&lock->mutex, NULL); + return 0; +} +EXPORT_SYMBOL(drm_modeset_lock); + +int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock, + struct drm_modeset_acquire_ctx *ctx) +{ + if (ctx) + return modeset_lock(lock, ctx, true); + + return ww_mutex_lock_interruptible(&lock->mutex, NULL); +} +EXPORT_SYMBOL(drm_modeset_lock_interruptible); + +/** + * drm_modeset_unlock - drop modeset lock + * @lock: lock to release + */ +void drm_modeset_unlock(struct drm_modeset_lock *lock) +{ + list_del_init(&lock->head); + lock->atomic_pending = false; + ww_mutex_unlock(&lock->mutex); + wake_up_all(&lock->event); +} +EXPORT_SYMBOL(drm_modeset_unlock); + +/** + * drm_modeset_lock_all_crtcs - helper to drm_modeset_lock() all CRTCs + */ +int drm_modeset_lock_all_crtcs(struct drm_device *dev, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_mode_config *config = &dev->mode_config; + struct drm_crtc *crtc; + int ret = 0; + + list_for_each_entry(crtc, &config->crtc_list, head) { + ret = drm_modeset_lock(&crtc->mutex, ctx); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(drm_modeset_lock_all_crtcs); diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 79f07f2..4552fe5 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -93,7 +93,7 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect int mode_flags = 0; bool verbose_prune = true;
- WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.mutex));
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, drm_get_connector_name(connector)); @@ -255,7 +255,7 @@ static void output_poll_execute(struct work_struct *work) if (!drm_kms_helper_poll) return;
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
/* Ignore forced connectors. */ @@ -293,7 +293,7 @@ static void output_poll_execute(struct work_struct *work) } }
- mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
if (changed) drm_kms_helper_hotplug_event(dev); @@ -419,7 +419,7 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) if (!dev->mode_config.poll_enabled) return false;
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
/* Only handle HPD capable connectors. */ @@ -438,7 +438,7 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) changed = true; }
- mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
if (changed) drm_kms_helper_hotplug_event(dev); diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index c22c309..981e570 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -173,12 +173,12 @@ static ssize_t status_show(struct device *device, enum drm_connector_status status; int ret;
- ret = mutex_lock_interruptible(&connector->dev->mode_config.mutex); + ret = drm_modeset_lock_interruptible(&connector->dev->mode_config.mutex, NULL); if (ret) return ret;
status = connector->funcs->detect(connector, true); - mutex_unlock(&connector->dev->mode_config.mutex); + drm_modeset_unlock(&connector->dev->mode_config.mutex);
return snprintf(buf, PAGE_SIZE, "%s\n", drm_get_connector_status_name(status)); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index addbf75..2371716 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -242,7 +242,7 @@ bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev) struct drm_connector *connector; bool ret = false;
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { if (connector->status != connector_status_connected) continue; @@ -250,7 +250,7 @@ bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev) ret = true; break; } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
return ret; } diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index 7ffaed4..f9ee300 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -714,7 +714,7 @@ void cdv_intel_lvds_init(struct drm_device *dev, * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. */ - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); psb_intel_ddc_get_modes(connector, &gma_encoder->ddc_bus->adapter); list_for_each_entry(scan, &connector->probed_modes, head) { @@ -775,12 +775,12 @@ void cdv_intel_lvds_init(struct drm_device *dev, }
out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); drm_sysfs_connector_add(connector); return;
failed_find: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); printk(KERN_ERR "Failed find\n"); if (gma_encoder->ddc_bus) psb_intel_i2c_destroy(gma_encoder->ddc_bus); diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c index 9b09946..b8c1c39 100644 --- a/drivers/gpu/drm/gma500/oaktrail_lvds.c +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c @@ -359,7 +359,7 @@ void oaktrail_lvds_init(struct drm_device *dev, * if closed, act like it's not there for now */
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus); if (i2c_adap == NULL) dev_err(dev->dev, "No ddc adapter available!\n"); @@ -402,13 +402,13 @@ void oaktrail_lvds_init(struct drm_device *dev, }
out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
drm_sysfs_connector_add(connector); return;
failed_find: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
dev_dbg(dev->dev, "No LVDS modes found, disabling.\n"); if (gma_encoder->ddc_bus) diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index 6b64d9d..3442e44 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -779,7 +779,7 @@ void psb_intel_lvds_init(struct drm_device *dev, * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. */ - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); psb_intel_ddc_get_modes(connector, &lvds_priv->ddc_bus->adapter); list_for_each_entry(scan, &connector->probed_modes, head) { if (scan->type & DRM_MODE_TYPE_PREFERRED) { @@ -830,12 +830,12 @@ void psb_intel_lvds_init(struct drm_device *dev, * actually having one. */ out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); drm_sysfs_connector_add(connector); return;
failed_find: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); if (lvds_priv->ddc_bus) psb_intel_i2c_destroy(lvds_priv->ddc_bus); failed_ddc: diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 18b3565..693c13b 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1635,7 +1635,7 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
#ifdef CONFIG_DRM_I915_FBDEV struct drm_i915_private *dev_priv = dev->dev_private; - int ret = mutex_lock_interruptible(&dev->mode_config.mutex); + int ret = drm_modeset_lock_interruptible(&dev->mode_config.mutex, NULL); if (ret) return ret;
@@ -1650,7 +1650,7 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) atomic_read(&fb->base.refcount.refcount)); describe_obj(m, fb->obj); seq_putc(m, '\n'); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); #endif
mutex_lock(&dev->mode_config.fb_lock); @@ -1681,7 +1681,7 @@ static int i915_context_status(struct seq_file *m, void *unused) struct i915_hw_context *ctx; int ret, i;
- ret = mutex_lock_interruptible(&dev->mode_config.mutex); + ret = drm_modeset_lock_interruptible(&dev->mode_config.mutex, NULL); if (ret) return ret;
@@ -1711,7 +1711,7 @@ static int i915_context_status(struct seq_file *m, void *unused) seq_putc(m, '\n'); }
- mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
return 0; } @@ -2573,7 +2573,7 @@ static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe,
*source = INTEL_PIPE_CRC_SOURCE_PIPE;
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { if (!encoder->base.crtc) @@ -2609,7 +2609,7 @@ static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe, break; } } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
return ret; } diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index e3a2482..8f2741a 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -475,10 +475,10 @@ static int i915_drm_freeze(struct drm_device *dev) * Disable CRTCs directly since we want to preserve sw state * for _thaw. */ - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) dev_priv->display.crtc_disable(crtc); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
intel_modeset_suspend_hw(dev); } @@ -546,14 +546,14 @@ static void intel_resume_hotplug(struct drm_device *dev) struct drm_mode_config *mode_config = &dev->mode_config; struct intel_encoder *encoder;
- mutex_lock(&mode_config->mutex); + drm_modeset_lock(&mode_config->mutex, NULL); DRM_DEBUG_KMS("running encoder hotplug functions\n");
list_for_each_entry(encoder, &mode_config->encoder_list, base.head) if (encoder->hot_plug) encoder->hot_plug(encoder);
- mutex_unlock(&mode_config->mutex); + drm_modeset_unlock(&mode_config->mutex);
/* Just fire off a uevent and let userspace tell us what to do */ drm_helper_hpd_irq_event(dev); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 2d76183..e8e931f 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -940,7 +940,7 @@ static bool intel_hpd_irq_event(struct drm_device *dev, { enum drm_connector_status old_status;
- WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.mutex)); old_status = connector->status;
connector->status = connector->funcs->detect(connector, false); @@ -979,7 +979,7 @@ static void i915_hotplug_work_func(struct work_struct *work) if (!dev_priv->enable_hotplug_processing) return;
- mutex_lock(&mode_config->mutex); + drm_modeset_lock(&mode_config->mutex, NULL); DRM_DEBUG_KMS("running encoder hotplug functions\n");
spin_lock_irqsave(&dev_priv->irq_lock, irqflags); @@ -1026,7 +1026,7 @@ static void i915_hotplug_work_func(struct work_struct *work) changed = true; } } - mutex_unlock(&mode_config->mutex); + drm_modeset_unlock(&mode_config->mutex);
if (changed) drm_kms_helper_hotplug_event(dev); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 72b5c34..562e575 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2363,7 +2363,7 @@ void intel_display_handle_reset(struct drm_device *dev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL); /* * FIXME: Once we have proper support for primary planes (and * disabling them without disabling the entire crtc) allow again @@ -2374,7 +2374,7 @@ void intel_display_handle_reset(struct drm_device *dev) crtc->primary->fb, crtc->x, crtc->y); - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); } }
@@ -8002,7 +8002,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, if (encoder->crtc) { crtc = encoder->crtc;
- mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL);
old->dpms_mode = connector->dpms; old->load_detect_temp = false; @@ -8033,7 +8033,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, return false; }
- mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL); intel_encoder->new_crtc = to_intel_crtc(crtc); to_intel_connector(connector)->new_encoder = intel_encoder;
@@ -8083,7 +8083,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, intel_crtc->new_config = &intel_crtc->config; else intel_crtc->new_config = NULL; - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); return false; }
@@ -8112,7 +8112,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, drm_framebuffer_unreference(old->release_fb); }
- mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); return; }
@@ -8120,7 +8120,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, if (old->dpms_mode != DRM_MODE_DPMS_ON) connector->funcs->dpms(connector, old->dpms_mode);
- mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); }
static int i9xx_pll_refclk(struct drm_device *dev, @@ -10578,7 +10578,7 @@ enum pipe intel_get_pipe_from_connector(struct intel_connector *connector) { struct drm_encoder *encoder = connector->base.encoder;
- WARN_ON(!mutex_is_locked(&connector->base.dev->mode_config.mutex)); + WARN_ON(!drm_modeset_is_locked(&connector->base.dev->mode_config.mutex));
if (!encoder) return INVALID_PIPE; @@ -11336,9 +11336,9 @@ void intel_modeset_init(struct drm_device *dev) /* Just in case the BIOS is doing something questionable. */ intel_disable_fbc(dev);
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); intel_modeset_setup_hw_state(dev, false); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) { diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index c63d210..e4c0b10 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1131,7 +1131,7 @@ static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp) u32 pp; u32 pp_stat_reg, pp_ctrl_reg;
- WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.mutex));
if (!intel_dp->want_panel_vdd && edp_have_panel_vdd(intel_dp)) { struct intel_digital_port *intel_dig_port = @@ -1168,9 +1168,9 @@ static void edp_panel_vdd_work(struct work_struct *__work) struct intel_dp, panel_vdd_work); struct drm_device *dev = intel_dp_to_dev(intel_dp);
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); edp_panel_vdd_off_sync(intel_dp); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); }
static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) @@ -3385,9 +3385,9 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder) drm_encoder_cleanup(encoder); if (is_edp(intel_dp)) { cancel_delayed_work_sync(&intel_dp->panel_vdd_work); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); edp_panel_vdd_off_sync(intel_dp); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); } kfree(intel_dig_port); } @@ -3829,7 +3829,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, /* We now know it's not a ghost, init power sequence regs. */ intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, power_seq);
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); edid = drm_get_edid(connector, &intel_dp->aux.ddc); if (edid) { if (drm_add_edid_modes(connector, edid)) { @@ -3863,7 +3863,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, if (fixed_mode) fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode); intel_panel_setup_backlight(connector); @@ -3966,9 +3966,9 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, drm_dp_aux_unregister_i2c_bus(&intel_dp->aux); if (is_edp(intel_dp)) { cancel_delayed_work_sync(&intel_dp->panel_vdd_work); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); edp_panel_vdd_off_sync(intel_dp); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); } drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 4028237..bdee5d8 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -997,7 +997,7 @@ void intel_lvds_init(struct drm_device *dev) * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. */ - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); edid = drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv, pin)); if (edid) { if (drm_add_edid_modes(connector, edid)) { @@ -1091,7 +1091,7 @@ void intel_lvds_init(struct drm_device *dev) goto failed;
out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder); DRM_DEBUG_KMS("detected %s-link lvds configuration\n", @@ -1121,7 +1121,7 @@ out: return;
failed: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
DRM_DEBUG_KMS("No LVDS modes found, disabling.\n"); drm_connector_cleanup(connector); diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index acde294..81d2998 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -410,7 +410,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) if (bclp > 255) return ASLC_BACKLIGHT_FAILED;
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL);
/* * Update backlight on all connectors that support backlight (usually @@ -421,7 +421,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) intel_panel_set_backlight(intel_connector, bclp, 255); iowrite32(DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID, &asle->cblv);
- mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
return 0; diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index d8adc91..38f8234 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -688,7 +688,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, u32 swidth, swidthsw, sheight, ostride;
BUG_ON(!mutex_is_locked(&dev->struct_mutex)); - BUG_ON(!mutex_is_locked(&dev->mode_config.mutex)); + BUG_ON(!drm_modeset_is_locked(&dev->mode_config.mutex)); BUG_ON(!overlay);
ret = intel_overlay_release_old_vid(overlay); @@ -793,7 +793,7 @@ int intel_overlay_switch_off(struct intel_overlay *overlay) int ret;
BUG_ON(!mutex_is_locked(&dev->struct_mutex)); - BUG_ON(!mutex_is_locked(&dev->mode_config.mutex)); + BUG_ON(!drm_modeset_is_locked(&dev->mode_config.mutex));
ret = intel_overlay_recover_from_interrupt(overlay); if (ret != 0) diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 44ad415..4121b48 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -823,12 +823,12 @@ static int intel_backlight_device_update_status(struct backlight_device *bd) struct intel_connector *connector = bl_get_data(bd); struct drm_device *dev = connector->base.dev;
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); DRM_DEBUG_KMS("updating intel_backlight, brightness=%d/%d\n", bd->props.brightness, bd->props.max_brightness); intel_panel_set_backlight(connector, bd->props.brightness, bd->props.max_brightness); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); return 0; }
@@ -840,9 +840,9 @@ static int intel_backlight_device_get_brightness(struct backlight_device *bd) int ret;
intel_runtime_pm_get(dev_priv); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); ret = intel_panel_get_backlight(connector); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); intel_runtime_pm_put(dev_priv);
return ret; diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 213cd58..c235546 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -55,7 +55,7 @@ static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl int scanline, min, max, vblank_start; DEFINE_WAIT(wait);
- WARN_ON(!mutex_is_locked(&crtc->base.mutex)); + WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex));
vblank_start = mode->crtc_vblank_start; if (mode->flags & DRM_MODE_FLAG_INTERLACE) diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index efa19c8..7cf0f78 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -198,9 +198,9 @@ static void unref_fb_worker(struct drm_flip_work *work, void *val) container_of(work, struct mdp4_crtc, unref_fb_work); struct drm_device *dev = mdp4_crtc->base.dev;
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); drm_framebuffer_unreference(val); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); }
static void unref_cursor_worker(struct drm_flip_work *work, void *val) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index ff48944..771390b 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -186,9 +186,9 @@ static void unref_fb_worker(struct drm_flip_work *work, void *val) container_of(work, struct mdp5_crtc, unref_fb_work); struct drm_device *dev = mdp5_crtc->base.dev;
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); drm_framebuffer_unreference(val); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); }
static void mdp5_crtc_destroy(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index a7c0010..a75934d 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -319,13 +319,13 @@ static void page_flip_worker(struct work_struct *work) struct drm_display_mode *mode = &crtc->mode; struct drm_gem_object *bo;
- mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL); omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb, 0, 0, mode->hdisplay, mode->vdisplay, crtc->x << 16, crtc->y << 16, mode->hdisplay << 16, mode->vdisplay << 16, vblank_cb, crtc); - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex);
bo = omap_framebuffer_bo(crtc->primary->fb, 0); drm_gem_object_unreference_unlocked(bo); @@ -467,7 +467,7 @@ static void apply_worker(struct work_struct *work) * the callbacks and list modification all serialized * with respect to modesetting ioctls from userspace. */ - mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL); dispc_runtime_get();
/* @@ -512,7 +512,7 @@ static void apply_worker(struct work_struct *work)
out: dispc_runtime_put(); - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); }
int omap_crtc_apply(struct drm_crtc *crtc, @@ -520,7 +520,7 @@ int omap_crtc_apply(struct drm_crtc *crtc, { struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
- WARN_ON(!mutex_is_locked(&crtc->mutex)); + WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
/* no need to queue it again if it is already queued: */ if (apply->queued) diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 8ae5c49..2d3c975 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -72,9 +72,9 @@ static void unpin_worker(struct drm_flip_work *work, void *val) struct drm_device *dev = omap_plane->base.dev;
omap_framebuffer_unpin(val); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); drm_framebuffer_unreference(val); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); }
/* update which fb (if any) is pinned for scanout */ diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index d642d4a0..92839ba 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -45,9 +45,9 @@ static void unref_worker(struct drm_flip_work *work, void *val) container_of(work, struct tilcdc_crtc, unref_work); struct drm_device *dev = tilcdc_crtc->base.dev;
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); drm_framebuffer_unreference(val); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); }
static void set_scanout(struct drm_crtc *crtc, int n) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 6b9db5e..f338e3e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -187,7 +187,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, * can do this since the caller in the drm core doesn't check anything * which is protected by any looks. */ - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); drm_modeset_lock_all(dev_priv->dev);
/* A lot of the code assumes this */ @@ -252,7 +252,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, ret = 0; out: drm_modeset_unlock_all(dev_priv->dev); - mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL);
return ret; } @@ -273,7 +273,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) * can do this since the caller in the drm core doesn't check anything * which is protected by any looks. */ - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); drm_modeset_lock_all(dev_priv->dev);
vmw_cursor_update_position(dev_priv, shown, @@ -281,7 +281,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) du->cursor_y + du->hotspot_y);
drm_modeset_unlock_all(dev_priv->dev); - mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL);
return 0; } @@ -387,7 +387,7 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) struct vmw_display_unit *du; struct drm_crtc *crtc;
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { du = vmw_crtc_to_du(crtc); @@ -401,7 +401,7 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) 64, 64, du->hotspot_x, du->hotspot_y); }
- mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); }
/* @@ -1506,7 +1506,7 @@ int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data, int ret = 0;
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL); if (arg->flags & DRM_VMW_CURSOR_BYPASS_ALL) {
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -1515,7 +1515,7 @@ int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data, du->hotspot_y = arg->yhot; }
- mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex); return 0; }
@@ -1532,7 +1532,7 @@ int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data, du->hotspot_y = arg->yhot;
out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
return ret; } @@ -1682,7 +1682,7 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num, struct vmw_display_unit *du; struct drm_connector *con;
- mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.mutex, NULL);
#if 0 { @@ -1712,7 +1712,7 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num, con->status = vmw_du_connector_detect(con, true); }
- mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock(&dev->mode_config.mutex);
return 0; } diff --git a/include/drm/drmP.h b/include/drm/drmP.h index e8d0d17..5ac3587 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1264,11 +1264,6 @@ static inline int drm_device_is_unplugged(struct drm_device *dev) return ret; }
-static inline bool drm_modeset_is_locked(struct drm_device *dev) -{ - return mutex_is_locked(&dev->mode_config.mutex); -} - static inline bool drm_is_render_client(const struct drm_file *file_priv) { return file_priv->minor->type == DRM_MINOR_RENDER; diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 5274212..ff72b81 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -80,6 +80,8 @@ int drm_atomic_set_event(struct drm_device *dev, struct drm_pending_vblank_event *event); int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state); int drm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state); +int drm_atomic_commit_unlocked(struct drm_device *dev, + struct drm_atomic_state *state); void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state);
/** @@ -89,6 +91,11 @@ struct drm_atomic_state { struct kref refcount; struct drm_device *dev; uint32_t flags; + + bool committed; + bool checked; /* just for debugging */ + + struct drm_modeset_acquire_ctx acquire_ctx; };
static inline void diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 4790cad..83e0f88 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -33,6 +33,7 @@ #include <linux/hdmi.h> #include <drm/drm_mode.h> #include <drm/drm_fourcc.h> +#include <drm/drm_modeset_lock.h>
struct drm_device; struct drm_mode_set; @@ -194,6 +195,10 @@ struct drm_property { struct list_head enum_blob_list; };
+void drm_modeset_lock_all(struct drm_device *dev); +void drm_modeset_unlock_all(struct drm_device *dev); +void drm_warn_on_modeset_not_all_locked(struct drm_device *dev); + struct drm_crtc; struct drm_connector; struct drm_encoder; @@ -273,6 +278,7 @@ struct drm_crtc_funcs { * drm_crtc - central CRTC control structure * @dev: parent DRM device * @head: list management + * @mutex: per-CRTC locking * @base: base KMS object for ID tracking etc. * @primary: primary plane for this CRTC * @cursor: cursor plane for this CRTC @@ -307,7 +313,7 @@ struct drm_crtc { * state, ...) and a write lock for everything which can be update * without a full modeset (fb, cursor data, ...) */ - struct mutex mutex; + struct drm_modeset_lock mutex;
struct drm_mode_object base;
@@ -729,7 +735,12 @@ struct drm_mode_group { * global restrictions are also here, e.g. dimension restrictions. */ struct drm_mode_config { - struct mutex mutex; /* protects configuration (mode lists etc.) */ + + /* protects configuration (mode lists etc.): */ + struct drm_modeset_lock mutex; + /* the current acquire ctx (for drm_modeset_lock_all()) */ + struct drm_modeset_acquire_ctx *acquire_ctx; + struct mutex idr_mutex; /* for IDR management */ struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */ /* this is limited to one for now */ @@ -830,10 +841,6 @@ struct drm_prop_enum_list { char *name; };
-extern void drm_modeset_lock_all(struct drm_device *dev); -extern void drm_modeset_unlock_all(struct drm_device *dev); -extern void drm_warn_on_modeset_not_all_locked(struct drm_device *dev); - extern int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, struct drm_plane *primary, diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h new file mode 100644 index 0000000..bc83a10 --- /dev/null +++ b/include/drm/drm_modeset_lock.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2014 Red Hat + * Author: Rob Clark robdclark@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef DRM_MODESET_LOCK_H_ +#define DRM_MODESET_LOCK_H_ + +#include <linux/ww_mutex.h> + +struct drm_modeset_acquire_ctx { + + struct ww_acquire_ctx ww_ctx; + + bool nolock : 1; + bool nonblock : 1; + + /* just for debugging, the context is 'frozen' in drm_atomic_check() + * to catch anyone who might be trying to acquire a lock after it is + * too late. + */ + bool frozen : 1; + + /* list of 'struct drm_modeset_lock': */ + struct list_head locked; + + /* currently simply for protecting against 'locked' list manipulation + * between original thread calling atomic->end() and driver thread + * calling back drm_atomic_commit_unlocked(). + * + * Other spots are sufficiently synchronized by virtue of holding + * the lock's ww_mutex. But during the lock/resource hand-over to the + * driver thread (drop_locks()/grab_locks()), we cannot rely on this. + */ + struct mutex mutex; +}; + +/** + * drm_modeset_lock - used for locking modeset resources. + * @mutex: resource locking + * @atomic_pending: is this resource part of a still-pending + * atomic update + * @head: used to hold it's place on state->locked list when + * part of an atomic update + * + * Used for locking CRTCs and other modeset resources. + */ +struct drm_modeset_lock { + /** + * modeset lock + */ + struct ww_mutex mutex; + + /** + * Are we busy (pending asynchronous/NONBLOCK update)? Any further + * asynchronous update will return -EBUSY if it also needs to acquire + * this lock. While a synchronous update will block until the pending + * async update completes. + * + * Drivers must ensure the update is completed before sending vblank + * event to userspace. Typically this just means don't send event + * before drm_atomic_commit_unlocked() returns. + */ + bool atomic_pending; + + /** + * Resources that are locked as part of an atomic update are added + * to a list (so we know what to unlock at the end). + */ + struct list_head head; + + /** + * For waiting on atomic_pending locks, if not a NONBLOCK operation. + */ + wait_queue_head_t event; +}; + +extern struct ww_class crtc_ww_class; + +void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, + bool nolock, bool nonblock); +void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx); +void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx); + +static inline void drm_modeset_lock_init(struct drm_modeset_lock *lock) +{ + ww_mutex_init(&lock->mutex, &crtc_ww_class); + INIT_LIST_HEAD(&lock->head); + init_waitqueue_head(&lock->event); +} + +static inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock) +{ + WARN_ON(!list_empty(&lock->head)); +} + +static inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock) +{ + return ww_mutex_is_locked(&lock->mutex); +} + +int drm_modeset_lock(struct drm_modeset_lock *lock, + struct drm_modeset_acquire_ctx *ctx); +int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock, + struct drm_modeset_acquire_ctx *ctx); +void drm_modeset_unlock(struct drm_modeset_lock *lock); + +struct drm_device; +int drm_modeset_lock_all_crtcs(struct drm_device *dev, + struct drm_modeset_acquire_ctx *ctx); + +#endif /* DRM_MODESET_LOCK_H_ */ diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index f104c26..12e2139 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -496,4 +496,7 @@ struct drm_mode_destroy_dumb { uint32_t handle; };
+#define DRM_MODE_ATOMIC_NONBLOCK 0x0200 +#define DRM_MODE_ATOMIC_NOLOCK 0x8000 /* only used internally */ + #endif
On Sat, May 24, 2014 at 8:30 PM, Rob Clark robdclark@gmail.com wrote:
@@ -8002,7 +8002,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, if (encoder->crtc) { crtc = encoder->crtc;
mutex_lock(&crtc->mutex);
drm_modeset_lock(&crtc->mutex, NULL);
This is pretty much the reason why I think switching the mode_config.mutex to a ww_mutex is a bad idea: This call here nests within the mode_config.mutex and so must be acquired. Wiring the acquire context through everything is going to be fairly horrible, especially since you must be able to bail out when trying to lock with an axquire context.
My original design behind the crtc->mutex and mode_config.mutex split was that as long as the connector->crtc links didn't change you can get away with the crtc lock. setplane made a bit a mess out of this, but strictly speaking as long as you acquire all crtc locks involved in a potential plane switch (which ww_mtuxes can do) it'll be fine. Since noticing whether any connector properties change should be doable upfront I think we should try _really_ hard to keep the mode_config.mutex a plain mutex which wraps all the more fine-grained locks and is a catch-all for everything else but crtcs/planes. -Daniel
On Sun, May 25, 2014 at 6:10 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 8:30 PM, Rob Clark robdclark@gmail.com wrote:
@@ -8002,7 +8002,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, if (encoder->crtc) { crtc = encoder->crtc;
mutex_lock(&crtc->mutex);
drm_modeset_lock(&crtc->mutex, NULL);
This is pretty much the reason why I think switching the mode_config.mutex to a ww_mutex is a bad idea: This call here nests within the mode_config.mutex and so must be acquired. Wiring the acquire context through everything is going to be fairly horrible, especially since you must be able to bail out when trying to lock with an axquire context.
which is the call-path to here from mode_config.mutex? Is it possible to just move the locking to a higher level for a drm_modeset_lock_all()?
My original design behind the crtc->mutex and mode_config.mutex split was that as long as the connector->crtc links didn't change you can get away with the crtc lock. setplane made a bit a mess out of this, but strictly speaking as long as you acquire all crtc locks involved in a potential plane switch (which ww_mtuxes can do) it'll be fine. Since noticing whether any connector properties change should be doable upfront I think we should try _really_ hard to keep the mode_config.mutex a plain mutex which wraps all the more fine-grained locks and is a catch-all for everything else but crtcs/planes.
That is basically how it was in one of the earlier iterations of atomic.. but didn't hold mode_config.mutex in a lot of places where it was previously held.. and while I do want to make locking more fine grained I didn't want to try and do it at the same time as landing all these other changes.
BR, -R
-Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Sun, May 25, 2014 at 07:16:43PM -0400, Rob Clark wrote:
On Sun, May 25, 2014 at 6:10 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 8:30 PM, Rob Clark robdclark@gmail.com wrote:
@@ -8002,7 +8002,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, if (encoder->crtc) { crtc = encoder->crtc;
mutex_lock(&crtc->mutex);
drm_modeset_lock(&crtc->mutex, NULL);
This is pretty much the reason why I think switching the mode_config.mutex to a ww_mutex is a bad idea: This call here nests within the mode_config.mutex and so must be acquired. Wiring the acquire context through everything is going to be fairly horrible, especially since you must be able to bail out when trying to lock with an axquire context.
which is the call-path to here from mode_config.mutex? Is it possible to just move the locking to a higher level for a drm_modeset_lock_all()?
Connector probing. And the entire point of crtc locks was to _not_ block all screen updates while we poke for a new edid or do load balancing. If you want to test this you need a gen3/4 with tv-out (native, not through sdvo) or a gen2 or i915g/gm with vga.
My original design behind the crtc->mutex and mode_config.mutex split was that as long as the connector->crtc links didn't change you can get away with the crtc lock. setplane made a bit a mess out of this, but strictly speaking as long as you acquire all crtc locks involved in a potential plane switch (which ww_mtuxes can do) it'll be fine. Since noticing whether any connector properties change should be doable upfront I think we should try _really_ hard to keep the mode_config.mutex a plain mutex which wraps all the more fine-grained locks and is a catch-all for everything else but crtcs/planes.
That is basically how it was in one of the earlier iterations of atomic.. but didn't hold mode_config.mutex in a lot of places where it was previously held.. and while I do want to make locking more fine grained I didn't want to try and do it at the same time as landing all these other changes.
Hm, maybe we should land the locking stuff first? I.e. just convert crtc->mutex into a ww_mutex, and then add more fine-grained locking to e.g. setplane by only grabbing the locks of the involved crtcs with the w/w logic. We might need an additional plane mutex to make it work. Iirc Ville had some patches for just this.
I'm arguing this since locking at the current interface I have a really hard time seeing how we're going to implement this in i915. Still reading around though. -Daniel
On Mon, May 26, 2014 at 4:23 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sun, May 25, 2014 at 07:16:43PM -0400, Rob Clark wrote:
On Sun, May 25, 2014 at 6:10 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 8:30 PM, Rob Clark robdclark@gmail.com wrote:
@@ -8002,7 +8002,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, if (encoder->crtc) { crtc = encoder->crtc;
mutex_lock(&crtc->mutex);
drm_modeset_lock(&crtc->mutex, NULL);
This is pretty much the reason why I think switching the mode_config.mutex to a ww_mutex is a bad idea: This call here nests within the mode_config.mutex and so must be acquired. Wiring the acquire context through everything is going to be fairly horrible, especially since you must be able to bail out when trying to lock with an axquire context.
which is the call-path to here from mode_config.mutex? Is it possible to just move the locking to a higher level for a drm_modeset_lock_all()?
Connector probing. And the entire point of crtc locks was to _not_ block all screen updates while we poke for a new edid or do load balancing. If you want to test this you need a gen3/4 with tv-out (native, not through sdvo) or a gen2 or i915g/gm with vga.
hmm, I guess I'm still not quite seeing the issue. For non-atomic paths, we are grabbing mode_config and/or crtc mutex as bare mutexes in same spots as we did before. So if it worked before without nested_lock stuff it should still work now.
BR, -R
On Mon, May 26, 2014 at 07:56:50AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 4:23 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sun, May 25, 2014 at 07:16:43PM -0400, Rob Clark wrote:
On Sun, May 25, 2014 at 6:10 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 8:30 PM, Rob Clark robdclark@gmail.com wrote:
@@ -8002,7 +8002,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, if (encoder->crtc) { crtc = encoder->crtc;
mutex_lock(&crtc->mutex);
drm_modeset_lock(&crtc->mutex, NULL);
This is pretty much the reason why I think switching the mode_config.mutex to a ww_mutex is a bad idea: This call here nests within the mode_config.mutex and so must be acquired. Wiring the acquire context through everything is going to be fairly horrible, especially since you must be able to bail out when trying to lock with an axquire context.
which is the call-path to here from mode_config.mutex? Is it possible to just move the locking to a higher level for a drm_modeset_lock_all()?
Connector probing. And the entire point of crtc locks was to _not_ block all screen updates while we poke for a new edid or do load balancing. If you want to test this you need a gen3/4 with tv-out (native, not through sdvo) or a gen2 or i915g/gm with vga.
hmm, I guess I'm still not quite seeing the issue. For non-atomic paths, we are grabbing mode_config and/or crtc mutex as bare mutexes in same spots as we did before. So if it worked before without nested_lock stuff it should still work now.
Thread A is doing output probing.
Thread B is doing atomic modeset
Grabs mode_config.mutex
Grabs crtc_A->ww_mutex
Tries to grab crtc_A->ww_mutex, blocks since normal ww_mutex_lock
Tries to grab mode_config.mutex with ww acuiquire context, blocks since current holder hasn't acquired the mutex with a ww ticket
-> Deadlock.
You really can't mix lock nesting with w/w and lock nesting with a static hierarchy. It's all or nothing. -Daniel
On Mon, May 26, 2014 at 04:35:04PM +0200, Daniel Vetter wrote:
On Mon, May 26, 2014 at 07:56:50AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 4:23 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sun, May 25, 2014 at 07:16:43PM -0400, Rob Clark wrote:
On Sun, May 25, 2014 at 6:10 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 8:30 PM, Rob Clark robdclark@gmail.com wrote:
@@ -8002,7 +8002,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, if (encoder->crtc) { crtc = encoder->crtc;
mutex_lock(&crtc->mutex);
drm_modeset_lock(&crtc->mutex, NULL);
This is pretty much the reason why I think switching the mode_config.mutex to a ww_mutex is a bad idea: This call here nests within the mode_config.mutex and so must be acquired. Wiring the acquire context through everything is going to be fairly horrible, especially since you must be able to bail out when trying to lock with an axquire context.
which is the call-path to here from mode_config.mutex? Is it possible to just move the locking to a higher level for a drm_modeset_lock_all()?
Connector probing. And the entire point of crtc locks was to _not_ block all screen updates while we poke for a new edid or do load balancing. If you want to test this you need a gen3/4 with tv-out (native, not through sdvo) or a gen2 or i915g/gm with vga.
hmm, I guess I'm still not quite seeing the issue. For non-atomic paths, we are grabbing mode_config and/or crtc mutex as bare mutexes in same spots as we did before. So if it worked before without nested_lock stuff it should still work now.
Thread A is doing output probing.
Thread B is doing atomic modeset
Grabs mode_config.mutex
Grabs crtc_A->ww_mutex
Tries to grab crtc_A->ww_mutex, blocks since normal ww_mutex_lock
.. blocks since normal ww_mutex_lock without acquire ticket.
Otherwise it's a bit unclear. -Daniel
Tries to grab mode_config.mutex with ww acuiquire context, blocks since current holder hasn't acquired the mutex with a ww ticket
-> Deadlock.
You really can't mix lock nesting with w/w and lock nesting with a static hierarchy. It's all or nothing.
-Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Mon, May 26, 2014 at 10:35 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, May 26, 2014 at 07:56:50AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 4:23 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sun, May 25, 2014 at 07:16:43PM -0400, Rob Clark wrote:
On Sun, May 25, 2014 at 6:10 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 8:30 PM, Rob Clark robdclark@gmail.com wrote:
@@ -8002,7 +8002,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, if (encoder->crtc) { crtc = encoder->crtc;
mutex_lock(&crtc->mutex);
drm_modeset_lock(&crtc->mutex, NULL);
This is pretty much the reason why I think switching the mode_config.mutex to a ww_mutex is a bad idea: This call here nests within the mode_config.mutex and so must be acquired. Wiring the acquire context through everything is going to be fairly horrible, especially since you must be able to bail out when trying to lock with an axquire context.
which is the call-path to here from mode_config.mutex? Is it possible to just move the locking to a higher level for a drm_modeset_lock_all()?
Connector probing. And the entire point of crtc locks was to _not_ block all screen updates while we poke for a new edid or do load balancing. If you want to test this you need a gen3/4 with tv-out (native, not through sdvo) or a gen2 or i915g/gm with vga.
hmm, I guess I'm still not quite seeing the issue. For non-atomic paths, we are grabbing mode_config and/or crtc mutex as bare mutexes in same spots as we did before. So if it worked before without nested_lock stuff it should still work now.
Thread A is doing output probing.
Thread B is doing atomic modeset
Grabs mode_config.mutex
Grabs crtc_A->ww_mutex
Tries to grab crtc_A->ww_mutex, blocks since normal ww_mutex_lock
Tries to grab mode_config.mutex with ww acuiquire context, blocks since current holder hasn't acquired the mutex with a ww ticket
hmm, ok, I had thought this case, thread B would get -EDEADLK because lock was held, and not his acquire ctx. If that is not the case, then I would propose this:
All places doing things the old way, must grab mode_config.mutex first currently. And we use mode_config.mutex to protect mode_config.acquire_ctx. So all the lower spots grabbing individual crtc mutexes can safely use mode_config.acquire_ctx.
Then the only headache is propagating -EDEADLK up the call stack. If we are lucky, the all already propagate -EINTR, etc.
BR, -R
-> Deadlock.
You really can't mix lock nesting with w/w and lock nesting with a static hierarchy. It's all or nothing.
-Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Mon, May 26, 2014 at 5:04 PM, Rob Clark robdclark@gmail.com wrote:
hmm, ok, I had thought this case, thread B would get -EDEADLK because lock was held, and not his acquire ctx. If that is not the case, then I would propose this:
All places doing things the old way, must grab mode_config.mutex first currently. And we use mode_config.mutex to protect mode_config.acquire_ctx. So all the lower spots grabbing individual crtc mutexes can safely use mode_config.acquire_ctx.
Then the only headache is propagating -EDEADLK up the call stack. If we are lucky, the all already propagate -EINTR, etc.
The output poll work most definitely doesn't propagate -EINTR. Like I've said, this will be painful. And imo doing this also makes the kms locking into quite a mess overall. -Daniel
On Mon, May 26, 2014 at 11:07 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, May 26, 2014 at 5:04 PM, Rob Clark robdclark@gmail.com wrote:
hmm, ok, I had thought this case, thread B would get -EDEADLK because lock was held, and not his acquire ctx. If that is not the case, then I would propose this:
All places doing things the old way, must grab mode_config.mutex first currently. And we use mode_config.mutex to protect mode_config.acquire_ctx. So all the lower spots grabbing individual crtc mutexes can safely use mode_config.acquire_ctx.
Then the only headache is propagating -EDEADLK up the call stack. If we are lucky, the all already propagate -EINTR, etc.
The output poll work most definitely doesn't propagate -EINTR. Like I've said, this will be painful. And imo doing this also makes the kms locking into quite a mess overall.
Well, we could hold mode_config.mutex as a traditional mutex around atomic operations. What you loose out would be now _NONBLOCK operations could conceivable call into driver paths without mode_config.mutex held. This was the advantage of converting mode_config.mutex as well. Granted, it is slightly theoretical because until we expose atomic ioctl it would only apply to page_flip (which was not holding mode_config.mutex). And we also want to get rid of mode_config.mutex in these paths too.
Otoh, if we want to make locking more fine grained, more use of ww_mutex seems like the best way. And if that means adding a return value to a fxn here/there and propagating errors properly, maybe we should just go ahead and do that. It sounds like the right long term solution anyways.
BR, -R
-Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Mon, May 26, 2014 at 11:20:49AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 11:07 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, May 26, 2014 at 5:04 PM, Rob Clark robdclark@gmail.com wrote:
hmm, ok, I had thought this case, thread B would get -EDEADLK because lock was held, and not his acquire ctx. If that is not the case, then I would propose this:
All places doing things the old way, must grab mode_config.mutex first currently. And we use mode_config.mutex to protect mode_config.acquire_ctx. So all the lower spots grabbing individual crtc mutexes can safely use mode_config.acquire_ctx.
Then the only headache is propagating -EDEADLK up the call stack. If we are lucky, the all already propagate -EINTR, etc.
The output poll work most definitely doesn't propagate -EINTR. Like I've said, this will be painful. And imo doing this also makes the kms locking into quite a mess overall.
Well, we could hold mode_config.mutex as a traditional mutex around atomic operations. What you loose out would be now _NONBLOCK operations could conceivable call into driver paths without mode_config.mutex held. This was the advantage of converting mode_config.mutex as well. Granted, it is slightly theoretical because until we expose atomic ioctl it would only apply to page_flip (which was not holding mode_config.mutex). And we also want to get rid of mode_config.mutex in these paths too.
Otoh, if we want to make locking more fine grained, more use of ww_mutex seems like the best way. And if that means adding a return value to a fxn here/there and propagating errors properly, maybe we should just go ahead and do that. It sounds like the right long term solution anyways.
Yeah, I'm starting to lean towards trying to elide mode_config.mutex completely from the atomic paths (and modesets in general). I think the only bits we really need is adding ww mutexes to planes _and_ to connectors. The atomic would _only_ ever acquire ww mutexes, and we would be able to guarante that most of them are only held short times so that we don't need to bother with trylocking them for NONBLOCK. That should simply the atomic logic a bit I hope.
So that, and a full subsystem audit unfortunately :( -Daniel
On Mon, May 26, 2014 at 11:35 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, May 26, 2014 at 11:20:49AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 11:07 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, May 26, 2014 at 5:04 PM, Rob Clark robdclark@gmail.com wrote:
hmm, ok, I had thought this case, thread B would get -EDEADLK because lock was held, and not his acquire ctx. If that is not the case, then I would propose this:
All places doing things the old way, must grab mode_config.mutex first currently. And we use mode_config.mutex to protect mode_config.acquire_ctx. So all the lower spots grabbing individual crtc mutexes can safely use mode_config.acquire_ctx.
Then the only headache is propagating -EDEADLK up the call stack. If we are lucky, the all already propagate -EINTR, etc.
The output poll work most definitely doesn't propagate -EINTR. Like I've said, this will be painful. And imo doing this also makes the kms locking into quite a mess overall.
Well, we could hold mode_config.mutex as a traditional mutex around atomic operations. What you loose out would be now _NONBLOCK operations could conceivable call into driver paths without mode_config.mutex held. This was the advantage of converting mode_config.mutex as well. Granted, it is slightly theoretical because until we expose atomic ioctl it would only apply to page_flip (which was not holding mode_config.mutex). And we also want to get rid of mode_config.mutex in these paths too.
Otoh, if we want to make locking more fine grained, more use of ww_mutex seems like the best way. And if that means adding a return value to a fxn here/there and propagating errors properly, maybe we should just go ahead and do that. It sounds like the right long term solution anyways.
Yeah, I'm starting to lean towards trying to elide mode_config.mutex completely from the atomic paths (and modesets in general). I think the only bits we really need is adding ww mutexes to planes _and_ to connectors. The atomic would _only_ ever acquire ww mutexes, and we would be able to guarante that most of them are only held short times so that we don't need to bother with trylocking them for NONBLOCK. That should simply the atomic logic a bit I hope.
So that, and a full subsystem audit unfortunately :(
well, getting rid of any global lock from atomic path is, I hope, the eventual goal. But we are *really* late already with atomic, and too many other things backing up on top of this. I suppose I should have a closer look at the detect paths, to just see how hairy the alternative of adding error propagation would be. Although maybe not today (it is supposed to be a holiday here.. and I've already spent enough of the weekend on atomic ;-))
Just to get *something* merged to unblock driver work and maybe get people starting to think about plumbing this through userspace (xrandr atomic modeset, for example), maybe the way to go is just revert mode_config.mutex to a traditional mutex for now. The weird cases only start to come up with real atomic ioctls, and we could use a driver flag to opt-in to that on a per driver basis.
BR, -R
-Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Mon, May 26, 2014 at 11:49:59AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 11:35 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, May 26, 2014 at 11:20:49AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 11:07 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, May 26, 2014 at 5:04 PM, Rob Clark robdclark@gmail.com wrote:
hmm, ok, I had thought this case, thread B would get -EDEADLK because lock was held, and not his acquire ctx. If that is not the case, then I would propose this:
All places doing things the old way, must grab mode_config.mutex first currently. And we use mode_config.mutex to protect mode_config.acquire_ctx. So all the lower spots grabbing individual crtc mutexes can safely use mode_config.acquire_ctx.
Then the only headache is propagating -EDEADLK up the call stack. If we are lucky, the all already propagate -EINTR, etc.
The output poll work most definitely doesn't propagate -EINTR. Like I've said, this will be painful. And imo doing this also makes the kms locking into quite a mess overall.
Well, we could hold mode_config.mutex as a traditional mutex around atomic operations. What you loose out would be now _NONBLOCK operations could conceivable call into driver paths without mode_config.mutex held. This was the advantage of converting mode_config.mutex as well. Granted, it is slightly theoretical because until we expose atomic ioctl it would only apply to page_flip (which was not holding mode_config.mutex). And we also want to get rid of mode_config.mutex in these paths too.
Otoh, if we want to make locking more fine grained, more use of ww_mutex seems like the best way. And if that means adding a return value to a fxn here/there and propagating errors properly, maybe we should just go ahead and do that. It sounds like the right long term solution anyways.
Yeah, I'm starting to lean towards trying to elide mode_config.mutex completely from the atomic paths (and modesets in general). I think the only bits we really need is adding ww mutexes to planes _and_ to connectors. The atomic would _only_ ever acquire ww mutexes, and we would be able to guarante that most of them are only held short times so that we don't need to bother with trylocking them for NONBLOCK. That should simply the atomic logic a bit I hope.
So that, and a full subsystem audit unfortunately :(
well, getting rid of any global lock from atomic path is, I hope, the eventual goal. But we are *really* late already with atomic, and too many other things backing up on top of this. I suppose I should have a closer look at the detect paths, to just see how hairy the alternative of adding error propagation would be. Although maybe not today (it is supposed to be a holiday here.. and I've already spent enough of the weekend on atomic ;-))
Just to get *something* merged to unblock driver work and maybe get people starting to think about plumbing this through userspace (xrandr atomic modeset, for example), maybe the way to go is just revert mode_config.mutex to a traditional mutex for now. The weird cases only start to come up with real atomic ioctls, and we could use a driver flag to opt-in to that on a per driver basis.
This will result in people raving about regressions, which from a maintainer pov I'll reject. If we want to chicken out of this question I'd vote for an drm_modeset_lock_all for the atomic paths, which also isn't really helpful. I don't really think there's an easy answer.
Wrt unblocking driver work: In i915 we keep on plunging ahead without the interface stuff thus far. And wrt unblocking everything else: I hear a lot of people screaming about the lack of atomic updates, but at least within Intel not many people throwing resources at it. So my impression is that it can't be that bad really. -Daniel
An object property is an id (idr) for a drm mode object. This will allow a property to be used set/get a framebuffer, CRTC, etc.
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/drm_crtc.c | 60 +++++++++++++++++++++++++++++++++++---------- include/drm/drm_crtc.h | 27 ++++++++++++++++++++ include/uapi/drm/drm_mode.h | 14 +++++++++++ 3 files changed, 88 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index dd10e4c..c049ba7 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3142,6 +3142,8 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags, if (!property) return NULL;
+ property->dev = dev; + if (num_values) { property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL); if (!property->values) @@ -3162,6 +3164,9 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags, }
list_add_tail(&property->head, &dev->mode_config.property_list); + + BUG_ON(!drm_property_type_valid(property)); + return property; fail: kfree(property->values); @@ -3299,6 +3304,23 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags } EXPORT_SYMBOL(drm_property_create_range);
+struct drm_property *drm_property_create_object(struct drm_device *dev, + int flags, const char *name, uint32_t type) +{ + struct drm_property *property; + + flags |= DRM_MODE_PROP_OBJECT; + + property = drm_property_create(dev, flags, name, 1); + if (!property) + return NULL; + + property->values[0] = type; + + return property; +} +EXPORT_SYMBOL(drm_property_create_object); + /** * drm_property_add_enum - add a possible value to an enumeration property * @property: enumeration property to change @@ -3319,14 +3341,16 @@ int drm_property_add_enum(struct drm_property *property, int index, { struct drm_property_enum *prop_enum;
- if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK))) + if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) || + drm_property_type_is(property, DRM_MODE_PROP_BITMASK))) return -EINVAL;
/* * Bitmask enum properties have the additional constraint of values * from 0 to 63 */ - if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63)) + if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) && + (value > 63)) return -EINVAL;
if (!list_empty(&property->enum_blob_list)) { @@ -3509,10 +3533,11 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, } property = obj_to_property(obj);
- if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { + if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || + drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { list_for_each_entry(prop_enum, &property->enum_blob_list, head) enum_count++; - } else if (property->flags & DRM_MODE_PROP_BLOB) { + } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { list_for_each_entry(prop_blob, &property->enum_blob_list, head) blob_count++; } @@ -3534,7 +3559,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, } out_resp->count_values = value_count;
- if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { + if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || + drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { copied = 0; enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; @@ -3556,7 +3582,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, out_resp->count_enum_blobs = enum_count; }
- if (property->flags & DRM_MODE_PROP_BLOB) { + if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { copied = 0; blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr; @@ -3712,19 +3738,25 @@ static bool drm_property_change_is_valid(struct drm_property *property, { if (property->flags & DRM_MODE_PROP_IMMUTABLE) return false; - if (property->flags & DRM_MODE_PROP_RANGE) { + + if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { if (value < property->values[0] || value > property->values[1]) return false; return true; - } else if (property->flags & DRM_MODE_PROP_BITMASK) { + } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { int i; uint64_t valid_mask = 0; for (i = 0; i < property->num_values; i++) valid_mask |= (1ULL << property->values[i]); return !(value & ~valid_mask); - } else if (property->flags & DRM_MODE_PROP_BLOB) { + } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { /* Only the driver knows */ return true; + } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { + /* a zero value for an object property translates to null: */ + if (value == 0) + return true; + return drm_property_get_obj(property, value) != NULL; } else { int i; for (i = 0; i < property->num_values; i++) @@ -3815,9 +3847,9 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, return ret; }
-static int drm_mode_set_obj_prop(struct drm_device *dev, - struct drm_mode_object *obj, struct drm_atomic_state *state, - struct drm_property *property, uint64_t value, void *blob_data) +static int drm_mode_set_obj_prop(struct drm_mode_object *obj, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data) { if (drm_property_change_is_valid(property, value)) { switch (obj->type) { @@ -3831,6 +3863,8 @@ static int drm_mode_set_obj_prop(struct drm_device *dev, return drm_mode_plane_set_obj_prop(obj_to_plane(obj), state, property, value, blob_data); } + } else { + DRM_DEBUG("invalid value: %s = %llx\n", property->name, value); }
return -EINVAL; @@ -3866,7 +3900,7 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, return -ENOENT; property = obj_to_property(prop_obj);
- return drm_mode_set_obj_prop(dev, arg_obj, state, property, + return drm_mode_set_obj_prop(arg_obj, state, property, value, blob_data); }
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 83e0f88..65bfb8b 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -191,6 +191,7 @@ struct drm_property { char name[DRM_PROP_NAME_LEN]; uint32_t num_values; uint64_t *values; + struct drm_device *dev;
struct list_head enum_blob_list; }; @@ -941,6 +942,23 @@ extern void drm_mode_config_cleanup(struct drm_device *dev);
extern int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid); + +static inline bool drm_property_type_is(struct drm_property *property, + uint32_t type) +{ + /* instanceof for props.. handles extended type vs original types: */ + if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) + return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type; + return property->flags & type; +} + +static inline bool drm_property_type_valid(struct drm_property *property) +{ + if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) + return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE); + return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE); +} + extern int drm_object_property_set_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t val); @@ -974,6 +992,8 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev, struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, const char *name, uint64_t min, uint64_t max); +struct drm_property *drm_property_create_object(struct drm_device *dev, + int flags, const char *name, uint32_t type); extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property); extern int drm_property_add_enum(struct drm_property *property, int index, uint64_t value, const char *name); @@ -990,6 +1010,13 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, int gamma_size); extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type); + +static inline struct drm_mode_object * +drm_property_get_obj(struct drm_property *property, uint64_t value) +{ + return drm_mode_object_find(property->dev, value, property->values[0]); +} + /* IOCTLs */ extern int drm_mode_getresources(struct drm_device *dev, void *data, struct drm_file *file_priv); diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 12e2139..516425d 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -251,6 +251,20 @@ struct drm_mode_get_connector { #define DRM_MODE_PROP_BLOB (1<<4) #define DRM_MODE_PROP_BITMASK (1<<5) /* bitmask of enumerated types */
+/* non-extended types: legacy bitmask, one bit per type: */ +#define DRM_MODE_PROP_LEGACY_TYPE ( \ + DRM_MODE_PROP_RANGE | \ + DRM_MODE_PROP_ENUM | \ + DRM_MODE_PROP_BLOB | \ + DRM_MODE_PROP_BITMASK) + +/* extended-types: rather than continue to consume a bit per type, + * grab a chunk of the bits to use as integer type id. + */ +#define DRM_MODE_PROP_EXTENDED_TYPE 0x0000ffc0 +#define DRM_MODE_PROP_TYPE(n) ((n) << 6) +#define DRM_MODE_PROP_OBJECT DRM_MODE_PROP_TYPE(1) + struct drm_mode_property_enum { __u64 value; char name[DRM_PROP_NAME_LEN];
On Sat, May 24, 2014 at 02:30:13PM -0400, Rob Clark wrote:
An object property is an id (idr) for a drm mode object. This will allow a property to be used set/get a framebuffer, CRTC, etc.
Signed-off-by: Rob Clark robdclark@gmail.com
drivers/gpu/drm/drm_crtc.c | 60 +++++++++++++++++++++++++++++++++++---------- include/drm/drm_crtc.h | 27 ++++++++++++++++++++ include/uapi/drm/drm_mode.h | 14 +++++++++++ 3 files changed, 88 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index dd10e4c..c049ba7 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3142,6 +3142,8 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags, if (!property) return NULL;
- property->dev = dev;
- if (num_values) { property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL); if (!property->values)
@@ -3162,6 +3164,9 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags, }
list_add_tail(&property->head, &dev->mode_config.property_list);
- BUG_ON(!drm_property_type_valid(property));
- return property;
fail: kfree(property->values); @@ -3299,6 +3304,23 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags } EXPORT_SYMBOL(drm_property_create_range);
+struct drm_property *drm_property_create_object(struct drm_device *dev,
int flags, const char *name, uint32_t type)
+{
- struct drm_property *property;
- flags |= DRM_MODE_PROP_OBJECT;
- property = drm_property_create(dev, flags, name, 1);
- if (!property)
return NULL;
- property->values[0] = type;
- return property;
+} +EXPORT_SYMBOL(drm_property_create_object);
/**
- drm_property_add_enum - add a possible value to an enumeration property
- @property: enumeration property to change
@@ -3319,14 +3341,16 @@ int drm_property_add_enum(struct drm_property *property, int index, { struct drm_property_enum *prop_enum;
- if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
return -EINVAL;
/*
- Bitmask enum properties have the additional constraint of values
- from 0 to 63
*/
- if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63))
if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
(value > 63))
return -EINVAL;
if (!list_empty(&property->enum_blob_list)) {
@@ -3509,10 +3533,11 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, } property = obj_to_property(obj);
- if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
- if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
list_for_each_entry(prop_enum, &property->enum_blob_list, head) enum_count++;drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
- } else if (property->flags & DRM_MODE_PROP_BLOB) {
- } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { list_for_each_entry(prop_blob, &property->enum_blob_list, head) blob_count++; }
@@ -3534,7 +3559,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, } out_resp->count_values = value_count;
- if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
- if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { copied = 0; enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
@@ -3556,7 +3582,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, out_resp->count_enum_blobs = enum_count; }
- if (property->flags & DRM_MODE_PROP_BLOB) {
- if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { copied = 0; blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr;
@@ -3712,19 +3738,25 @@ static bool drm_property_change_is_valid(struct drm_property *property, { if (property->flags & DRM_MODE_PROP_IMMUTABLE) return false;
- if (property->flags & DRM_MODE_PROP_RANGE) {
- if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { if (value < property->values[0] || value > property->values[1]) return false; return true;
- } else if (property->flags & DRM_MODE_PROP_BITMASK) {
- } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { int i; uint64_t valid_mask = 0; for (i = 0; i < property->num_values; i++) valid_mask |= (1ULL << property->values[i]); return !(value & ~valid_mask);
- } else if (property->flags & DRM_MODE_PROP_BLOB) {
- } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { /* Only the driver knows */ return true;
- } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
/* a zero value for an object property translates to null: */
if (value == 0)
return true;
return drm_property_get_obj(property, value) != NULL;
Ok, so this usage is indeed safe for framebuffers since you only compare the pointer against NULL. But you can't ever access this pointer since it might disappear any time after you've dropped the idr lock. I think what you actually want here is a new interface drm_mode_object_exists which just returns a bool.
That won't expose any risky things to callers and we can still keep the restriction that calling drm_mode_object_get for framebuffers is a BUG. -Daniel
} else { int i; for (i = 0; i < property->num_values; i++) @@ -3815,9 +3847,9 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, return ret; }
-static int drm_mode_set_obj_prop(struct drm_device *dev,
struct drm_mode_object *obj, struct drm_atomic_state *state,
struct drm_property *property, uint64_t value, void *blob_data)
+static int drm_mode_set_obj_prop(struct drm_mode_object *obj,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t value, void *blob_data)
{ if (drm_property_change_is_valid(property, value)) { switch (obj->type) { @@ -3831,6 +3863,8 @@ static int drm_mode_set_obj_prop(struct drm_device *dev, return drm_mode_plane_set_obj_prop(obj_to_plane(obj), state, property, value, blob_data); }
} else {
DRM_DEBUG("invalid value: %s = %llx\n", property->name, value);
}
return -EINVAL;
@@ -3866,7 +3900,7 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, return -ENOENT; property = obj_to_property(prop_obj);
- return drm_mode_set_obj_prop(dev, arg_obj, state, property,
- return drm_mode_set_obj_prop(arg_obj, state, property, value, blob_data);
}
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 83e0f88..65bfb8b 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -191,6 +191,7 @@ struct drm_property { char name[DRM_PROP_NAME_LEN]; uint32_t num_values; uint64_t *values;
struct drm_device *dev;
struct list_head enum_blob_list;
}; @@ -941,6 +942,23 @@ extern void drm_mode_config_cleanup(struct drm_device *dev);
extern int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid);
+static inline bool drm_property_type_is(struct drm_property *property,
uint32_t type)
+{
- /* instanceof for props.. handles extended type vs original types: */
- if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type;
- return property->flags & type;
+}
+static inline bool drm_property_type_valid(struct drm_property *property) +{
- if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
- return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
+}
extern int drm_object_property_set_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t val); @@ -974,6 +992,8 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev, struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, const char *name, uint64_t min, uint64_t max); +struct drm_property *drm_property_create_object(struct drm_device *dev,
int flags, const char *name, uint32_t type);
extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property); extern int drm_property_add_enum(struct drm_property *property, int index, uint64_t value, const char *name); @@ -990,6 +1010,13 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, int gamma_size); extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type);
+static inline struct drm_mode_object * +drm_property_get_obj(struct drm_property *property, uint64_t value) +{
- return drm_mode_object_find(property->dev, value, property->values[0]);
+}
/* IOCTLs */ extern int drm_mode_getresources(struct drm_device *dev, void *data, struct drm_file *file_priv); diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 12e2139..516425d 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -251,6 +251,20 @@ struct drm_mode_get_connector { #define DRM_MODE_PROP_BLOB (1<<4) #define DRM_MODE_PROP_BITMASK (1<<5) /* bitmask of enumerated types */
+/* non-extended types: legacy bitmask, one bit per type: */ +#define DRM_MODE_PROP_LEGACY_TYPE ( \
DRM_MODE_PROP_RANGE | \
DRM_MODE_PROP_ENUM | \
DRM_MODE_PROP_BLOB | \
DRM_MODE_PROP_BITMASK)
+/* extended-types: rather than continue to consume a bit per type,
- grab a chunk of the bits to use as integer type id.
- */
+#define DRM_MODE_PROP_EXTENDED_TYPE 0x0000ffc0 +#define DRM_MODE_PROP_TYPE(n) ((n) << 6) +#define DRM_MODE_PROP_OBJECT DRM_MODE_PROP_TYPE(1)
struct drm_mode_property_enum { __u64 value; char name[DRM_PROP_NAME_LEN]; -- 1.9.0
On Mon, May 26, 2014 at 10:29:47AM +0200, Daniel Vetter wrote:
On Sat, May 24, 2014 at 02:30:13PM -0400, Rob Clark wrote:
- } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
/* a zero value for an object property translates to null: */
if (value == 0)
return true;
return drm_property_get_obj(property, value) != NULL;
Ok, so this usage is indeed safe for framebuffers since you only compare the pointer against NULL. But you can't ever access this pointer since it might disappear any time after you've dropped the idr lock. I think what you actually want here is a new interface drm_mode_object_exists which just returns a bool.
That won't expose any risky things to callers and we can still keep the restriction that calling drm_mode_object_get for framebuffers is a BUG.
s/object_get/object_find/ I've meant ofc. -Daniel
On Mon, May 26, 2014 at 4:29 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 02:30:13PM -0400, Rob Clark wrote:
An object property is an id (idr) for a drm mode object. This will allow a property to be used set/get a framebuffer, CRTC, etc.
Signed-off-by: Rob Clark robdclark@gmail.com
drivers/gpu/drm/drm_crtc.c | 60 +++++++++++++++++++++++++++++++++++---------- include/drm/drm_crtc.h | 27 ++++++++++++++++++++ include/uapi/drm/drm_mode.h | 14 +++++++++++ 3 files changed, 88 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index dd10e4c..c049ba7 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3142,6 +3142,8 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags, if (!property) return NULL;
property->dev = dev;
if (num_values) { property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL); if (!property->values)
@@ -3162,6 +3164,9 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags, }
list_add_tail(&property->head, &dev->mode_config.property_list);
BUG_ON(!drm_property_type_valid(property));
return property;
fail: kfree(property->values); @@ -3299,6 +3304,23 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags } EXPORT_SYMBOL(drm_property_create_range);
+struct drm_property *drm_property_create_object(struct drm_device *dev,
int flags, const char *name, uint32_t type)
+{
struct drm_property *property;
flags |= DRM_MODE_PROP_OBJECT;
property = drm_property_create(dev, flags, name, 1);
if (!property)
return NULL;
property->values[0] = type;
return property;
+} +EXPORT_SYMBOL(drm_property_create_object);
/**
- drm_property_add_enum - add a possible value to an enumeration property
- @property: enumeration property to change
@@ -3319,14 +3341,16 @@ int drm_property_add_enum(struct drm_property *property, int index, { struct drm_property_enum *prop_enum;
if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
drm_property_type_is(property, DRM_MODE_PROP_BITMASK))) return -EINVAL; /* * Bitmask enum properties have the additional constraint of values * from 0 to 63 */
if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63))
if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
(value > 63)) return -EINVAL; if (!list_empty(&property->enum_blob_list)) {
@@ -3509,10 +3533,11 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, } property = obj_to_property(obj);
if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { list_for_each_entry(prop_enum, &property->enum_blob_list, head) enum_count++;
} else if (property->flags & DRM_MODE_PROP_BLOB) {
} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { list_for_each_entry(prop_blob, &property->enum_blob_list, head) blob_count++; }
@@ -3534,7 +3559,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, } out_resp->count_values = value_count;
if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { copied = 0; enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
@@ -3556,7 +3582,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, out_resp->count_enum_blobs = enum_count; }
if (property->flags & DRM_MODE_PROP_BLOB) {
if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { copied = 0; blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr;
@@ -3712,19 +3738,25 @@ static bool drm_property_change_is_valid(struct drm_property *property, { if (property->flags & DRM_MODE_PROP_IMMUTABLE) return false;
if (property->flags & DRM_MODE_PROP_RANGE) {
if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { if (value < property->values[0] || value > property->values[1]) return false; return true;
} else if (property->flags & DRM_MODE_PROP_BITMASK) {
} else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { int i; uint64_t valid_mask = 0; for (i = 0; i < property->num_values; i++) valid_mask |= (1ULL << property->values[i]); return !(value & ~valid_mask);
} else if (property->flags & DRM_MODE_PROP_BLOB) {
} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { /* Only the driver knows */ return true;
} else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
/* a zero value for an object property translates to null: */
if (value == 0)
return true;
return drm_property_get_obj(property, value) != NULL;
Ok, so this usage is indeed safe for framebuffers since you only compare the pointer against NULL. But you can't ever access this pointer since it might disappear any time after you've dropped the idr lock. I think what you actually want here is a new interface drm_mode_object_exists which just returns a bool.
Hmm, I think I'll just drop drm_property_get_obj() and call directly _object_find() here, I think. Unless we come up with other spots that need a drm_mode_object_exists(). That would avoid potential future misunderstandings.
BR, -R
That won't expose any risky things to callers and we can still keep the restriction that calling drm_mode_object_get for framebuffers is a BUG. -Daniel
} else { int i; for (i = 0; i < property->num_values; i++)
@@ -3815,9 +3847,9 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, return ret; }
-static int drm_mode_set_obj_prop(struct drm_device *dev,
struct drm_mode_object *obj, struct drm_atomic_state *state,
struct drm_property *property, uint64_t value, void *blob_data)
+static int drm_mode_set_obj_prop(struct drm_mode_object *obj,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t value, void *blob_data)
{ if (drm_property_change_is_valid(property, value)) { switch (obj->type) { @@ -3831,6 +3863,8 @@ static int drm_mode_set_obj_prop(struct drm_device *dev, return drm_mode_plane_set_obj_prop(obj_to_plane(obj), state, property, value, blob_data); }
} else {
DRM_DEBUG("invalid value: %s = %llx\n", property->name, value); } return -EINVAL;
@@ -3866,7 +3900,7 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, return -ENOENT; property = obj_to_property(prop_obj);
return drm_mode_set_obj_prop(dev, arg_obj, state, property,
return drm_mode_set_obj_prop(arg_obj, state, property, value, blob_data);
}
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 83e0f88..65bfb8b 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -191,6 +191,7 @@ struct drm_property { char name[DRM_PROP_NAME_LEN]; uint32_t num_values; uint64_t *values;
struct drm_device *dev; struct list_head enum_blob_list;
}; @@ -941,6 +942,23 @@ extern void drm_mode_config_cleanup(struct drm_device *dev);
extern int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid);
+static inline bool drm_property_type_is(struct drm_property *property,
uint32_t type)
+{
/* instanceof for props.. handles extended type vs original types: */
if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type;
return property->flags & type;
+}
+static inline bool drm_property_type_valid(struct drm_property *property) +{
if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
+}
extern int drm_object_property_set_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t val); @@ -974,6 +992,8 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev, struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, const char *name, uint64_t min, uint64_t max); +struct drm_property *drm_property_create_object(struct drm_device *dev,
int flags, const char *name, uint32_t type);
extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property); extern int drm_property_add_enum(struct drm_property *property, int index, uint64_t value, const char *name); @@ -990,6 +1010,13 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, int gamma_size); extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type);
+static inline struct drm_mode_object * +drm_property_get_obj(struct drm_property *property, uint64_t value) +{
return drm_mode_object_find(property->dev, value, property->values[0]);
+}
/* IOCTLs */ extern int drm_mode_getresources(struct drm_device *dev, void *data, struct drm_file *file_priv); diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 12e2139..516425d 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -251,6 +251,20 @@ struct drm_mode_get_connector { #define DRM_MODE_PROP_BLOB (1<<4) #define DRM_MODE_PROP_BITMASK (1<<5) /* bitmask of enumerated types */
+/* non-extended types: legacy bitmask, one bit per type: */ +#define DRM_MODE_PROP_LEGACY_TYPE ( \
DRM_MODE_PROP_RANGE | \
DRM_MODE_PROP_ENUM | \
DRM_MODE_PROP_BLOB | \
DRM_MODE_PROP_BITMASK)
+/* extended-types: rather than continue to consume a bit per type,
- grab a chunk of the bits to use as integer type id.
- */
+#define DRM_MODE_PROP_EXTENDED_TYPE 0x0000ffc0 +#define DRM_MODE_PROP_TYPE(n) ((n) << 6) +#define DRM_MODE_PROP_OBJECT DRM_MODE_PROP_TYPE(1)
struct drm_mode_property_enum { __u64 value; char name[DRM_PROP_NAME_LEN]; -- 1.9.0
-- Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
Like range, but values are signed.
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/drm_crtc.c | 45 +++++++++++++++++++++++++++++++++------------ include/drm/drm_crtc.h | 12 ++++++++++++ include/uapi/drm/drm_mode.h | 1 + 3 files changed, 46 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index c049ba7..fa86fba 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3267,6 +3267,22 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev, } EXPORT_SYMBOL(drm_property_create_bitmask);
+static struct drm_property *property_create_range(struct drm_device *dev, + int flags, const char *name, + uint64_t min, uint64_t max) +{ + struct drm_property *property; + + property = drm_property_create(dev, flags, name, 2); + if (!property) + return NULL; + + property->values[0] = min; + property->values[1] = max; + + return property; +} + /** * drm_property_create - create a new ranged property type * @dev: drm device @@ -3289,21 +3305,20 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags const char *name, uint64_t min, uint64_t max) { - struct drm_property *property; - - flags |= DRM_MODE_PROP_RANGE; - - property = drm_property_create(dev, flags, name, 2); - if (!property) - return NULL; - - property->values[0] = min; - property->values[1] = max; - - return property; + return property_create_range(dev, DRM_MODE_PROP_RANGE | flags, + name, min, max); } EXPORT_SYMBOL(drm_property_create_range);
+struct drm_property *drm_property_create_signed_range(struct drm_device *dev, + int flags, const char *name, + int64_t min, int64_t max) +{ + return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags, + name, I642U64(min), I642U64(max)); +} +EXPORT_SYMBOL(drm_property_create_signed_range); + struct drm_property *drm_property_create_object(struct drm_device *dev, int flags, const char *name, uint32_t type) { @@ -3743,6 +3758,12 @@ static bool drm_property_change_is_valid(struct drm_property *property, if (value < property->values[0] || value > property->values[1]) return false; return true; + } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) { + int64_t svalue = U642I64(value); + if (svalue < U642I64(property->values[0]) || + svalue > U642I64(property->values[1])) + return false; + return true; } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { int i; uint64_t valid_mask = 0; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 65bfb8b..cd4a61a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -65,6 +65,15 @@ struct drm_object_properties { uint64_t values[DRM_OBJECT_MAX_PROPERTY]; };
+static inline int64_t U642I64(uint64_t val) +{ + return (int64_t)*((int64_t *)&val); +} +static inline uint64_t I642U64(int64_t val) +{ + return (uint64_t)*((uint64_t *)&val); +} + enum drm_connector_force { DRM_FORCE_UNSPECIFIED, DRM_FORCE_OFF, @@ -992,6 +1001,9 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev, struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, const char *name, uint64_t min, uint64_t max); +struct drm_property *drm_property_create_signed_range(struct drm_device *dev, + int flags, const char *name, + int64_t min, int64_t max); struct drm_property *drm_property_create_object(struct drm_device *dev, int flags, const char *name, uint32_t type); extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property); diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 516425d..6421edc 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -264,6 +264,7 @@ struct drm_mode_get_connector { #define DRM_MODE_PROP_EXTENDED_TYPE 0x0000ffc0 #define DRM_MODE_PROP_TYPE(n) ((n) << 6) #define DRM_MODE_PROP_OBJECT DRM_MODE_PROP_TYPE(1) +#define DRM_MODE_PROP_SIGNED_RANGE DRM_MODE_PROP_TYPE(2)
struct drm_mode_property_enum { __u64 value;
Add a few more useful helpers to find mode objects.
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/drm_crtc.c | 97 ++++++++++++++-------------------------------- include/drm/drm_crtc.h | 33 ++++++++++++++++ 2 files changed, 63 insertions(+), 67 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index fa86fba..bd12185 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1736,7 +1736,6 @@ int drm_mode_getcrtc(struct drm_device *dev, { struct drm_mode_crtc *crtc_resp = data; struct drm_crtc *crtc; - struct drm_mode_object *obj; int ret = 0;
if (!drm_core_check_feature(dev, DRIVER_MODESET)) @@ -1744,13 +1743,11 @@ int drm_mode_getcrtc(struct drm_device *dev,
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, crtc_resp->crtc_id, - DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(dev, crtc_resp->crtc_id); + if (!crtc) { ret = -ENOENT; goto out; } - crtc = obj_to_crtc(obj);
crtc_resp->x = crtc->x; crtc_resp->y = crtc->y; @@ -1804,7 +1801,6 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_connector *out_resp = data; - struct drm_mode_object *obj; struct drm_connector *connector; struct drm_display_mode *mode; int mode_count = 0; @@ -1828,13 +1824,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
drm_modeset_lock(&dev->mode_config.mutex, NULL);
- obj = drm_mode_object_find(dev, out_resp->connector_id, - DRM_MODE_OBJECT_CONNECTOR); - if (!obj) { + connector = drm_connector_find(dev, out_resp->connector_id); + if (!connector) { ret = -ENOENT; goto out; } - connector = obj_to_connector(obj);
props_count = connector->properties.count;
@@ -1949,7 +1943,6 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_encoder *enc_resp = data; - struct drm_mode_object *obj; struct drm_encoder *encoder; int ret = 0;
@@ -1957,13 +1950,11 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, return -EINVAL;
drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, enc_resp->encoder_id, - DRM_MODE_OBJECT_ENCODER); - if (!obj) { + encoder = drm_encoder_find(dev, enc_resp->encoder_id); + if (!encoder) { ret = -ENOENT; goto out; } - encoder = obj_to_encoder(obj);
if (encoder->crtc) enc_resp->crtc_id = encoder->crtc->base.id; @@ -2061,7 +2052,6 @@ int drm_mode_getplane(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_plane *plane_resp = data; - struct drm_mode_object *obj; struct drm_plane *plane; uint32_t __user *format_ptr; int ret = 0; @@ -2070,13 +2060,11 @@ int drm_mode_getplane(struct drm_device *dev, void *data, return -EINVAL;
drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, plane_resp->plane_id, - DRM_MODE_OBJECT_PLANE); - if (!obj) { + plane = drm_plane_find(dev, plane_resp->plane_id); + if (!plane) { ret = -ENOENT; goto out; } - plane = obj_to_plane(obj);
if (plane->crtc) plane_resp->crtc_id = plane->crtc->base.id; @@ -2129,7 +2117,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; struct drm_framebuffer *fb = NULL, *old_fb = NULL; @@ -2144,14 +2131,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);
/* No fb means shut it down */ if (!plane_req->fb_id) { @@ -2168,15 +2153,13 @@ int drm_mode_setplane(struct drm_device *dev, void *data, goto out; }
- 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); ret = -ENOENT; goto out; } - crtc = obj_to_crtc(obj);
fb = drm_framebuffer_lookup(dev, plane_req->fb_id); if (!fb) { @@ -2363,7 +2346,6 @@ 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_mode_object *obj; struct drm_crtc *crtc; struct drm_connector **connector_set = NULL, *connector; struct drm_framebuffer *fb = NULL; @@ -2381,14 +2363,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, return -ERANGE;
drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, crtc_req->crtc_id, - DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(dev, crtc_req->crtc_id); + if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); ret = -ENOENT; goto out; } - crtc = obj_to_crtc(obj); DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
if (crtc_req->mode_valid) { @@ -2471,15 +2451,13 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; }
- obj = drm_mode_object_find(dev, out_id, - DRM_MODE_OBJECT_CONNECTOR); - if (!obj) { + connector = drm_connector_find(dev, out_id); + if (!connector) { DRM_DEBUG_KMS("Connector id %d unknown\n", out_id); ret = -ENOENT; goto out; } - connector = obj_to_connector(obj); DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, drm_get_connector_name(connector)); @@ -2511,7 +2489,6 @@ static int drm_mode_cursor_common(struct drm_device *dev, struct drm_mode_cursor2 *req, struct drm_file *file_priv) { - struct drm_mode_object *obj; struct drm_crtc *crtc; int ret = 0;
@@ -2521,12 +2498,11 @@ static int drm_mode_cursor_common(struct drm_device *dev, if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL;
- obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(dev, req->crtc_id); + if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); return -ENOENT; } - crtc = obj_to_crtc(obj);
drm_modeset_lock(&crtc->mutex, NULL); if (req->flags & DRM_MODE_CURSOR_BO) { @@ -3522,7 +3498,6 @@ EXPORT_SYMBOL(drm_object_property_get_value); int drm_mode_getproperty_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct drm_mode_object *obj; struct drm_mode_get_property *out_resp = data; struct drm_property *property; int enum_count = 0; @@ -3541,12 +3516,11 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, return -EINVAL;
drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); - if (!obj) { + property = drm_property_find(dev, out_resp->prop_id); + if (!property) { ret = -ENOENT; goto done; } - property = obj_to_property(obj);
if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { @@ -3676,7 +3650,6 @@ static void drm_property_destroy_blob(struct drm_device *dev, int drm_mode_getblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct drm_mode_object *obj; struct drm_mode_get_blob *out_resp = data; struct drm_property_blob *blob; int ret = 0; @@ -3686,12 +3659,11 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, return -EINVAL;
drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); - if (!obj) { + blob = drm_property_blob_find(dev, out_resp->blob_id); + if (!blob) { ret = -ENOENT; goto done; } - blob = obj_to_blob(obj);
if (out_resp->length == blob->length) { blob_ptr = (void __user *)(unsigned long)out_resp->data; @@ -3898,7 +3870,6 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, uint32_t prop_id, uint64_t value, void *blob_data) { struct drm_mode_object *arg_obj; - struct drm_mode_object *prop_obj; struct drm_property *property; int i;
@@ -3915,11 +3886,9 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, if (i == arg_obj->properties->count) return -EINVAL;
- prop_obj = drm_mode_object_find(dev, prop_id, - DRM_MODE_OBJECT_PROPERTY); - if (!prop_obj) + property = drm_property_find(dev, prop_id); + if (!property) return -ENOENT; - property = obj_to_property(prop_obj);
return drm_mode_set_obj_prop(arg_obj, state, property, value, blob_data); @@ -4126,7 +4095,6 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_lut *crtc_lut = data; - struct drm_mode_object *obj; struct drm_crtc *crtc; void *r_base, *g_base, *b_base; int size; @@ -4136,12 +4104,11 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, return -EINVAL;
drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(dev, crtc_lut->crtc_id); + if (!crtc) { ret = -ENOENT; goto out; } - crtc = obj_to_crtc(obj);
if (crtc->funcs->gamma_set == NULL) { ret = -ENOSYS; @@ -4200,7 +4167,6 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_lut *crtc_lut = data; - struct drm_mode_object *obj; struct drm_crtc *crtc; void *r_base, *g_base, *b_base; int size; @@ -4210,12 +4176,11 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, return -EINVAL;
drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(dev, crtc_lut->crtc_id); + if (!crtc) { ret = -ENOENT; goto out; } - crtc = obj_to_crtc(obj);
/* memcpy into gamma store */ if (crtc_lut->gamma_size != crtc->gamma_size) { @@ -4268,7 +4233,6 @@ 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_object *obj; struct drm_crtc *crtc; struct drm_framebuffer *fb = NULL, *old_fb = NULL; struct drm_pending_vblank_event *e = NULL; @@ -4282,10 +4246,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip) return -EINVAL;
- obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); - if (!obj) + crtc = drm_crtc_find(dev, page_flip->crtc_id); + if (!crtc) return -ENOENT; - crtc = obj_to_crtc(obj);
drm_modeset_lock(&crtc->mutex, NULL); if (crtc->primary->fb == NULL) { diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index cd4a61a..b940a29 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1112,6 +1112,15 @@ extern int drm_format_vert_chroma_subsampling(uint32_t format); extern const char *drm_get_format_name(uint32_t format);
/* Helpers */ + +static inline struct drm_plane *drm_plane_find(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *mo; + mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PLANE); + return mo ? obj_to_plane(mo) : NULL; +} + static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev, uint32_t id) { @@ -1128,6 +1137,30 @@ static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev, return mo ? obj_to_encoder(mo) : NULL; }
+static inline struct drm_connector *drm_connector_find(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *mo; + mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CONNECTOR); + return mo ? obj_to_connector(mo) : NULL; +} + +static inline struct drm_property *drm_property_find(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *mo; + mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PROPERTY); + return mo ? obj_to_property(mo) : NULL; +} + +static inline struct drm_property_blob * +drm_property_blob_find(struct drm_device *dev, uint32_t id) +{ + struct drm_mode_object *mo; + mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB); + return mo ? obj_to_blob(mo) : NULL; +} + /* Plane list iterator for legacy (overlay only) planes. */ #define drm_for_each_legacy_plane(plane, planelist) \ list_for_each_entry(plane, planelist, head) \
On Sat, May 24, 2014 at 02:30:15PM -0400, Rob Clark wrote:
Add a few more useful helpers to find mode objects.
Signed-off-by: Rob Clark robdclark@gmail.com
There's a pile more in drivers for these. I like this, but imo we should also convert drivers while at it. Also, docbook is missing. And since it's a cleanup I think it's better to do this first, directly on top of drm-next. The patch itself looks good. -Daniel
drivers/gpu/drm/drm_crtc.c | 97 ++++++++++++++-------------------------------- include/drm/drm_crtc.h | 33 ++++++++++++++++ 2 files changed, 63 insertions(+), 67 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index fa86fba..bd12185 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1736,7 +1736,6 @@ int drm_mode_getcrtc(struct drm_device *dev, { struct drm_mode_crtc *crtc_resp = data; struct drm_crtc *crtc;
struct drm_mode_object *obj; int ret = 0;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
@@ -1744,13 +1743,11 @@ int drm_mode_getcrtc(struct drm_device *dev,
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, crtc_resp->crtc_id,
DRM_MODE_OBJECT_CRTC);
- if (!obj) {
- crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
- if (!crtc) { ret = -ENOENT; goto out; }
crtc = obj_to_crtc(obj);
crtc_resp->x = crtc->x; crtc_resp->y = crtc->y;
@@ -1804,7 +1801,6 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_connector *out_resp = data;
- struct drm_mode_object *obj; struct drm_connector *connector; struct drm_display_mode *mode; int mode_count = 0;
@@ -1828,13 +1824,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
drm_modeset_lock(&dev->mode_config.mutex, NULL);
- obj = drm_mode_object_find(dev, out_resp->connector_id,
DRM_MODE_OBJECT_CONNECTOR);
- if (!obj) {
- connector = drm_connector_find(dev, out_resp->connector_id);
- if (!connector) { ret = -ENOENT; goto out; }
connector = obj_to_connector(obj);
props_count = connector->properties.count;
@@ -1949,7 +1943,6 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_encoder *enc_resp = data;
- struct drm_mode_object *obj; struct drm_encoder *encoder; int ret = 0;
@@ -1957,13 +1950,11 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, return -EINVAL;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, enc_resp->encoder_id,
DRM_MODE_OBJECT_ENCODER);
- if (!obj) {
- encoder = drm_encoder_find(dev, enc_resp->encoder_id);
- if (!encoder) { ret = -ENOENT; goto out; }
encoder = obj_to_encoder(obj);
if (encoder->crtc) enc_resp->crtc_id = encoder->crtc->base.id;
@@ -2061,7 +2052,6 @@ int drm_mode_getplane(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_plane *plane_resp = data;
- struct drm_mode_object *obj; struct drm_plane *plane; uint32_t __user *format_ptr; int ret = 0;
@@ -2070,13 +2060,11 @@ int drm_mode_getplane(struct drm_device *dev, void *data, return -EINVAL;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, plane_resp->plane_id,
DRM_MODE_OBJECT_PLANE);
- if (!obj) {
- plane = drm_plane_find(dev, plane_resp->plane_id);
- if (!plane) { ret = -ENOENT; goto out; }
plane = obj_to_plane(obj);
if (plane->crtc) plane_resp->crtc_id = plane->crtc->base.id;
@@ -2129,7 +2117,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; struct drm_framebuffer *fb = NULL, *old_fb = NULL;
@@ -2144,14 +2131,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);
/* No fb means shut it down */ if (!plane_req->fb_id) {
@@ -2168,15 +2153,13 @@ int drm_mode_setplane(struct drm_device *dev, void *data, goto out; }
- 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); ret = -ENOENT; goto out; }
crtc = obj_to_crtc(obj);
fb = drm_framebuffer_lookup(dev, plane_req->fb_id); if (!fb) {
@@ -2363,7 +2346,6 @@ 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_mode_object *obj; struct drm_crtc *crtc; struct drm_connector **connector_set = NULL, *connector; struct drm_framebuffer *fb = NULL;
@@ -2381,14 +2363,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, return -ERANGE;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, crtc_req->crtc_id,
DRM_MODE_OBJECT_CRTC);
- if (!obj) {
- crtc = drm_crtc_find(dev, crtc_req->crtc_id);
- if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); ret = -ENOENT; goto out; }
crtc = obj_to_crtc(obj); DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
if (crtc_req->mode_valid) {
@@ -2471,15 +2451,13 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; }
obj = drm_mode_object_find(dev, out_id,
DRM_MODE_OBJECT_CONNECTOR);
if (!obj) {
connector = drm_connector_find(dev, out_id);
if (!connector) { DRM_DEBUG_KMS("Connector id %d unknown\n", out_id); ret = -ENOENT; goto out; }
connector = obj_to_connector(obj); DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, drm_get_connector_name(connector));
@@ -2511,7 +2489,6 @@ static int drm_mode_cursor_common(struct drm_device *dev, struct drm_mode_cursor2 *req, struct drm_file *file_priv) {
- struct drm_mode_object *obj; struct drm_crtc *crtc; int ret = 0;
@@ -2521,12 +2498,11 @@ static int drm_mode_cursor_common(struct drm_device *dev, if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL;
- obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
- if (!obj) {
- crtc = drm_crtc_find(dev, req->crtc_id);
- if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); return -ENOENT; }
crtc = obj_to_crtc(obj);
drm_modeset_lock(&crtc->mutex, NULL); if (req->flags & DRM_MODE_CURSOR_BO) {
@@ -3522,7 +3498,6 @@ EXPORT_SYMBOL(drm_object_property_get_value); int drm_mode_getproperty_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) {
- struct drm_mode_object *obj; struct drm_mode_get_property *out_resp = data; struct drm_property *property; int enum_count = 0;
@@ -3541,12 +3516,11 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, return -EINVAL;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
- if (!obj) {
- property = drm_property_find(dev, out_resp->prop_id);
- if (!property) { ret = -ENOENT; goto done; }
property = obj_to_property(obj);
if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
@@ -3676,7 +3650,6 @@ static void drm_property_destroy_blob(struct drm_device *dev, int drm_mode_getblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) {
- struct drm_mode_object *obj; struct drm_mode_get_blob *out_resp = data; struct drm_property_blob *blob; int ret = 0;
@@ -3686,12 +3659,11 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, return -EINVAL;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
- if (!obj) {
- blob = drm_property_blob_find(dev, out_resp->blob_id);
- if (!blob) { ret = -ENOENT; goto done; }
blob = obj_to_blob(obj);
if (out_resp->length == blob->length) { blob_ptr = (void __user *)(unsigned long)out_resp->data;
@@ -3898,7 +3870,6 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, uint32_t prop_id, uint64_t value, void *blob_data) { struct drm_mode_object *arg_obj;
- struct drm_mode_object *prop_obj; struct drm_property *property; int i;
@@ -3915,11 +3886,9 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, if (i == arg_obj->properties->count) return -EINVAL;
- prop_obj = drm_mode_object_find(dev, prop_id,
DRM_MODE_OBJECT_PROPERTY);
- if (!prop_obj)
- property = drm_property_find(dev, prop_id);
- if (!property) return -ENOENT;
property = obj_to_property(prop_obj);
return drm_mode_set_obj_prop(arg_obj, state, property, value, blob_data);
@@ -4126,7 +4095,6 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_lut *crtc_lut = data;
- struct drm_mode_object *obj; struct drm_crtc *crtc; void *r_base, *g_base, *b_base; int size;
@@ -4136,12 +4104,11 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, return -EINVAL;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
- if (!obj) {
- crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
- if (!crtc) { ret = -ENOENT; goto out; }
crtc = obj_to_crtc(obj);
if (crtc->funcs->gamma_set == NULL) { ret = -ENOSYS;
@@ -4200,7 +4167,6 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_lut *crtc_lut = data;
- struct drm_mode_object *obj; struct drm_crtc *crtc; void *r_base, *g_base, *b_base; int size;
@@ -4210,12 +4176,11 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, return -EINVAL;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
- if (!obj) {
- crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
- if (!crtc) { ret = -ENOENT; goto out; }
crtc = obj_to_crtc(obj);
/* memcpy into gamma store */ if (crtc_lut->gamma_size != crtc->gamma_size) {
@@ -4268,7 +4233,6 @@ 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_object *obj; struct drm_crtc *crtc; struct drm_framebuffer *fb = NULL, *old_fb = NULL; struct drm_pending_vblank_event *e = NULL;
@@ -4282,10 +4246,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip) return -EINVAL;
- obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
- if (!obj)
- crtc = drm_crtc_find(dev, page_flip->crtc_id);
- if (!crtc) return -ENOENT;
crtc = obj_to_crtc(obj);
drm_modeset_lock(&crtc->mutex, NULL); if (crtc->primary->fb == NULL) {
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index cd4a61a..b940a29 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1112,6 +1112,15 @@ extern int drm_format_vert_chroma_subsampling(uint32_t format); extern const char *drm_get_format_name(uint32_t format);
/* Helpers */
+static inline struct drm_plane *drm_plane_find(struct drm_device *dev,
uint32_t id)
+{
- struct drm_mode_object *mo;
- mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PLANE);
- return mo ? obj_to_plane(mo) : NULL;
+}
static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev, uint32_t id) { @@ -1128,6 +1137,30 @@ static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev, return mo ? obj_to_encoder(mo) : NULL; }
+static inline struct drm_connector *drm_connector_find(struct drm_device *dev,
uint32_t id)
+{
- struct drm_mode_object *mo;
- mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CONNECTOR);
- return mo ? obj_to_connector(mo) : NULL;
+}
+static inline struct drm_property *drm_property_find(struct drm_device *dev,
uint32_t id)
+{
- struct drm_mode_object *mo;
- mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PROPERTY);
- return mo ? obj_to_property(mo) : NULL;
+}
+static inline struct drm_property_blob * +drm_property_blob_find(struct drm_device *dev, uint32_t id) +{
- struct drm_mode_object *mo;
- mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB);
- return mo ? obj_to_blob(mo) : NULL;
+}
/* Plane list iterator for legacy (overlay only) planes. */ #define drm_for_each_legacy_plane(plane, planelist) \ list_for_each_entry(plane, planelist, head) \ -- 1.9.0
On Mon, May 26, 2014 at 10:37 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 02:30:15PM -0400, Rob Clark wrote:
Add a few more useful helpers to find mode objects.
Signed-off-by: Rob Clark robdclark@gmail.com
There's a pile more in drivers for these. I like this, but imo we should also convert drivers while at it. Also, docbook is missing. And since it's a cleanup I think it's better to do this first, directly on top of drm-next. The patch itself looks good.
Aside: This is a pretty perfect case for coccinelle since it'll match object_find users no matter how crazily they're split up. And you can remove the unused local variable with it too (but that part is a bit more fuzz). -Daniel
On Mon, May 26, 2014 at 4:37 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 02:30:15PM -0400, Rob Clark wrote:
Add a few more useful helpers to find mode objects.
Signed-off-by: Rob Clark robdclark@gmail.com
There's a pile more in drivers for these. I like this, but imo we should also convert drivers while at it. Also, docbook is missing. And since it's a cleanup I think it's better to do this first, directly on top of drm-next. The patch itself looks good.
Yeah, the intent was to go back and add missing docbook as follow-up patches, to try to get this into a shape where it could be merged earlier.
I can have a go at coccinelle too for follow-up patches.
BR, -R
-Daniel
drivers/gpu/drm/drm_crtc.c | 97 ++++++++++++++-------------------------------- include/drm/drm_crtc.h | 33 ++++++++++++++++ 2 files changed, 63 insertions(+), 67 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index fa86fba..bd12185 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1736,7 +1736,6 @@ int drm_mode_getcrtc(struct drm_device *dev, { struct drm_mode_crtc *crtc_resp = data; struct drm_crtc *crtc;
struct drm_mode_object *obj; int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET))
@@ -1744,13 +1743,11 @@ int drm_mode_getcrtc(struct drm_device *dev,
drm_modeset_lock_all(dev);
obj = drm_mode_object_find(dev, crtc_resp->crtc_id,
DRM_MODE_OBJECT_CRTC);
if (!obj) {
crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
if (!crtc) { ret = -ENOENT; goto out; }
crtc = obj_to_crtc(obj); crtc_resp->x = crtc->x; crtc_resp->y = crtc->y;
@@ -1804,7 +1801,6 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_connector *out_resp = data;
struct drm_mode_object *obj; struct drm_connector *connector; struct drm_display_mode *mode; int mode_count = 0;
@@ -1828,13 +1824,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
drm_modeset_lock(&dev->mode_config.mutex, NULL);
obj = drm_mode_object_find(dev, out_resp->connector_id,
DRM_MODE_OBJECT_CONNECTOR);
if (!obj) {
connector = drm_connector_find(dev, out_resp->connector_id);
if (!connector) { ret = -ENOENT; goto out; }
connector = obj_to_connector(obj); props_count = connector->properties.count;
@@ -1949,7 +1943,6 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_encoder *enc_resp = data;
struct drm_mode_object *obj; struct drm_encoder *encoder; int ret = 0;
@@ -1957,13 +1950,11 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, return -EINVAL;
drm_modeset_lock_all(dev);
obj = drm_mode_object_find(dev, enc_resp->encoder_id,
DRM_MODE_OBJECT_ENCODER);
if (!obj) {
encoder = drm_encoder_find(dev, enc_resp->encoder_id);
if (!encoder) { ret = -ENOENT; goto out; }
encoder = obj_to_encoder(obj); if (encoder->crtc) enc_resp->crtc_id = encoder->crtc->base.id;
@@ -2061,7 +2052,6 @@ int drm_mode_getplane(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_plane *plane_resp = data;
struct drm_mode_object *obj; struct drm_plane *plane; uint32_t __user *format_ptr; int ret = 0;
@@ -2070,13 +2060,11 @@ int drm_mode_getplane(struct drm_device *dev, void *data, return -EINVAL;
drm_modeset_lock_all(dev);
obj = drm_mode_object_find(dev, plane_resp->plane_id,
DRM_MODE_OBJECT_PLANE);
if (!obj) {
plane = drm_plane_find(dev, plane_resp->plane_id);
if (!plane) { ret = -ENOENT; goto out; }
plane = obj_to_plane(obj); if (plane->crtc) plane_resp->crtc_id = plane->crtc->base.id;
@@ -2129,7 +2117,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; struct drm_framebuffer *fb = NULL, *old_fb = NULL;
@@ -2144,14 +2131,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); /* No fb means shut it down */ if (!plane_req->fb_id) {
@@ -2168,15 +2153,13 @@ int drm_mode_setplane(struct drm_device *dev, void *data, goto out; }
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); ret = -ENOENT; goto out; }
crtc = obj_to_crtc(obj); fb = drm_framebuffer_lookup(dev, plane_req->fb_id); if (!fb) {
@@ -2363,7 +2346,6 @@ 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_mode_object *obj; struct drm_crtc *crtc; struct drm_connector **connector_set = NULL, *connector; struct drm_framebuffer *fb = NULL;
@@ -2381,14 +2363,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, return -ERANGE;
drm_modeset_lock_all(dev);
obj = drm_mode_object_find(dev, crtc_req->crtc_id,
DRM_MODE_OBJECT_CRTC);
if (!obj) {
crtc = drm_crtc_find(dev, crtc_req->crtc_id);
if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); ret = -ENOENT; goto out; }
crtc = obj_to_crtc(obj); DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); if (crtc_req->mode_valid) {
@@ -2471,15 +2451,13 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; }
obj = drm_mode_object_find(dev, out_id,
DRM_MODE_OBJECT_CONNECTOR);
if (!obj) {
connector = drm_connector_find(dev, out_id);
if (!connector) { DRM_DEBUG_KMS("Connector id %d unknown\n", out_id); ret = -ENOENT; goto out; }
connector = obj_to_connector(obj); DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, drm_get_connector_name(connector));
@@ -2511,7 +2489,6 @@ static int drm_mode_cursor_common(struct drm_device *dev, struct drm_mode_cursor2 *req, struct drm_file *file_priv) {
struct drm_mode_object *obj; struct drm_crtc *crtc; int ret = 0;
@@ -2521,12 +2498,11 @@ static int drm_mode_cursor_common(struct drm_device *dev, if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL;
obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
if (!obj) {
crtc = drm_crtc_find(dev, req->crtc_id);
if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); return -ENOENT; }
crtc = obj_to_crtc(obj); drm_modeset_lock(&crtc->mutex, NULL); if (req->flags & DRM_MODE_CURSOR_BO) {
@@ -3522,7 +3498,6 @@ EXPORT_SYMBOL(drm_object_property_get_value); int drm_mode_getproperty_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) {
struct drm_mode_object *obj; struct drm_mode_get_property *out_resp = data; struct drm_property *property; int enum_count = 0;
@@ -3541,12 +3516,11 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, return -EINVAL;
drm_modeset_lock_all(dev);
obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
if (!obj) {
property = drm_property_find(dev, out_resp->prop_id);
if (!property) { ret = -ENOENT; goto done; }
property = obj_to_property(obj); if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
@@ -3676,7 +3650,6 @@ static void drm_property_destroy_blob(struct drm_device *dev, int drm_mode_getblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) {
struct drm_mode_object *obj; struct drm_mode_get_blob *out_resp = data; struct drm_property_blob *blob; int ret = 0;
@@ -3686,12 +3659,11 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, return -EINVAL;
drm_modeset_lock_all(dev);
obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
if (!obj) {
blob = drm_property_blob_find(dev, out_resp->blob_id);
if (!blob) { ret = -ENOENT; goto done; }
blob = obj_to_blob(obj); if (out_resp->length == blob->length) { blob_ptr = (void __user *)(unsigned long)out_resp->data;
@@ -3898,7 +3870,6 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, uint32_t prop_id, uint64_t value, void *blob_data) { struct drm_mode_object *arg_obj;
struct drm_mode_object *prop_obj; struct drm_property *property; int i;
@@ -3915,11 +3886,9 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, if (i == arg_obj->properties->count) return -EINVAL;
prop_obj = drm_mode_object_find(dev, prop_id,
DRM_MODE_OBJECT_PROPERTY);
if (!prop_obj)
property = drm_property_find(dev, prop_id);
if (!property) return -ENOENT;
property = obj_to_property(prop_obj); return drm_mode_set_obj_prop(arg_obj, state, property, value, blob_data);
@@ -4126,7 +4095,6 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_lut *crtc_lut = data;
struct drm_mode_object *obj; struct drm_crtc *crtc; void *r_base, *g_base, *b_base; int size;
@@ -4136,12 +4104,11 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, return -EINVAL;
drm_modeset_lock_all(dev);
obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
if (!obj) {
crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
if (!crtc) { ret = -ENOENT; goto out; }
crtc = obj_to_crtc(obj); if (crtc->funcs->gamma_set == NULL) { ret = -ENOSYS;
@@ -4200,7 +4167,6 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_lut *crtc_lut = data;
struct drm_mode_object *obj; struct drm_crtc *crtc; void *r_base, *g_base, *b_base; int size;
@@ -4210,12 +4176,11 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, return -EINVAL;
drm_modeset_lock_all(dev);
obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
if (!obj) {
crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
if (!crtc) { ret = -ENOENT; goto out; }
crtc = obj_to_crtc(obj); /* memcpy into gamma store */ if (crtc_lut->gamma_size != crtc->gamma_size) {
@@ -4268,7 +4233,6 @@ 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_object *obj; struct drm_crtc *crtc; struct drm_framebuffer *fb = NULL, *old_fb = NULL; struct drm_pending_vblank_event *e = NULL;
@@ -4282,10 +4246,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip) return -EINVAL;
obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
if (!obj)
crtc = drm_crtc_find(dev, page_flip->crtc_id);
if (!crtc) return -ENOENT;
crtc = obj_to_crtc(obj); drm_modeset_lock(&crtc->mutex, NULL); if (crtc->primary->fb == NULL) {
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index cd4a61a..b940a29 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1112,6 +1112,15 @@ extern int drm_format_vert_chroma_subsampling(uint32_t format); extern const char *drm_get_format_name(uint32_t format);
/* Helpers */
+static inline struct drm_plane *drm_plane_find(struct drm_device *dev,
uint32_t id)
+{
struct drm_mode_object *mo;
mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PLANE);
return mo ? obj_to_plane(mo) : NULL;
+}
static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev, uint32_t id) { @@ -1128,6 +1137,30 @@ static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev, return mo ? obj_to_encoder(mo) : NULL; }
+static inline struct drm_connector *drm_connector_find(struct drm_device *dev,
uint32_t id)
+{
struct drm_mode_object *mo;
mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CONNECTOR);
return mo ? obj_to_connector(mo) : NULL;
+}
+static inline struct drm_property *drm_property_find(struct drm_device *dev,
uint32_t id)
+{
struct drm_mode_object *mo;
mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PROPERTY);
return mo ? obj_to_property(mo) : NULL;
+}
+static inline struct drm_property_blob * +drm_property_blob_find(struct drm_device *dev, uint32_t id) +{
struct drm_mode_object *mo;
mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB);
return mo ? obj_to_blob(mo) : NULL;
+}
/* Plane list iterator for legacy (overlay only) planes. */ #define drm_for_each_legacy_plane(plane, planelist) \ list_for_each_entry(plane, planelist, head) \ -- 1.9.0
-- Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
Split property values out into a different struct, so we can later move property values into state structs. This will allow the property values to stay in sync w/ the state updates which are either discarded or atomically committed.
And since we are touching all the same code, add support for mutable blob properties, which will also be needed for atomic modeset.
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/drm_crtc.c | 79 +++++++++++++++++++++-------- drivers/gpu/drm/drm_fb_helper.c | 3 +- drivers/gpu/drm/gma500/cdv_intel_dp.c | 3 +- drivers/gpu/drm/gma500/cdv_intel_hdmi.c | 3 +- drivers/gpu/drm/gma500/cdv_intel_lvds.c | 6 ++- drivers/gpu/drm/gma500/mdfld_dsi_output.c | 8 +-- drivers/gpu/drm/gma500/psb_intel_lvds.c | 6 ++- drivers/gpu/drm/gma500/psb_intel_sdvo.c | 19 +++++-- drivers/gpu/drm/i2c/ch7006_drv.c | 4 +- drivers/gpu/drm/i915/intel_display.c | 3 +- drivers/gpu/drm/i915/intel_dp.c | 3 +- drivers/gpu/drm/i915/intel_hdmi.c | 3 +- drivers/gpu/drm/i915/intel_sdvo.c | 19 +++++-- drivers/gpu/drm/i915/intel_tv.c | 6 ++- drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 3 +- drivers/gpu/drm/nouveau/nouveau_connector.c | 4 +- drivers/gpu/drm/omapdrm/omap_drv.c | 6 ++- drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c | 3 +- drivers/gpu/drm/rcar-du/rcar_du_vgacon.c | 3 +- drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 4 +- include/drm/drm_crtc.h | 11 +++- 21 files changed, 142 insertions(+), 57 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index bd12185..e01c286 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -123,6 +123,14 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) } EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
+static int drm_mode_set_obj_prop(struct drm_mode_object *obj, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data); +static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, + int length, void *data); +static void drm_property_destroy_blob(struct drm_device *dev, + struct drm_property_blob *blob); + /* Avoid boilerplate. I'm tired of typing. */ #define DRM_ENUM_NAME_FN(fnname, list) \ const char *fnname(int val) \ @@ -755,6 +763,7 @@ 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;
list_add_tail(&crtc->head, &dev->mode_config.crtc_list); dev->mode_config.num_crtc++; @@ -859,6 +868,7 @@ int drm_connector_init(struct drm_device *dev, goto out;
connector->base.properties = &connector->properties; + connector->base.propvals = &connector->propvals; connector->dev = dev; connector->funcs = funcs; connector->connector_type = connector_type; @@ -1077,6 +1087,7 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, goto out;
plane->base.properties = &plane->properties; + plane->base.propvals = &plane->propvals; plane->dev = dev; plane->funcs = funcs; plane->format_types = kmalloc(sizeof(uint32_t) * format_count, @@ -1894,7 +1905,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, goto out; }
- if (put_user(connector->properties.values[i], + if (put_user(connector->propvals.values[i], prop_values + copied)) { ret = -EFAULT; goto out; @@ -3418,7 +3429,7 @@ void drm_object_attach_property(struct drm_mode_object *obj, }
obj->properties->ids[count] = property->base.id; - obj->properties->values[count] = init_val; + obj->propvals->values[count] = init_val; obj->properties->count++; } EXPORT_SYMBOL(drm_object_attach_property); @@ -3437,13 +3448,27 @@ EXPORT_SYMBOL(drm_object_attach_property); * Zero on success, error code on failure. */ int drm_object_property_set_value(struct drm_mode_object *obj, - struct drm_property *property, uint64_t val) + struct drm_object_property_values *propvals, + struct drm_property *property, uint64_t val, + void *blob_data) { int i;
for (i = 0; i < obj->properties->count; i++) { if (obj->properties->ids[i] == property->base.id) { - obj->properties->values[i] = val; + struct drm_device *dev = property->dev; + if (property->flags & DRM_MODE_PROP_BLOB) { + struct drm_property_blob *blob, *old_blob = NULL; + old_blob = drm_property_blob_find(dev, propvals->values[i]); + blob = drm_property_create_blob(dev, val, blob_data); + if (!blob) + return -ENOMEM; + propvals->values[i] = blob->base.id; + if (old_blob) + drm_property_destroy_blob(dev, old_blob); + } else { + propvals->values[i] = val; + } return 0; } } @@ -3473,7 +3498,7 @@ int drm_object_property_get_value(struct drm_mode_object *obj,
for (i = 0; i < obj->properties->count; i++) { if (obj->properties->ids[i] == property->base.id) { - *val = obj->properties->values[i]; + *val = obj->propvals->values[i]; return 0; } } @@ -3694,27 +3719,35 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid) { struct drm_device *dev = connector->dev; - int ret, size; + struct drm_mode_object *obj = &connector->base; + struct drm_property *edid_prop = dev->mode_config.edid_property; + int i, ret, size;
- if (connector->edid_blob_ptr) - drm_property_destroy_blob(dev, connector->edid_blob_ptr); + /* property_blob will be recreated by drm_object_property_set_value(): */ + connector->edid_blob_ptr = NULL;
/* Delete edid, when there is none. */ if (!edid) { - connector->edid_blob_ptr = NULL; - ret = drm_object_property_set_value(&connector->base, dev->mode_config.edid_property, 0); + ret = drm_object_property_set_value(obj, + &connector->propvals, edid_prop, 0, NULL); return ret; }
size = EDID_LENGTH * (1 + edid->extensions); - connector->edid_blob_ptr = drm_property_create_blob(connector->dev, - size, edid); - if (!connector->edid_blob_ptr) - return -EINVAL;
- ret = drm_object_property_set_value(&connector->base, - dev->mode_config.edid_property, - connector->edid_blob_ptr->base.id); + ret = drm_object_property_set_value(obj, + &connector->propvals, edid_prop, size, edid); + if (ret) + return ret; + + /* find the blob object created for us by drm_object_property_set_value(): */ + for (i = 0; i < obj->properties->count; i++) { + if (obj->properties->ids[i] == edid_prop->base.id) { + connector->edid_blob_ptr = drm_property_blob_find(dev, + connector->propvals.values[i]); + break; + } + }
return ret; } @@ -3806,7 +3839,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
/* store the property value if successful */ if (!ret) - drm_object_property_set_value(&connector->base, property, value); + drm_object_property_set_value(&connector->base, + &connector->propvals, property, value, blob_data); + return ret; }
@@ -3820,7 +3855,8 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, ret = crtc->funcs->set_property(crtc, state, property, value, blob_data); if (!ret) - drm_object_property_set_value(&crtc->base, property, value); + drm_object_property_set_value(&crtc->base, &crtc->propvals, + property, value, NULL);
return ret; } @@ -3835,7 +3871,8 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, ret = plane->funcs->set_property(plane, state, property, value, blob_data); if (!ret) - drm_object_property_set_value(&plane->base, property, value); + drm_object_property_set_value(&plane->base, &plane->propvals, + property, value, NULL);
return ret; } @@ -3951,7 +3988,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, ret = -EFAULT; goto out; } - if (put_user(obj->properties->values[i], + if (put_user(obj->propvals->values[i], prop_values_ptr + copied)) { ret = -EFAULT; goto out; diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 142f6bd..97b0d84 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -451,7 +451,8 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) connector = fb_helper->connector_info[j]->connector; connector->funcs->dpms(connector, dpms_mode); drm_object_property_set_value(&connector->base, - dev->mode_config.dpms_property, dpms_mode); + &connector->propvals, + dev->mode_config.dpms_property, dpms_mode, NULL); } } drm_modeset_unlock_all(dev); diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 31d872d..69160d9 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -1655,7 +1655,8 @@ cdv_intel_dp_set_property(struct drm_connector *connector, struct cdv_intel_dp *intel_dp = encoder->dev_priv; int ret;
- ret = drm_object_property_set_value(&connector->base, property, val); + ret = drm_object_property_set_value(&connector->base, + &connector->propvals, property, val, blob_data); if (ret) return ret;
diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index f0e4d78..f71e513 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c @@ -184,7 +184,8 @@ static int cdv_hdmi_set_property(struct drm_connector *connector, return 0;
if (drm_object_property_set_value(&connector->base, - property, value)) + &connector->propvals, + property, value, blob_data)) return -1;
centre = (curValue == DRM_MODE_SCALE_NO_SCALE) || diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index f9ee300..76f7508 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -486,8 +486,9 @@ static int cdv_intel_lvds_set_property(struct drm_connector *connector, return 0;
if (drm_object_property_set_value(&connector->base, + &connector->propvals, property, - value)) + value, blob_data)) return -1;
if (crtc->saved_mode.hdisplay != 0 && @@ -501,8 +502,9 @@ static int cdv_intel_lvds_set_property(struct drm_connector *connector, } } else if (!strcmp(property->name, "backlight") && encoder) { if (drm_object_property_set_value(&connector->base, + &connector->propvals, property, - value)) + value, blob_data)) return -1; else gma_backlight_set(encoder->dev, value); diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c index 2318a46..17f2c19 100644 --- a/drivers/gpu/drm/gma500/mdfld_dsi_output.c +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c @@ -276,7 +276,8 @@ static int mdfld_dsi_connector_set_property(struct drm_connector *connector, goto set_prop_done;
if (drm_object_property_set_value(&connector->base, - property, value)) + &connector->propvals, + property, value, blob_data)) goto set_prop_error;
centerechange = (val == DRM_MODE_SCALE_NO_SCALE) || @@ -300,8 +301,9 @@ static int mdfld_dsi_connector_set_property(struct drm_connector *connector, } } } else if (!strcmp(property->name, "backlight") && encoder) { - if (drm_object_property_set_value(&connector->base, property, - value)) + if (drm_object_property_set_value(&connector->base, + &connector->propvals, + property, value, blob_data)) goto set_prop_error; else gma_backlight_set(encoder->dev, value); diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index 3442e44..d1a3a0c 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -606,8 +606,9 @@ int psb_intel_lvds_set_property(struct drm_connector *connector, goto set_prop_done;
if (drm_object_property_set_value(&connector->base, + &connector->propvals, property, - value)) + value, blob_data)) goto set_prop_error;
if (crtc->saved_mode.hdisplay != 0 && @@ -621,8 +622,9 @@ int psb_intel_lvds_set_property(struct drm_connector *connector, } } else if (!strcmp(property->name, "backlight")) { if (drm_object_property_set_value(&connector->base, + &connector->propvals, property, - value)) + value, blob_data)) goto set_prop_error; else gma_backlight_set(encoder->dev, value); diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index 724b525..8191309 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -1717,7 +1717,8 @@ psb_intel_sdvo_set_property(struct drm_connector *connector, uint8_t cmd; int ret;
- ret = drm_object_property_set_value(&connector->base, property, val); + ret = drm_object_property_set_value(&connector->base, + &connector->propvals, property, val, blob_data); if (ret) return ret;
@@ -1773,7 +1774,9 @@ psb_intel_sdvo_set_property(struct drm_connector *connector, temp_value = val; if (psb_intel_sdvo_connector->left == property) { drm_object_property_set_value(&connector->base, - psb_intel_sdvo_connector->right, val); + &connector->propvals, + psb_intel_sdvo_connector->right, + val, blob_data); if (psb_intel_sdvo_connector->left_margin == temp_value) return 0;
@@ -1785,7 +1788,9 @@ psb_intel_sdvo_set_property(struct drm_connector *connector, goto set_value; } else if (psb_intel_sdvo_connector->right == property) { drm_object_property_set_value(&connector->base, - psb_intel_sdvo_connector->left, val); + &connector->propvals, + psb_intel_sdvo_connector->left, + val, blob_data); if (psb_intel_sdvo_connector->right_margin == temp_value) return 0;
@@ -1797,7 +1802,9 @@ psb_intel_sdvo_set_property(struct drm_connector *connector, goto set_value; } else if (psb_intel_sdvo_connector->top == property) { drm_object_property_set_value(&connector->base, - psb_intel_sdvo_connector->bottom, val); + &connector->propvals, + psb_intel_sdvo_connector->bottom, + val, blob_data); if (psb_intel_sdvo_connector->top_margin == temp_value) return 0;
@@ -1809,7 +1816,9 @@ psb_intel_sdvo_set_property(struct drm_connector *connector, goto set_value; } else if (psb_intel_sdvo_connector->bottom == property) { drm_object_property_set_value(&connector->base, - psb_intel_sdvo_connector->top, val); + &connector->propvals, + psb_intel_sdvo_connector->top, + val, blob_data); if (psb_intel_sdvo_connector->bottom_margin == temp_value) return 0;
diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c index 51fa323..a89c205 100644 --- a/drivers/gpu/drm/i2c/ch7006_drv.c +++ b/drivers/gpu/drm/i2c/ch7006_drv.c @@ -214,9 +214,9 @@ static enum drm_connector_status ch7006_encoder_detect(struct drm_encoder *encod else priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
- drm_object_property_set_value(&connector->base, + drm_object_property_set_value(&connector->base, &connector->propvals, encoder->dev->mode_config.tv_subconnector_property, - priv->subconnector); + priv->subconnector, NULL);
return priv->subconnector ? connector_status_connected : connector_status_disconnected; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 562e575..e9f6eb7 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9494,8 +9494,9 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes)
connector->dpms = DRM_MODE_DPMS_ON; drm_object_property_set_value(&connector->base, + &connector->propvals, dpms_property, - DRM_MODE_DPMS_ON); + DRM_MODE_DPMS_ON, NULL);
intel_encoder = to_intel_encoder(connector->encoder); intel_encoder->connectors_active = true; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index e4c0b10..50f2260 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3281,7 +3281,8 @@ intel_dp_set_property(struct drm_connector *connector, struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base); int ret;
- ret = drm_object_property_set_value(&connector->base, property, val); + ret = drm_object_property_set_value(&connector->base, + &connector->propvals, property, val, blob_data); if (ret) return ret;
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 8e160b0..8c8b7d3 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1055,7 +1055,8 @@ intel_hdmi_set_property(struct drm_connector *connector, struct drm_i915_private *dev_priv = connector->dev->dev_private; int ret;
- ret = drm_object_property_set_value(&connector->base, property, val); + ret = drm_object_property_set_value(&connector->base, + &connector->propvals, property, val, blob_data); if (ret) return ret;
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 4e36ee99..83bec46 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2064,7 +2064,8 @@ intel_sdvo_set_property(struct drm_connector *connector, uint8_t cmd; int ret;
- ret = drm_object_property_set_value(&connector->base, property, val); + ret = drm_object_property_set_value(&connector->base, + &connector->propvals, property, val, blob_data); if (ret) return ret;
@@ -2141,7 +2142,9 @@ intel_sdvo_set_property(struct drm_connector *connector, temp_value = val; if (intel_sdvo_connector->left == property) { drm_object_property_set_value(&connector->base, - intel_sdvo_connector->right, val); + &connector->propvals, + intel_sdvo_connector->right, + val, blob_data); if (intel_sdvo_connector->left_margin == temp_value) return 0;
@@ -2153,7 +2156,9 @@ intel_sdvo_set_property(struct drm_connector *connector, goto set_value; } else if (intel_sdvo_connector->right == property) { drm_object_property_set_value(&connector->base, - intel_sdvo_connector->left, val); + &connector->propvals, + intel_sdvo_connector->left, + val, blob_data); if (intel_sdvo_connector->right_margin == temp_value) return 0;
@@ -2165,7 +2170,9 @@ intel_sdvo_set_property(struct drm_connector *connector, goto set_value; } else if (intel_sdvo_connector->top == property) { drm_object_property_set_value(&connector->base, - intel_sdvo_connector->bottom, val); + &connector->propvals, + intel_sdvo_connector->bottom, + val, blob_data); if (intel_sdvo_connector->top_margin == temp_value) return 0;
@@ -2177,7 +2184,9 @@ intel_sdvo_set_property(struct drm_connector *connector, goto set_value; } else if (intel_sdvo_connector->bottom == property) { drm_object_property_set_value(&connector->base, - intel_sdvo_connector->top, val); + &connector->propvals, + intel_sdvo_connector->top, + val, blob_data); if (intel_sdvo_connector->bottom_margin == temp_value) return 0;
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 05ee0d5..f7a7bf4 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1297,7 +1297,8 @@ static void intel_tv_find_better_format(struct drm_connector *connector)
intel_tv->tv_format = tv_mode->name; drm_object_property_set_value(&connector->base, - connector->dev->mode_config.tv_mode_property, i); + &connector->propvals, + connector->dev->mode_config.tv_mode_property, i, NULL); }
/** @@ -1455,7 +1456,8 @@ intel_tv_set_property(struct drm_connector *connector, int ret = 0; bool changed = false;
- ret = drm_object_property_set_value(&connector->base, property, val); + ret = drm_object_property_set_value(&connector->base, + &connector->propvals, property, val, blob_data); if (ret < 0) goto out;
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c index acef48f..64c60fcb 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c +++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c @@ -196,8 +196,9 @@ nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector) }
drm_object_property_set_value(&connector->base, + &connector->propvals, conf->tv_subconnector_property, - tv_enc->subconnector); + tv_enc->subconnector, NULL);
if (!reliable) { return connector_status_unknown; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 3351e72..8948c66 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -227,10 +227,12 @@ nouveau_connector_set_encoder(struct drm_connector *connector,
if (nv_connector->type == DCB_CONNECTOR_DVI_I) { drm_object_property_set_value(&connector->base, + &connector->propvals, dev->mode_config.dvi_i_subconnector_property, nv_encoder->dcb->type == DCB_OUTPUT_TMDS ? DRM_MODE_SUBCONNECTOR_DVID : - DRM_MODE_SUBCONNECTOR_DVIA); + DRM_MODE_SUBCONNECTOR_DVIA, + NULL); } }
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index d3c9161..3dca538 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -579,12 +579,14 @@ static void dev_lastclose(struct drm_device *dev) */ for (i = 0; i < priv->num_crtcs; i++) { drm_object_property_set_value(&priv->crtcs[i]->base, - priv->rotation_prop, 0); + &priv->crtcs[i]->propvals, + priv->rotation_prop, 0, NULL); }
for (i = 0; i < priv->num_planes; i++) { drm_object_property_set_value(&priv->planes[i]->base, - priv->rotation_prop, 0); + &priv->planes[i]->propvals, + priv->rotation_prop, 0, NULL); } }
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index 289048d..503bac9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -111,7 +111,8 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); drm_object_property_set_value(&connector->base, - rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); + &connector->propvals, + rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF, NULL);
ret = drm_mode_connector_attach_encoder(connector, &renc->encoder); if (ret < 0) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index ccfe64c..c2d0054 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -76,7 +76,8 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); drm_object_property_set_value(&connector->base, - rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); + &connector->propvals, + rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF, NULL);
ret = drm_mode_connector_attach_encoder(connector, &renc->encoder); if (ret < 0) diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index faf176b..90e023a 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -741,8 +741,8 @@ int shmob_drm_connector_create(struct shmob_drm_device *sdev, connector->encoder = encoder;
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); - drm_object_property_set_value(&connector->base, - sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); + drm_object_property_set_value(&connector->base, &connector->propvals, + sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF, NULL);
return 0;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index b940a29..9caf434 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -41,6 +41,7 @@ struct drm_framebuffer; struct drm_object_properties; struct drm_file; struct drm_clip_rect; +struct drm_object_property_values;
#define DRM_MODE_OBJECT_CRTC 0xcccccccc #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 @@ -56,12 +57,16 @@ struct drm_mode_object { uint32_t id; uint32_t type; struct drm_object_properties *properties; + struct drm_object_property_values *propvals; };
#define DRM_OBJECT_MAX_PROPERTY 24 struct drm_object_properties { int count; uint32_t ids[DRM_OBJECT_MAX_PROPERTY]; +}; + +struct drm_object_property_values { uint64_t values[DRM_OBJECT_MAX_PROPERTY]; };
@@ -361,6 +366,7 @@ struct drm_crtc { void *helper_private;
struct drm_object_properties properties; + struct drm_object_property_values propvals; };
@@ -520,6 +526,7 @@ struct drm_connector {
struct drm_property_blob *edid_blob_ptr; struct drm_object_properties properties; + struct drm_object_property_values propvals;
uint8_t polled; /* DRM_CONNECTOR_POLL_* */
@@ -603,6 +610,7 @@ struct drm_plane { const struct drm_plane_funcs *funcs;
struct drm_object_properties properties; + struct drm_object_property_values propvals;
enum drm_plane_type type; }; @@ -969,8 +977,9 @@ static inline bool drm_property_type_valid(struct drm_property *property) }
extern int drm_object_property_set_value(struct drm_mode_object *obj, + struct drm_object_property_values *propvals, struct drm_property *property, - uint64_t val); + uint64_t val, void *blob_data); extern int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *value);
From: Ville Syrjälä ville.syrjala@linux.intel.com
To avoid having to pass object types from userspace for atomic mode setting ioctl, allow drm_mode_object_find() to look up an object of any type. This will only work as long as the all object types share the ID space.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/drm_crtc.c | 3 ++- include/drm/drm_crtc.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index e01c286..511d33e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -463,7 +463,8 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
mutex_lock(&dev->mode_config.idr_mutex); obj = idr_find(&dev->mode_config.crtc_idr, id); - if (!obj || (obj->type != type) || (obj->id != id)) + if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) || + (obj->id != id)) obj = NULL; mutex_unlock(&dev->mode_config.idr_mutex);
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 9caf434..31ed7c6 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -52,6 +52,7 @@ struct drm_object_property_values; #define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb #define DRM_MODE_OBJECT_PLANE 0xeeeeeeee #define DRM_MODE_OBJECT_BRIDGE 0xbdbdbdbd +#define DRM_MODE_OBJECT_ANY 0
struct drm_mode_object { uint32_t id;
From: Ville Syrjälä ville.syrjala@linux.intel.com
Refactor the code to check whether an object has a specific property to a new function.
v1: original v2: rebase on atomic -- Rob Clark v3: EINVAL->ENOENT
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/drm_crtc.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 511d33e..9b95601 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3901,6 +3901,19 @@ static int drm_mode_set_obj_prop(struct drm_mode_object *obj, return -EINVAL; }
+static bool object_has_prop(const struct drm_mode_object *obj, u32 prop_id) +{ + int i; + + if (!obj->properties) + return false; + + for (i = 0; i < obj->properties->count; i++) + if (obj->properties->ids[i] == prop_id) + return true; + return false; +} + /* call with mode_config mutex held */ static int drm_mode_set_obj_prop_id(struct drm_device *dev, struct drm_atomic_state *state, @@ -3909,20 +3922,10 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, { struct drm_mode_object *arg_obj; struct drm_property *property; - int i;
arg_obj = drm_mode_object_find(dev, obj_id, obj_type); - if (!arg_obj) + if (!(arg_obj && object_has_prop(arg_obj, prop_id))) return -ENOENT; - if (!arg_obj->properties) - return -EINVAL; - - for (i = 0; i < arg_obj->properties->count; i++) - if (arg_obj->properties->ids[i] == prop_id) - break; - - if (i == arg_obj->properties->count) - return -EINVAL;
property = drm_property_find(dev, prop_id); if (!property)
We do actually want to permit FB's in atomic case, since FB will be looked up like any other object property value in a few code paths (like property value validation).
So split out into an internal function without the WARN_ON() which we can use in those special cases.
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/drm_crtc.c | 33 ++++++++++++++++++++++----------- include/drm/drm_crtc.h | 6 ------ 2 files changed, 22 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 9b95601..48555724 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -443,6 +443,21 @@ void drm_mode_object_put(struct drm_device *dev, mutex_unlock(&dev->mode_config.idr_mutex); }
+static struct drm_mode_object *_object_find(struct drm_device *dev, + uint32_t id, uint32_t type) +{ + struct drm_mode_object *obj = NULL; + + mutex_lock(&dev->mode_config.idr_mutex); + obj = idr_find(&dev->mode_config.crtc_idr, id); + if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) || + (obj->id != id)) + obj = NULL; + mutex_unlock(&dev->mode_config.idr_mutex); + + return obj; +} + /** * drm_mode_object_find - look up a drm object with static lifetime * @dev: drm device @@ -455,23 +470,19 @@ void drm_mode_object_put(struct drm_device *dev, struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type) { - struct drm_mode_object *obj = NULL; - /* Framebuffers are reference counted and need their own lookup * function.*/ WARN_ON(type == DRM_MODE_OBJECT_FB); - - mutex_lock(&dev->mode_config.idr_mutex); - obj = idr_find(&dev->mode_config.crtc_idr, id); - if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) || - (obj->id != id)) - obj = NULL; - mutex_unlock(&dev->mode_config.idr_mutex); - - return obj; + return _object_find(dev, id, type); } EXPORT_SYMBOL(drm_mode_object_find);
+static struct drm_mode_object * +drm_property_get_obj(struct drm_property *property, uint64_t value) +{ + return _object_find(property->dev, value, property->values[0]); +} + /** * drm_framebuffer_init - initialize a framebuffer * @dev: DRM device diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 31ed7c6..547b75a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1033,12 +1033,6 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type);
-static inline struct drm_mode_object * -drm_property_get_obj(struct drm_property *property, uint64_t value) -{ - return drm_mode_object_find(property->dev, value, property->values[0]); -} - /* IOCTLs */ extern int drm_mode_getresources(struct drm_device *dev, void *data, struct drm_file *file_priv);
On Sat, May 24, 2014 at 02:30:19PM -0400, Rob Clark wrote:
We do actually want to permit FB's in atomic case, since FB will be looked up like any other object property value in a few code paths (like property value validation).
So split out into an internal function without the WARN_ON() which we can use in those special cases.
Signed-off-by: Rob Clark robdclark@gmail.com
See my other mail, but Nack. Imo you want a drm_mode_object_exists instead, which just returns a bool. Gives you want you want and is safe. -Daniel
drivers/gpu/drm/drm_crtc.c | 33 ++++++++++++++++++++++----------- include/drm/drm_crtc.h | 6 ------ 2 files changed, 22 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 9b95601..48555724 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -443,6 +443,21 @@ void drm_mode_object_put(struct drm_device *dev, mutex_unlock(&dev->mode_config.idr_mutex); }
+static struct drm_mode_object *_object_find(struct drm_device *dev,
uint32_t id, uint32_t type)
+{
- struct drm_mode_object *obj = NULL;
- mutex_lock(&dev->mode_config.idr_mutex);
- obj = idr_find(&dev->mode_config.crtc_idr, id);
- if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
(obj->id != id))
obj = NULL;
- mutex_unlock(&dev->mode_config.idr_mutex);
- return obj;
+}
/**
- drm_mode_object_find - look up a drm object with static lifetime
- @dev: drm device
@@ -455,23 +470,19 @@ void drm_mode_object_put(struct drm_device *dev, struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type) {
- struct drm_mode_object *obj = NULL;
- /* Framebuffers are reference counted and need their own lookup
WARN_ON(type == DRM_MODE_OBJECT_FB);
- function.*/
- mutex_lock(&dev->mode_config.idr_mutex);
- obj = idr_find(&dev->mode_config.crtc_idr, id);
- if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
(obj->id != id))
obj = NULL;
- mutex_unlock(&dev->mode_config.idr_mutex);
- return obj;
- return _object_find(dev, id, type);
} EXPORT_SYMBOL(drm_mode_object_find);
+static struct drm_mode_object * +drm_property_get_obj(struct drm_property *property, uint64_t value) +{
- return _object_find(property->dev, value, property->values[0]);
+}
/**
- drm_framebuffer_init - initialize a framebuffer
- @dev: DRM device
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 31ed7c6..547b75a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1033,12 +1033,6 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type);
-static inline struct drm_mode_object * -drm_property_get_obj(struct drm_property *property, uint64_t value) -{
- return drm_mode_object_find(property->dev, value, property->values[0]);
-}
/* IOCTLs */ extern int drm_mode_getresources(struct drm_device *dev, void *data, struct drm_file *file_priv); -- 1.9.0
Break the mutable state of a plane out into a separate structure and use atomic properties mechanism to set plane attributes. This makes it easier to have some helpers for plane->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.
The same should be done for CRTC, encoder, and connector, but this patch only includes the first part (plane).
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/armada/armada_overlay.c | 11 +- drivers/gpu/drm/drm_atomic.c | 225 ++++++++++++++- drivers/gpu/drm/drm_crtc.c | 433 ++++++++++++++++++++--------- drivers/gpu/drm/drm_fb_helper.c | 17 +- drivers/gpu/drm/drm_plane_helper.c | 2 + drivers/gpu/drm/exynos/exynos_drm_plane.c | 7 +- drivers/gpu/drm/i915/intel_sprite.c | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c | 6 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 6 +- drivers/gpu/drm/nouveau/dispnv04/overlay.c | 8 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/omapdrm/omap_plane.c | 7 + drivers/gpu/drm/rcar-du/rcar_du_plane.c | 8 +- drivers/gpu/drm/shmobile/shmob_drm_plane.c | 2 + include/drm/drm_atomic.h | 29 +- include/drm/drm_crtc.h | 114 +++++++- 16 files changed, 725 insertions(+), 153 deletions(-)
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c index 601ba9a..041ea89 100644 --- a/drivers/gpu/drm/armada/armada_overlay.c +++ b/drivers/gpu/drm/armada/armada_overlay.c @@ -7,6 +7,7 @@ * published by the Free Software Foundation. */ #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include "armada_crtc.h" #include "armada_drm.h" #include "armada_fb.h" @@ -288,7 +289,12 @@ static int armada_plane_set_property(struct drm_plane *plane, { struct armada_private *priv = plane->dev->dev_private; struct armada_plane *dplane = drm_to_armada_plane(plane); + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); bool update_attr = false; + int ret = 0; + + if (IS_ERR(pstate)) + return PTR_ERR(pstate);
if (property == priv->colorkey_prop) { #define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8) @@ -342,13 +348,16 @@ static int armada_plane_set_property(struct drm_plane *plane, } else if (property == priv->saturation_prop) { dplane->prop.saturation = val; update_attr = true; + } else { + ret = drm_plane_set_property(plane, pstate, property, + val, blob_data); }
if (update_attr && dplane->base.crtc) armada_ovl_update_attr(&dplane->prop, drm_to_armada_crtc(dplane->base.crtc));
- return 0; + return ret; }
static const struct drm_plane_funcs armada_plane_funcs = { diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 45df5e5..403ffc5 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -24,6 +24,7 @@
#include <drm/drmP.h> #include <drm/drm_atomic.h> +#include <drm/drm_plane_helper.h>
/** * drm_atomic_begin - start a sequence of atomic updates @@ -46,10 +47,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, uint32_t flags) { struct drm_atomic_state *state; + int nplanes = dev->mode_config.num_total_plane; int sz; void *ptr;
sz = sizeof(*state); + sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
ptr = kzalloc(sz, GFP_KERNEL);
@@ -65,6 +68,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, state->dev = dev; state->flags = flags;
+ state->planes = ptr; + ptr = &state->planes[nplanes]; + + state->pstates = ptr; + ptr = &state->pstates[nplanes]; + return state; } EXPORT_SYMBOL(drm_atomic_begin); @@ -101,8 +110,20 @@ EXPORT_SYMBOL(drm_atomic_set_event); int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_atomic_state *a = state; + int nplanes = dev->mode_config.num_total_plane; + int i, ret = 0; + + for (i = 0; i < nplanes; i++) { + if (a->planes[i]) { + ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]); + if (ret) + break; + } + } + a->acquire_ctx.frozen = true; - return 0; /* for now */ + + return ret; } EXPORT_SYMBOL(drm_atomic_check);
@@ -180,6 +201,18 @@ fail: static void commit_locks(struct drm_atomic_state *a, struct ww_acquire_ctx *ww_ctx) { + struct drm_device *dev = a->dev; + int nplanes = dev->mode_config.num_total_plane; + int i; + + for (i = 0; i < nplanes; i++) { + struct drm_plane *plane = a->planes[i]; + if (plane) { + plane->state->state = NULL; + drm_plane_destroy_state(plane, a->pstates[i]); + } + } + /* and properly release them (clear in_atomic, remove from list): */ drm_modeset_drop_locks(&a->acquire_ctx); ww_acquire_fini(ww_ctx); @@ -189,7 +222,17 @@ static void commit_locks(struct drm_atomic_state *a, static int atomic_commit(struct drm_atomic_state *a, struct ww_acquire_ctx *ww_ctx) { - int ret = 0; + int nplanes = a->dev->mode_config.num_total_plane; + int i, ret = 0; + + for (i = 0; i < nplanes; i++) { + struct drm_plane *plane = a->planes[i]; + if (plane) { + ret = drm_atomic_commit_plane_state(plane, a->pstates[i]); + if (ret) + break; + } + }
commit_locks(a, ww_ctx);
@@ -264,7 +307,185 @@ void _drm_atomic_state_free(struct kref *kref) } EXPORT_SYMBOL(_drm_atomic_state_free);
+int drm_atomic_plane_set_property(struct drm_plane *plane, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) +{ + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); + if (IS_ERR(pstate)) + return PTR_ERR(pstate); + return drm_plane_set_property(plane, pstate, property, val, blob_data); +} +EXPORT_SYMBOL(drm_atomic_plane_set_property); + +static void init_plane_state(struct drm_plane *plane, + struct drm_plane_state *pstate, struct drm_atomic_state *state) +{ + /* snapshot current state: */ + *pstate = *plane->state; + pstate->state = state; + if (pstate->fb) + drm_framebuffer_reference(pstate->fb); +} + +struct drm_plane_state * +drm_atomic_get_plane_state(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_atomic_state *a = state; + struct drm_plane_state *pstate; + int ret; + + pstate = a->pstates[plane->id]; + + if (!pstate) { + struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx; + + /* grab lock of current crtc.. if crtc is NULL then grab all: */ + if (plane->state->crtc) + ret = drm_modeset_lock(&plane->state->crtc->mutex, ctx); + else + ret = drm_modeset_lock_all_crtcs(plane->dev, ctx); + if (ret) + return ERR_PTR(ret); + + pstate = drm_plane_create_state(plane); + if (!pstate) + return ERR_PTR(-ENOMEM); + init_plane_state(plane, pstate, state); + a->planes[plane->id] = plane; + a->pstates[plane->id] = pstate; + } + + return pstate; +} +EXPORT_SYMBOL(drm_atomic_get_plane_state); + +static void +swap_plane_state(struct drm_plane *plane, struct drm_atomic_state *a) +{ + struct drm_plane_state *pstate = a->pstates[plane->id]; + + /* clear transient state (only valid during atomic update): */ + pstate->update_plane = false; + pstate->new_fb = false; + + swap(plane->state, a->pstates[plane->id]); + plane->base.propvals = &plane->state->propvals; +} + +/* For primary plane, if the driver implements ->page_flip(), then + * we can use that. But drivers can now choose not to bother with + * implementing page_flip(). + */ +static bool can_flip(struct drm_plane *plane, struct drm_plane_state *pstate) +{ + struct drm_crtc *crtc = pstate->crtc; + return (plane == crtc->primary) && crtc->funcs->page_flip && + !pstate->update_plane; +} + +/* clear crtc/fb, ie. after disable_plane(). But takes care to keep + * the property state in sync. Once we get rid of plane->crtc/fb ptrs + * and just use state, we can get rid of this fxn: + */ +static void +reset_plane(struct drm_plane *plane, struct drm_plane_state *pstate) +{ + struct drm_mode_config *config = &plane->dev->mode_config; + drm_plane_set_property(plane, pstate, config->prop_fb_id, 0, NULL); + drm_plane_set_property(plane, pstate, config->prop_crtc_id, 0, NULL); + plane->crtc = NULL; + plane->fb = NULL; +} + +static int +commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) +{ + struct drm_atomic_state *a = pstate->state; + struct drm_framebuffer *old_fb = plane->fb; + struct drm_framebuffer *fb = pstate->fb; + bool enabled = pstate->crtc && fb; + int ret = 0; + + if (fb) + drm_framebuffer_reference(fb); + + if (!enabled) { + if ((plane->type == DRM_PLANE_TYPE_PRIMARY) && + (plane->funcs == &drm_primary_helper_funcs)) { + /* primary plane helpers don't like ->disable_plane().. + * so this hack for now until someone comes up with + * something better: + */ + ret = 0; + } else { + ret = plane->funcs->disable_plane(plane); + reset_plane(plane, pstate); + } + } else { + struct drm_crtc *crtc = pstate->crtc; + if (pstate->update_plane || + (pstate->new_fb && !can_flip(plane, pstate))) { + ret = plane->funcs->update_plane(plane, crtc, pstate->fb, + pstate->crtc_x, pstate->crtc_y, + pstate->crtc_w, pstate->crtc_h, + pstate->src_x, pstate->src_y, + pstate->src_w, pstate->src_h); + if (ret == 0) { + /* + * For page_flip(), the driver does this, but for + * update_plane() it doesn't.. hurray \o/ + */ + plane->crtc = crtc; + plane->fb = fb; + fb = NULL; /* don't unref */ + } + + } else if (pstate->new_fb) { + ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags); + if (ret == 0) { + /* + * Warn if the driver hasn't properly updated the plane->fb + * field to reflect that the new framebuffer is now used. + * Failing to do so will screw with the reference counting + * on framebuffers. + */ + WARN_ON(plane->fb != fb); + fb = NULL; /* don't unref */ + } + } else { + old_fb = NULL; + ret = 0; + } + } + + if (ret) { + /* Keep the old fb, don't unref it. */ + old_fb = NULL; + } else { + /* on success, update state and fb refcnting: */ + /* NOTE: if we ensure no driver sets plane->state->fb = NULL + * on disable, we can move this up a level and not duplicate + * nearly the same thing for both update_plane and disable_plane + * cases.. I leave it like this for now to be paranoid due to + * the slightly different ordering in the two cases in the + * original code. + */ + swap_plane_state(plane, pstate->state); + } + + + if (fb) + drm_framebuffer_unreference(fb); + if (old_fb) + drm_framebuffer_unreference(old_fb); + + return ret; +}
const struct drm_atomic_funcs drm_atomic_funcs = { + .check_plane_state = drm_plane_check_state, + .commit_plane_state = commit_plane_state, }; EXPORT_SYMBOL(drm_atomic_funcs); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 48555724..b556a31 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -712,6 +712,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) * in this manner. */ if (atomic_read(&fb->refcount.refcount) > 1) { + void *state; + + state = dev->driver->atomic_begin(dev, 0); + if (IS_ERR(state)) { + DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n"); + 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) { @@ -728,8 +740,17 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
list_for_each_entry(plane, &dev->mode_config.plane_list, head) { if (plane->fb == fb) - drm_plane_force_disable(plane); + drm_plane_force_disable(plane, state); } + + /* just disabling stuff shouldn't fail, hopefully: */ + if(dev->driver->atomic_check(dev, state)) + DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n"); + else + dev->driver->atomic_commit(dev, state); + + dev->driver->atomic_end(dev, state); + drm_modeset_unlock_all(dev); }
@@ -1090,18 +1111,23 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, const uint32_t *formats, uint32_t format_count, enum drm_plane_type type) { + struct drm_mode_config *config = &dev->mode_config; int ret;
+ /* this is now required: */ + WARN_ON(!funcs->set_property); + drm_modeset_lock_all(dev);
ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); if (ret) goto out;
+ plane->funcs = funcs; + plane->state = drm_plane_create_state(plane); plane->base.properties = &plane->properties; - plane->base.propvals = &plane->propvals; + plane->base.propvals = &plane->state->propvals; plane->dev = dev; - plane->funcs = funcs; plane->format_types = kmalloc(sizeof(uint32_t) * format_count, GFP_KERNEL); if (!plane->format_types) { @@ -1116,15 +1142,27 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, plane->possible_crtcs = possible_crtcs; plane->type = type;
- list_add_tail(&plane->head, &dev->mode_config.plane_list); - dev->mode_config.num_total_plane++; + list_add_tail(&plane->head, &config->plane_list); + plane->id = config->num_total_plane; + config->num_total_plane++; if (plane->type == DRM_PLANE_TYPE_OVERLAY) - dev->mode_config.num_overlay_plane++; + config->num_overlay_plane++;
drm_object_attach_property(&plane->base, - dev->mode_config.plane_type_property, + config->plane_type_property, plane->type);
+ drm_object_attach_property(&plane->base, config->prop_fb_id, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_id, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_x, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_y, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_w, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_h, 0); + drm_object_attach_property(&plane->base, config->prop_src_x, 0); + drm_object_attach_property(&plane->base, config->prop_src_y, 0); + drm_object_attach_property(&plane->base, config->prop_src_w, 0); + drm_object_attach_property(&plane->base, config->prop_src_h, 0); + out: drm_modeset_unlock_all(dev);
@@ -1185,10 +1223,151 @@ void drm_plane_cleanup(struct drm_plane *plane) dev->mode_config.num_total_plane--; if (plane->type == DRM_PLANE_TYPE_OVERLAY) dev->mode_config.num_overlay_plane--; + drm_plane_destroy_state(plane, plane->state); drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_plane_cleanup);
+int drm_plane_check_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + unsigned int fb_width, fb_height; + struct drm_framebuffer *fb = state->fb; + int i; + + /* disabling the plane is allowed: */ + if (!fb) + return 0; + + fb_width = fb->width << 16; + fb_height = fb->height << 16; + + /* Check whether this plane supports the fb pixel format. */ + for (i = 0; i < plane->format_count; i++) + if (fb->pixel_format == plane->format_types[i]) + break; + if (i == plane->format_count) { + DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format); + return -EINVAL; + } + + /* Make sure source coordinates are inside the fb. */ + if (state->src_w > fb_width || + state->src_x > fb_width - state->src_w || + state->src_h > fb_height || + state->src_y > fb_height - state->src_h) { + DRM_DEBUG_KMS("Invalid source coordinates " + "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", + state->src_w >> 16, + ((state->src_w & 0xffff) * 15625) >> 10, + state->src_h >> 16, + ((state->src_h & 0xffff) * 15625) >> 10, + state->src_x >> 16, + ((state->src_x & 0xffff) * 15625) >> 10, + state->src_y >> 16, + ((state->src_y & 0xffff) * 15625) >> 10); + return -ENOSPC; + } + + /* Give drivers some help against integer overflows */ + if (state->crtc_w > INT_MAX || + state->crtc_x > INT_MAX - (int32_t) state->crtc_w || + state->crtc_h > INT_MAX || + state->crtc_y > INT_MAX - (int32_t) state->crtc_h) { + DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", + state->crtc_w, state->crtc_h, + state->crtc_x, state->crtc_y); + return -ERANGE; + } + + return 0; +} +EXPORT_SYMBOL(drm_plane_check_state); + +void drm_plane_commit_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + plane->state = state; + plane->base.propvals = &state->propvals; +} +EXPORT_SYMBOL(drm_plane_commit_state); + +int drm_plane_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, + uint64_t value, void *blob_data) +{ + struct drm_device *dev = plane->dev; + struct drm_mode_config *config = &dev->mode_config; + + drm_object_property_set_value(&plane->base, + &state->propvals, property, value, blob_data); + + if (property == config->prop_fb_id) { + struct drm_framebuffer *old_fb = state->fb; + /* + * NOTE: the ref to the fb could have been lost between + * drm_property_change_is_valid() and now. The upshot + * is that drm_framebuffer_lookup() could return NULL + * and we'd disable the plane. + * + * We *could* return an error in that case. But if (for + * example) _setcrtc() raced with _rmfb() and _rmfb() + * came after, it would disable what was enabled in the + * _setcrtc(). Which is the same end result that we get + * here, just skipping briefly setting the mode. + */ + state->fb = drm_framebuffer_lookup(dev, value); + if (old_fb) + drm_framebuffer_unreference(old_fb); + state->new_fb = true; + } else if (property == config->prop_crtc_id) { + struct drm_mode_object *obj = drm_property_get_obj(property, value); + struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL; + /* take the lock of the incoming crtc as well, moving + * plane between crtcs is synchronized on both incoming + * and outgoing crtc. + */ + if (crtc) { + struct drm_atomic_state *a = state->state; + int ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx); + if (ret) + return ret; + } + state->crtc = crtc; + state->update_plane = true; + } else if (property == config->prop_crtc_x) { + state->crtc_x = U642I64(value); + state->update_plane = true; + } else if (property == config->prop_crtc_y) { + state->crtc_y = U642I64(value); + state->update_plane = true; + } else if (property == config->prop_crtc_w) { + state->crtc_w = value; + state->update_plane = true; + } else if (property == config->prop_crtc_h) { + state->crtc_h = value; + state->update_plane = true; + } else if (property == config->prop_src_x) { + state->src_x = value; + state->update_plane = true; + } else if (property == config->prop_src_y) { + state->src_y = value; + state->update_plane = true; + } else if (property == config->prop_src_w) { + state->src_w = value; + state->update_plane = true; + } else if (property == config->prop_src_h) { + state->src_h = value; + state->update_plane = true; + } else { + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(drm_plane_set_property); + /** * drm_plane_force_disable - Forcibly disable a plane * @plane: plane to disable @@ -1198,43 +1377,93 @@ EXPORT_SYMBOL(drm_plane_cleanup); * Used when the plane's current framebuffer is destroyed, * and when restoring fbdev mode. */ -void drm_plane_force_disable(struct drm_plane *plane) +void drm_plane_force_disable(struct drm_plane *plane, + struct drm_atomic_state *state) { - struct drm_framebuffer *old_fb = plane->fb; - int ret; + struct drm_mode_config *config = &plane->dev->mode_config;
- if (!old_fb) - return; - - ret = plane->funcs->disable_plane(plane); - if (ret) { - DRM_ERROR("failed to disable plane with busy fb\n"); - return; - } - /* disconnect the plane from the fb and crtc: */ - __drm_framebuffer_unreference(old_fb); - plane->fb = NULL; - plane->crtc = NULL; + /* should turn off the crtc */ + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_id, 0, NULL); + drm_mode_plane_set_obj_prop(plane, state, + config->prop_fb_id, 0, NULL); } EXPORT_SYMBOL(drm_plane_force_disable);
static int drm_mode_create_standard_connector_properties(struct drm_device *dev) { - struct drm_property *edid; - struct drm_property *dpms; + struct drm_property *prop;
/* * Standard properties (apply to all connectors) */ - edid = drm_property_create(dev, DRM_MODE_PROP_BLOB | + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE, "EDID", 0); - dev->mode_config.edid_property = edid; + if (!prop) + return -ENOMEM; + dev->mode_config.edid_property = prop;
- dpms = drm_property_create_enum(dev, 0, + prop = drm_property_create_enum(dev, 0, "DPMS", drm_dpms_enum_list, ARRAY_SIZE(drm_dpms_enum_list)); - dev->mode_config.dpms_property = dpms; + if (!prop) + return -ENOMEM; + dev->mode_config.dpms_property = prop; + + + prop = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_x = prop; + + prop = drm_property_create_range(dev, 0, "SRC_Y", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_y = prop; + + prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_w = prop; + + prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_h = prop; + + prop = drm_property_create_signed_range(dev, 0, "CRTC_X", + INT_MIN, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_x = prop; + + prop = drm_property_create_signed_range(dev, 0, "CRTC_Y", + INT_MIN, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_y = prop; + + prop = drm_property_create_range(dev, 0, "CRTC_W", 0, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_w = prop; + + prop = drm_property_create_range(dev, 0, "CRTC_H", 0, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_h = prop; + + prop = drm_property_create_object(dev, 0, "FB_ID", DRM_MODE_OBJECT_FB); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_fb_id = prop; + + prop = drm_property_create_object(dev, 0, + "CRTC_ID", DRM_MODE_OBJECT_CRTC); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_id = prop;
return 0; } @@ -2140,20 +2369,19 @@ 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_config *config = &dev->mode_config; struct drm_plane *plane; - struct drm_crtc *crtc; - struct drm_framebuffer *fb = NULL, *old_fb = NULL; + struct drm_atomic_state *state; int ret = 0; - unsigned int fb_width, fb_height; - int i;
if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL;
- /* - * First, find the plane, crtc, and fb objects. If not available, - * we don't bother to call the driver. - */ +retry: + state = dev->driver->atomic_begin(dev, 0); + if (IS_ERR(state)) + return PTR_ERR(state); + plane = drm_plane_find(dev, plane_req->plane_id); if (!plane) { DRM_DEBUG_KMS("Unknown plane ID %d\n", @@ -2161,104 +2389,37 @@ int drm_mode_setplane(struct drm_device *dev, void *data, return -ENOENT; }
- /* No fb means shut it down */ - if (!plane_req->fb_id) { - drm_modeset_lock_all(dev); - old_fb = plane->fb; - ret = plane->funcs->disable_plane(plane); - if (!ret) { - plane->crtc = NULL; - plane->fb = NULL; - } else { - old_fb = NULL; - } - drm_modeset_unlock_all(dev); - goto out; - } - - crtc = drm_crtc_find(dev, plane_req->crtc_id); - if (!crtc) { - DRM_DEBUG_KMS("Unknown crtc ID %d\n", - plane_req->crtc_id); - ret = -ENOENT; - goto out; - } - - fb = drm_framebuffer_lookup(dev, plane_req->fb_id); - if (!fb) { - DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", - plane_req->fb_id); - ret = -ENOENT; - goto out; - } - - /* Check whether this plane supports the fb pixel format. */ - for (i = 0; i < plane->format_count; i++) - if (fb->pixel_format == plane->format_types[i]) - break; - if (i == plane->format_count) { - DRM_DEBUG_KMS("Invalid pixel format %s\n", - drm_get_format_name(fb->pixel_format)); - ret = -EINVAL; - goto out; - } - - fb_width = fb->width << 16; - fb_height = fb->height << 16; - - /* Make sure source coordinates are inside the fb. */ - if (plane_req->src_w > fb_width || - plane_req->src_x > fb_width - plane_req->src_w || - plane_req->src_h > fb_height || - plane_req->src_y > fb_height - plane_req->src_h) { - DRM_DEBUG_KMS("Invalid source coordinates " - "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", - plane_req->src_w >> 16, - ((plane_req->src_w & 0xffff) * 15625) >> 10, - plane_req->src_h >> 16, - ((plane_req->src_h & 0xffff) * 15625) >> 10, - plane_req->src_x >> 16, - ((plane_req->src_x & 0xffff) * 15625) >> 10, - plane_req->src_y >> 16, - ((plane_req->src_y & 0xffff) * 15625) >> 10); - ret = -ENOSPC; - goto out; - } - - /* Give drivers some help against integer overflows */ - if (plane_req->crtc_w > INT_MAX || - plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || - plane_req->crtc_h > INT_MAX || - plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { - DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", - plane_req->crtc_w, plane_req->crtc_h, - plane_req->crtc_x, plane_req->crtc_y); - ret = -ERANGE; + ret = + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_id, plane_req->crtc_id, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_fb_id, plane_req->fb_id, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_w, plane_req->crtc_w, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_h, plane_req->crtc_h, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_src_w, plane_req->src_w, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_src_h, plane_req->src_h, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_src_x, plane_req->src_x, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_src_y, plane_req->src_y, NULL) || + dev->driver->atomic_check(dev, state); + if (ret) goto out; - }
- drm_modeset_lock_all(dev); - old_fb = plane->fb; - ret = plane->funcs->update_plane(plane, crtc, fb, - plane_req->crtc_x, plane_req->crtc_y, - plane_req->crtc_w, plane_req->crtc_h, - plane_req->src_x, plane_req->src_y, - plane_req->src_w, plane_req->src_h); - if (!ret) { - plane->crtc = crtc; - plane->fb = fb; - fb = NULL; - } else { - old_fb = NULL; - } - drm_modeset_unlock_all(dev); + ret = dev->driver->atomic_commit(dev, state);
out: - if (fb) - drm_framebuffer_unreference(fb); - if (old_fb) - drm_framebuffer_unreference(old_fb); - + dev->driver->atomic_end(dev, state); + if (ret == -EDEADLK) + goto retry; return ret; }
@@ -3833,7 +3994,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); }
-static int drm_mode_connector_set_obj_prop(struct drm_connector *connector, +int drm_mode_connector_set_obj_prop(struct drm_connector *connector, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3856,8 +4017,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
return ret; } +EXPORT_SYMBOL(drm_mode_connector_set_obj_prop);
-static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3872,8 +4034,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
return ret; } +EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop);
-static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3882,12 +4045,10 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, if (plane->funcs->set_property) ret = plane->funcs->set_property(plane, state, property, value, blob_data); - if (!ret) - drm_object_property_set_value(&plane->base, &plane->propvals, - property, value, NULL);
return ret; } +EXPORT_SYMBOL(drm_mode_plane_set_obj_prop);
static int drm_mode_set_obj_prop(struct drm_mode_object *obj, struct drm_atomic_state *state, struct drm_property *property, diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 97b0d84..b73d3b0 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -286,13 +286,28 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; struct drm_plane *plane; bool error = false; + void *state; int i;
drm_warn_on_modeset_not_all_locked(dev);
+ state = dev->driver->atomic_begin(dev, 0); + if (IS_ERR(state)) { + DRM_ERROR("failed to restore fbdev mode\n"); + return true; + } + list_for_each_entry(plane, &dev->mode_config.plane_list, head) if (plane->type != DRM_PLANE_TYPE_PRIMARY) - drm_plane_force_disable(plane); + drm_plane_force_disable(plane, state); + + /* just disabling stuff shouldn't fail, hopefully: */ + if(dev->driver->atomic_check(dev, state)) + DRM_ERROR("failed to restore fbdev mode\n"); + else + dev->driver->atomic_commit(dev, state); + + dev->driver->atomic_end(dev, state);
for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index d966afa..7a32383 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -26,6 +26,7 @@ #include <linux/list.h> #include <drm/drmP.h> #include <drm/drm_rect.h> +#include <drm/drm_atomic.h>
#define SUBPIXEL_MASK 0xffff
@@ -234,6 +235,7 @@ EXPORT_SYMBOL(drm_primary_helper_destroy); const struct drm_plane_funcs drm_primary_helper_funcs = { .update_plane = drm_primary_helper_update, .disable_plane = drm_primary_helper_disable, + .set_property = drm_atomic_plane_set_property, .destroy = drm_primary_helper_destroy, }; EXPORT_SYMBOL(drm_primary_helper_funcs); diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 9da0935..8cf7442 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -10,6 +10,7 @@ */
#include <drm/drmP.h> +#include <drm/drm_atomic.h>
#include <drm/exynos_drm.h> #include "exynos_drm_drv.h" @@ -220,13 +221,17 @@ static int exynos_plane_set_property(struct drm_plane *plane, struct drm_device *dev = plane->dev; struct exynos_plane *exynos_plane = to_exynos_plane(plane); struct exynos_drm_private *dev_priv = dev->dev_private; + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); + + if (IS_ERR(pstate)) + return PTR_ERR(pstate);
if (property == dev_priv->plane_zpos_property) { exynos_plane->overlay.zpos = val; return 0; }
- return -EINVAL; + return drm_plane_set_property(plane, pstate, property, val, blob_data); }
static struct drm_plane_funcs exynos_plane_funcs = { diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index c235546..3f742f5 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1190,6 +1190,7 @@ static const struct drm_plane_funcs intel_plane_funcs = { .update_plane = intel_update_plane, .disable_plane = intel_disable_plane, .destroy = intel_destroy_plane, + .set_property = drm_atomic_plane_set_property, };
static uint32_t ilk_plane_formats[] = { diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 8c064dc..4c92985 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -88,8 +88,10 @@ int mdp4_plane_set_property(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) { - // XXX - return -EINVAL; + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); + if (IS_ERR(pstate)) + return PTR_ERR(pstate); + return drm_plane_set_property(plane, pstate, property, val, blob_data); }
static const struct drm_plane_funcs mdp4_plane_funcs = { diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 5cbf226..53cc8c6 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -103,8 +103,10 @@ int mdp5_plane_set_property(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) { - // XXX - return -EINVAL; + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); + if (IS_ERR(pstate)) + return PTR_ERR(pstate); + return drm_plane_set_property(plane, pstate, property, val, blob_data); }
static const struct drm_plane_funcs mdp5_plane_funcs = { diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c index 577e6aa..97b48b5 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c @@ -24,6 +24,7 @@ */
#include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_crtc.h> #include <drm/drm_fourcc.h>
@@ -226,6 +227,10 @@ nv_set_property(struct drm_plane *plane, uint64_t value, void *blob_data) { struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane; + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); + + if (IS_ERR(pstate)) + return PTR_ERR(pstate);
if (property == nv_plane->props.colorkey) nv_plane->colorkey = value; @@ -240,7 +245,8 @@ nv_set_property(struct drm_plane *plane, else if (property == nv_plane->props.iturbt_709) nv_plane->iturbt_709 = value; else - return -EINVAL; + return drm_plane_set_property(plane, pstate, + property, value, blob_data);
if (nv_plane->set_params) nv_plane->set_params(nv_plane); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 3dca538..da80bdc 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -585,7 +585,7 @@ static void dev_lastclose(struct drm_device *dev)
for (i = 0; i < priv->num_planes; i++) { drm_object_property_set_value(&priv->planes[i]->base, - &priv->planes[i]->propvals, + &priv->planes[i]->state->propvals, priv->rotation_prop, 0, NULL); } } diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 2d3c975..a9acc58 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -341,8 +341,12 @@ int omap_plane_set_property(struct drm_plane *plane, { struct omap_plane *omap_plane = to_omap_plane(plane); struct omap_drm_private *priv = plane->dev->dev_private; + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); int ret = -EINVAL;
+ if (IS_ERR(pstate)) + return PTR_ERR(pstate); + if (property == priv->rotation_prop) { DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val); omap_plane->win.rotation = val; @@ -351,6 +355,9 @@ int omap_plane_set_property(struct drm_plane *plane, DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val); omap_plane->info.zorder = val; ret = apply(plane); + } else { + ret = drm_plane_set_property(plane, pstate, property, + val, blob_data); }
return ret; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 3a5d843..015c76a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.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 <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
@@ -404,6 +405,10 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, { struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_group *rgrp = rplane->group; + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); + + if (IS_ERR(pstate)) + return PTR_ERR(pstate);
if (property == rgrp->planes.alpha) rcar_du_plane_set_alpha(rplane, value); @@ -412,7 +417,8 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, else if (property == rgrp->planes.zpos) rcar_du_plane_set_zpos(rplane, value); else - return -EINVAL; + return drm_plane_set_property(plane, pstate, + property, value, blob_data);
return 0; } diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c index 060ae03..ccf03ea 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.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 <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
@@ -228,6 +229,7 @@ static void shmob_drm_plane_destroy(struct drm_plane *plane) static const struct drm_plane_funcs shmob_drm_plane_funcs = { .update_plane = shmob_drm_plane_update, .disable_plane = shmob_drm_plane_disable, + .set_property = drm_atomic_plane_set_property, .destroy = shmob_drm_plane_destroy, };
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index ff72b81..78e93ec 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -68,7 +68,8 @@ * struct drm_atomic_funcs - helper funcs used by the atomic helpers */ struct drm_atomic_funcs { - int dummy; /* for now */ + 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); };
const extern struct drm_atomic_funcs drm_atomic_funcs; @@ -84,6 +85,30 @@ int drm_atomic_commit_unlocked(struct drm_device *dev, struct drm_atomic_state *state); void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state);
+int drm_atomic_plane_set_property(struct drm_plane *plane, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data); +struct drm_plane_state *drm_atomic_get_plane_state(struct drm_plane *plane, + struct drm_atomic_state *state); + +static inline int +drm_atomic_check_plane_state(struct drm_plane *plane, + struct drm_plane_state *pstate) +{ + const struct drm_atomic_funcs *funcs = + plane->dev->driver->atomic_funcs; + return funcs->check_plane_state(plane, pstate); +} + +static inline int +drm_atomic_commit_plane_state(struct drm_plane *plane, + struct drm_plane_state *pstate) +{ + const struct drm_atomic_funcs *funcs = + plane->dev->driver->atomic_funcs; + return funcs->commit_plane_state(plane, pstate); +} + /** * struct drm_atomic_state - the state object used by atomic helpers */ @@ -91,6 +116,8 @@ struct drm_atomic_state { struct kref refcount; struct drm_device *dev; uint32_t flags; + struct drm_plane **planes; + struct drm_plane_state **pstates;
bool committed; bool checked; /* just for debugging */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 547b75a..58309cc 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -569,7 +569,10 @@ struct drm_plane_funcs { int (*disable_plane)(struct drm_plane *plane); void (*destroy)(struct drm_plane *plane);
- int (*set_property)(struct drm_plane *plane, + struct drm_plane_state *(*create_state)(struct drm_plane *plane); + void (*destroy_state)(struct drm_plane *plane, + struct drm_plane_state *pstate); + int (*set_property)(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data); @@ -582,6 +585,48 @@ enum drm_plane_type { };
/** + * drm_plane_state - mutable plane state + * @update_plane: if full update_plane() is needed (vs pageflip) + * @new_fb: has the fb been changed + * @crtc: currently bound CRTC + * @fb: currently bound fb + * @crtc_x: left position of visible portion of plane on crtc + * @crtc_y: upper position of visible portion of plane on crtc + * @crtc_w: width of visible portion of plane on crtc + * @crtc_h: height of visible portion of plane on crtc + * @src_x: left position of visible portion of plane within + * plane (in 16.16) + * @src_y: upper position of visible portion of plane within + * plane (in 16.16) + * @src_w: width of visible portion of plane (in 16.16) + * @src_h: height of visible portion of plane (in 16.16) + * @propvals: property values + * @state: current global/toplevel state object (for atomic) while an + * update is in progress, NULL otherwise. + */ +struct drm_plane_state { + bool update_plane : 1; + bool new_fb : 1; + + struct drm_crtc *crtc; + struct drm_framebuffer *fb; + + /* Signed dest location allows it to be partially off screen */ + int32_t crtc_x, crtc_y; + uint32_t crtc_w, crtc_h; + + /* Source values are 16.16 fixed point */ + uint32_t src_x, src_y; + uint32_t src_h, src_w; + + bool enabled; + + struct drm_object_property_values propvals; + + struct drm_atomic_state *state; +}; + +/** * drm_plane - central DRM plane control structure * @dev: DRM device this plane belongs to * @head: for list management @@ -591,6 +636,8 @@ enum drm_plane_type { * @format_count: number of formats supported * @crtc: currently bound CRTC * @fb: currently bound fb + * @id: plane number, 0..n + * @state: the mutable state * @funcs: helper functions * @properties: property tracking for this plane * @type: type of plane (overlay, primary, cursor) @@ -608,10 +655,17 @@ struct drm_plane { struct drm_crtc *crtc; struct drm_framebuffer *fb;
+ int id; + + /* + * State that can be updated from userspace, and atomically + * commited or rolled back: + */ + struct drm_plane_state *state; + const struct drm_plane_funcs *funcs;
struct drm_object_properties properties; - struct drm_object_property_values propvals;
enum drm_plane_type type; }; @@ -807,8 +861,20 @@ struct drm_mode_config { bool poll_running; struct delayed_work output_poll_work;
- /* pointers to standard properties */ + /* just so blob properties can always be in a list: */ struct list_head property_blob_list; + + /* pointers to standard properties */ + struct drm_property *prop_src_x; + struct drm_property *prop_src_y; + struct drm_property *prop_src_w; + struct drm_property *prop_src_h; + struct drm_property *prop_crtc_x; + struct drm_property *prop_crtc_y; + struct drm_property *prop_crtc_w; + struct drm_property *prop_crtc_h; + struct drm_property *prop_fb_id; + struct drm_property *prop_crtc_id; struct drm_property *edid_property; struct drm_property *dpms_property; struct drm_property *plane_type_property; @@ -930,11 +996,20 @@ extern int drm_plane_init(struct drm_device *dev, const uint32_t *formats, uint32_t format_count, bool is_primary); extern void drm_plane_cleanup(struct drm_plane *plane); -extern void drm_plane_force_disable(struct drm_plane *plane); +extern void drm_plane_force_disable(struct drm_plane *plane, + struct drm_atomic_state *state); extern int drm_crtc_check_viewport(const struct drm_crtc *crtc, int x, int y, const struct drm_display_mode *mode, const struct drm_framebuffer *fb); +extern int drm_plane_check_state(struct drm_plane *plane, + struct drm_plane_state *state); +extern void drm_plane_commit_state(struct drm_plane *plane, + struct drm_plane_state *state); +extern int drm_plane_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, + uint64_t value, void *blob_data);
extern void drm_encoder_cleanup(struct drm_encoder *encoder);
@@ -984,6 +1059,17 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj, extern int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *value); + +int drm_mode_connector_set_obj_prop(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data); +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data); +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data); + extern int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, const struct drm_framebuffer_funcs *funcs); @@ -1165,6 +1251,26 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id) return mo ? obj_to_blob(mo) : NULL; }
+static inline struct drm_plane_state * +drm_plane_create_state(struct drm_plane *plane) +{ + if (plane->funcs->create_state) + return plane->funcs->create_state(plane); + return kzalloc(sizeof(struct drm_plane_state), GFP_KERNEL); +} + +static inline void +drm_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *pstate) +{ + if (pstate->fb) + drm_framebuffer_unreference(pstate->fb); + if (plane->funcs->destroy_state) + plane->funcs->destroy_state(plane, pstate); + else + kfree(pstate); +} + /* Plane list iterator for legacy (overlay only) planes. */ #define drm_for_each_legacy_plane(plane, planelist) \ list_for_each_entry(plane, planelist, head) \
On Sat, May 24, 2014 at 02:30:20PM -0400, Rob Clark wrote:
Break the mutable state of a plane out into a separate structure and use atomic properties mechanism to set plane attributes. This makes it easier to have some helpers for plane->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.
The same should be done for CRTC, encoder, and connector, but this patch only includes the first part (plane).
Signed-off-by: Rob Clark robdclark@gmail.com
Imo s/plane->id/plane->index/ since plane_id can be too easily confused with the mode object id used for the idr lookup. We already have drm_crtc_index, so this is established convention already.
A few comments below.
drivers/gpu/drm/armada/armada_overlay.c | 11 +- drivers/gpu/drm/drm_atomic.c | 225 ++++++++++++++- drivers/gpu/drm/drm_crtc.c | 433 ++++++++++++++++++++--------- drivers/gpu/drm/drm_fb_helper.c | 17 +- drivers/gpu/drm/drm_plane_helper.c | 2 + drivers/gpu/drm/exynos/exynos_drm_plane.c | 7 +- drivers/gpu/drm/i915/intel_sprite.c | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c | 6 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 6 +- drivers/gpu/drm/nouveau/dispnv04/overlay.c | 8 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/omapdrm/omap_plane.c | 7 + drivers/gpu/drm/rcar-du/rcar_du_plane.c | 8 +- drivers/gpu/drm/shmobile/shmob_drm_plane.c | 2 + include/drm/drm_atomic.h | 29 +- include/drm/drm_crtc.h | 114 +++++++- 16 files changed, 725 insertions(+), 153 deletions(-)
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c index 601ba9a..041ea89 100644 --- a/drivers/gpu/drm/armada/armada_overlay.c +++ b/drivers/gpu/drm/armada/armada_overlay.c @@ -7,6 +7,7 @@
- published by the Free Software Foundation.
*/ #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include "armada_crtc.h" #include "armada_drm.h" #include "armada_fb.h" @@ -288,7 +289,12 @@ static int armada_plane_set_property(struct drm_plane *plane, { struct armada_private *priv = plane->dev->dev_private; struct armada_plane *dplane = drm_to_armada_plane(plane);
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); bool update_attr = false;
int ret = 0;
if (IS_ERR(pstate))
return PTR_ERR(pstate);
if (property == priv->colorkey_prop) {
#define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8) @@ -342,13 +348,16 @@ static int armada_plane_set_property(struct drm_plane *plane, } else if (property == priv->saturation_prop) { dplane->prop.saturation = val; update_attr = true;
} else {
ret = drm_plane_set_property(plane, pstate, property,
val, blob_data);
}
if (update_attr && dplane->base.crtc) armada_ovl_update_attr(&dplane->prop, drm_to_armada_crtc(dplane->base.crtc));
- return 0;
- return ret;
}
static const struct drm_plane_funcs armada_plane_funcs = { diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 45df5e5..403ffc5 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -24,6 +24,7 @@
#include <drm/drmP.h> #include <drm/drm_atomic.h> +#include <drm/drm_plane_helper.h>
/**
- drm_atomic_begin - start a sequence of atomic updates
@@ -46,10 +47,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, uint32_t flags) { struct drm_atomic_state *state;
int nplanes = dev->mode_config.num_total_plane; int sz; void *ptr;
sz = sizeof(*state);
sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
ptr = kzalloc(sz, GFP_KERNEL);
@@ -65,6 +68,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, state->dev = dev; state->flags = flags;
- state->planes = ptr;
- ptr = &state->planes[nplanes];
- state->pstates = ptr;
- ptr = &state->pstates[nplanes];
- return state;
} EXPORT_SYMBOL(drm_atomic_begin); @@ -101,8 +110,20 @@ EXPORT_SYMBOL(drm_atomic_set_event); int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_atomic_state *a = state;
- int nplanes = dev->mode_config.num_total_plane;
- int i, ret = 0;
- for (i = 0; i < nplanes; i++) {
if (a->planes[i]) {
ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]);
if (ret)
break;
}
- }
- a->acquire_ctx.frozen = true;
- return 0; /* for now */
- return ret;
} EXPORT_SYMBOL(drm_atomic_check);
@@ -180,6 +201,18 @@ fail: static void commit_locks(struct drm_atomic_state *a, struct ww_acquire_ctx *ww_ctx) {
- struct drm_device *dev = a->dev;
- int nplanes = dev->mode_config.num_total_plane;
- int i;
- for (i = 0; i < nplanes; i++) {
struct drm_plane *plane = a->planes[i];
if (plane) {
plane->state->state = NULL;
drm_plane_destroy_state(plane, a->pstates[i]);
}
- }
- /* and properly release them (clear in_atomic, remove from list): */ drm_modeset_drop_locks(&a->acquire_ctx); ww_acquire_fini(ww_ctx);
@@ -189,7 +222,17 @@ static void commit_locks(struct drm_atomic_state *a, static int atomic_commit(struct drm_atomic_state *a, struct ww_acquire_ctx *ww_ctx) {
- int ret = 0;
int nplanes = a->dev->mode_config.num_total_plane;
int i, ret = 0;
for (i = 0; i < nplanes; i++) {
struct drm_plane *plane = a->planes[i];
if (plane) {
ret = drm_atomic_commit_plane_state(plane, a->pstates[i]);
if (ret)
break;
}
}
commit_locks(a, ww_ctx);
@@ -264,7 +307,185 @@ void _drm_atomic_state_free(struct kref *kref) } EXPORT_SYMBOL(_drm_atomic_state_free);
+int drm_atomic_plane_set_property(struct drm_plane *plane,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t val, void *blob_data)
+{
- struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
- if (IS_ERR(pstate))
return PTR_ERR(pstate);
- return drm_plane_set_property(plane, pstate, property, val, blob_data);
+} +EXPORT_SYMBOL(drm_atomic_plane_set_property);
+static void init_plane_state(struct drm_plane *plane,
struct drm_plane_state *pstate, struct drm_atomic_state *state)
+{
- /* snapshot current state: */
- *pstate = *plane->state;
- pstate->state = state;
- if (pstate->fb)
drm_framebuffer_reference(pstate->fb);
+}
+struct drm_plane_state * +drm_atomic_get_plane_state(struct drm_plane *plane,
struct drm_atomic_state *state)
+{
- struct drm_atomic_state *a = state;
- struct drm_plane_state *pstate;
- int ret;
- pstate = a->pstates[plane->id];
- if (!pstate) {
struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx;
/* grab lock of current crtc.. if crtc is NULL then grab all: */
if (plane->state->crtc)
Looking here looks fishy - who's preventing someone else from touching plane->state while we don't yet hold any locks? Or do I miss something big here?
ret = drm_modeset_lock(&plane->state->crtc->mutex, ctx);
else
ret = drm_modeset_lock_all_crtcs(plane->dev, ctx);
if (ret)
return ERR_PTR(ret);
pstate = drm_plane_create_state(plane);
if (!pstate)
return ERR_PTR(-ENOMEM);
init_plane_state(plane, pstate, state);
a->planes[plane->id] = plane;
a->pstates[plane->id] = pstate;
- }
- return pstate;
+} +EXPORT_SYMBOL(drm_atomic_get_plane_state);
+static void +swap_plane_state(struct drm_plane *plane, struct drm_atomic_state *a) +{
- struct drm_plane_state *pstate = a->pstates[plane->id];
- /* clear transient state (only valid during atomic update): */
- pstate->update_plane = false;
- pstate->new_fb = false;
- swap(plane->state, a->pstates[plane->id]);
- plane->base.propvals = &plane->state->propvals;
+}
+/* For primary plane, if the driver implements ->page_flip(), then
- we can use that. But drivers can now choose not to bother with
- implementing page_flip().
- */
+static bool can_flip(struct drm_plane *plane, struct drm_plane_state *pstate) +{
- struct drm_crtc *crtc = pstate->crtc;
- return (plane == crtc->primary) && crtc->funcs->page_flip &&
!pstate->update_plane;
+}
+/* clear crtc/fb, ie. after disable_plane(). But takes care to keep
- the property state in sync. Once we get rid of plane->crtc/fb ptrs
- and just use state, we can get rid of this fxn:
- */
+static void +reset_plane(struct drm_plane *plane, struct drm_plane_state *pstate) +{
- struct drm_mode_config *config = &plane->dev->mode_config;
- drm_plane_set_property(plane, pstate, config->prop_fb_id, 0, NULL);
- drm_plane_set_property(plane, pstate, config->prop_crtc_id, 0, NULL);
- plane->crtc = NULL;
- plane->fb = NULL;
+}
+static int +commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) +{
- struct drm_atomic_state *a = pstate->state;
- struct drm_framebuffer *old_fb = plane->fb;
- struct drm_framebuffer *fb = pstate->fb;
- bool enabled = pstate->crtc && fb;
- int ret = 0;
- if (fb)
drm_framebuffer_reference(fb);
- if (!enabled) {
if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
(plane->funcs == &drm_primary_helper_funcs)) {
/* primary plane helpers don't like ->disable_plane()..
* so this hack for now until someone comes up with
* something better:
*/
Imo that's something we could check for in ->check and reject early.
ret = 0;
} else {
ret = plane->funcs->disable_plane(plane);
reset_plane(plane, pstate);
}
- } else {
struct drm_crtc *crtc = pstate->crtc;
if (pstate->update_plane ||
(pstate->new_fb && !can_flip(plane, pstate))) {
ret = plane->funcs->update_plane(plane, crtc, pstate->fb,
pstate->crtc_x, pstate->crtc_y,
pstate->crtc_w, pstate->crtc_h,
pstate->src_x, pstate->src_y,
pstate->src_w, pstate->src_h);
if (ret == 0) {
/*
* For page_flip(), the driver does this, but for
* update_plane() it doesn't.. hurray \o/
*/
Imo a patch to unify this first wouldn't hurt ...
plane->crtc = crtc;
plane->fb = fb;
fb = NULL; /* don't unref */
}
} else if (pstate->new_fb) {
ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
if (ret == 0) {
/*
* Warn if the driver hasn't properly updated the plane->fb
* field to reflect that the new framebuffer is now used.
* Failing to do so will screw with the reference counting
* on framebuffers.
*/
WARN_ON(plane->fb != fb);
fb = NULL; /* don't unref */
}
} else {
old_fb = NULL;
ret = 0;
}
- }
This entire logic here kinda raises the question about transitioning drivers to the atomic interfaces. For modeset operations it might work fairly well since everything but i915 uses the crtc helpers and so can be converted fairly easily.
But doing nuclear pageflips, even more so if we want completion events is an entire new deal. No idea how that should work really.
- if (ret) {
/* Keep the old fb, don't unref it. */
old_fb = NULL;
- } else {
/* on success, update state and fb refcnting: */
/* NOTE: if we ensure no driver sets plane->state->fb = NULL
* on disable, we can move this up a level and not duplicate
* nearly the same thing for both update_plane and disable_plane
* cases.. I leave it like this for now to be paranoid due to
* the slightly different ordering in the two cases in the
* original code.
*/
swap_plane_state(plane, pstate->state);
- }
- if (fb)
drm_framebuffer_unreference(fb);
- if (old_fb)
drm_framebuffer_unreference(old_fb);
- return ret;
+}
const struct drm_atomic_funcs drm_atomic_funcs = {
.check_plane_state = drm_plane_check_state,
.commit_plane_state = commit_plane_state,
}; EXPORT_SYMBOL(drm_atomic_funcs); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 48555724..b556a31 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -712,6 +712,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) * in this manner. */ if (atomic_read(&fb->refcount.refcount) > 1) {
void *state;
state = dev->driver->atomic_begin(dev, 0);
if (IS_ERR(state)) {
DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
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) {*/
@@ -728,8 +740,17 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
list_for_each_entry(plane, &dev->mode_config.plane_list, head) { if (plane->fb == fb)
drm_plane_force_disable(plane);
}drm_plane_force_disable(plane, state);
/* just disabling stuff shouldn't fail, hopefully: */
if(dev->driver->atomic_check(dev, state))
DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
else
dev->driver->atomic_commit(dev, state);
dev->driver->atomic_end(dev, state);
- drm_modeset_unlock_all(dev); }
@@ -1090,18 +1111,23 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, const uint32_t *formats, uint32_t format_count, enum drm_plane_type type) {
struct drm_mode_config *config = &dev->mode_config; int ret;
/* this is now required: */
WARN_ON(!funcs->set_property);
drm_modeset_lock_all(dev);
ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); if (ret) goto out;
plane->funcs = funcs;
plane->state = drm_plane_create_state(plane); plane->base.properties = &plane->properties;
- plane->base.propvals = &plane->propvals;
- plane->base.propvals = &plane->state->propvals; plane->dev = dev;
- plane->funcs = funcs; plane->format_types = kmalloc(sizeof(uint32_t) * format_count, GFP_KERNEL); if (!plane->format_types) {
@@ -1116,15 +1142,27 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, plane->possible_crtcs = possible_crtcs; plane->type = type;
- list_add_tail(&plane->head, &dev->mode_config.plane_list);
- dev->mode_config.num_total_plane++;
- list_add_tail(&plane->head, &config->plane_list);
- plane->id = config->num_total_plane;
- config->num_total_plane++; if (plane->type == DRM_PLANE_TYPE_OVERLAY)
dev->mode_config.num_overlay_plane++;
config->num_overlay_plane++;
drm_object_attach_property(&plane->base,
dev->mode_config.plane_type_property,
config->plane_type_property, plane->type);
drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
drm_object_attach_property(&plane->base, config->prop_src_x, 0);
drm_object_attach_property(&plane->base, config->prop_src_y, 0);
drm_object_attach_property(&plane->base, config->prop_src_w, 0);
drm_object_attach_property(&plane->base, config->prop_src_h, 0);
out: drm_modeset_unlock_all(dev);
@@ -1185,10 +1223,151 @@ void drm_plane_cleanup(struct drm_plane *plane) dev->mode_config.num_total_plane--; if (plane->type == DRM_PLANE_TYPE_OVERLAY) dev->mode_config.num_overlay_plane--;
- drm_plane_destroy_state(plane, plane->state); drm_modeset_unlock_all(dev);
} EXPORT_SYMBOL(drm_plane_cleanup);
+int drm_plane_check_state(struct drm_plane *plane,
struct drm_plane_state *state)
+{
- unsigned int fb_width, fb_height;
- struct drm_framebuffer *fb = state->fb;
- int i;
- /* disabling the plane is allowed: */
- if (!fb)
return 0;
- fb_width = fb->width << 16;
- fb_height = fb->height << 16;
- /* Check whether this plane supports the fb pixel format. */
- for (i = 0; i < plane->format_count; i++)
if (fb->pixel_format == plane->format_types[i])
break;
- if (i == plane->format_count) {
DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
return -EINVAL;
- }
- /* Make sure source coordinates are inside the fb. */
- if (state->src_w > fb_width ||
state->src_x > fb_width - state->src_w ||
state->src_h > fb_height ||
state->src_y > fb_height - state->src_h) {
DRM_DEBUG_KMS("Invalid source coordinates "
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
state->src_w >> 16,
((state->src_w & 0xffff) * 15625) >> 10,
state->src_h >> 16,
((state->src_h & 0xffff) * 15625) >> 10,
state->src_x >> 16,
((state->src_x & 0xffff) * 15625) >> 10,
state->src_y >> 16,
((state->src_y & 0xffff) * 15625) >> 10);
return -ENOSPC;
- }
- /* Give drivers some help against integer overflows */
- if (state->crtc_w > INT_MAX ||
state->crtc_x > INT_MAX - (int32_t) state->crtc_w ||
state->crtc_h > INT_MAX ||
state->crtc_y > INT_MAX - (int32_t) state->crtc_h) {
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
state->crtc_w, state->crtc_h,
state->crtc_x, state->crtc_y);
return -ERANGE;
- }
- return 0;
+} +EXPORT_SYMBOL(drm_plane_check_state);
+void drm_plane_commit_state(struct drm_plane *plane,
struct drm_plane_state *state)
+{
- plane->state = state;
- plane->base.propvals = &state->propvals;
+} +EXPORT_SYMBOL(drm_plane_commit_state);
+int drm_plane_set_property(struct drm_plane *plane,
struct drm_plane_state *state,
struct drm_property *property,
uint64_t value, void *blob_data)
+{
- struct drm_device *dev = plane->dev;
- struct drm_mode_config *config = &dev->mode_config;
- drm_object_property_set_value(&plane->base,
&state->propvals, property, value, blob_data);
- if (property == config->prop_fb_id) {
struct drm_framebuffer *old_fb = state->fb;
/*
* NOTE: the ref to the fb could have been lost between
* drm_property_change_is_valid() and now. The upshot
* is that drm_framebuffer_lookup() could return NULL
* and we'd disable the plane.
*
* We *could* return an error in that case. But if (for
* example) _setcrtc() raced with _rmfb() and _rmfb()
* came after, it would disable what was enabled in the
* _setcrtc(). Which is the same end result that we get
* here, just skipping briefly setting the mode.
*/
state->fb = drm_framebuffer_lookup(dev, value);
if (old_fb)
drm_framebuffer_unreference(old_fb);
state->new_fb = true;
- } else if (property == config->prop_crtc_id) {
struct drm_mode_object *obj = drm_property_get_obj(property, value);
struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL;
/* take the lock of the incoming crtc as well, moving
* plane between crtcs is synchronized on both incoming
* and outgoing crtc.
*/
if (crtc) {
struct drm_atomic_state *a = state->state;
int ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
if (ret)
return ret;
}
state->crtc = crtc;
state->update_plane = true;
- } else if (property == config->prop_crtc_x) {
state->crtc_x = U642I64(value);
state->update_plane = true;
- } else if (property == config->prop_crtc_y) {
state->crtc_y = U642I64(value);
state->update_plane = true;
- } else if (property == config->prop_crtc_w) {
state->crtc_w = value;
state->update_plane = true;
- } else if (property == config->prop_crtc_h) {
state->crtc_h = value;
state->update_plane = true;
- } else if (property == config->prop_src_x) {
state->src_x = value;
state->update_plane = true;
- } else if (property == config->prop_src_y) {
state->src_y = value;
state->update_plane = true;
- } else if (property == config->prop_src_w) {
state->src_w = value;
state->update_plane = true;
- } else if (property == config->prop_src_h) {
state->src_h = value;
state->update_plane = true;
- } else {
return -EINVAL;
- }
- return 0;
+} +EXPORT_SYMBOL(drm_plane_set_property);
/**
- drm_plane_force_disable - Forcibly disable a plane
- @plane: plane to disable
@@ -1198,43 +1377,93 @@ EXPORT_SYMBOL(drm_plane_cleanup);
- Used when the plane's current framebuffer is destroyed,
- and when restoring fbdev mode.
*/ -void drm_plane_force_disable(struct drm_plane *plane) +void drm_plane_force_disable(struct drm_plane *plane,
struct drm_atomic_state *state)
{
- struct drm_framebuffer *old_fb = plane->fb;
- int ret;
- struct drm_mode_config *config = &plane->dev->mode_config;
- if (!old_fb)
return;
- ret = plane->funcs->disable_plane(plane);
- if (ret) {
DRM_ERROR("failed to disable plane with busy fb\n");
return;
- }
- /* disconnect the plane from the fb and crtc: */
- __drm_framebuffer_unreference(old_fb);
- plane->fb = NULL;
- plane->crtc = NULL;
- /* should turn off the crtc */
- drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_id, 0, NULL);
- drm_mode_plane_set_obj_prop(plane, state,
config->prop_fb_id, 0, NULL);
} EXPORT_SYMBOL(drm_plane_force_disable);
static int drm_mode_create_standard_connector_properties(struct drm_device *dev) {
- struct drm_property *edid;
- struct drm_property *dpms;
struct drm_property *prop;
/*
- Standard properties (apply to all connectors)
*/
- edid = drm_property_create(dev, DRM_MODE_PROP_BLOB |
- prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE, "EDID", 0);
- dev->mode_config.edid_property = edid;
- if (!prop)
return -ENOMEM;
- dev->mode_config.edid_property = prop;
- dpms = drm_property_create_enum(dev, 0,
- prop = drm_property_create_enum(dev, 0, "DPMS", drm_dpms_enum_list, ARRAY_SIZE(drm_dpms_enum_list));
- dev->mode_config.dpms_property = dpms;
if (!prop)
return -ENOMEM;
dev->mode_config.dpms_property = prop;
prop = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_x = prop;
prop = drm_property_create_range(dev, 0, "SRC_Y", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_y = prop;
prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_w = prop;
prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_h = prop;
prop = drm_property_create_signed_range(dev, 0, "CRTC_X",
INT_MIN, INT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_x = prop;
prop = drm_property_create_signed_range(dev, 0, "CRTC_Y",
INT_MIN, INT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_y = prop;
prop = drm_property_create_range(dev, 0, "CRTC_W", 0, INT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_w = prop;
prop = drm_property_create_range(dev, 0, "CRTC_H", 0, INT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_h = prop;
prop = drm_property_create_object(dev, 0, "FB_ID", DRM_MODE_OBJECT_FB);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_fb_id = prop;
prop = drm_property_create_object(dev, 0,
"CRTC_ID", DRM_MODE_OBJECT_CRTC);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_id = prop;
return 0;
} @@ -2140,20 +2369,19 @@ 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_config *config = &dev->mode_config; struct drm_plane *plane;
- struct drm_crtc *crtc;
- struct drm_framebuffer *fb = NULL, *old_fb = NULL;
- struct drm_atomic_state *state; int ret = 0;
unsigned int fb_width, fb_height;
int i;
if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL;
/*
* First, find the plane, crtc, and fb objects. If not available,
* we don't bother to call the driver.
*/
+retry:
- state = dev->driver->atomic_begin(dev, 0);
- if (IS_ERR(state))
return PTR_ERR(state);
- plane = drm_plane_find(dev, plane_req->plane_id); if (!plane) { DRM_DEBUG_KMS("Unknown plane ID %d\n",
@@ -2161,104 +2389,37 @@ int drm_mode_setplane(struct drm_device *dev, void *data, return -ENOENT; }
- /* No fb means shut it down */
- if (!plane_req->fb_id) {
drm_modeset_lock_all(dev);
old_fb = plane->fb;
ret = plane->funcs->disable_plane(plane);
if (!ret) {
plane->crtc = NULL;
plane->fb = NULL;
} else {
old_fb = NULL;
}
drm_modeset_unlock_all(dev);
goto out;
- }
- crtc = drm_crtc_find(dev, plane_req->crtc_id);
- if (!crtc) {
DRM_DEBUG_KMS("Unknown crtc ID %d\n",
plane_req->crtc_id);
ret = -ENOENT;
goto out;
- }
- fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
- if (!fb) {
DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
plane_req->fb_id);
ret = -ENOENT;
goto out;
- }
- /* Check whether this plane supports the fb pixel format. */
- for (i = 0; i < plane->format_count; i++)
if (fb->pixel_format == plane->format_types[i])
break;
- if (i == plane->format_count) {
DRM_DEBUG_KMS("Invalid pixel format %s\n",
drm_get_format_name(fb->pixel_format));
ret = -EINVAL;
goto out;
- }
- fb_width = fb->width << 16;
- fb_height = fb->height << 16;
- /* Make sure source coordinates are inside the fb. */
- if (plane_req->src_w > fb_width ||
plane_req->src_x > fb_width - plane_req->src_w ||
plane_req->src_h > fb_height ||
plane_req->src_y > fb_height - plane_req->src_h) {
DRM_DEBUG_KMS("Invalid source coordinates "
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
plane_req->src_w >> 16,
((plane_req->src_w & 0xffff) * 15625) >> 10,
plane_req->src_h >> 16,
((plane_req->src_h & 0xffff) * 15625) >> 10,
plane_req->src_x >> 16,
((plane_req->src_x & 0xffff) * 15625) >> 10,
plane_req->src_y >> 16,
((plane_req->src_y & 0xffff) * 15625) >> 10);
ret = -ENOSPC;
goto out;
- }
- /* Give drivers some help against integer overflows */
- if (plane_req->crtc_w > INT_MAX ||
plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
plane_req->crtc_h > INT_MAX ||
plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
plane_req->crtc_w, plane_req->crtc_h,
plane_req->crtc_x, plane_req->crtc_y);
ret = -ERANGE;
- ret =
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_id, plane_req->crtc_id, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_fb_id, plane_req->fb_id, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_w, plane_req->crtc_w, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_h, plane_req->crtc_h, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_src_w, plane_req->src_w, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_src_h, plane_req->src_h, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_src_x, plane_req->src_x, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_src_y, plane_req->src_y, NULL) ||
dev->driver->atomic_check(dev, state);
- if (ret) goto out;
}
drm_modeset_lock_all(dev);
old_fb = plane->fb;
ret = plane->funcs->update_plane(plane, crtc, fb,
plane_req->crtc_x, plane_req->crtc_y,
plane_req->crtc_w, plane_req->crtc_h,
plane_req->src_x, plane_req->src_y,
plane_req->src_w, plane_req->src_h);
if (!ret) {
plane->crtc = crtc;
plane->fb = fb;
fb = NULL;
} else {
old_fb = NULL;
}
drm_modeset_unlock_all(dev);
- ret = dev->driver->atomic_commit(dev, state);
out:
- if (fb)
drm_framebuffer_unreference(fb);
- if (old_fb)
drm_framebuffer_unreference(old_fb);
- dev->driver->atomic_end(dev, state);
- if (ret == -EDEADLK)
return ret;goto retry;
}
@@ -3833,7 +3994,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); }
-static int drm_mode_connector_set_obj_prop(struct drm_connector *connector, +int drm_mode_connector_set_obj_prop(struct drm_connector *connector, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3856,8 +4017,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
return ret; } +EXPORT_SYMBOL(drm_mode_connector_set_obj_prop);
-static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3872,8 +4034,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
return ret; } +EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop);
-static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3882,12 +4045,10 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, if (plane->funcs->set_property) ret = plane->funcs->set_property(plane, state, property, value, blob_data);
if (!ret)
drm_object_property_set_value(&plane->base, &plane->propvals,
property, value, NULL);
return ret;
} +EXPORT_SYMBOL(drm_mode_plane_set_obj_prop);
static int drm_mode_set_obj_prop(struct drm_mode_object *obj, struct drm_atomic_state *state, struct drm_property *property, diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 97b0d84..b73d3b0 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -286,13 +286,28 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; struct drm_plane *plane; bool error = false;
void *state; int i;
drm_warn_on_modeset_not_all_locked(dev);
state = dev->driver->atomic_begin(dev, 0);
if (IS_ERR(state)) {
DRM_ERROR("failed to restore fbdev mode\n");
return true;
}
list_for_each_entry(plane, &dev->mode_config.plane_list, head) if (plane->type != DRM_PLANE_TYPE_PRIMARY)
drm_plane_force_disable(plane);
drm_plane_force_disable(plane, state);
/* just disabling stuff shouldn't fail, hopefully: */
if(dev->driver->atomic_check(dev, state))
DRM_ERROR("failed to restore fbdev mode\n");
else
dev->driver->atomic_commit(dev, state);
dev->driver->atomic_end(dev, state);
for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index d966afa..7a32383 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -26,6 +26,7 @@ #include <linux/list.h> #include <drm/drmP.h> #include <drm/drm_rect.h> +#include <drm/drm_atomic.h>
#define SUBPIXEL_MASK 0xffff
@@ -234,6 +235,7 @@ EXPORT_SYMBOL(drm_primary_helper_destroy); const struct drm_plane_funcs drm_primary_helper_funcs = { .update_plane = drm_primary_helper_update, .disable_plane = drm_primary_helper_disable,
- .set_property = drm_atomic_plane_set_property, .destroy = drm_primary_helper_destroy,
}; EXPORT_SYMBOL(drm_primary_helper_funcs); diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 9da0935..8cf7442 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -10,6 +10,7 @@ */
#include <drm/drmP.h> +#include <drm/drm_atomic.h>
#include <drm/exynos_drm.h> #include "exynos_drm_drv.h" @@ -220,13 +221,17 @@ static int exynos_plane_set_property(struct drm_plane *plane, struct drm_device *dev = plane->dev; struct exynos_plane *exynos_plane = to_exynos_plane(plane); struct exynos_drm_private *dev_priv = dev->dev_private;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate);
if (property == dev_priv->plane_zpos_property) { exynos_plane->overlay.zpos = val; return 0; }
- return -EINVAL;
- return drm_plane_set_property(plane, pstate, property, val, blob_data);
}
Imo the interfaces here are a bit wonky - most drivers have the exact same structure of allocating a plane state object if it's not there and setting the property. Imo we should push this down a bit and have type-specific set_prop interfaces which take the state-specific state object. Core properties would be fully handled in the core. Which means that driver don't need to implement set_prop callbacks if they don't have any special properties on top of the core stuff. Much less boilerplate that way.
This would also give us a strong incentive to have common properties for e.g. blending since we could simply pimp the core with them, and leave drivers to just register the properties if they support them. If they have additional restrictions they only need to implement the ->check hook, which has the awesome advantage that they can deal with a real structure instead of abstract prop arrays.
E.g. we could add a plane->supports_blending bool which would enabled/disable the default blending/Z-order properties for updating.
static struct drm_plane_funcs exynos_plane_funcs = { diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index c235546..3f742f5 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1190,6 +1190,7 @@ static const struct drm_plane_funcs intel_plane_funcs = { .update_plane = intel_update_plane, .disable_plane = intel_disable_plane, .destroy = intel_destroy_plane,
- .set_property = drm_atomic_plane_set_property,
};
static uint32_t ilk_plane_formats[] = { diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 8c064dc..4c92985 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -88,8 +88,10 @@ int mdp4_plane_set_property(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) {
- // XXX
- return -EINVAL;
- struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
- if (IS_ERR(pstate))
return PTR_ERR(pstate);
- return drm_plane_set_property(plane, pstate, property, val, blob_data);
}
static const struct drm_plane_funcs mdp4_plane_funcs = { diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 5cbf226..53cc8c6 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -103,8 +103,10 @@ int mdp5_plane_set_property(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) {
- // XXX
- return -EINVAL;
- struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
- if (IS_ERR(pstate))
return PTR_ERR(pstate);
- return drm_plane_set_property(plane, pstate, property, val, blob_data);
}
static const struct drm_plane_funcs mdp5_plane_funcs = { diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c index 577e6aa..97b48b5 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c @@ -24,6 +24,7 @@ */
#include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_crtc.h> #include <drm/drm_fourcc.h>
@@ -226,6 +227,10 @@ nv_set_property(struct drm_plane *plane, uint64_t value, void *blob_data) { struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate);
if (property == nv_plane->props.colorkey) nv_plane->colorkey = value;
@@ -240,7 +245,8 @@ nv_set_property(struct drm_plane *plane, else if (property == nv_plane->props.iturbt_709) nv_plane->iturbt_709 = value; else
return -EINVAL;
return drm_plane_set_property(plane, pstate,
property, value, blob_data);
if (nv_plane->set_params) nv_plane->set_params(nv_plane);
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 3dca538..da80bdc 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -585,7 +585,7 @@ static void dev_lastclose(struct drm_device *dev)
for (i = 0; i < priv->num_planes; i++) { drm_object_property_set_value(&priv->planes[i]->base,
&priv->planes[i]->propvals,
} }&priv->planes[i]->state->propvals, priv->rotation_prop, 0, NULL);
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 2d3c975..a9acc58 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -341,8 +341,12 @@ int omap_plane_set_property(struct drm_plane *plane, { struct omap_plane *omap_plane = to_omap_plane(plane); struct omap_drm_private *priv = plane->dev->dev_private;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); int ret = -EINVAL;
if (IS_ERR(pstate))
return PTR_ERR(pstate);
if (property == priv->rotation_prop) { DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val); omap_plane->win.rotation = val;
@@ -351,6 +355,9 @@ int omap_plane_set_property(struct drm_plane *plane, DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val); omap_plane->info.zorder = val; ret = apply(plane);
} else {
ret = drm_plane_set_property(plane, pstate, property,
val, blob_data);
}
return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 3a5d843..015c76a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.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 <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
@@ -404,6 +405,10 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, { struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_group *rgrp = rplane->group;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate);
if (property == rgrp->planes.alpha) rcar_du_plane_set_alpha(rplane, value);
@@ -412,7 +417,8 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, else if (property == rgrp->planes.zpos) rcar_du_plane_set_zpos(rplane, value); else
return -EINVAL;
return drm_plane_set_property(plane, pstate,
property, value, blob_data);
return 0;
} diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c index 060ae03..ccf03ea 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.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 <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
@@ -228,6 +229,7 @@ static void shmob_drm_plane_destroy(struct drm_plane *plane) static const struct drm_plane_funcs shmob_drm_plane_funcs = { .update_plane = shmob_drm_plane_update, .disable_plane = shmob_drm_plane_disable,
- .set_property = drm_atomic_plane_set_property, .destroy = shmob_drm_plane_destroy,
};
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index ff72b81..78e93ec 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -68,7 +68,8 @@
- struct drm_atomic_funcs - helper funcs used by the atomic helpers
*/ struct drm_atomic_funcs {
- int dummy; /* for now */
- 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);
};
const extern struct drm_atomic_funcs drm_atomic_funcs; @@ -84,6 +85,30 @@ int drm_atomic_commit_unlocked(struct drm_device *dev, struct drm_atomic_state *state); void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state);
+int drm_atomic_plane_set_property(struct drm_plane *plane,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t val, void *blob_data);
+struct drm_plane_state *drm_atomic_get_plane_state(struct drm_plane *plane,
struct drm_atomic_state *state);
+static inline int +drm_atomic_check_plane_state(struct drm_plane *plane,
struct drm_plane_state *pstate)
+{
- const struct drm_atomic_funcs *funcs =
plane->dev->driver->atomic_funcs;
- return funcs->check_plane_state(plane, pstate);
+}
+static inline int +drm_atomic_commit_plane_state(struct drm_plane *plane,
struct drm_plane_state *pstate)
+{
- const struct drm_atomic_funcs *funcs =
plane->dev->driver->atomic_funcs;
- return funcs->commit_plane_state(plane, pstate);
+}
/**
- struct drm_atomic_state - the state object used by atomic helpers
*/ @@ -91,6 +116,8 @@ struct drm_atomic_state { struct kref refcount; struct drm_device *dev; uint32_t flags;
struct drm_plane **planes;
struct drm_plane_state **pstates;
bool committed; bool checked; /* just for debugging */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 547b75a..58309cc 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -569,7 +569,10 @@ struct drm_plane_funcs { int (*disable_plane)(struct drm_plane *plane); void (*destroy)(struct drm_plane *plane);
- int (*set_property)(struct drm_plane *plane,
- struct drm_plane_state *(*create_state)(struct drm_plane *plane);
- void (*destroy_state)(struct drm_plane *plane,
struct drm_plane_state *pstate);
- int (*set_property)(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data);
@@ -582,6 +585,48 @@ enum drm_plane_type { };
/**
- drm_plane_state - mutable plane state
- @update_plane: if full update_plane() is needed (vs pageflip)
- @new_fb: has the fb been changed
- @crtc: currently bound CRTC
- @fb: currently bound fb
- @crtc_x: left position of visible portion of plane on crtc
- @crtc_y: upper position of visible portion of plane on crtc
- @crtc_w: width of visible portion of plane on crtc
- @crtc_h: height of visible portion of plane on crtc
- @src_x: left position of visible portion of plane within
- plane (in 16.16)
- @src_y: upper position of visible portion of plane within
- plane (in 16.16)
- @src_w: width of visible portion of plane (in 16.16)
- @src_h: height of visible portion of plane (in 16.16)
- @propvals: property values
- @state: current global/toplevel state object (for atomic) while an
- update is in progress, NULL otherwise.
- */
+struct drm_plane_state {
- bool update_plane : 1;
- bool new_fb : 1;
- struct drm_crtc *crtc;
- struct drm_framebuffer *fb;
- /* Signed dest location allows it to be partially off screen */
- int32_t crtc_x, crtc_y;
- uint32_t crtc_w, crtc_h;
- /* Source values are 16.16 fixed point */
- uint32_t src_x, src_y;
- uint32_t src_h, src_w;
- bool enabled;
- struct drm_object_property_values propvals;
- struct drm_atomic_state *state;
+};
+/**
- drm_plane - central DRM plane control structure
- @dev: DRM device this plane belongs to
- @head: for list management
@@ -591,6 +636,8 @@ enum drm_plane_type {
- @format_count: number of formats supported
- @crtc: currently bound CRTC
- @fb: currently bound fb
- @id: plane number, 0..n
- @state: the mutable state
- @funcs: helper functions
- @properties: property tracking for this plane
- @type: type of plane (overlay, primary, cursor)
@@ -608,10 +655,17 @@ struct drm_plane { struct drm_crtc *crtc; struct drm_framebuffer *fb;
int id;
/*
* State that can be updated from userspace, and atomically
* commited or rolled back:
*/
struct drm_plane_state *state;
const struct drm_plane_funcs *funcs;
struct drm_object_properties properties;
struct drm_object_property_values propvals;
enum drm_plane_type type;
}; @@ -807,8 +861,20 @@ struct drm_mode_config { bool poll_running; struct delayed_work output_poll_work;
- /* pointers to standard properties */
- /* just so blob properties can always be in a list: */ struct list_head property_blob_list;
- /* pointers to standard properties */
- struct drm_property *prop_src_x;
- struct drm_property *prop_src_y;
- struct drm_property *prop_src_w;
- struct drm_property *prop_src_h;
- struct drm_property *prop_crtc_x;
- struct drm_property *prop_crtc_y;
- struct drm_property *prop_crtc_w;
- struct drm_property *prop_crtc_h;
- struct drm_property *prop_fb_id;
- struct drm_property *prop_crtc_id; struct drm_property *edid_property; struct drm_property *dpms_property; struct drm_property *plane_type_property;
@@ -930,11 +996,20 @@ extern int drm_plane_init(struct drm_device *dev, const uint32_t *formats, uint32_t format_count, bool is_primary); extern void drm_plane_cleanup(struct drm_plane *plane); -extern void drm_plane_force_disable(struct drm_plane *plane); +extern void drm_plane_force_disable(struct drm_plane *plane,
struct drm_atomic_state *state);
extern int drm_crtc_check_viewport(const struct drm_crtc *crtc, int x, int y, const struct drm_display_mode *mode, const struct drm_framebuffer *fb); +extern int drm_plane_check_state(struct drm_plane *plane,
struct drm_plane_state *state);
+extern void drm_plane_commit_state(struct drm_plane *plane,
struct drm_plane_state *state);
+extern int drm_plane_set_property(struct drm_plane *plane,
struct drm_plane_state *state,
struct drm_property *property,
uint64_t value, void *blob_data);
extern void drm_encoder_cleanup(struct drm_encoder *encoder);
@@ -984,6 +1059,17 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj, extern int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *value);
+int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t value, void *blob_data);
+int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t value, void *blob_data);
+int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t value, void *blob_data);
extern int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, const struct drm_framebuffer_funcs *funcs); @@ -1165,6 +1251,26 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id) return mo ? obj_to_blob(mo) : NULL; }
+static inline struct drm_plane_state * +drm_plane_create_state(struct drm_plane *plane) +{
- if (plane->funcs->create_state)
return plane->funcs->create_state(plane);
- return kzalloc(sizeof(struct drm_plane_state), GFP_KERNEL);
+}
+static inline void +drm_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *pstate)
+{
- if (pstate->fb)
drm_framebuffer_unreference(pstate->fb);
- if (plane->funcs->destroy_state)
plane->funcs->destroy_state(plane, pstate);
- else
kfree(pstate);
+}
/* Plane list iterator for legacy (overlay only) planes. */ #define drm_for_each_legacy_plane(plane, planelist) \ list_for_each_entry(plane, planelist, head) \ -- 1.9.0
On Mon, May 26, 2014 at 5:12 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 02:30:20PM -0400, Rob Clark wrote:
Break the mutable state of a plane out into a separate structure and use atomic properties mechanism to set plane attributes. This makes it easier to have some helpers for plane->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.
The same should be done for CRTC, encoder, and connector, but this patch only includes the first part (plane).
Signed-off-by: Rob Clark robdclark@gmail.com
Imo s/plane->id/plane->index/ since plane_id can be too easily confused with the mode object id used for the idr lookup. We already have drm_crtc_index, so this is established convention already.
fair enough.. I suppose I could do the same in crtc.
A few comments below.
drivers/gpu/drm/armada/armada_overlay.c | 11 +- drivers/gpu/drm/drm_atomic.c | 225 ++++++++++++++- drivers/gpu/drm/drm_crtc.c | 433 ++++++++++++++++++++--------- drivers/gpu/drm/drm_fb_helper.c | 17 +- drivers/gpu/drm/drm_plane_helper.c | 2 + drivers/gpu/drm/exynos/exynos_drm_plane.c | 7 +- drivers/gpu/drm/i915/intel_sprite.c | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c | 6 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 6 +- drivers/gpu/drm/nouveau/dispnv04/overlay.c | 8 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/omapdrm/omap_plane.c | 7 + drivers/gpu/drm/rcar-du/rcar_du_plane.c | 8 +- drivers/gpu/drm/shmobile/shmob_drm_plane.c | 2 + include/drm/drm_atomic.h | 29 +- include/drm/drm_crtc.h | 114 +++++++- 16 files changed, 725 insertions(+), 153 deletions(-)
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c index 601ba9a..041ea89 100644 --- a/drivers/gpu/drm/armada/armada_overlay.c +++ b/drivers/gpu/drm/armada/armada_overlay.c @@ -7,6 +7,7 @@
- published by the Free Software Foundation.
*/ #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include "armada_crtc.h" #include "armada_drm.h" #include "armada_fb.h" @@ -288,7 +289,12 @@ static int armada_plane_set_property(struct drm_plane *plane, { struct armada_private *priv = plane->dev->dev_private; struct armada_plane *dplane = drm_to_armada_plane(plane);
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); bool update_attr = false;
int ret = 0;
if (IS_ERR(pstate))
return PTR_ERR(pstate); if (property == priv->colorkey_prop) {
#define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8) @@ -342,13 +348,16 @@ static int armada_plane_set_property(struct drm_plane *plane, } else if (property == priv->saturation_prop) { dplane->prop.saturation = val; update_attr = true;
} else {
ret = drm_plane_set_property(plane, pstate, property,
val, blob_data); } if (update_attr && dplane->base.crtc) armada_ovl_update_attr(&dplane->prop, drm_to_armada_crtc(dplane->base.crtc));
return 0;
return ret;
}
static const struct drm_plane_funcs armada_plane_funcs = { diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 45df5e5..403ffc5 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -24,6 +24,7 @@
#include <drm/drmP.h> #include <drm/drm_atomic.h> +#include <drm/drm_plane_helper.h>
/**
- drm_atomic_begin - start a sequence of atomic updates
@@ -46,10 +47,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, uint32_t flags) { struct drm_atomic_state *state;
int nplanes = dev->mode_config.num_total_plane; int sz; void *ptr; sz = sizeof(*state);
sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes; ptr = kzalloc(sz, GFP_KERNEL);
@@ -65,6 +68,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, state->dev = dev; state->flags = flags;
state->planes = ptr;
ptr = &state->planes[nplanes];
state->pstates = ptr;
ptr = &state->pstates[nplanes];
return state;
} EXPORT_SYMBOL(drm_atomic_begin); @@ -101,8 +110,20 @@ EXPORT_SYMBOL(drm_atomic_set_event); int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_atomic_state *a = state;
int nplanes = dev->mode_config.num_total_plane;
int i, ret = 0;
for (i = 0; i < nplanes; i++) {
if (a->planes[i]) {
ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]);
if (ret)
break;
}
}
a->acquire_ctx.frozen = true;
return 0; /* for now */
return ret;
} EXPORT_SYMBOL(drm_atomic_check);
@@ -180,6 +201,18 @@ fail: static void commit_locks(struct drm_atomic_state *a, struct ww_acquire_ctx *ww_ctx) {
struct drm_device *dev = a->dev;
int nplanes = dev->mode_config.num_total_plane;
int i;
for (i = 0; i < nplanes; i++) {
struct drm_plane *plane = a->planes[i];
if (plane) {
plane->state->state = NULL;
drm_plane_destroy_state(plane, a->pstates[i]);
}
}
/* and properly release them (clear in_atomic, remove from list): */ drm_modeset_drop_locks(&a->acquire_ctx); ww_acquire_fini(ww_ctx);
@@ -189,7 +222,17 @@ static void commit_locks(struct drm_atomic_state *a, static int atomic_commit(struct drm_atomic_state *a, struct ww_acquire_ctx *ww_ctx) {
int ret = 0;
int nplanes = a->dev->mode_config.num_total_plane;
int i, ret = 0;
for (i = 0; i < nplanes; i++) {
struct drm_plane *plane = a->planes[i];
if (plane) {
ret = drm_atomic_commit_plane_state(plane, a->pstates[i]);
if (ret)
break;
}
} commit_locks(a, ww_ctx);
@@ -264,7 +307,185 @@ void _drm_atomic_state_free(struct kref *kref) } EXPORT_SYMBOL(_drm_atomic_state_free);
+int drm_atomic_plane_set_property(struct drm_plane *plane,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t val, void *blob_data)
+{
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate);
return drm_plane_set_property(plane, pstate, property, val, blob_data);
+} +EXPORT_SYMBOL(drm_atomic_plane_set_property);
+static void init_plane_state(struct drm_plane *plane,
struct drm_plane_state *pstate, struct drm_atomic_state *state)
+{
/* snapshot current state: */
*pstate = *plane->state;
pstate->state = state;
if (pstate->fb)
drm_framebuffer_reference(pstate->fb);
+}
+struct drm_plane_state * +drm_atomic_get_plane_state(struct drm_plane *plane,
struct drm_atomic_state *state)
+{
struct drm_atomic_state *a = state;
struct drm_plane_state *pstate;
int ret;
pstate = a->pstates[plane->id];
if (!pstate) {
struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx;
/* grab lock of current crtc.. if crtc is NULL then grab all: */
if (plane->state->crtc)
Looking here looks fishy - who's preventing someone else from touching plane->state while we don't yet hold any locks? Or do I miss something big here?
Yeah, probably should hoist that out into a local variable to avoid problems on transition to null crtc. I don't think it can happen at the moment, but when we start to relax locking it could
ret = drm_modeset_lock(&plane->state->crtc->mutex, ctx);
else
ret = drm_modeset_lock_all_crtcs(plane->dev, ctx);
if (ret)
return ERR_PTR(ret);
pstate = drm_plane_create_state(plane);
if (!pstate)
return ERR_PTR(-ENOMEM);
init_plane_state(plane, pstate, state);
a->planes[plane->id] = plane;
a->pstates[plane->id] = pstate;
}
return pstate;
+} +EXPORT_SYMBOL(drm_atomic_get_plane_state);
+static void +swap_plane_state(struct drm_plane *plane, struct drm_atomic_state *a) +{
struct drm_plane_state *pstate = a->pstates[plane->id];
/* clear transient state (only valid during atomic update): */
pstate->update_plane = false;
pstate->new_fb = false;
swap(plane->state, a->pstates[plane->id]);
plane->base.propvals = &plane->state->propvals;
+}
+/* For primary plane, if the driver implements ->page_flip(), then
- we can use that. But drivers can now choose not to bother with
- implementing page_flip().
- */
+static bool can_flip(struct drm_plane *plane, struct drm_plane_state *pstate) +{
struct drm_crtc *crtc = pstate->crtc;
return (plane == crtc->primary) && crtc->funcs->page_flip &&
!pstate->update_plane;
+}
+/* clear crtc/fb, ie. after disable_plane(). But takes care to keep
- the property state in sync. Once we get rid of plane->crtc/fb ptrs
- and just use state, we can get rid of this fxn:
- */
+static void +reset_plane(struct drm_plane *plane, struct drm_plane_state *pstate) +{
struct drm_mode_config *config = &plane->dev->mode_config;
drm_plane_set_property(plane, pstate, config->prop_fb_id, 0, NULL);
drm_plane_set_property(plane, pstate, config->prop_crtc_id, 0, NULL);
plane->crtc = NULL;
plane->fb = NULL;
+}
+static int +commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) +{
struct drm_atomic_state *a = pstate->state;
struct drm_framebuffer *old_fb = plane->fb;
struct drm_framebuffer *fb = pstate->fb;
bool enabled = pstate->crtc && fb;
int ret = 0;
if (fb)
drm_framebuffer_reference(fb);
if (!enabled) {
if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
(plane->funcs == &drm_primary_helper_funcs)) {
/* primary plane helpers don't like ->disable_plane()..
* so this hack for now until someone comes up with
* something better:
*/
Imo that's something we could check for in ->check and reject early.
well, we aren't actually treating it as a failure. If we did want to treat it as an error, then yes, we should fail it in ->check(). I'm going to let someone who actually uses primary planes helpers decide how they want it and change it if necessary.
ret = 0;
} else {
ret = plane->funcs->disable_plane(plane);
reset_plane(plane, pstate);
}
} else {
struct drm_crtc *crtc = pstate->crtc;
if (pstate->update_plane ||
(pstate->new_fb && !can_flip(plane, pstate))) {
ret = plane->funcs->update_plane(plane, crtc, pstate->fb,
pstate->crtc_x, pstate->crtc_y,
pstate->crtc_w, pstate->crtc_h,
pstate->src_x, pstate->src_y,
pstate->src_w, pstate->src_h);
if (ret == 0) {
/*
* For page_flip(), the driver does this, but for
* update_plane() it doesn't.. hurray \o/
*/
Imo a patch to unify this first wouldn't hurt ...
I was kinda leaning more towards introducing a new API with unified behaviour, and deprecating the old eventually. Doing it all at once and not having someone who is more expert in each different drivers, seems like too big a chance to introduce problems.
I kinda want to more to an api that is more like atomic_commit() on a per object basis (ie. {crtc,plane,etc}->atomic_commit()). Then driver can hook in to the commit process at device level (dev->atomic_commit()) and/or per-object level as needed.
So yes, I want to unify.. but by building on top of atomic and deprecating the old.
plane->crtc = crtc;
plane->fb = fb;
fb = NULL; /* don't unref */
}
} else if (pstate->new_fb) {
ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
if (ret == 0) {
/*
* Warn if the driver hasn't properly updated the plane->fb
* field to reflect that the new framebuffer is now used.
* Failing to do so will screw with the reference counting
* on framebuffers.
*/
WARN_ON(plane->fb != fb);
fb = NULL; /* don't unref */
}
} else {
old_fb = NULL;
ret = 0;
}
}
This entire logic here kinda raises the question about transitioning drivers to the atomic interfaces. For modeset operations it might work fairly well since everything but i915 uses the crtc helpers and so can be converted fairly easily.
But doing nuclear pageflips, even more so if we want completion events is an entire new deal. No idea how that should work really.
Currently we only have completion events on CRTCs, but we are going to need no APIs internally for completion events on planes. Which might end up being the motivation for plane->atomic_commit() API.
At an rate, that should not hold up this patchset, or even adding atomic ioctl itself (as long as we don't expose new events yet, which is something we could leave out of the first version).
if (ret) {
/* Keep the old fb, don't unref it. */
old_fb = NULL;
} else {
/* on success, update state and fb refcnting: */
/* NOTE: if we ensure no driver sets plane->state->fb = NULL
* on disable, we can move this up a level and not duplicate
* nearly the same thing for both update_plane and disable_plane
* cases.. I leave it like this for now to be paranoid due to
* the slightly different ordering in the two cases in the
* original code.
*/
swap_plane_state(plane, pstate->state);
}
if (fb)
drm_framebuffer_unreference(fb);
if (old_fb)
drm_framebuffer_unreference(old_fb);
return ret;
+}
const struct drm_atomic_funcs drm_atomic_funcs = {
.check_plane_state = drm_plane_check_state,
.commit_plane_state = commit_plane_state,
}; EXPORT_SYMBOL(drm_atomic_funcs); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 48555724..b556a31 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -712,6 +712,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) * in this manner. */ if (atomic_read(&fb->refcount.refcount) > 1) {
void *state;
state = dev->driver->atomic_begin(dev, 0);
if (IS_ERR(state)) {
DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
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) {
@@ -728,8 +740,17 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
list_for_each_entry(plane, &dev->mode_config.plane_list, head) { if (plane->fb == fb)
drm_plane_force_disable(plane);
drm_plane_force_disable(plane, state); }
/* just disabling stuff shouldn't fail, hopefully: */
if(dev->driver->atomic_check(dev, state))
DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
else
dev->driver->atomic_commit(dev, state);
dev->driver->atomic_end(dev, state);
drm_modeset_unlock_all(dev); }
@@ -1090,18 +1111,23 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, const uint32_t *formats, uint32_t format_count, enum drm_plane_type type) {
struct drm_mode_config *config = &dev->mode_config; int ret;
/* this is now required: */
WARN_ON(!funcs->set_property);
drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); if (ret) goto out;
plane->funcs = funcs;
plane->state = drm_plane_create_state(plane); plane->base.properties = &plane->properties;
plane->base.propvals = &plane->propvals;
plane->base.propvals = &plane->state->propvals; plane->dev = dev;
plane->funcs = funcs; plane->format_types = kmalloc(sizeof(uint32_t) * format_count, GFP_KERNEL); if (!plane->format_types) {
@@ -1116,15 +1142,27 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, plane->possible_crtcs = possible_crtcs; plane->type = type;
list_add_tail(&plane->head, &dev->mode_config.plane_list);
dev->mode_config.num_total_plane++;
list_add_tail(&plane->head, &config->plane_list);
plane->id = config->num_total_plane;
config->num_total_plane++; if (plane->type == DRM_PLANE_TYPE_OVERLAY)
dev->mode_config.num_overlay_plane++;
config->num_overlay_plane++; drm_object_attach_property(&plane->base,
dev->mode_config.plane_type_property,
config->plane_type_property, plane->type);
drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
drm_object_attach_property(&plane->base, config->prop_src_x, 0);
drm_object_attach_property(&plane->base, config->prop_src_y, 0);
drm_object_attach_property(&plane->base, config->prop_src_w, 0);
drm_object_attach_property(&plane->base, config->prop_src_h, 0);
out: drm_modeset_unlock_all(dev);
@@ -1185,10 +1223,151 @@ void drm_plane_cleanup(struct drm_plane *plane) dev->mode_config.num_total_plane--; if (plane->type == DRM_PLANE_TYPE_OVERLAY) dev->mode_config.num_overlay_plane--;
drm_plane_destroy_state(plane, plane->state); drm_modeset_unlock_all(dev);
} EXPORT_SYMBOL(drm_plane_cleanup);
+int drm_plane_check_state(struct drm_plane *plane,
struct drm_plane_state *state)
+{
unsigned int fb_width, fb_height;
struct drm_framebuffer *fb = state->fb;
int i;
/* disabling the plane is allowed: */
if (!fb)
return 0;
fb_width = fb->width << 16;
fb_height = fb->height << 16;
/* Check whether this plane supports the fb pixel format. */
for (i = 0; i < plane->format_count; i++)
if (fb->pixel_format == plane->format_types[i])
break;
if (i == plane->format_count) {
DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
return -EINVAL;
}
/* Make sure source coordinates are inside the fb. */
if (state->src_w > fb_width ||
state->src_x > fb_width - state->src_w ||
state->src_h > fb_height ||
state->src_y > fb_height - state->src_h) {
DRM_DEBUG_KMS("Invalid source coordinates "
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
state->src_w >> 16,
((state->src_w & 0xffff) * 15625) >> 10,
state->src_h >> 16,
((state->src_h & 0xffff) * 15625) >> 10,
state->src_x >> 16,
((state->src_x & 0xffff) * 15625) >> 10,
state->src_y >> 16,
((state->src_y & 0xffff) * 15625) >> 10);
return -ENOSPC;
}
/* Give drivers some help against integer overflows */
if (state->crtc_w > INT_MAX ||
state->crtc_x > INT_MAX - (int32_t) state->crtc_w ||
state->crtc_h > INT_MAX ||
state->crtc_y > INT_MAX - (int32_t) state->crtc_h) {
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
state->crtc_w, state->crtc_h,
state->crtc_x, state->crtc_y);
return -ERANGE;
}
return 0;
+} +EXPORT_SYMBOL(drm_plane_check_state);
+void drm_plane_commit_state(struct drm_plane *plane,
struct drm_plane_state *state)
+{
plane->state = state;
plane->base.propvals = &state->propvals;
+} +EXPORT_SYMBOL(drm_plane_commit_state);
+int drm_plane_set_property(struct drm_plane *plane,
struct drm_plane_state *state,
struct drm_property *property,
uint64_t value, void *blob_data)
+{
struct drm_device *dev = plane->dev;
struct drm_mode_config *config = &dev->mode_config;
drm_object_property_set_value(&plane->base,
&state->propvals, property, value, blob_data);
if (property == config->prop_fb_id) {
struct drm_framebuffer *old_fb = state->fb;
/*
* NOTE: the ref to the fb could have been lost between
* drm_property_change_is_valid() and now. The upshot
* is that drm_framebuffer_lookup() could return NULL
* and we'd disable the plane.
*
* We *could* return an error in that case. But if (for
* example) _setcrtc() raced with _rmfb() and _rmfb()
* came after, it would disable what was enabled in the
* _setcrtc(). Which is the same end result that we get
* here, just skipping briefly setting the mode.
*/
state->fb = drm_framebuffer_lookup(dev, value);
if (old_fb)
drm_framebuffer_unreference(old_fb);
state->new_fb = true;
} else if (property == config->prop_crtc_id) {
struct drm_mode_object *obj = drm_property_get_obj(property, value);
struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL;
/* take the lock of the incoming crtc as well, moving
* plane between crtcs is synchronized on both incoming
* and outgoing crtc.
*/
if (crtc) {
struct drm_atomic_state *a = state->state;
int ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
if (ret)
return ret;
}
state->crtc = crtc;
state->update_plane = true;
} else if (property == config->prop_crtc_x) {
state->crtc_x = U642I64(value);
state->update_plane = true;
} else if (property == config->prop_crtc_y) {
state->crtc_y = U642I64(value);
state->update_plane = true;
} else if (property == config->prop_crtc_w) {
state->crtc_w = value;
state->update_plane = true;
} else if (property == config->prop_crtc_h) {
state->crtc_h = value;
state->update_plane = true;
} else if (property == config->prop_src_x) {
state->src_x = value;
state->update_plane = true;
} else if (property == config->prop_src_y) {
state->src_y = value;
state->update_plane = true;
} else if (property == config->prop_src_w) {
state->src_w = value;
state->update_plane = true;
} else if (property == config->prop_src_h) {
state->src_h = value;
state->update_plane = true;
} else {
return -EINVAL;
}
return 0;
+} +EXPORT_SYMBOL(drm_plane_set_property);
/**
- drm_plane_force_disable - Forcibly disable a plane
- @plane: plane to disable
@@ -1198,43 +1377,93 @@ EXPORT_SYMBOL(drm_plane_cleanup);
- Used when the plane's current framebuffer is destroyed,
- and when restoring fbdev mode.
*/ -void drm_plane_force_disable(struct drm_plane *plane) +void drm_plane_force_disable(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_framebuffer *old_fb = plane->fb;
int ret;
struct drm_mode_config *config = &plane->dev->mode_config;
if (!old_fb)
return;
ret = plane->funcs->disable_plane(plane);
if (ret) {
DRM_ERROR("failed to disable plane with busy fb\n");
return;
}
/* disconnect the plane from the fb and crtc: */
__drm_framebuffer_unreference(old_fb);
plane->fb = NULL;
plane->crtc = NULL;
/* should turn off the crtc */
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_id, 0, NULL);
drm_mode_plane_set_obj_prop(plane, state,
config->prop_fb_id, 0, NULL);
} EXPORT_SYMBOL(drm_plane_force_disable);
static int drm_mode_create_standard_connector_properties(struct drm_device *dev) {
struct drm_property *edid;
struct drm_property *dpms;
struct drm_property *prop; /* * Standard properties (apply to all connectors) */
edid = drm_property_create(dev, DRM_MODE_PROP_BLOB |
prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE, "EDID", 0);
dev->mode_config.edid_property = edid;
if (!prop)
return -ENOMEM;
dev->mode_config.edid_property = prop;
dpms = drm_property_create_enum(dev, 0,
prop = drm_property_create_enum(dev, 0, "DPMS", drm_dpms_enum_list, ARRAY_SIZE(drm_dpms_enum_list));
dev->mode_config.dpms_property = dpms;
if (!prop)
return -ENOMEM;
dev->mode_config.dpms_property = prop;
prop = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_x = prop;
prop = drm_property_create_range(dev, 0, "SRC_Y", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_y = prop;
prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_w = prop;
prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_h = prop;
prop = drm_property_create_signed_range(dev, 0, "CRTC_X",
INT_MIN, INT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_x = prop;
prop = drm_property_create_signed_range(dev, 0, "CRTC_Y",
INT_MIN, INT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_y = prop;
prop = drm_property_create_range(dev, 0, "CRTC_W", 0, INT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_w = prop;
prop = drm_property_create_range(dev, 0, "CRTC_H", 0, INT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_h = prop;
prop = drm_property_create_object(dev, 0, "FB_ID", DRM_MODE_OBJECT_FB);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_fb_id = prop;
prop = drm_property_create_object(dev, 0,
"CRTC_ID", DRM_MODE_OBJECT_CRTC);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_id = prop; return 0;
} @@ -2140,20 +2369,19 @@ 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_config *config = &dev->mode_config; struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_framebuffer *fb = NULL, *old_fb = NULL;
struct drm_atomic_state *state; int ret = 0;
unsigned int fb_width, fb_height;
int i; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL;
/*
* First, find the plane, crtc, and fb objects. If not available,
* we don't bother to call the driver.
*/
+retry:
state = dev->driver->atomic_begin(dev, 0);
if (IS_ERR(state))
return PTR_ERR(state);
plane = drm_plane_find(dev, plane_req->plane_id); if (!plane) { DRM_DEBUG_KMS("Unknown plane ID %d\n",
@@ -2161,104 +2389,37 @@ int drm_mode_setplane(struct drm_device *dev, void *data, return -ENOENT; }
/* No fb means shut it down */
if (!plane_req->fb_id) {
drm_modeset_lock_all(dev);
old_fb = plane->fb;
ret = plane->funcs->disable_plane(plane);
if (!ret) {
plane->crtc = NULL;
plane->fb = NULL;
} else {
old_fb = NULL;
}
drm_modeset_unlock_all(dev);
goto out;
}
crtc = drm_crtc_find(dev, plane_req->crtc_id);
if (!crtc) {
DRM_DEBUG_KMS("Unknown crtc ID %d\n",
plane_req->crtc_id);
ret = -ENOENT;
goto out;
}
fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
if (!fb) {
DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
plane_req->fb_id);
ret = -ENOENT;
goto out;
}
/* Check whether this plane supports the fb pixel format. */
for (i = 0; i < plane->format_count; i++)
if (fb->pixel_format == plane->format_types[i])
break;
if (i == plane->format_count) {
DRM_DEBUG_KMS("Invalid pixel format %s\n",
drm_get_format_name(fb->pixel_format));
ret = -EINVAL;
goto out;
}
fb_width = fb->width << 16;
fb_height = fb->height << 16;
/* Make sure source coordinates are inside the fb. */
if (plane_req->src_w > fb_width ||
plane_req->src_x > fb_width - plane_req->src_w ||
plane_req->src_h > fb_height ||
plane_req->src_y > fb_height - plane_req->src_h) {
DRM_DEBUG_KMS("Invalid source coordinates "
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
plane_req->src_w >> 16,
((plane_req->src_w & 0xffff) * 15625) >> 10,
plane_req->src_h >> 16,
((plane_req->src_h & 0xffff) * 15625) >> 10,
plane_req->src_x >> 16,
((plane_req->src_x & 0xffff) * 15625) >> 10,
plane_req->src_y >> 16,
((plane_req->src_y & 0xffff) * 15625) >> 10);
ret = -ENOSPC;
goto out;
}
/* Give drivers some help against integer overflows */
if (plane_req->crtc_w > INT_MAX ||
plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
plane_req->crtc_h > INT_MAX ||
plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
plane_req->crtc_w, plane_req->crtc_h,
plane_req->crtc_x, plane_req->crtc_y);
ret = -ERANGE;
ret =
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_id, plane_req->crtc_id, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_fb_id, plane_req->fb_id, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_w, plane_req->crtc_w, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_h, plane_req->crtc_h, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_src_w, plane_req->src_w, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_src_h, plane_req->src_h, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_src_x, plane_req->src_x, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_src_y, plane_req->src_y, NULL) ||
dev->driver->atomic_check(dev, state);
if (ret) goto out;
}
drm_modeset_lock_all(dev);
old_fb = plane->fb;
ret = plane->funcs->update_plane(plane, crtc, fb,
plane_req->crtc_x, plane_req->crtc_y,
plane_req->crtc_w, plane_req->crtc_h,
plane_req->src_x, plane_req->src_y,
plane_req->src_w, plane_req->src_h);
if (!ret) {
plane->crtc = crtc;
plane->fb = fb;
fb = NULL;
} else {
old_fb = NULL;
}
drm_modeset_unlock_all(dev);
ret = dev->driver->atomic_commit(dev, state);
out:
if (fb)
drm_framebuffer_unreference(fb);
if (old_fb)
drm_framebuffer_unreference(old_fb);
dev->driver->atomic_end(dev, state);
if (ret == -EDEADLK)
goto retry; return ret;
}
@@ -3833,7 +3994,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); }
-static int drm_mode_connector_set_obj_prop(struct drm_connector *connector, +int drm_mode_connector_set_obj_prop(struct drm_connector *connector, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3856,8 +4017,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
return ret;
} +EXPORT_SYMBOL(drm_mode_connector_set_obj_prop);
-static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3872,8 +4034,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
return ret;
} +EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop);
-static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3882,12 +4045,10 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, if (plane->funcs->set_property) ret = plane->funcs->set_property(plane, state, property, value, blob_data);
if (!ret)
drm_object_property_set_value(&plane->base, &plane->propvals,
property, value, NULL); return ret;
} +EXPORT_SYMBOL(drm_mode_plane_set_obj_prop);
static int drm_mode_set_obj_prop(struct drm_mode_object *obj, struct drm_atomic_state *state, struct drm_property *property, diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 97b0d84..b73d3b0 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -286,13 +286,28 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; struct drm_plane *plane; bool error = false;
void *state; int i; drm_warn_on_modeset_not_all_locked(dev);
state = dev->driver->atomic_begin(dev, 0);
if (IS_ERR(state)) {
DRM_ERROR("failed to restore fbdev mode\n");
return true;
}
list_for_each_entry(plane, &dev->mode_config.plane_list, head) if (plane->type != DRM_PLANE_TYPE_PRIMARY)
drm_plane_force_disable(plane);
drm_plane_force_disable(plane, state);
/* just disabling stuff shouldn't fail, hopefully: */
if(dev->driver->atomic_check(dev, state))
DRM_ERROR("failed to restore fbdev mode\n");
else
dev->driver->atomic_commit(dev, state);
dev->driver->atomic_end(dev, state); for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index d966afa..7a32383 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -26,6 +26,7 @@ #include <linux/list.h> #include <drm/drmP.h> #include <drm/drm_rect.h> +#include <drm/drm_atomic.h>
#define SUBPIXEL_MASK 0xffff
@@ -234,6 +235,7 @@ EXPORT_SYMBOL(drm_primary_helper_destroy); const struct drm_plane_funcs drm_primary_helper_funcs = { .update_plane = drm_primary_helper_update, .disable_plane = drm_primary_helper_disable,
.set_property = drm_atomic_plane_set_property, .destroy = drm_primary_helper_destroy,
}; EXPORT_SYMBOL(drm_primary_helper_funcs); diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 9da0935..8cf7442 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -10,6 +10,7 @@ */
#include <drm/drmP.h> +#include <drm/drm_atomic.h>
#include <drm/exynos_drm.h> #include "exynos_drm_drv.h" @@ -220,13 +221,17 @@ static int exynos_plane_set_property(struct drm_plane *plane, struct drm_device *dev = plane->dev; struct exynos_plane *exynos_plane = to_exynos_plane(plane); struct exynos_drm_private *dev_priv = dev->dev_private;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate); if (property == dev_priv->plane_zpos_property) { exynos_plane->overlay.zpos = val; return 0; }
return -EINVAL;
return drm_plane_set_property(plane, pstate, property, val, blob_data);
}
Imo the interfaces here are a bit wonky - most drivers have the exact same structure of allocating a plane state object if it's not there and setting the property. Imo we should push this down a bit and have type-specific set_prop interfaces which take the state-specific state object. Core properties would be fully handled in the core. Which means that driver don't need to implement set_prop callbacks if they don't have any special properties on top of the core stuff. Much less boilerplate that way.
Drivers which do not have any custom properties already just directly use drm_{plane,crtc}_set_property().
Basically, a vanilla driver with no planes, no custom properties, and no inter-crtc dependencies could plug in helpers everywhere a be "fully atomic"(TM).
BR, -R
This would also give us a strong incentive to have common properties for e.g. blending since we could simply pimp the core with them, and leave drivers to just register the properties if they support them. If they have additional restrictions they only need to implement the ->check hook, which has the awesome advantage that they can deal with a real structure instead of abstract prop arrays.
E.g. we could add a plane->supports_blending bool which would enabled/disable the default blending/Z-order properties for updating.
static struct drm_plane_funcs exynos_plane_funcs = { diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index c235546..3f742f5 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1190,6 +1190,7 @@ static const struct drm_plane_funcs intel_plane_funcs = { .update_plane = intel_update_plane, .disable_plane = intel_disable_plane, .destroy = intel_destroy_plane,
.set_property = drm_atomic_plane_set_property,
};
static uint32_t ilk_plane_formats[] = { diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 8c064dc..4c92985 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -88,8 +88,10 @@ int mdp4_plane_set_property(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) {
// XXX
return -EINVAL;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate);
return drm_plane_set_property(plane, pstate, property, val, blob_data);
}
static const struct drm_plane_funcs mdp4_plane_funcs = { diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 5cbf226..53cc8c6 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -103,8 +103,10 @@ int mdp5_plane_set_property(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) {
// XXX
return -EINVAL;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate);
return drm_plane_set_property(plane, pstate, property, val, blob_data);
}
static const struct drm_plane_funcs mdp5_plane_funcs = { diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c index 577e6aa..97b48b5 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c @@ -24,6 +24,7 @@ */
#include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_crtc.h> #include <drm/drm_fourcc.h>
@@ -226,6 +227,10 @@ nv_set_property(struct drm_plane *plane, uint64_t value, void *blob_data) { struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate); if (property == nv_plane->props.colorkey) nv_plane->colorkey = value;
@@ -240,7 +245,8 @@ nv_set_property(struct drm_plane *plane, else if (property == nv_plane->props.iturbt_709) nv_plane->iturbt_709 = value; else
return -EINVAL;
return drm_plane_set_property(plane, pstate,
property, value, blob_data); if (nv_plane->set_params) nv_plane->set_params(nv_plane);
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 3dca538..da80bdc 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -585,7 +585,7 @@ static void dev_lastclose(struct drm_device *dev)
for (i = 0; i < priv->num_planes; i++) { drm_object_property_set_value(&priv->planes[i]->base,
&priv->planes[i]->propvals,
&priv->planes[i]->state->propvals, priv->rotation_prop, 0, NULL); } }
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 2d3c975..a9acc58 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -341,8 +341,12 @@ int omap_plane_set_property(struct drm_plane *plane, { struct omap_plane *omap_plane = to_omap_plane(plane); struct omap_drm_private *priv = plane->dev->dev_private;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); int ret = -EINVAL;
if (IS_ERR(pstate))
return PTR_ERR(pstate);
if (property == priv->rotation_prop) { DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val); omap_plane->win.rotation = val;
@@ -351,6 +355,9 @@ int omap_plane_set_property(struct drm_plane *plane, DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val); omap_plane->info.zorder = val; ret = apply(plane);
} else {
ret = drm_plane_set_property(plane, pstate, property,
val, blob_data); } return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 3a5d843..015c76a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.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 <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
@@ -404,6 +405,10 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, { struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_group *rgrp = rplane->group;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate); if (property == rgrp->planes.alpha) rcar_du_plane_set_alpha(rplane, value);
@@ -412,7 +417,8 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, else if (property == rgrp->planes.zpos) rcar_du_plane_set_zpos(rplane, value); else
return -EINVAL;
return drm_plane_set_property(plane, pstate,
property, value, blob_data); return 0;
} diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c index 060ae03..ccf03ea 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.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 <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
@@ -228,6 +229,7 @@ static void shmob_drm_plane_destroy(struct drm_plane *plane) static const struct drm_plane_funcs shmob_drm_plane_funcs = { .update_plane = shmob_drm_plane_update, .disable_plane = shmob_drm_plane_disable,
.set_property = drm_atomic_plane_set_property, .destroy = shmob_drm_plane_destroy,
};
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index ff72b81..78e93ec 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -68,7 +68,8 @@
- struct drm_atomic_funcs - helper funcs used by the atomic helpers
*/ struct drm_atomic_funcs {
int dummy; /* for now */
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);
};
const extern struct drm_atomic_funcs drm_atomic_funcs; @@ -84,6 +85,30 @@ int drm_atomic_commit_unlocked(struct drm_device *dev, struct drm_atomic_state *state); void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state);
+int drm_atomic_plane_set_property(struct drm_plane *plane,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t val, void *blob_data);
+struct drm_plane_state *drm_atomic_get_plane_state(struct drm_plane *plane,
struct drm_atomic_state *state);
+static inline int +drm_atomic_check_plane_state(struct drm_plane *plane,
struct drm_plane_state *pstate)
+{
const struct drm_atomic_funcs *funcs =
plane->dev->driver->atomic_funcs;
return funcs->check_plane_state(plane, pstate);
+}
+static inline int +drm_atomic_commit_plane_state(struct drm_plane *plane,
struct drm_plane_state *pstate)
+{
const struct drm_atomic_funcs *funcs =
plane->dev->driver->atomic_funcs;
return funcs->commit_plane_state(plane, pstate);
+}
/**
- struct drm_atomic_state - the state object used by atomic helpers
*/ @@ -91,6 +116,8 @@ struct drm_atomic_state { struct kref refcount; struct drm_device *dev; uint32_t flags;
struct drm_plane **planes;
struct drm_plane_state **pstates; bool committed; bool checked; /* just for debugging */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 547b75a..58309cc 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -569,7 +569,10 @@ struct drm_plane_funcs { int (*disable_plane)(struct drm_plane *plane); void (*destroy)(struct drm_plane *plane);
int (*set_property)(struct drm_plane *plane,
struct drm_plane_state *(*create_state)(struct drm_plane *plane);
void (*destroy_state)(struct drm_plane *plane,
struct drm_plane_state *pstate);
int (*set_property)(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data);
@@ -582,6 +585,48 @@ enum drm_plane_type { };
/**
- drm_plane_state - mutable plane state
- @update_plane: if full update_plane() is needed (vs pageflip)
- @new_fb: has the fb been changed
- @crtc: currently bound CRTC
- @fb: currently bound fb
- @crtc_x: left position of visible portion of plane on crtc
- @crtc_y: upper position of visible portion of plane on crtc
- @crtc_w: width of visible portion of plane on crtc
- @crtc_h: height of visible portion of plane on crtc
- @src_x: left position of visible portion of plane within
- plane (in 16.16)
- @src_y: upper position of visible portion of plane within
- plane (in 16.16)
- @src_w: width of visible portion of plane (in 16.16)
- @src_h: height of visible portion of plane (in 16.16)
- @propvals: property values
- @state: current global/toplevel state object (for atomic) while an
- update is in progress, NULL otherwise.
- */
+struct drm_plane_state {
bool update_plane : 1;
bool new_fb : 1;
struct drm_crtc *crtc;
struct drm_framebuffer *fb;
/* Signed dest location allows it to be partially off screen */
int32_t crtc_x, crtc_y;
uint32_t crtc_w, crtc_h;
/* Source values are 16.16 fixed point */
uint32_t src_x, src_y;
uint32_t src_h, src_w;
bool enabled;
struct drm_object_property_values propvals;
struct drm_atomic_state *state;
+};
+/**
- drm_plane - central DRM plane control structure
- @dev: DRM device this plane belongs to
- @head: for list management
@@ -591,6 +636,8 @@ enum drm_plane_type {
- @format_count: number of formats supported
- @crtc: currently bound CRTC
- @fb: currently bound fb
- @id: plane number, 0..n
- @state: the mutable state
- @funcs: helper functions
- @properties: property tracking for this plane
- @type: type of plane (overlay, primary, cursor)
@@ -608,10 +655,17 @@ struct drm_plane { struct drm_crtc *crtc; struct drm_framebuffer *fb;
int id;
/*
* State that can be updated from userspace, and atomically
* commited or rolled back:
*/
struct drm_plane_state *state;
const struct drm_plane_funcs *funcs; struct drm_object_properties properties;
struct drm_object_property_values propvals; enum drm_plane_type type;
}; @@ -807,8 +861,20 @@ struct drm_mode_config { bool poll_running; struct delayed_work output_poll_work;
/* pointers to standard properties */
/* just so blob properties can always be in a list: */ struct list_head property_blob_list;
/* pointers to standard properties */
struct drm_property *prop_src_x;
struct drm_property *prop_src_y;
struct drm_property *prop_src_w;
struct drm_property *prop_src_h;
struct drm_property *prop_crtc_x;
struct drm_property *prop_crtc_y;
struct drm_property *prop_crtc_w;
struct drm_property *prop_crtc_h;
struct drm_property *prop_fb_id;
struct drm_property *prop_crtc_id; struct drm_property *edid_property; struct drm_property *dpms_property; struct drm_property *plane_type_property;
@@ -930,11 +996,20 @@ extern int drm_plane_init(struct drm_device *dev, const uint32_t *formats, uint32_t format_count, bool is_primary); extern void drm_plane_cleanup(struct drm_plane *plane); -extern void drm_plane_force_disable(struct drm_plane *plane); +extern void drm_plane_force_disable(struct drm_plane *plane,
struct drm_atomic_state *state);
extern int drm_crtc_check_viewport(const struct drm_crtc *crtc, int x, int y, const struct drm_display_mode *mode, const struct drm_framebuffer *fb); +extern int drm_plane_check_state(struct drm_plane *plane,
struct drm_plane_state *state);
+extern void drm_plane_commit_state(struct drm_plane *plane,
struct drm_plane_state *state);
+extern int drm_plane_set_property(struct drm_plane *plane,
struct drm_plane_state *state,
struct drm_property *property,
uint64_t value, void *blob_data);
extern void drm_encoder_cleanup(struct drm_encoder *encoder);
@@ -984,6 +1059,17 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj, extern int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *value);
+int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t value, void *blob_data);
+int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t value, void *blob_data);
+int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t value, void *blob_data);
extern int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, const struct drm_framebuffer_funcs *funcs); @@ -1165,6 +1251,26 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id) return mo ? obj_to_blob(mo) : NULL; }
+static inline struct drm_plane_state * +drm_plane_create_state(struct drm_plane *plane) +{
if (plane->funcs->create_state)
return plane->funcs->create_state(plane);
return kzalloc(sizeof(struct drm_plane_state), GFP_KERNEL);
+}
+static inline void +drm_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *pstate)
+{
if (pstate->fb)
drm_framebuffer_unreference(pstate->fb);
if (plane->funcs->destroy_state)
plane->funcs->destroy_state(plane, pstate);
else
kfree(pstate);
+}
/* Plane list iterator for legacy (overlay only) planes. */ #define drm_for_each_legacy_plane(plane, planelist) \ list_for_each_entry(plane, planelist, head) \ -- 1.9.0
-- Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Mon, May 26, 2014 at 07:32:13AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 5:12 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 02:30:20PM -0400, Rob Clark wrote:
Break the mutable state of a plane out into a separate structure and use atomic properties mechanism to set plane attributes. This makes it easier to have some helpers for plane->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.
The same should be done for CRTC, encoder, and connector, but this patch only includes the first part (plane).
Signed-off-by: Rob Clark robdclark@gmail.com
Imo s/plane->id/plane->index/ since plane_id can be too easily confused with the mode object id used for the idr lookup. We already have drm_crtc_index, so this is established convention already.
fair enough.. I suppose I could do the same in crtc.
I've forgotten to mention: We then should replace drm_crtc_index with crtc->index since that helper is redundant.
A few comments below.
drivers/gpu/drm/armada/armada_overlay.c | 11 +- drivers/gpu/drm/drm_atomic.c | 225 ++++++++++++++- drivers/gpu/drm/drm_crtc.c | 433 ++++++++++++++++++++--------- drivers/gpu/drm/drm_fb_helper.c | 17 +- drivers/gpu/drm/drm_plane_helper.c | 2 + drivers/gpu/drm/exynos/exynos_drm_plane.c | 7 +- drivers/gpu/drm/i915/intel_sprite.c | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c | 6 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 6 +- drivers/gpu/drm/nouveau/dispnv04/overlay.c | 8 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/omapdrm/omap_plane.c | 7 + drivers/gpu/drm/rcar-du/rcar_du_plane.c | 8 +- drivers/gpu/drm/shmobile/shmob_drm_plane.c | 2 + include/drm/drm_atomic.h | 29 +- include/drm/drm_crtc.h | 114 +++++++- 16 files changed, 725 insertions(+), 153 deletions(-)
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c index 601ba9a..041ea89 100644 --- a/drivers/gpu/drm/armada/armada_overlay.c +++ b/drivers/gpu/drm/armada/armada_overlay.c @@ -7,6 +7,7 @@
- published by the Free Software Foundation.
*/ #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include "armada_crtc.h" #include "armada_drm.h" #include "armada_fb.h" @@ -288,7 +289,12 @@ static int armada_plane_set_property(struct drm_plane *plane, { struct armada_private *priv = plane->dev->dev_private; struct armada_plane *dplane = drm_to_armada_plane(plane);
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); bool update_attr = false;
int ret = 0;
if (IS_ERR(pstate))
return PTR_ERR(pstate); if (property == priv->colorkey_prop) {
#define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8) @@ -342,13 +348,16 @@ static int armada_plane_set_property(struct drm_plane *plane, } else if (property == priv->saturation_prop) { dplane->prop.saturation = val; update_attr = true;
} else {
ret = drm_plane_set_property(plane, pstate, property,
val, blob_data); } if (update_attr && dplane->base.crtc) armada_ovl_update_attr(&dplane->prop, drm_to_armada_crtc(dplane->base.crtc));
return 0;
return ret;
}
static const struct drm_plane_funcs armada_plane_funcs = { diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 45df5e5..403ffc5 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -24,6 +24,7 @@
#include <drm/drmP.h> #include <drm/drm_atomic.h> +#include <drm/drm_plane_helper.h>
/**
- drm_atomic_begin - start a sequence of atomic updates
@@ -46,10 +47,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, uint32_t flags) { struct drm_atomic_state *state;
int nplanes = dev->mode_config.num_total_plane; int sz; void *ptr; sz = sizeof(*state);
sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes; ptr = kzalloc(sz, GFP_KERNEL);
@@ -65,6 +68,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, state->dev = dev; state->flags = flags;
state->planes = ptr;
ptr = &state->planes[nplanes];
state->pstates = ptr;
ptr = &state->pstates[nplanes];
return state;
} EXPORT_SYMBOL(drm_atomic_begin); @@ -101,8 +110,20 @@ EXPORT_SYMBOL(drm_atomic_set_event); int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_atomic_state *a = state;
int nplanes = dev->mode_config.num_total_plane;
int i, ret = 0;
for (i = 0; i < nplanes; i++) {
if (a->planes[i]) {
ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]);
if (ret)
break;
}
}
a->acquire_ctx.frozen = true;
return 0; /* for now */
return ret;
} EXPORT_SYMBOL(drm_atomic_check);
@@ -180,6 +201,18 @@ fail: static void commit_locks(struct drm_atomic_state *a, struct ww_acquire_ctx *ww_ctx) {
struct drm_device *dev = a->dev;
int nplanes = dev->mode_config.num_total_plane;
int i;
for (i = 0; i < nplanes; i++) {
struct drm_plane *plane = a->planes[i];
if (plane) {
plane->state->state = NULL;
drm_plane_destroy_state(plane, a->pstates[i]);
}
}
/* and properly release them (clear in_atomic, remove from list): */ drm_modeset_drop_locks(&a->acquire_ctx); ww_acquire_fini(ww_ctx);
@@ -189,7 +222,17 @@ static void commit_locks(struct drm_atomic_state *a, static int atomic_commit(struct drm_atomic_state *a, struct ww_acquire_ctx *ww_ctx) {
int ret = 0;
int nplanes = a->dev->mode_config.num_total_plane;
int i, ret = 0;
for (i = 0; i < nplanes; i++) {
struct drm_plane *plane = a->planes[i];
if (plane) {
ret = drm_atomic_commit_plane_state(plane, a->pstates[i]);
if (ret)
break;
}
} commit_locks(a, ww_ctx);
@@ -264,7 +307,185 @@ void _drm_atomic_state_free(struct kref *kref) } EXPORT_SYMBOL(_drm_atomic_state_free);
+int drm_atomic_plane_set_property(struct drm_plane *plane,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t val, void *blob_data)
+{
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate);
return drm_plane_set_property(plane, pstate, property, val, blob_data);
+} +EXPORT_SYMBOL(drm_atomic_plane_set_property);
+static void init_plane_state(struct drm_plane *plane,
struct drm_plane_state *pstate, struct drm_atomic_state *state)
+{
/* snapshot current state: */
*pstate = *plane->state;
pstate->state = state;
if (pstate->fb)
drm_framebuffer_reference(pstate->fb);
+}
+struct drm_plane_state * +drm_atomic_get_plane_state(struct drm_plane *plane,
struct drm_atomic_state *state)
+{
struct drm_atomic_state *a = state;
struct drm_plane_state *pstate;
int ret;
pstate = a->pstates[plane->id];
if (!pstate) {
struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx;
/* grab lock of current crtc.. if crtc is NULL then grab all: */
if (plane->state->crtc)
Looking here looks fishy - who's preventing someone else from touching plane->state while we don't yet hold any locks? Or do I miss something big here?
Yeah, probably should hoist that out into a local variable to avoid problems on transition to null crtc. I don't think it can happen at the moment, but when we start to relax locking it could
So what if some manages to race a 2nd atomic modeset and flips out the plane->state from underneath you're now chasing a freed structure?
Trying to be clever and attempting to do this without locks is imo just not worth the (review) pain to keep it race-free.
ret = drm_modeset_lock(&plane->state->crtc->mutex, ctx);
else
ret = drm_modeset_lock_all_crtcs(plane->dev, ctx);
if (ret)
return ERR_PTR(ret);
pstate = drm_plane_create_state(plane);
if (!pstate)
return ERR_PTR(-ENOMEM);
init_plane_state(plane, pstate, state);
a->planes[plane->id] = plane;
a->pstates[plane->id] = pstate;
}
return pstate;
+} +EXPORT_SYMBOL(drm_atomic_get_plane_state);
+static void +swap_plane_state(struct drm_plane *plane, struct drm_atomic_state *a) +{
struct drm_plane_state *pstate = a->pstates[plane->id];
/* clear transient state (only valid during atomic update): */
pstate->update_plane = false;
pstate->new_fb = false;
swap(plane->state, a->pstates[plane->id]);
plane->base.propvals = &plane->state->propvals;
+}
+/* For primary plane, if the driver implements ->page_flip(), then
- we can use that. But drivers can now choose not to bother with
- implementing page_flip().
- */
+static bool can_flip(struct drm_plane *plane, struct drm_plane_state *pstate) +{
struct drm_crtc *crtc = pstate->crtc;
return (plane == crtc->primary) && crtc->funcs->page_flip &&
!pstate->update_plane;
+}
+/* clear crtc/fb, ie. after disable_plane(). But takes care to keep
- the property state in sync. Once we get rid of plane->crtc/fb ptrs
- and just use state, we can get rid of this fxn:
- */
+static void +reset_plane(struct drm_plane *plane, struct drm_plane_state *pstate) +{
struct drm_mode_config *config = &plane->dev->mode_config;
drm_plane_set_property(plane, pstate, config->prop_fb_id, 0, NULL);
drm_plane_set_property(plane, pstate, config->prop_crtc_id, 0, NULL);
plane->crtc = NULL;
plane->fb = NULL;
+}
+static int +commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) +{
struct drm_atomic_state *a = pstate->state;
struct drm_framebuffer *old_fb = plane->fb;
struct drm_framebuffer *fb = pstate->fb;
bool enabled = pstate->crtc && fb;
int ret = 0;
if (fb)
drm_framebuffer_reference(fb);
if (!enabled) {
if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
(plane->funcs == &drm_primary_helper_funcs)) {
/* primary plane helpers don't like ->disable_plane()..
* so this hack for now until someone comes up with
* something better:
*/
Imo that's something we could check for in ->check and reject early.
well, we aren't actually treating it as a failure. If we did want to treat it as an error, then yes, we should fail it in ->check(). I'm going to let someone who actually uses primary planes helpers decide how they want it and change it if necessary.
Atm all drivers use the primary plane helpers. And I guess for a long time most will stick to them, so I think we should have an answer here. Rejecting the modeset sounds like the right one, since after all arbitrary driver restrictions is what we have the ->check hook for.
ret = 0;
} else {
ret = plane->funcs->disable_plane(plane);
reset_plane(plane, pstate);
}
} else {
struct drm_crtc *crtc = pstate->crtc;
if (pstate->update_plane ||
(pstate->new_fb && !can_flip(plane, pstate))) {
ret = plane->funcs->update_plane(plane, crtc, pstate->fb,
pstate->crtc_x, pstate->crtc_y,
pstate->crtc_w, pstate->crtc_h,
pstate->src_x, pstate->src_y,
pstate->src_w, pstate->src_h);
if (ret == 0) {
/*
* For page_flip(), the driver does this, but for
* update_plane() it doesn't.. hurray \o/
*/
Imo a patch to unify this first wouldn't hurt ...
I was kinda leaning more towards introducing a new API with unified behaviour, and deprecating the old eventually. Doing it all at once and not having someone who is more expert in each different drivers, seems like too big a chance to introduce problems.
I kinda want to more to an api that is more like atomic_commit() on a per object basis (ie. {crtc,plane,etc}->atomic_commit()). Then driver can hook in to the commit process at device level (dev->atomic_commit()) and/or per-object level as needed.
So yes, I want to unify.. but by building on top of atomic and deprecating the old.
I only meant to unify the refcounting and updating rules for obj->fb and similar. Like I've done for update_plane and set_crtc as part of Matt's primary plane work. Unifying all the flip interfaces is seriously out of scope ;-)
Also I really don't think you can make atomic pageflips work in a generic fashion with per-obj callbacks. After all the entire point is to have an atomic, global update, and the means (or lack thereof) the hw provides to make this happen will be different everywhere.
Atomic modesets (i.e. "do we have sufficient plls for this output config") are a bit different and for those it might work. But that should clearly be part of some helper library.
Yet another reason why imo it's really important to make a clear disdinction between atomic modesets (which we can mostly do for everyone using crtc helpers) and atomic/nuclear pageflips (for which we completely lack the unified driver interface and for which a per-obj ->commit hook is imo useless).
plane->crtc = crtc;
plane->fb = fb;
fb = NULL; /* don't unref */
}
} else if (pstate->new_fb) {
ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
if (ret == 0) {
/*
* Warn if the driver hasn't properly updated the plane->fb
* field to reflect that the new framebuffer is now used.
* Failing to do so will screw with the reference counting
* on framebuffers.
*/
WARN_ON(plane->fb != fb);
fb = NULL; /* don't unref */
}
} else {
old_fb = NULL;
ret = 0;
}
}
This entire logic here kinda raises the question about transitioning drivers to the atomic interfaces. For modeset operations it might work fairly well since everything but i915 uses the crtc helpers and so can be converted fairly easily.
But doing nuclear pageflips, even more so if we want completion events is an entire new deal. No idea how that should work really.
Currently we only have completion events on CRTCs, but we are going to need no APIs internally for completion events on planes. Which might end up being the motivation for plane->atomic_commit() API.
At an rate, that should not hold up this patchset, or even adding atomic ioctl itself (as long as we don't expose new events yet, which is something we could leave out of the first version).
Hm, what's the use-case for per-plane completion events? Userspace can always multiplex it when they want, and one single nuclear pageflip certainly shouldn't result in different vblank events for different planes on the same crtc. Otherwise it's just not atomic enough ;-)
Imo the pain we gain in making this generic (I've commented about this in other places) doesn't justify the possible gain. Especially since I don't see a real use case.
I think we should have a NUCLEAR_FLIP option in the atomic ioctl which requests that: - all updates happen on one vblank - all updates happen asynchronously
and promises that the update only concerns one crtc. All drivers without support for it (i.e. atm everything, hopefully soonish i915 will have support for this) would then reject this.
Which means that for the start atomic updates would only work for modesets, but I also think that's about as good as it gets without driver-specific work anyway.
if (ret) {
/* Keep the old fb, don't unref it. */
old_fb = NULL;
} else {
/* on success, update state and fb refcnting: */
/* NOTE: if we ensure no driver sets plane->state->fb = NULL
* on disable, we can move this up a level and not duplicate
* nearly the same thing for both update_plane and disable_plane
* cases.. I leave it like this for now to be paranoid due to
* the slightly different ordering in the two cases in the
* original code.
*/
swap_plane_state(plane, pstate->state);
}
if (fb)
drm_framebuffer_unreference(fb);
if (old_fb)
drm_framebuffer_unreference(old_fb);
return ret;
+}
const struct drm_atomic_funcs drm_atomic_funcs = {
.check_plane_state = drm_plane_check_state,
.commit_plane_state = commit_plane_state,
}; EXPORT_SYMBOL(drm_atomic_funcs); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 48555724..b556a31 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -712,6 +712,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) * in this manner. */ if (atomic_read(&fb->refcount.refcount) > 1) {
void *state;
state = dev->driver->atomic_begin(dev, 0);
if (IS_ERR(state)) {
DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
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) {
@@ -728,8 +740,17 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
list_for_each_entry(plane, &dev->mode_config.plane_list, head) { if (plane->fb == fb)
drm_plane_force_disable(plane);
drm_plane_force_disable(plane, state); }
/* just disabling stuff shouldn't fail, hopefully: */
if(dev->driver->atomic_check(dev, state))
DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
else
dev->driver->atomic_commit(dev, state);
dev->driver->atomic_end(dev, state);
drm_modeset_unlock_all(dev); }
@@ -1090,18 +1111,23 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, const uint32_t *formats, uint32_t format_count, enum drm_plane_type type) {
struct drm_mode_config *config = &dev->mode_config; int ret;
/* this is now required: */
WARN_ON(!funcs->set_property);
drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); if (ret) goto out;
plane->funcs = funcs;
plane->state = drm_plane_create_state(plane); plane->base.properties = &plane->properties;
plane->base.propvals = &plane->propvals;
plane->base.propvals = &plane->state->propvals; plane->dev = dev;
plane->funcs = funcs; plane->format_types = kmalloc(sizeof(uint32_t) * format_count, GFP_KERNEL); if (!plane->format_types) {
@@ -1116,15 +1142,27 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, plane->possible_crtcs = possible_crtcs; plane->type = type;
list_add_tail(&plane->head, &dev->mode_config.plane_list);
dev->mode_config.num_total_plane++;
list_add_tail(&plane->head, &config->plane_list);
plane->id = config->num_total_plane;
config->num_total_plane++; if (plane->type == DRM_PLANE_TYPE_OVERLAY)
dev->mode_config.num_overlay_plane++;
config->num_overlay_plane++; drm_object_attach_property(&plane->base,
dev->mode_config.plane_type_property,
config->plane_type_property, plane->type);
drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
drm_object_attach_property(&plane->base, config->prop_src_x, 0);
drm_object_attach_property(&plane->base, config->prop_src_y, 0);
drm_object_attach_property(&plane->base, config->prop_src_w, 0);
drm_object_attach_property(&plane->base, config->prop_src_h, 0);
out: drm_modeset_unlock_all(dev);
@@ -1185,10 +1223,151 @@ void drm_plane_cleanup(struct drm_plane *plane) dev->mode_config.num_total_plane--; if (plane->type == DRM_PLANE_TYPE_OVERLAY) dev->mode_config.num_overlay_plane--;
drm_plane_destroy_state(plane, plane->state); drm_modeset_unlock_all(dev);
} EXPORT_SYMBOL(drm_plane_cleanup);
+int drm_plane_check_state(struct drm_plane *plane,
struct drm_plane_state *state)
+{
unsigned int fb_width, fb_height;
struct drm_framebuffer *fb = state->fb;
int i;
/* disabling the plane is allowed: */
if (!fb)
return 0;
fb_width = fb->width << 16;
fb_height = fb->height << 16;
/* Check whether this plane supports the fb pixel format. */
for (i = 0; i < plane->format_count; i++)
if (fb->pixel_format == plane->format_types[i])
break;
if (i == plane->format_count) {
DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
return -EINVAL;
}
/* Make sure source coordinates are inside the fb. */
if (state->src_w > fb_width ||
state->src_x > fb_width - state->src_w ||
state->src_h > fb_height ||
state->src_y > fb_height - state->src_h) {
DRM_DEBUG_KMS("Invalid source coordinates "
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
state->src_w >> 16,
((state->src_w & 0xffff) * 15625) >> 10,
state->src_h >> 16,
((state->src_h & 0xffff) * 15625) >> 10,
state->src_x >> 16,
((state->src_x & 0xffff) * 15625) >> 10,
state->src_y >> 16,
((state->src_y & 0xffff) * 15625) >> 10);
return -ENOSPC;
}
/* Give drivers some help against integer overflows */
if (state->crtc_w > INT_MAX ||
state->crtc_x > INT_MAX - (int32_t) state->crtc_w ||
state->crtc_h > INT_MAX ||
state->crtc_y > INT_MAX - (int32_t) state->crtc_h) {
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
state->crtc_w, state->crtc_h,
state->crtc_x, state->crtc_y);
return -ERANGE;
}
return 0;
+} +EXPORT_SYMBOL(drm_plane_check_state);
+void drm_plane_commit_state(struct drm_plane *plane,
struct drm_plane_state *state)
+{
plane->state = state;
plane->base.propvals = &state->propvals;
+} +EXPORT_SYMBOL(drm_plane_commit_state);
+int drm_plane_set_property(struct drm_plane *plane,
struct drm_plane_state *state,
struct drm_property *property,
uint64_t value, void *blob_data)
+{
struct drm_device *dev = plane->dev;
struct drm_mode_config *config = &dev->mode_config;
drm_object_property_set_value(&plane->base,
&state->propvals, property, value, blob_data);
if (property == config->prop_fb_id) {
struct drm_framebuffer *old_fb = state->fb;
/*
* NOTE: the ref to the fb could have been lost between
* drm_property_change_is_valid() and now. The upshot
* is that drm_framebuffer_lookup() could return NULL
* and we'd disable the plane.
*
* We *could* return an error in that case. But if (for
* example) _setcrtc() raced with _rmfb() and _rmfb()
* came after, it would disable what was enabled in the
* _setcrtc(). Which is the same end result that we get
* here, just skipping briefly setting the mode.
*/
state->fb = drm_framebuffer_lookup(dev, value);
if (old_fb)
drm_framebuffer_unreference(old_fb);
state->new_fb = true;
} else if (property == config->prop_crtc_id) {
struct drm_mode_object *obj = drm_property_get_obj(property, value);
struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL;
/* take the lock of the incoming crtc as well, moving
* plane between crtcs is synchronized on both incoming
* and outgoing crtc.
*/
if (crtc) {
struct drm_atomic_state *a = state->state;
int ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
if (ret)
return ret;
}
state->crtc = crtc;
state->update_plane = true;
} else if (property == config->prop_crtc_x) {
state->crtc_x = U642I64(value);
state->update_plane = true;
} else if (property == config->prop_crtc_y) {
state->crtc_y = U642I64(value);
state->update_plane = true;
} else if (property == config->prop_crtc_w) {
state->crtc_w = value;
state->update_plane = true;
} else if (property == config->prop_crtc_h) {
state->crtc_h = value;
state->update_plane = true;
} else if (property == config->prop_src_x) {
state->src_x = value;
state->update_plane = true;
} else if (property == config->prop_src_y) {
state->src_y = value;
state->update_plane = true;
} else if (property == config->prop_src_w) {
state->src_w = value;
state->update_plane = true;
} else if (property == config->prop_src_h) {
state->src_h = value;
state->update_plane = true;
} else {
return -EINVAL;
}
return 0;
+} +EXPORT_SYMBOL(drm_plane_set_property);
/**
- drm_plane_force_disable - Forcibly disable a plane
- @plane: plane to disable
@@ -1198,43 +1377,93 @@ EXPORT_SYMBOL(drm_plane_cleanup);
- Used when the plane's current framebuffer is destroyed,
- and when restoring fbdev mode.
*/ -void drm_plane_force_disable(struct drm_plane *plane) +void drm_plane_force_disable(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_framebuffer *old_fb = plane->fb;
int ret;
struct drm_mode_config *config = &plane->dev->mode_config;
if (!old_fb)
return;
ret = plane->funcs->disable_plane(plane);
if (ret) {
DRM_ERROR("failed to disable plane with busy fb\n");
return;
}
/* disconnect the plane from the fb and crtc: */
__drm_framebuffer_unreference(old_fb);
plane->fb = NULL;
plane->crtc = NULL;
/* should turn off the crtc */
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_id, 0, NULL);
drm_mode_plane_set_obj_prop(plane, state,
config->prop_fb_id, 0, NULL);
} EXPORT_SYMBOL(drm_plane_force_disable);
static int drm_mode_create_standard_connector_properties(struct drm_device *dev) {
struct drm_property *edid;
struct drm_property *dpms;
struct drm_property *prop; /* * Standard properties (apply to all connectors) */
edid = drm_property_create(dev, DRM_MODE_PROP_BLOB |
prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE, "EDID", 0);
dev->mode_config.edid_property = edid;
if (!prop)
return -ENOMEM;
dev->mode_config.edid_property = prop;
dpms = drm_property_create_enum(dev, 0,
prop = drm_property_create_enum(dev, 0, "DPMS", drm_dpms_enum_list, ARRAY_SIZE(drm_dpms_enum_list));
dev->mode_config.dpms_property = dpms;
if (!prop)
return -ENOMEM;
dev->mode_config.dpms_property = prop;
prop = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_x = prop;
prop = drm_property_create_range(dev, 0, "SRC_Y", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_y = prop;
prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_w = prop;
prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_h = prop;
prop = drm_property_create_signed_range(dev, 0, "CRTC_X",
INT_MIN, INT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_x = prop;
prop = drm_property_create_signed_range(dev, 0, "CRTC_Y",
INT_MIN, INT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_y = prop;
prop = drm_property_create_range(dev, 0, "CRTC_W", 0, INT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_w = prop;
prop = drm_property_create_range(dev, 0, "CRTC_H", 0, INT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_h = prop;
prop = drm_property_create_object(dev, 0, "FB_ID", DRM_MODE_OBJECT_FB);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_fb_id = prop;
prop = drm_property_create_object(dev, 0,
"CRTC_ID", DRM_MODE_OBJECT_CRTC);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_crtc_id = prop; return 0;
} @@ -2140,20 +2369,19 @@ 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_config *config = &dev->mode_config; struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_framebuffer *fb = NULL, *old_fb = NULL;
struct drm_atomic_state *state; int ret = 0;
unsigned int fb_width, fb_height;
int i; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL;
/*
* First, find the plane, crtc, and fb objects. If not available,
* we don't bother to call the driver.
*/
+retry:
state = dev->driver->atomic_begin(dev, 0);
if (IS_ERR(state))
return PTR_ERR(state);
plane = drm_plane_find(dev, plane_req->plane_id); if (!plane) { DRM_DEBUG_KMS("Unknown plane ID %d\n",
@@ -2161,104 +2389,37 @@ int drm_mode_setplane(struct drm_device *dev, void *data, return -ENOENT; }
/* No fb means shut it down */
if (!plane_req->fb_id) {
drm_modeset_lock_all(dev);
old_fb = plane->fb;
ret = plane->funcs->disable_plane(plane);
if (!ret) {
plane->crtc = NULL;
plane->fb = NULL;
} else {
old_fb = NULL;
}
drm_modeset_unlock_all(dev);
goto out;
}
crtc = drm_crtc_find(dev, plane_req->crtc_id);
if (!crtc) {
DRM_DEBUG_KMS("Unknown crtc ID %d\n",
plane_req->crtc_id);
ret = -ENOENT;
goto out;
}
fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
if (!fb) {
DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
plane_req->fb_id);
ret = -ENOENT;
goto out;
}
/* Check whether this plane supports the fb pixel format. */
for (i = 0; i < plane->format_count; i++)
if (fb->pixel_format == plane->format_types[i])
break;
if (i == plane->format_count) {
DRM_DEBUG_KMS("Invalid pixel format %s\n",
drm_get_format_name(fb->pixel_format));
ret = -EINVAL;
goto out;
}
fb_width = fb->width << 16;
fb_height = fb->height << 16;
/* Make sure source coordinates are inside the fb. */
if (plane_req->src_w > fb_width ||
plane_req->src_x > fb_width - plane_req->src_w ||
plane_req->src_h > fb_height ||
plane_req->src_y > fb_height - plane_req->src_h) {
DRM_DEBUG_KMS("Invalid source coordinates "
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
plane_req->src_w >> 16,
((plane_req->src_w & 0xffff) * 15625) >> 10,
plane_req->src_h >> 16,
((plane_req->src_h & 0xffff) * 15625) >> 10,
plane_req->src_x >> 16,
((plane_req->src_x & 0xffff) * 15625) >> 10,
plane_req->src_y >> 16,
((plane_req->src_y & 0xffff) * 15625) >> 10);
ret = -ENOSPC;
goto out;
}
/* Give drivers some help against integer overflows */
if (plane_req->crtc_w > INT_MAX ||
plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
plane_req->crtc_h > INT_MAX ||
plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
plane_req->crtc_w, plane_req->crtc_h,
plane_req->crtc_x, plane_req->crtc_y);
ret = -ERANGE;
ret =
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_id, plane_req->crtc_id, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_fb_id, plane_req->fb_id, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_w, plane_req->crtc_w, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_crtc_h, plane_req->crtc_h, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_src_w, plane_req->src_w, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_src_h, plane_req->src_h, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_src_x, plane_req->src_x, NULL) ||
drm_mode_plane_set_obj_prop(plane, state,
config->prop_src_y, plane_req->src_y, NULL) ||
dev->driver->atomic_check(dev, state);
if (ret) goto out;
}
drm_modeset_lock_all(dev);
old_fb = plane->fb;
ret = plane->funcs->update_plane(plane, crtc, fb,
plane_req->crtc_x, plane_req->crtc_y,
plane_req->crtc_w, plane_req->crtc_h,
plane_req->src_x, plane_req->src_y,
plane_req->src_w, plane_req->src_h);
if (!ret) {
plane->crtc = crtc;
plane->fb = fb;
fb = NULL;
} else {
old_fb = NULL;
}
drm_modeset_unlock_all(dev);
ret = dev->driver->atomic_commit(dev, state);
out:
if (fb)
drm_framebuffer_unreference(fb);
if (old_fb)
drm_framebuffer_unreference(old_fb);
dev->driver->atomic_end(dev, state);
if (ret == -EDEADLK)
goto retry; return ret;
}
@@ -3833,7 +3994,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); }
-static int drm_mode_connector_set_obj_prop(struct drm_connector *connector, +int drm_mode_connector_set_obj_prop(struct drm_connector *connector, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3856,8 +4017,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
return ret;
} +EXPORT_SYMBOL(drm_mode_connector_set_obj_prop);
-static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3872,8 +4034,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
return ret;
} +EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop);
-static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3882,12 +4045,10 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, if (plane->funcs->set_property) ret = plane->funcs->set_property(plane, state, property, value, blob_data);
if (!ret)
drm_object_property_set_value(&plane->base, &plane->propvals,
property, value, NULL); return ret;
} +EXPORT_SYMBOL(drm_mode_plane_set_obj_prop);
static int drm_mode_set_obj_prop(struct drm_mode_object *obj, struct drm_atomic_state *state, struct drm_property *property, diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 97b0d84..b73d3b0 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -286,13 +286,28 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; struct drm_plane *plane; bool error = false;
void *state; int i; drm_warn_on_modeset_not_all_locked(dev);
state = dev->driver->atomic_begin(dev, 0);
if (IS_ERR(state)) {
DRM_ERROR("failed to restore fbdev mode\n");
return true;
}
list_for_each_entry(plane, &dev->mode_config.plane_list, head) if (plane->type != DRM_PLANE_TYPE_PRIMARY)
drm_plane_force_disable(plane);
drm_plane_force_disable(plane, state);
/* just disabling stuff shouldn't fail, hopefully: */
if(dev->driver->atomic_check(dev, state))
DRM_ERROR("failed to restore fbdev mode\n");
else
dev->driver->atomic_commit(dev, state);
dev->driver->atomic_end(dev, state); for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index d966afa..7a32383 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -26,6 +26,7 @@ #include <linux/list.h> #include <drm/drmP.h> #include <drm/drm_rect.h> +#include <drm/drm_atomic.h>
#define SUBPIXEL_MASK 0xffff
@@ -234,6 +235,7 @@ EXPORT_SYMBOL(drm_primary_helper_destroy); const struct drm_plane_funcs drm_primary_helper_funcs = { .update_plane = drm_primary_helper_update, .disable_plane = drm_primary_helper_disable,
.set_property = drm_atomic_plane_set_property, .destroy = drm_primary_helper_destroy,
}; EXPORT_SYMBOL(drm_primary_helper_funcs); diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 9da0935..8cf7442 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -10,6 +10,7 @@ */
#include <drm/drmP.h> +#include <drm/drm_atomic.h>
#include <drm/exynos_drm.h> #include "exynos_drm_drv.h" @@ -220,13 +221,17 @@ static int exynos_plane_set_property(struct drm_plane *plane, struct drm_device *dev = plane->dev; struct exynos_plane *exynos_plane = to_exynos_plane(plane); struct exynos_drm_private *dev_priv = dev->dev_private;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate); if (property == dev_priv->plane_zpos_property) { exynos_plane->overlay.zpos = val; return 0; }
return -EINVAL;
return drm_plane_set_property(plane, pstate, property, val, blob_data);
}
Imo the interfaces here are a bit wonky - most drivers have the exact same structure of allocating a plane state object if it's not there and setting the property. Imo we should push this down a bit and have type-specific set_prop interfaces which take the state-specific state object. Core properties would be fully handled in the core. Which means that driver don't need to implement set_prop callbacks if they don't have any special properties on top of the core stuff. Much less boilerplate that way.
Drivers which do not have any custom properties already just directly use drm_{plane,crtc}_set_property().
Basically, a vanilla driver with no planes, no custom properties, and no inter-crtc dependencies could plug in helpers everywhere a be "fully atomic"(TM).
My critique is two-fold: - It looks like the core property parsing is helper code which can be overriden. We very much don't want that, since we want to expose a common set of kms operations that work everywhere.
- Since the callbacks are not object specific there's a bit too much casting going on for my taste.
- For drivers that do have driver props we could move the allocation error handling into the core. Which is imo a Very Good Thing(tm).
Cheers, Daniel
BR, -R
This would also give us a strong incentive to have common properties for e.g. blending since we could simply pimp the core with them, and leave drivers to just register the properties if they support them. If they have additional restrictions they only need to implement the ->check hook, which has the awesome advantage that they can deal with a real structure instead of abstract prop arrays.
E.g. we could add a plane->supports_blending bool which would enabled/disable the default blending/Z-order properties for updating.
static struct drm_plane_funcs exynos_plane_funcs = { diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index c235546..3f742f5 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1190,6 +1190,7 @@ static const struct drm_plane_funcs intel_plane_funcs = { .update_plane = intel_update_plane, .disable_plane = intel_disable_plane, .destroy = intel_destroy_plane,
.set_property = drm_atomic_plane_set_property,
};
static uint32_t ilk_plane_formats[] = { diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 8c064dc..4c92985 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -88,8 +88,10 @@ int mdp4_plane_set_property(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) {
// XXX
return -EINVAL;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate);
return drm_plane_set_property(plane, pstate, property, val, blob_data);
}
static const struct drm_plane_funcs mdp4_plane_funcs = { diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 5cbf226..53cc8c6 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -103,8 +103,10 @@ int mdp5_plane_set_property(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) {
// XXX
return -EINVAL;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate);
return drm_plane_set_property(plane, pstate, property, val, blob_data);
}
static const struct drm_plane_funcs mdp5_plane_funcs = { diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c index 577e6aa..97b48b5 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c @@ -24,6 +24,7 @@ */
#include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_crtc.h> #include <drm/drm_fourcc.h>
@@ -226,6 +227,10 @@ nv_set_property(struct drm_plane *plane, uint64_t value, void *blob_data) { struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate); if (property == nv_plane->props.colorkey) nv_plane->colorkey = value;
@@ -240,7 +245,8 @@ nv_set_property(struct drm_plane *plane, else if (property == nv_plane->props.iturbt_709) nv_plane->iturbt_709 = value; else
return -EINVAL;
return drm_plane_set_property(plane, pstate,
property, value, blob_data); if (nv_plane->set_params) nv_plane->set_params(nv_plane);
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 3dca538..da80bdc 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -585,7 +585,7 @@ static void dev_lastclose(struct drm_device *dev)
for (i = 0; i < priv->num_planes; i++) { drm_object_property_set_value(&priv->planes[i]->base,
&priv->planes[i]->propvals,
&priv->planes[i]->state->propvals, priv->rotation_prop, 0, NULL); } }
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 2d3c975..a9acc58 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -341,8 +341,12 @@ int omap_plane_set_property(struct drm_plane *plane, { struct omap_plane *omap_plane = to_omap_plane(plane); struct omap_drm_private *priv = plane->dev->dev_private;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); int ret = -EINVAL;
if (IS_ERR(pstate))
return PTR_ERR(pstate);
if (property == priv->rotation_prop) { DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val); omap_plane->win.rotation = val;
@@ -351,6 +355,9 @@ int omap_plane_set_property(struct drm_plane *plane, DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val); omap_plane->info.zorder = val; ret = apply(plane);
} else {
ret = drm_plane_set_property(plane, pstate, property,
val, blob_data); } return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 3a5d843..015c76a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.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 <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
@@ -404,6 +405,10 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, { struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_group *rgrp = rplane->group;
struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
if (IS_ERR(pstate))
return PTR_ERR(pstate); if (property == rgrp->planes.alpha) rcar_du_plane_set_alpha(rplane, value);
@@ -412,7 +417,8 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, else if (property == rgrp->planes.zpos) rcar_du_plane_set_zpos(rplane, value); else
return -EINVAL;
return drm_plane_set_property(plane, pstate,
property, value, blob_data); return 0;
} diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c index 060ae03..ccf03ea 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.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 <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
@@ -228,6 +229,7 @@ static void shmob_drm_plane_destroy(struct drm_plane *plane) static const struct drm_plane_funcs shmob_drm_plane_funcs = { .update_plane = shmob_drm_plane_update, .disable_plane = shmob_drm_plane_disable,
.set_property = drm_atomic_plane_set_property, .destroy = shmob_drm_plane_destroy,
};
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index ff72b81..78e93ec 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -68,7 +68,8 @@
- struct drm_atomic_funcs - helper funcs used by the atomic helpers
*/ struct drm_atomic_funcs {
int dummy; /* for now */
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);
};
const extern struct drm_atomic_funcs drm_atomic_funcs; @@ -84,6 +85,30 @@ int drm_atomic_commit_unlocked(struct drm_device *dev, struct drm_atomic_state *state); void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state);
+int drm_atomic_plane_set_property(struct drm_plane *plane,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t val, void *blob_data);
+struct drm_plane_state *drm_atomic_get_plane_state(struct drm_plane *plane,
struct drm_atomic_state *state);
+static inline int +drm_atomic_check_plane_state(struct drm_plane *plane,
struct drm_plane_state *pstate)
+{
const struct drm_atomic_funcs *funcs =
plane->dev->driver->atomic_funcs;
return funcs->check_plane_state(plane, pstate);
+}
+static inline int +drm_atomic_commit_plane_state(struct drm_plane *plane,
struct drm_plane_state *pstate)
+{
const struct drm_atomic_funcs *funcs =
plane->dev->driver->atomic_funcs;
return funcs->commit_plane_state(plane, pstate);
+}
/**
- struct drm_atomic_state - the state object used by atomic helpers
*/ @@ -91,6 +116,8 @@ struct drm_atomic_state { struct kref refcount; struct drm_device *dev; uint32_t flags;
struct drm_plane **planes;
struct drm_plane_state **pstates; bool committed; bool checked; /* just for debugging */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 547b75a..58309cc 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -569,7 +569,10 @@ struct drm_plane_funcs { int (*disable_plane)(struct drm_plane *plane); void (*destroy)(struct drm_plane *plane);
int (*set_property)(struct drm_plane *plane,
struct drm_plane_state *(*create_state)(struct drm_plane *plane);
void (*destroy_state)(struct drm_plane *plane,
struct drm_plane_state *pstate);
int (*set_property)(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data);
@@ -582,6 +585,48 @@ enum drm_plane_type { };
/**
- drm_plane_state - mutable plane state
- @update_plane: if full update_plane() is needed (vs pageflip)
- @new_fb: has the fb been changed
- @crtc: currently bound CRTC
- @fb: currently bound fb
- @crtc_x: left position of visible portion of plane on crtc
- @crtc_y: upper position of visible portion of plane on crtc
- @crtc_w: width of visible portion of plane on crtc
- @crtc_h: height of visible portion of plane on crtc
- @src_x: left position of visible portion of plane within
- plane (in 16.16)
- @src_y: upper position of visible portion of plane within
- plane (in 16.16)
- @src_w: width of visible portion of plane (in 16.16)
- @src_h: height of visible portion of plane (in 16.16)
- @propvals: property values
- @state: current global/toplevel state object (for atomic) while an
- update is in progress, NULL otherwise.
- */
+struct drm_plane_state {
bool update_plane : 1;
bool new_fb : 1;
struct drm_crtc *crtc;
struct drm_framebuffer *fb;
/* Signed dest location allows it to be partially off screen */
int32_t crtc_x, crtc_y;
uint32_t crtc_w, crtc_h;
/* Source values are 16.16 fixed point */
uint32_t src_x, src_y;
uint32_t src_h, src_w;
bool enabled;
struct drm_object_property_values propvals;
struct drm_atomic_state *state;
+};
+/**
- drm_plane - central DRM plane control structure
- @dev: DRM device this plane belongs to
- @head: for list management
@@ -591,6 +636,8 @@ enum drm_plane_type {
- @format_count: number of formats supported
- @crtc: currently bound CRTC
- @fb: currently bound fb
- @id: plane number, 0..n
- @state: the mutable state
- @funcs: helper functions
- @properties: property tracking for this plane
- @type: type of plane (overlay, primary, cursor)
@@ -608,10 +655,17 @@ struct drm_plane { struct drm_crtc *crtc; struct drm_framebuffer *fb;
int id;
/*
* State that can be updated from userspace, and atomically
* commited or rolled back:
*/
struct drm_plane_state *state;
const struct drm_plane_funcs *funcs; struct drm_object_properties properties;
struct drm_object_property_values propvals; enum drm_plane_type type;
}; @@ -807,8 +861,20 @@ struct drm_mode_config { bool poll_running; struct delayed_work output_poll_work;
/* pointers to standard properties */
/* just so blob properties can always be in a list: */ struct list_head property_blob_list;
/* pointers to standard properties */
struct drm_property *prop_src_x;
struct drm_property *prop_src_y;
struct drm_property *prop_src_w;
struct drm_property *prop_src_h;
struct drm_property *prop_crtc_x;
struct drm_property *prop_crtc_y;
struct drm_property *prop_crtc_w;
struct drm_property *prop_crtc_h;
struct drm_property *prop_fb_id;
struct drm_property *prop_crtc_id; struct drm_property *edid_property; struct drm_property *dpms_property; struct drm_property *plane_type_property;
@@ -930,11 +996,20 @@ extern int drm_plane_init(struct drm_device *dev, const uint32_t *formats, uint32_t format_count, bool is_primary); extern void drm_plane_cleanup(struct drm_plane *plane); -extern void drm_plane_force_disable(struct drm_plane *plane); +extern void drm_plane_force_disable(struct drm_plane *plane,
struct drm_atomic_state *state);
extern int drm_crtc_check_viewport(const struct drm_crtc *crtc, int x, int y, const struct drm_display_mode *mode, const struct drm_framebuffer *fb); +extern int drm_plane_check_state(struct drm_plane *plane,
struct drm_plane_state *state);
+extern void drm_plane_commit_state(struct drm_plane *plane,
struct drm_plane_state *state);
+extern int drm_plane_set_property(struct drm_plane *plane,
struct drm_plane_state *state,
struct drm_property *property,
uint64_t value, void *blob_data);
extern void drm_encoder_cleanup(struct drm_encoder *encoder);
@@ -984,6 +1059,17 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj, extern int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *value);
+int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t value, void *blob_data);
+int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t value, void *blob_data);
+int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t value, void *blob_data);
extern int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, const struct drm_framebuffer_funcs *funcs); @@ -1165,6 +1251,26 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id) return mo ? obj_to_blob(mo) : NULL; }
+static inline struct drm_plane_state * +drm_plane_create_state(struct drm_plane *plane) +{
if (plane->funcs->create_state)
return plane->funcs->create_state(plane);
return kzalloc(sizeof(struct drm_plane_state), GFP_KERNEL);
+}
+static inline void +drm_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *pstate)
+{
if (pstate->fb)
drm_framebuffer_unreference(pstate->fb);
if (plane->funcs->destroy_state)
plane->funcs->destroy_state(plane, pstate);
else
kfree(pstate);
+}
/* Plane list iterator for legacy (overlay only) planes. */ #define drm_for_each_legacy_plane(plane, planelist) \ list_for_each_entry(plane, planelist, head) \ -- 1.9.0
-- Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
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 --- drivers/gpu/drm/armada/armada_crtc.c | 11 +- drivers/gpu/drm/ast/ast_mode.c | 1 + drivers/gpu/drm/cirrus/cirrus_mode.c | 1 + drivers/gpu/drm/drm_atomic.c | 231 ++++++++++- drivers/gpu/drm/drm_crtc.c | 598 ++++++++++++++++++----------- drivers/gpu/drm/drm_fb_helper.c | 2 +- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 7 +- drivers/gpu/drm/gma500/cdv_intel_display.c | 1 + drivers/gpu/drm/gma500/psb_intel_display.c | 1 + drivers/gpu/drm/i915/intel_display.c | 1 + drivers/gpu/drm/mgag200/mgag200_mode.c | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 6 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 6 +- drivers/gpu/drm/nouveau/dispnv04/crtc.c | 1 + drivers/gpu/drm/nouveau/nv50_display.c | 1 + drivers/gpu/drm/omapdrm/omap_crtc.c | 12 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/qxl/qxl_display.c | 2 + drivers/gpu/drm/radeon/radeon_display.c | 2 + drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 2 + drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 2 + drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 1 + drivers/gpu/drm/udl/udl_modeset.c | 2 + drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 1 + include/drm/drm_atomic.h | 29 ++ include/drm/drm_crtc.h | 103 ++++- 27 files changed, 785 insertions(+), 243 deletions(-)
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 7d3c649..6237af4 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -9,6 +9,7 @@ #include <linux/clk.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include "armada_crtc.h" #include "armada_drm.h" #include "armada_fb.h" @@ -966,7 +967,12 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc, { struct armada_private *priv = crtc->dev->dev_private; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); + struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); bool update_csc = false; + int ret = 0; + + if (IS_ERR(cstate)) + return PTR_ERR(cstate);
if (property == priv->csc_yuv_prop) { dcrtc->csc_yuv_mode = val; @@ -974,6 +980,9 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc, } else if (property == priv->csc_rgb_prop) { dcrtc->csc_rgb_mode = val; update_csc = true; + } else { + ret = drm_crtc_set_property(crtc, cstate, property, + val, blob_data); }
if (update_csc) { @@ -984,7 +993,7 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc, writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL); }
- return 0; + return ret; }
static struct drm_crtc_funcs armada_crtc_funcs = { diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 114aee9..c08e0e1 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -632,6 +632,7 @@ static const struct drm_crtc_funcs ast_crtc_funcs = { .cursor_move = ast_cursor_move, .reset = ast_crtc_reset, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .gamma_set = ast_crtc_gamma_set, .destroy = ast_crtc_destroy, }; diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index 49332c5..3c4428c 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -366,6 +366,7 @@ static void cirrus_crtc_destroy(struct drm_crtc *crtc) static const struct drm_crtc_funcs cirrus_crtc_funcs = { .gamma_set = cirrus_crtc_gamma_set, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = cirrus_crtc_destroy, };
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 403ffc5..863a0fe 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -48,11 +48,13 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, { struct drm_atomic_state *state; int nplanes = dev->mode_config.num_total_plane; + int ncrtcs = dev->mode_config.num_crtc; int sz; void *ptr;
sz = sizeof(*state); sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes; + sz += (sizeof(state->crtcs) + sizeof(state->cstates)) * ncrtcs;
ptr = kzalloc(sz, GFP_KERNEL);
@@ -74,6 +76,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, state->pstates = ptr; ptr = &state->pstates[nplanes];
+ state->crtcs = ptr; + ptr = &state->crtcs[ncrtcs]; + + state->cstates = ptr; + ptr = &state->cstates[ncrtcs]; + return state; } EXPORT_SYMBOL(drm_atomic_begin); @@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev, struct drm_atomic_state *state, struct drm_mode_object *obj, struct drm_pending_vblank_event *event) { - return -EINVAL; /* for now */ + switch (obj->type) { + case DRM_MODE_OBJECT_CRTC: { + struct drm_crtc_state *cstate = + drm_atomic_get_crtc_state(obj_to_crtc(obj), state); + if (IS_ERR(cstate)) + return PTR_ERR(cstate); + cstate->event = event; + return 0; + } + default: + return -EINVAL; + } } EXPORT_SYMBOL(drm_atomic_set_event);
@@ -111,6 +130,7 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_atomic_state *a = state; int nplanes = dev->mode_config.num_total_plane; + int ncrtcs = dev->mode_config.num_crtc; int i, ret = 0;
for (i = 0; i < nplanes; i++) { @@ -120,6 +140,13 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) break; } } + for (i = 0; i < ncrtcs; i++) { + if (a->crtcs[i]) { + ret = drm_atomic_check_crtc_state(a->crtcs[i], a->cstates[i]); + if (ret) + break; + } + }
a->acquire_ctx.frozen = true;
@@ -203,6 +230,7 @@ static void commit_locks(struct drm_atomic_state *a, { struct drm_device *dev = a->dev; int nplanes = dev->mode_config.num_total_plane; + int ncrtcs = dev->mode_config.num_crtc; int i;
for (i = 0; i < nplanes; i++) { @@ -213,6 +241,14 @@ static void commit_locks(struct drm_atomic_state *a, } }
+ for (i = 0; i < ncrtcs; i++) { + struct drm_crtc *crtc = a->crtcs[i]; + if (crtc) { + crtc->state->state = NULL; + drm_crtc_destroy_state(crtc, a->cstates[i]); + } + } + /* and properly release them (clear in_atomic, remove from list): */ drm_modeset_drop_locks(&a->acquire_ctx); ww_acquire_fini(ww_ctx); @@ -223,8 +259,18 @@ static int atomic_commit(struct drm_atomic_state *a, struct ww_acquire_ctx *ww_ctx) { int nplanes = a->dev->mode_config.num_total_plane; + int ncrtcs = a->dev->mode_config.num_crtc; int i, ret = 0;
+ for (i = 0; i < ncrtcs; i++) { + struct drm_crtc *crtc = a->crtcs[i]; + if (crtc) { + ret = drm_atomic_commit_crtc_state(crtc, a->cstates[i]); + if (ret) + break; + } + } + for (i = 0; i < nplanes; i++) { struct drm_plane *plane = a->planes[i]; if (plane) { @@ -403,6 +449,7 @@ static int commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) { struct drm_atomic_state *a = pstate->state; + struct drm_crtc_state *cstate = NULL; struct drm_framebuffer *old_fb = plane->fb; struct drm_framebuffer *fb = pstate->fb; bool enabled = pstate->crtc && fb; @@ -425,8 +472,11 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) } } else { struct drm_crtc *crtc = pstate->crtc; + cstate = drm_atomic_get_crtc_state(crtc, pstate->state); if (pstate->update_plane || (pstate->new_fb && !can_flip(plane, pstate))) { +/* TODO pass event to update_plane().. */ +WARN_ON(cstate->event); ret = plane->funcs->update_plane(plane, crtc, pstate->fb, pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h, @@ -443,7 +493,7 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) }
} else if (pstate->new_fb) { - ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags); + ret = crtc->funcs->page_flip(crtc, fb, cstate->event, a->flags); if (ret == 0) { /* * Warn if the driver hasn't properly updated the plane->fb @@ -473,9 +523,10 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) * original code. */ swap_plane_state(plane, pstate->state); + if (cstate) + cstate->event = NULL; }
- if (fb) drm_framebuffer_unreference(fb); if (old_fb) @@ -484,8 +535,182 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) return ret; }
+int drm_atomic_crtc_set_property(struct drm_crtc *crtc, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) +{ + struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); + if (IS_ERR(cstate)) + return PTR_ERR(cstate); + return drm_crtc_set_property(crtc, cstate, property, val, blob_data); +} +EXPORT_SYMBOL(drm_atomic_crtc_set_property); + +static void init_crtc_state(struct drm_crtc *crtc, + struct drm_crtc_state *cstate, struct drm_atomic_state *state) +{ + /* snapshot current state: */ + *cstate = *crtc->state; + cstate->state = state; + + if (cstate->connector_ids) { + int sz = cstate->num_connector_ids * sizeof(cstate->connector_ids[0]); + cstate->connector_ids = kmemdup(cstate->connector_ids, sz, GFP_KERNEL); + } + + /* this should never happen.. but make sure! */ + WARN_ON(cstate->event); + cstate->event = NULL; +} + +struct drm_crtc_state * +drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a) +{ + struct drm_crtc_state *cstate; + int ret; + + cstate = a->cstates[crtc->id]; + + if (!cstate) { + ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx); + if (ret) + return ERR_PTR(ret); + + cstate = drm_crtc_create_state(crtc); + if (!cstate) + return ERR_PTR(-ENOMEM); + init_crtc_state(crtc, cstate, a); + a->crtcs[crtc->id] = crtc; + a->cstates[crtc->id] = cstate; + + /* we'll need it later, so make sure we have state + * for primary plane too: + */ + drm_atomic_get_plane_state(crtc->primary, a); + } + return cstate; +} +EXPORT_SYMBOL(drm_atomic_get_crtc_state); + +static void +swap_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a) +{ + struct drm_crtc_state *cstate = a->cstates[crtc->id]; + struct drm_device *dev = crtc->dev; + struct drm_pending_vblank_event *event = cstate->event; + + if (event) { + /* hrm, need to sort out a better way to send events for + * other-than-pageflip.. but modeset is not async, so: + */ + unsigned long flags; + spin_lock_irqsave(&dev->event_lock, flags); + drm_send_vblank_event(dev, crtc->id, event); + cstate->event = NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); + } + + /* clear transient state (only valid during atomic update): */ + cstate->set_config = false; + cstate->connectors_change = false; + + swap(crtc->state, a->cstates[crtc->id]); + crtc->base.propvals = &crtc->state->propvals; +} + +static struct drm_connector **get_connector_set(struct drm_device *dev, + uint32_t *connector_ids, uint32_t num_connector_ids) +{ + struct drm_connector **connector_set = NULL; + int i; + + connector_set = kmalloc(num_connector_ids * + sizeof(struct drm_connector *), + GFP_KERNEL); + if (!connector_set) + return NULL; + + for (i = 0; i < num_connector_ids; i++) + connector_set[i] = drm_connector_find(dev, connector_ids[i]); + + return connector_set; +} + +static int set_config(struct drm_crtc *crtc, struct drm_crtc_state *cstate) +{ + struct drm_device *dev = crtc->dev; + struct drm_plane_state *pstate = + drm_atomic_get_plane_state(crtc->primary, cstate->state); + struct drm_framebuffer *fb = pstate->fb; + struct drm_connector **connector_set = get_connector_set(crtc->dev, + cstate->connector_ids, cstate->num_connector_ids); + struct drm_display_mode *mode = drm_crtc_get_mode(crtc, cstate); + struct drm_mode_set set = { + .crtc = crtc, + .x = pstate->src_x >> 16, + .y = pstate->src_y >> 16, + .mode = mode, + .num_connectors = cstate->num_connector_ids, + .connectors = connector_set, + .fb = fb, + }; + int ret; + + if (IS_ERR(mode)) { + ret = PTR_ERR(mode); + return ret; + } + + if (fb) + drm_framebuffer_reference(fb); + + ret = drm_mode_set_config_internal(&set); + if (!ret) { + swap_crtc_state(crtc, cstate->state); + pstate->new_fb = pstate->update_plane = false; + } + + if (fb) + drm_framebuffer_unreference(fb); + + kfree(connector_set); + if (mode) + drm_mode_destroy(dev, mode); + return ret; +} + +static int +commit_crtc_state(struct drm_crtc *crtc, + struct drm_crtc_state *cstate) +{ + struct drm_plane_state *pstate = + drm_atomic_get_plane_state(crtc->primary, cstate->state); + int ret = -EINVAL; + + if (cstate->set_config) + return set_config(crtc, cstate); + + if (!pstate->fb) { + /* disable */ + struct drm_mode_set set = { + .crtc = crtc, + .fb = NULL, + }; + + ret = drm_mode_set_config_internal(&set); + if (!ret) { + swap_crtc_state(crtc, cstate->state); + } + } + + return ret; +} + const struct drm_atomic_funcs drm_atomic_funcs = { .check_plane_state = drm_plane_check_state, .commit_plane_state = commit_plane_state, + + .check_crtc_state = drm_crtc_check_state, + .commit_crtc_state = commit_crtc_state, }; EXPORT_SYMBOL(drm_atomic_funcs); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index b556a31..e14d517 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -689,10 +689,7 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); void drm_framebuffer_remove(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev; - struct drm_crtc *crtc; struct drm_plane *plane; - struct drm_mode_set set; - int ret;
WARN_ON(!list_empty(&fb->filp_head));
@@ -712,7 +709,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) * in this manner. */ if (atomic_read(&fb->refcount.refcount) > 1) { - void *state; + struct drm_atomic_state *state;
state = dev->driver->atomic_begin(dev, 0); if (IS_ERR(state)) { @@ -720,24 +717,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) return; }
- /* TODO once CRTC is converted to state/properties, we can push the - * locking down into drm_atomic_commit(), since that is where - * the actual changes take place.. - */ - drm_modeset_lock_all(dev); - /* remove from any CRTC */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->primary->fb == fb) { - /* should turn off the crtc */ - memset(&set, 0, sizeof(struct drm_mode_set)); - set.crtc = crtc; - set.fb = NULL; - ret = drm_mode_set_config_internal(&set); - if (ret) - DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); - } - } - + /* remove from any plane */ list_for_each_entry(plane, &dev->mode_config.plane_list, head) { if (plane->fb == fb) drm_plane_force_disable(plane, state); @@ -750,8 +730,6 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) dev->driver->atomic_commit(dev, state);
dev->driver->atomic_end(dev, state); - - drm_modeset_unlock_all(dev); }
drm_framebuffer_unreference(fb); @@ -782,9 +760,13 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, struct drm_mode_config *config = &dev->mode_config; int ret;
+ /* this is now required: */ + WARN_ON(!funcs->set_property); + crtc->dev = dev; crtc->funcs = funcs; - crtc->invert_dimensions = false; + crtc->state = drm_crtc_create_state(crtc); + crtc->state->invert_dimensions = false;
drm_modeset_lock_all(dev); drm_modeset_lock_init(&crtc->mutex); @@ -796,14 +778,17 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, goto out;
crtc->base.properties = &crtc->properties; - crtc->base.propvals = &crtc->propvals; + crtc->base.propvals = &crtc->state->propvals;
list_add_tail(&crtc->head, &dev->mode_config.crtc_list); dev->mode_config.num_crtc++;
crtc->primary = primary; if (primary) - primary->possible_crtcs = 1 << drm_crtc_index(crtc); + primary->possible_crtcs = 1 << crtc->id; + + drm_object_attach_property(&crtc->base, config->prop_mode, 0); + drm_object_attach_property(&crtc->base, config->prop_connector_ids, 0);
out: drm_modeset_unlock_all(dev); @@ -832,31 +817,245 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) drm_mode_object_put(dev, &crtc->base); list_del(&crtc->head); dev->mode_config.num_crtc--; + + drm_crtc_destroy_state(crtc, crtc->state); } EXPORT_SYMBOL(drm_crtc_cleanup);
-/** - * drm_crtc_index - find the index of a registered CRTC - * @crtc: CRTC to find index for - * - * Given a registered CRTC, return the index of that CRTC within a DRM - * device's list of CRTCs. - */ -unsigned int drm_crtc_index(struct drm_crtc *crtc) +/* get display-mode from user-mode */ +struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc, + struct drm_crtc_state *cstate) { - unsigned int index = 0; - struct drm_crtc *tmp; + struct drm_display_mode *mode = NULL; + if (cstate->mode_valid) { + struct drm_device *dev = crtc->dev; + int ret;
- list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { - if (tmp == crtc) - return index; + mode = drm_mode_create(dev); + if (!mode) + return ERR_PTR(-ENOMEM); + + ret = drm_crtc_convert_umode(mode, &cstate->mode); + if (ret) { + DRM_DEBUG_KMS("Invalid mode\n"); + drm_mode_destroy(dev, mode); + return ERR_PTR(ret); + }
- index++; + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); } + return mode; +}
- BUG(); +static int connector_idx(struct drm_crtc_state *state, + uint32_t connector_id) +{ + int i; + for (i = 0; i < state->num_connector_ids; i++) + if (state->connector_ids[i] == connector_id) + return i; + return -1; +} + +static int remove_connector(struct drm_crtc *ocrtc, + struct drm_crtc_state *ostate, struct drm_atomic_state *state, + int idx) +{ + struct drm_mode_config *config = &ocrtc->dev->mode_config; + uint32_t *new_connector_ids; + int a, b; + + /* before deletion point: */ + a = idx * sizeof(ostate->connector_ids[0]); + + /* after deletion point: */ + b = (ostate->num_connector_ids - 1 - idx) * + sizeof(ostate->connector_ids[0]); + + new_connector_ids = kmalloc(a+b, GFP_KERNEL); + if (!new_connector_ids) + return -ENOMEM; + + memcpy(new_connector_ids, ostate->connector_ids, a); + memcpy(&new_connector_ids[idx], + &ostate->connector_ids[idx + 1], b); + + return drm_mode_crtc_set_obj_prop(ocrtc, state, + config->prop_connector_ids, a + b, + new_connector_ids); } -EXPORT_SYMBOL(drm_crtc_index); + +static int check_connectors(struct drm_crtc *crtc, + struct drm_atomic_state *state, bool fix, + uint32_t *connector_ids, uint32_t num_connector_ids) +{ + struct drm_mode_config *config = &crtc->dev->mode_config; + struct drm_crtc *ocrtc; /* other connector */ + + list_for_each_entry(ocrtc, &config->crtc_list, head) { + struct drm_crtc_state *ostate; /* other state */ + unsigned i; + + if (ocrtc == crtc) + continue; + + ostate = drm_atomic_get_crtc_state(crtc, state); + if (IS_ERR(ostate)) + return PTR_ERR(ostate); + + for (i = 0; i < num_connector_ids; i++) { + struct drm_connector *connector; + uint32_t cid = connector_ids[i]; + int idx; + +retry: + idx = connector_idx(ostate, cid); + if (idx < 0) + continue; + + if (fix) { + int ret = remove_connector(ocrtc, + ostate, state, idx); + if (ret) + return ret; + goto retry; + } + + connector = drm_connector_find(crtc->dev, cid); + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] already in use\n", + connector->base.id, + drm_get_connector_name(connector)); + return -EINVAL; + } + } + + return 0; +} + +int drm_crtc_check_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct drm_plane *primary = crtc->primary; + struct drm_plane_state *pstate = + drm_atomic_get_plane_state(primary, state->state); + struct drm_framebuffer *fb = pstate->fb; + int hdisplay, vdisplay; + struct drm_display_mode *mode = drm_crtc_get_mode(crtc, state); + unsigned x, y; + + if (IS_ERR(mode)) + return PTR_ERR(mode); + + /* disabling the crtc is allowed: */ + if (!(fb && state->mode_valid)) + return 0; + + hdisplay = state->mode.hdisplay; + vdisplay = state->mode.vdisplay; + + if (mode && drm_mode_is_stereo(mode)) { + struct drm_display_mode adjusted = *mode; + + drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE); + hdisplay = adjusted.crtc_hdisplay; + vdisplay = adjusted.crtc_vdisplay; + } + + if (state->invert_dimensions) + swap(hdisplay, vdisplay); + + x = pstate->src_x >> 16; + y = pstate->src_y >> 16; + + if (hdisplay > fb->width || + vdisplay > fb->height || + x > fb->width - hdisplay || + y > fb->height - vdisplay) { + DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", + fb->width, fb->height, hdisplay, vdisplay, + x, y, state->invert_dimensions ? " (inverted)" : ""); + return -ENOSPC; + } + + if (crtc->enabled && !state->set_config) { + if (primary->state->fb->pixel_format != fb->pixel_format) { + DRM_DEBUG_KMS("Page flip is not allowed to " + "change frame buffer format.\n"); + return -EINVAL; + } + } + + if (state->num_connector_ids == 0) { + DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); + return -EINVAL; + } + + if (state->connectors_change) { + int ret = check_connectors(crtc, state->state, false, + state->connector_ids, state->num_connector_ids); + if (ret) + return ret; + } + + if (mode) + drm_mode_destroy(crtc->dev, mode); + + return 0; +} +EXPORT_SYMBOL(drm_crtc_check_state); + +void drm_crtc_commit_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + crtc->state = state; + crtc->base.propvals = &state->propvals; +} +EXPORT_SYMBOL(drm_crtc_commit_state); + +int drm_crtc_set_property(struct drm_crtc *crtc, + struct drm_crtc_state *state, + struct drm_property *property, + uint64_t value, void *blob_data) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_config *config = &dev->mode_config; + + /* grab primary plane state now, to ensure locks are held, etc. */ + drm_atomic_get_plane_state(crtc->primary, state->state); + + drm_object_property_set_value(&crtc->base, + &state->propvals, property, value, blob_data); + + if (property == config->prop_mode) { + if (!blob_data) { + memset(&state->mode, 0, sizeof(state->mode)); + state->mode_valid = false; + } else { + /* check size: */ + if (value < sizeof(struct drm_mode_modeinfo)) + return -EINVAL; + state->mode = *(struct drm_mode_modeinfo *)blob_data; + state->mode_valid = true; + } + state->set_config = true; + } else if (property == config->prop_connector_ids) { + /* if connector-id's changing, we need to have all the locks: */ + struct drm_atomic_state *a = state->state; + int ret = drm_modeset_lock_all_crtcs(crtc->dev, &a->acquire_ctx); + if (ret) + return ret; + state->connectors_change = true; + state->num_connector_ids = value / sizeof(state->connector_ids[0]); + kfree(state->connector_ids); + state->connector_ids = blob_data; + state->set_config = true; + } else { + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(drm_crtc_set_property);
/* * drm_mode_remove - remove and free a mode @@ -1239,6 +1438,10 @@ int drm_plane_check_state(struct drm_plane *plane, if (!fb) return 0;
+ /* we'll need this later during commit: */ + if (state->crtc) + drm_atomic_get_crtc_state(state->crtc, state->state); + fb_width = fb->width << 16; fb_height = fb->height << 16;
@@ -1465,6 +1668,16 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev) return -ENOMEM; dev->mode_config.prop_crtc_id = prop;
+ prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_connector_ids = prop; + + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_mode = prop; + return 0; }
@@ -1754,7 +1967,7 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, * Returns: * Zero on success, errno on failure. */ -static int drm_crtc_convert_umode(struct drm_display_mode *out, +int drm_crtc_convert_umode(struct drm_display_mode *out, const struct drm_mode_modeinfo *in) { if (in->clock > INT_MAX || in->vrefresh > INT_MAX) @@ -2001,8 +2214,8 @@ int drm_mode_getcrtc(struct drm_device *dev, goto out; }
- crtc_resp->x = crtc->x; - crtc_resp->y = crtc->y; + crtc_resp->x = crtc->primary->state->src_x >> 16; + crtc_resp->y = crtc->primary->state->src_y >> 16; crtc_resp->gamma_size = crtc->gamma_size; if (crtc->primary->fb) crtc_resp->fb_id = crtc->primary->fb->base.id; @@ -2495,7 +2708,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc, vdisplay = adjusted.crtc_vdisplay; }
- if (crtc->invert_dimensions) + if (crtc->state->invert_dimensions) swap(hdisplay, vdisplay);
if (hdisplay > fb->width || @@ -2504,7 +2717,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc, y > fb->height - vdisplay) { DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", fb->width, fb->height, hdisplay, vdisplay, x, y, - crtc->invert_dimensions ? " (inverted)" : ""); + crtc->state->invert_dimensions ? " (inverted)" : ""); return -ENOSPC; }
@@ -2531,22 +2744,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_mode_config *config = &dev->mode_config; struct drm_mode_crtc *crtc_req = data; struct drm_crtc *crtc; - struct drm_connector **connector_set = NULL, *connector; - struct drm_framebuffer *fb = NULL; - struct drm_display_mode *mode = NULL; - struct drm_mode_set set; - uint32_t __user *set_connectors_ptr; + uint32_t fb_id = -1; + uint32_t *connector_ids = NULL; + struct drm_atomic_state *state = NULL; int ret; int i;
if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL;
- /* For some reason crtc x/y offsets are signed internally. */ - if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX) - return -ERANGE; - - drm_modeset_lock_all(dev); crtc = drm_crtc_find(dev, crtc_req->crtc_id); if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); @@ -2564,55 +2770,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ret = -EINVAL; goto out; } - fb = crtc->primary->fb; - /* Make refcounting symmetric with the lookup path. */ - drm_framebuffer_reference(fb); + fb_id = crtc->primary->fb->base.id; } else { - fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); - if (!fb) { - DRM_DEBUG_KMS("Unknown FB ID%d\n", - crtc_req->fb_id); - ret = -ENOENT; - goto out; - } - } - - mode = drm_mode_create(dev); - if (!mode) { - ret = -ENOMEM; - goto out; + fb_id = crtc_req->fb_id; } - - ret = drm_crtc_convert_umode(mode, &crtc_req->mode); - if (ret) { - DRM_DEBUG_KMS("Invalid mode\n"); - goto out; - } - - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); - - ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y, - mode, fb); - if (ret) - goto out; - - } - - if (crtc_req->count_connectors == 0 && mode) { - DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); - ret = -EINVAL; - goto out; - } - - if (crtc_req->count_connectors > 0 && (!mode || !fb)) { - DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n", - crtc_req->count_connectors); - ret = -EINVAL; - goto out; }
if (crtc_req->count_connectors > 0) { - u32 out_id; + uint32_t __user *set_connectors_ptr = + (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
/* Avoid unbounded kernel memory allocation */ if (crtc_req->count_connectors > config->num_connector) { @@ -2620,52 +2786,65 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; }
- connector_set = kmalloc(crtc_req->count_connectors * - sizeof(struct drm_connector *), + connector_ids = kmalloc(crtc_req->count_connectors * + sizeof(connector_ids[0]), GFP_KERNEL); - if (!connector_set) { + if (!connector_ids) { ret = -ENOMEM; goto out; }
for (i = 0; i < crtc_req->count_connectors; i++) { - set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr; + u32 out_id; + if (get_user(out_id, &set_connectors_ptr[i])) { ret = -EFAULT; goto out; } - - connector = drm_connector_find(dev, out_id); - if (!connector) { - DRM_DEBUG_KMS("Connector id %d unknown\n", - out_id); - ret = -ENOENT; - goto out; - } - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", - connector->base.id, - drm_get_connector_name(connector)); - - connector_set[i] = connector; + connector_ids[i] = out_id; } }
- set.crtc = crtc; - set.x = crtc_req->x; - set.y = crtc_req->y; - set.mode = mode; - set.connectors = connector_set; - set.num_connectors = crtc_req->count_connectors; - set.fb = fb; - ret = drm_mode_set_config_internal(&set); +retry: + state = dev->driver->atomic_begin(dev, 0); + if (IS_ERR(state)) + return PTR_ERR(state);
-out: - if (fb) - drm_framebuffer_unreference(fb); + /* If connectors change, we need to check if we need to steal one + * from another CRTC.. setcrtc makes this implicit, but atomic + * treats it as an error so we need to handle here: + */ + ret = check_connectors(crtc, state, true, + connector_ids, crtc_req->count_connectors); + if (ret) + goto out;
- kfree(connector_set); - drm_mode_destroy(dev, mode); - drm_modeset_unlock_all(dev); + ret = + drm_mode_crtc_set_obj_prop(crtc, state, + config->prop_mode, sizeof(crtc_req->mode), &crtc_req->mode) || + drm_mode_crtc_set_obj_prop(crtc, state, + config->prop_connector_ids, + crtc_req->count_connectors * sizeof(connector_ids[0]), + connector_ids) || + drm_mode_plane_set_obj_prop(crtc->primary, state, + config->prop_crtc_id, crtc->base.id, NULL) || + drm_mode_plane_set_obj_prop(crtc->primary, state, + config->prop_fb_id, fb_id, NULL) || + drm_mode_plane_set_obj_prop(crtc->primary, state, + config->prop_src_x, crtc_req->x << 16, NULL) || + drm_mode_plane_set_obj_prop(crtc->primary, state, + config->prop_src_y, crtc_req->y << 16, NULL) || + dev->driver->atomic_check(dev, state); + if (ret) + goto out; + + ret = dev->driver->atomic_commit(dev, state); + +out: + if (state) + dev->driver->atomic_end(dev, state); + if (ret == -EDEADLK) + goto retry; return ret; }
@@ -4028,9 +4207,6 @@ int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, if (crtc->funcs->set_property) ret = crtc->funcs->set_property(crtc, state, property, value, blob_data); - if (!ret) - drm_object_property_set_value(&crtc->base, &crtc->propvals, - property, value, NULL);
return ret; } @@ -4424,6 +4600,51 @@ out: return ret; }
+static struct drm_pending_vblank_event *create_vblank_event( + struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data) +{ + struct drm_pending_vblank_event *e = NULL; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + if (file_priv->event_space < sizeof e->event) { + spin_unlock_irqrestore(&dev->event_lock, flags); + goto out; + } + file_priv->event_space -= sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + + e = kzalloc(sizeof *e, GFP_KERNEL); + if (e == NULL) { + spin_lock_irqsave(&dev->event_lock, flags); + file_priv->event_space += sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + goto out; + } + + e->event.base.type = DRM_EVENT_FLIP_COMPLETE; + e->event.base.length = sizeof e->event; + e->event.user_data = user_data; + e->base.event = &e->event.base; + e->base.file_priv = file_priv; + e->base.destroy = + (void (*) (struct drm_pending_event *)) kfree; + +out: + return e; +} + +static void destroy_vblank_event(struct drm_device *dev, + struct drm_file *file_priv, struct drm_pending_vblank_event *e) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + file_priv->event_space += sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + kfree(e); +} + /** * drm_mode_page_flip_ioctl - schedule an asynchronous fb update * @dev: DRM device @@ -4446,10 +4667,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_page_flip *page_flip = data; + struct drm_mode_config *config = &dev->mode_config; struct drm_crtc *crtc; - struct drm_framebuffer *fb = NULL, *old_fb = NULL; struct drm_pending_vblank_event *e = NULL; - unsigned long flags; + struct drm_atomic_state *state; int ret = -EINVAL;
if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS || @@ -4463,92 +4684,41 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (!crtc) return -ENOENT;
- drm_modeset_lock(&crtc->mutex, NULL); - if (crtc->primary->fb == NULL) { - /* The framebuffer is currently unbound, presumably - * due to a hotplug event, that userspace has not - * yet discovered. - */ - ret = -EBUSY; - goto out; - } - - if (crtc->funcs->page_flip == NULL) - goto out; - - fb = drm_framebuffer_lookup(dev, page_flip->fb_id); - if (!fb) { - ret = -ENOENT; - goto out; - } - - ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); - if (ret) - goto out; - - if (crtc->primary->fb->pixel_format != fb->pixel_format) { - DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); - ret = -EINVAL; - goto out; - } +retry: + state = dev->driver->atomic_begin(dev, + page_flip->flags | DRM_MODE_ATOMIC_NONBLOCK); + if (IS_ERR(state)) + return PTR_ERR(state);
if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { - ret = -ENOMEM; - spin_lock_irqsave(&dev->event_lock, flags); - if (file_priv->event_space < sizeof e->event) { - spin_unlock_irqrestore(&dev->event_lock, flags); + e = create_vblank_event(dev, file_priv, page_flip->user_data); + if (!e) { + ret = -ENOMEM; goto out; } - file_priv->event_space -= sizeof e->event; - spin_unlock_irqrestore(&dev->event_lock, flags); - - e = kzalloc(sizeof *e, GFP_KERNEL); - if (e == NULL) { - spin_lock_irqsave(&dev->event_lock, flags); - file_priv->event_space += sizeof e->event; - spin_unlock_irqrestore(&dev->event_lock, flags); + ret = dev->driver->atomic_set_event(dev, state, &crtc->base, e); + if (ret) { goto out; } - - e->event.base.type = DRM_EVENT_FLIP_COMPLETE; - e->event.base.length = sizeof e->event; - e->event.user_data = page_flip->user_data; - e->base.event = &e->event.base; - e->base.file_priv = file_priv; - e->base.destroy = - (void (*) (struct drm_pending_event *)) kfree; }
- old_fb = crtc->primary->fb; - ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags); - if (ret) { - if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { - spin_lock_irqsave(&dev->event_lock, flags); - file_priv->event_space += sizeof e->event; - spin_unlock_irqrestore(&dev->event_lock, flags); - kfree(e); - } - /* Keep the old fb, don't unref it. */ - old_fb = NULL; - } else { - /* - * Warn if the driver hasn't properly updated the crtc->fb - * field to reflect that the new framebuffer is now used. - * Failing to do so will screw with the reference counting - * on framebuffers. - */ - WARN_ON(crtc->primary->fb != fb); - /* Unref only the old framebuffer. */ - fb = NULL; - } + ret = drm_mode_plane_set_obj_prop(crtc->primary, state, + config->prop_fb_id, page_flip->fb_id, NULL); + if (ret) + goto out;
-out: - if (fb) - drm_framebuffer_unreference(fb); - if (old_fb) - drm_framebuffer_unreference(old_fb); - drm_modeset_unlock(&crtc->mutex); + ret = dev->driver->atomic_check(dev, state); + if (ret) + goto out; + + ret = dev->driver->atomic_commit(dev, state);
+out: + if (ret && e) + destroy_vblank_event(dev, file_priv, e); + dev->driver->atomic_end(dev, state); + if (ret == -EDEADLK) + goto retry; return ret; }
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index b73d3b0..4669e69 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -286,7 +286,7 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; struct drm_plane *plane; bool error = false; - void *state; + struct drm_atomic_state *state; int i;
drm_warn_on_modeset_not_all_locked(dev); diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 2a56973..f3c7e77 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -14,6 +14,7 @@
#include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h>
#include "exynos_drm_crtc.h" #include "exynos_drm_drv.h" @@ -289,6 +290,10 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct exynos_drm_private *dev_priv = dev->dev_private; struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); + + if (IS_ERR(cstate)) + return PTR_ERR(cstate);
if (property == dev_priv->crtc_mode_property) { enum exynos_crtc_mode mode = val; @@ -313,7 +318,7 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, return 0; }
- return -EINVAL; + return drm_crtc_set_property(crtc, cstate, property, val, blob_data); }
static struct drm_crtc_funcs exynos_crtc_funcs = { diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 6672732..5b6eee9 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -989,6 +989,7 @@ const struct drm_crtc_funcs cdv_intel_crtc_funcs = { .cursor_move = gma_crtc_cursor_move, .gamma_set = gma_crtc_gamma_set, .set_config = gma_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = gma_crtc_destroy, };
diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index 87b50ba..79b5692 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c @@ -444,6 +444,7 @@ const struct drm_crtc_funcs psb_intel_crtc_funcs = { .cursor_move = gma_crtc_cursor_move, .gamma_set = gma_crtc_gamma_set, .set_config = gma_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = gma_crtc_destroy, };
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e9f6eb7..53b996f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -10430,6 +10430,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { .cursor_move = intel_crtc_cursor_move, .gamma_set = intel_crtc_gamma_set, .set_config = intel_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = intel_crtc_destroy, .page_flip = intel_crtc_page_flip, }; diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index a034ed4..ba9bd91 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -1296,6 +1296,7 @@ static const struct drm_crtc_funcs mga_crtc_funcs = { .cursor_move = mga_crtc_cursor_move, .gamma_set = mga_crtc_gamma_set, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = mga_crtc_destroy, };
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index 7cf0f78..d0d8befd 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -471,8 +471,10 @@ static int mdp4_crtc_set_property(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) { - // XXX - return -EINVAL; + struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); + if (IS_ERR(cstate)) + return PTR_ERR(cstate); + return drm_crtc_set_property(crtc, cstate, property, val, blob_data); }
#define CURSOR_WIDTH 64 diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 771390b..7f4ee99 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -389,8 +389,10 @@ static int mdp5_crtc_set_property(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) { - // XXX - return -EINVAL; + struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); + if (IS_ERR(cstate)) + return PTR_ERR(cstate); + return drm_crtc_set_property(crtc, cstate, property, val, blob_data); }
static const struct drm_crtc_funcs mdp5_crtc_funcs = { diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index 41be342..9e24632 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -1086,6 +1086,7 @@ static const struct drm_crtc_funcs nv04_crtc_funcs = { .cursor_move = nv04_crtc_cursor_move, .gamma_set = nv_crtc_gamma_set, .set_config = nouveau_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .page_flip = nouveau_crtc_page_flip, .destroy = nv_crtc_destroy, }; diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 58af547..ecbffeb 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -1329,6 +1329,7 @@ static const struct drm_crtc_funcs nv50_crtc_func = { .cursor_move = nv50_crtc_cursor_move, .gamma_set = nv50_crtc_gamma_set, .set_config = nouveau_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = nv50_crtc_destroy, .page_flip = nouveau_crtc_page_flip, }; diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index a75934d..772687b 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -387,14 +387,22 @@ static int omap_crtc_set_property(struct drm_crtc *crtc, { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct omap_drm_private *priv = crtc->dev->dev_private; + struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); + int ret; + + if (IS_ERR(cstate)) + return PTR_ERR(cstate);
if (property == priv->rotation_prop) { - crtc->invert_dimensions = + cstate->invert_dimensions = !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270))); }
- return omap_plane_set_property(omap_crtc->plane, state, + ret = omap_plane_set_property(omap_crtc->plane, state, property, val, blob_data); + if (ret) + ret = drm_crtc_set_property(crtc, cstate, property, val, blob_data); + return ret; }
static const struct drm_crtc_funcs omap_crtc_funcs = { diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index da80bdc..3f64c47 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -579,7 +579,7 @@ static void dev_lastclose(struct drm_device *dev) */ for (i = 0; i < priv->num_crtcs; i++) { drm_object_property_set_value(&priv->crtcs[i]->base, - &priv->crtcs[i]->propvals, + &priv->crtcs[i]->state->propvals, priv->rotation_prop, 0, NULL); }
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index b54c970..25896a9 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -29,6 +29,7 @@ #include "qxl_drv.h" #include "qxl_object.h" #include "drm_crtc_helper.h" +#include "drm_atomic.h"
static bool qxl_head_enabled(struct qxl_head *head) { @@ -373,6 +374,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = { .cursor_set2 = qxl_crtc_cursor_set2, .cursor_move = qxl_crtc_cursor_move, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = qxl_crtc_destroy, };
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 8d99d5e..cc86aac 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -32,6 +32,7 @@
#include <linux/pm_runtime.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include <drm/drm_edid.h>
#include <linux/gcd.h> @@ -546,6 +547,7 @@ static const struct drm_crtc_funcs radeon_crtc_funcs = { .cursor_move = radeon_crtc_cursor_move, .gamma_set = radeon_crtc_gamma_set, .set_config = radeon_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = radeon_crtc_destroy, .page_flip = radeon_crtc_page_flip, }; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 299267d..f5a3d55 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -17,6 +17,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
@@ -527,6 +528,7 @@ static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, static const struct drm_crtc_funcs crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .page_flip = rcar_du_crtc_page_flip, };
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index 90e023a..0a5280c 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -17,6 +17,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
@@ -506,6 +507,7 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc, static const struct drm_crtc_funcs crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .page_flip = shmob_drm_crtc_page_flip, };
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 92839ba..b07f116 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -411,6 +411,7 @@ static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, static const struct drm_crtc_funcs tilcdc_crtc_funcs = { .destroy = tilcdc_crtc_destroy, .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .page_flip = tilcdc_crtc_page_flip, };
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index cddc4fc..36d0116 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -14,6 +14,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include "udl_drv.h"
/* @@ -383,6 +384,7 @@ static struct drm_crtc_helper_funcs udl_helper_funcs = {
static const struct drm_crtc_funcs udl_crtc_funcs = { .set_config = drm_crtc_helper_set_config, + .set_property = drm_atomic_crtc_set_property, .destroy = udl_crtc_destroy, };
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index b2b9bd2..0313b00 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -300,6 +300,7 @@ static struct drm_crtc_funcs vmw_legacy_crtc_funcs = { .cursor_move = vmw_du_crtc_cursor_move, .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_ldu_crtc_destroy, + .set_property = drm_atomic_crtc_set_property, .set_config = vmw_ldu_crtc_set_config, };
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index a95d3a0..b723e09 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -397,6 +397,7 @@ static struct drm_crtc_funcs vmw_screen_object_crtc_funcs = { .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_sou_crtc_destroy, .set_config = vmw_sou_crtc_set_config, + .set_property = drm_atomic_crtc_set_property, .page_flip = vmw_du_page_flip, };
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 78e93ec..7946b7f 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -70,6 +70,9 @@ struct drm_atomic_funcs { int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate); int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate); + + int (*check_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate); + int (*commit_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate); };
const extern struct drm_atomic_funcs drm_atomic_funcs; @@ -109,6 +112,30 @@ drm_atomic_commit_plane_state(struct drm_plane *plane, return funcs->commit_plane_state(plane, pstate); }
+int drm_atomic_crtc_set_property(struct drm_crtc *crtc, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data); +struct drm_crtc_state *drm_atomic_get_crtc_state(struct drm_crtc *crtc, + struct drm_atomic_state *state); + +static inline int +drm_atomic_check_crtc_state(struct drm_crtc *crtc, + struct drm_crtc_state *cstate) +{ + const struct drm_atomic_funcs *funcs = + crtc->dev->driver->atomic_funcs; + return funcs->check_crtc_state(crtc, cstate); +} + +static inline int +drm_atomic_commit_crtc_state(struct drm_crtc *crtc, + struct drm_crtc_state *cstate) +{ + const struct drm_atomic_funcs *funcs = + crtc->dev->driver->atomic_funcs; + return funcs->commit_crtc_state(crtc, cstate); +} + /** * struct drm_atomic_state - the state object used by atomic helpers */ @@ -118,6 +145,8 @@ struct drm_atomic_state { uint32_t flags; struct drm_plane **planes; struct drm_plane_state **pstates; + struct drm_crtc **crtcs; + struct drm_crtc_state **cstates;
bool committed; bool checked; /* just for debugging */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 58309cc..2fbf13a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -284,6 +284,10 @@ struct drm_crtc_funcs { struct drm_pending_vblank_event *event, uint32_t flags);
+ struct drm_crtc_state *(*create_state)(struct drm_crtc *crtc); + void (*destroy_state)(struct drm_crtc *crtc, + struct drm_crtc_state *cstate); + int (*set_property)(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, @@ -291,21 +295,52 @@ struct drm_crtc_funcs { };
/** + * drm_crtc_state - mutable crtc state + * @invert_dimensions: for purposes of error checking crtc vs fb sizes, + * invert the width/height of the crtc. This is used if the driver + * is performing 90 or 270 degree rotated scanout + * @mode_valid: a valid mode has been set + * @set_config: needs modeset (crtc->set_config()) + * @connectors_change: the connector-ids array has changed + * @num_connector_ids: the number of connector-ids + * @connector_ids: array of connector ids + * @mode: current mode timings + * @event: pending pageflip event + * @propvals: property values + * @state: current global/toplevel state object (for atomic) while an + * update is in progress, NULL otherwise. + */ +struct drm_crtc_state { + bool invert_dimensions : 1; + bool mode_valid : 1; + + /* transient state, only valid during atomic operation: */ + bool set_config : 1; + bool connectors_change : 1; + + uint8_t num_connector_ids; + uint32_t *connector_ids; + struct drm_mode_modeinfo mode; + + struct drm_pending_vblank_event *event; + + struct drm_object_property_values propvals; + + struct drm_atomic_state *state; +}; + +/** * drm_crtc - central CRTC control structure * @dev: parent DRM device * @head: list management + * @id: CRTC number, 0..n * @mutex: per-CRTC locking * @base: base KMS object for ID tracking etc. * @primary: primary plane for this CRTC * @cursor: cursor plane for this CRTC + * @state: the mutable state * @enabled: is this CRTC enabled? - * @mode: current mode timings * @hwmode: mode timings as programmed to hw regs - * @invert_dimensions: for purposes of error checking crtc vs fb sizes, - * invert the width/height of the crtc. This is used if the driver - * is performing 90 or 270 degree rotated scanout - * @x: x position on screen - * @y: y position on screen * @funcs: CRTC control functions * @gamma_size: size of gamma ramp * @gamma_store: gamma ramp values @@ -322,6 +357,8 @@ struct drm_crtc { struct drm_device *dev; struct list_head head;
+ int id; + /** * crtc mutex * @@ -337,23 +374,19 @@ struct drm_crtc { struct drm_plane *primary; struct drm_plane *cursor;
+ struct drm_crtc_state *state; + /* Temporary tracking of the old fb while a modeset is ongoing. Used * by drm_mode_set_config_internal to implement correct refcounting. */ struct drm_framebuffer *old_fb;
bool enabled;
- /* Requested mode from modesetting. */ - struct drm_display_mode mode; - /* Programmed mode in hw, after adjustments for encoders, * crtc, panel scaling etc. Needed for timestamping etc. */ struct drm_display_mode hwmode;
- bool invert_dimensions; - - int x, y; const struct drm_crtc_funcs *funcs;
/* CRTC gamma size for reporting to userspace */ @@ -367,9 +400,15 @@ struct drm_crtc { void *helper_private;
struct drm_object_properties properties; - struct drm_object_property_values propvals; -};
+ /* These are (temporary) duplicate information from what is in the + * drm_crtc_state struct.. keeping duplicate copy here makes the + * switch to atomic far less intrusive. Once all the drivers and + * the crtc/fb helpers are updated, then we can remove these: + */ + int x, y; + struct drm_display_mode mode; +};
/** * drm_connector_funcs - control connectors on a given device @@ -875,6 +914,8 @@ struct drm_mode_config { struct drm_property *prop_crtc_h; struct drm_property *prop_fb_id; struct drm_property *prop_crtc_id; + struct drm_property *prop_connector_ids; + struct drm_property *prop_mode; struct drm_property *edid_property; struct drm_property *dpms_property; struct drm_property *plane_type_property; @@ -935,7 +976,8 @@ extern int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, const struct drm_crtc_funcs *funcs); extern void drm_crtc_cleanup(struct drm_crtc *crtc); -extern unsigned int drm_crtc_index(struct drm_crtc *crtc); +struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc, + struct drm_crtc_state *cstate);
/** * drm_crtc_mask - find the mask of a registered CRTC @@ -946,9 +988,18 @@ extern unsigned int drm_crtc_index(struct drm_crtc *crtc); */ static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc) { - return 1 << drm_crtc_index(crtc); + return 1 << crtc->id; }
+extern int drm_crtc_check_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); +extern void drm_crtc_commit_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); +extern int drm_crtc_set_property(struct drm_crtc *crtc, + struct drm_crtc_state *state, + struct drm_property *property, + uint64_t value, void *blob_data); + extern void drm_connector_ida_init(void); extern void drm_connector_ida_destroy(void); extern int drm_connector_init(struct drm_device *dev, @@ -1024,6 +1075,7 @@ extern const char *drm_get_tv_select_name(int val); extern void drm_fb_release(struct drm_file *file_priv); extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); extern void drm_mode_group_destroy(struct drm_mode_group *group); +extern int drm_crtc_convert_umode(struct drm_display_mode *out, const struct drm_mode_modeinfo *in); extern bool drm_probe_ddc(struct i2c_adapter *adapter); extern struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter); @@ -1251,6 +1303,25 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id) return mo ? obj_to_blob(mo) : NULL; }
+static inline struct drm_crtc_state * +drm_crtc_create_state(struct drm_crtc *crtc) +{ + if (crtc->funcs->create_state) + return crtc->funcs->create_state(crtc); + return kzalloc(sizeof(struct drm_crtc_state), GFP_KERNEL); +} + +static inline void +drm_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *cstate) +{ + kfree(cstate->connector_ids); + if (crtc->funcs->destroy_state) + crtc->funcs->destroy_state(crtc, cstate); + else + kfree(cstate); +} + static inline struct drm_plane_state * drm_plane_create_state(struct drm_plane *plane) {
On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark wrote:
Break the mutable state of a crtc out into a separate structure and use atomic properties mechanism to set crtc attributes. This makes it easier to have some helpers for crtc->set_property() and for checking for invalid params. The idea is that individual drivers can wrap the state struct in their own struct which adds driver specific parameters, for easy build-up of state across multiple set_property() calls and for easy atomic commit or roll- back.
Signed-off-by: Rob Clark robdclark@gmail.com
Same comments about interface design as for the plane patch apply here. One additional comment below.
drivers/gpu/drm/armada/armada_crtc.c | 11 +- drivers/gpu/drm/ast/ast_mode.c | 1 + drivers/gpu/drm/cirrus/cirrus_mode.c | 1 + drivers/gpu/drm/drm_atomic.c | 231 ++++++++++- drivers/gpu/drm/drm_crtc.c | 598 ++++++++++++++++++----------- drivers/gpu/drm/drm_fb_helper.c | 2 +- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 7 +- drivers/gpu/drm/gma500/cdv_intel_display.c | 1 + drivers/gpu/drm/gma500/psb_intel_display.c | 1 + drivers/gpu/drm/i915/intel_display.c | 1 + drivers/gpu/drm/mgag200/mgag200_mode.c | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 6 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 6 +- drivers/gpu/drm/nouveau/dispnv04/crtc.c | 1 + drivers/gpu/drm/nouveau/nv50_display.c | 1 + drivers/gpu/drm/omapdrm/omap_crtc.c | 12 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/qxl/qxl_display.c | 2 + drivers/gpu/drm/radeon/radeon_display.c | 2 + drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 2 + drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 2 + drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 1 + drivers/gpu/drm/udl/udl_modeset.c | 2 + drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 1 + include/drm/drm_atomic.h | 29 ++ include/drm/drm_crtc.h | 103 ++++- 27 files changed, 785 insertions(+), 243 deletions(-)
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 7d3c649..6237af4 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -9,6 +9,7 @@ #include <linux/clk.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include "armada_crtc.h" #include "armada_drm.h" #include "armada_fb.h" @@ -966,7 +967,12 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc, { struct armada_private *priv = crtc->dev->dev_private; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); bool update_csc = false;
int ret = 0;
if (IS_ERR(cstate))
return PTR_ERR(cstate);
if (property == priv->csc_yuv_prop) { dcrtc->csc_yuv_mode = val;
@@ -974,6 +980,9 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc, } else if (property == priv->csc_rgb_prop) { dcrtc->csc_rgb_mode = val; update_csc = true;
} else {
ret = drm_crtc_set_property(crtc, cstate, property,
val, blob_data);
}
if (update_csc) {
@@ -984,7 +993,7 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc, writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL); }
- return 0;
- return ret;
}
static struct drm_crtc_funcs armada_crtc_funcs = { diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 114aee9..c08e0e1 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -632,6 +632,7 @@ static const struct drm_crtc_funcs ast_crtc_funcs = { .cursor_move = ast_cursor_move, .reset = ast_crtc_reset, .set_config = drm_crtc_helper_set_config,
- .set_property = drm_atomic_crtc_set_property, .gamma_set = ast_crtc_gamma_set, .destroy = ast_crtc_destroy,
}; diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index 49332c5..3c4428c 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -366,6 +366,7 @@ static void cirrus_crtc_destroy(struct drm_crtc *crtc) static const struct drm_crtc_funcs cirrus_crtc_funcs = { .gamma_set = cirrus_crtc_gamma_set, .set_config = drm_crtc_helper_set_config,
- .set_property = drm_atomic_crtc_set_property, .destroy = cirrus_crtc_destroy,
};
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 403ffc5..863a0fe 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -48,11 +48,13 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, { struct drm_atomic_state *state; int nplanes = dev->mode_config.num_total_plane;
int ncrtcs = dev->mode_config.num_crtc; int sz; void *ptr;
sz = sizeof(*state); sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
sz += (sizeof(state->crtcs) + sizeof(state->cstates)) * ncrtcs;
ptr = kzalloc(sz, GFP_KERNEL);
@@ -74,6 +76,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, state->pstates = ptr; ptr = &state->pstates[nplanes];
- state->crtcs = ptr;
- ptr = &state->crtcs[ncrtcs];
- state->cstates = ptr;
- ptr = &state->cstates[ncrtcs];
- return state;
} EXPORT_SYMBOL(drm_atomic_begin); @@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev, struct drm_atomic_state *state, struct drm_mode_object *obj, struct drm_pending_vblank_event *event) {
- return -EINVAL; /* for now */
- switch (obj->type) {
- case DRM_MODE_OBJECT_CRTC: {
struct drm_crtc_state *cstate =
drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
if (IS_ERR(cstate))
return PTR_ERR(cstate);
cstate->event = event;
return 0;
- }
- default:
return -EINVAL;
- }
Hm, I think if we only want completion events on crtcs (which I agree on) then we should make the set_event interface more specific by passing struct drm_crtc * and only call it for crtcs.
} EXPORT_SYMBOL(drm_atomic_set_event);
@@ -111,6 +130,7 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_atomic_state *a = state; int nplanes = dev->mode_config.num_total_plane;
int ncrtcs = dev->mode_config.num_crtc; int i, ret = 0;
for (i = 0; i < nplanes; i++) {
@@ -120,6 +140,13 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) break; } }
for (i = 0; i < ncrtcs; i++) {
if (a->crtcs[i]) {
ret = drm_atomic_check_crtc_state(a->crtcs[i], a->cstates[i]);
if (ret)
break;
}
}
a->acquire_ctx.frozen = true;
@@ -203,6 +230,7 @@ static void commit_locks(struct drm_atomic_state *a, { struct drm_device *dev = a->dev; int nplanes = dev->mode_config.num_total_plane;
int ncrtcs = dev->mode_config.num_crtc; int i;
for (i = 0; i < nplanes; i++) {
@@ -213,6 +241,14 @@ static void commit_locks(struct drm_atomic_state *a, } }
- for (i = 0; i < ncrtcs; i++) {
struct drm_crtc *crtc = a->crtcs[i];
if (crtc) {
crtc->state->state = NULL;
drm_crtc_destroy_state(crtc, a->cstates[i]);
}
- }
- /* and properly release them (clear in_atomic, remove from list): */ drm_modeset_drop_locks(&a->acquire_ctx); ww_acquire_fini(ww_ctx);
@@ -223,8 +259,18 @@ static int atomic_commit(struct drm_atomic_state *a, struct ww_acquire_ctx *ww_ctx) { int nplanes = a->dev->mode_config.num_total_plane;
int ncrtcs = a->dev->mode_config.num_crtc; int i, ret = 0;
for (i = 0; i < ncrtcs; i++) {
struct drm_crtc *crtc = a->crtcs[i];
if (crtc) {
ret = drm_atomic_commit_crtc_state(crtc, a->cstates[i]);
if (ret)
break;
}
}
for (i = 0; i < nplanes; i++) { struct drm_plane *plane = a->planes[i]; if (plane) {
@@ -403,6 +449,7 @@ static int commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) { struct drm_atomic_state *a = pstate->state;
- struct drm_crtc_state *cstate = NULL; struct drm_framebuffer *old_fb = plane->fb; struct drm_framebuffer *fb = pstate->fb; bool enabled = pstate->crtc && fb;
@@ -425,8 +472,11 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) } } else { struct drm_crtc *crtc = pstate->crtc;
if (pstate->update_plane || (pstate->new_fb && !can_flip(plane, pstate))) {cstate = drm_atomic_get_crtc_state(crtc, pstate->state);
+/* TODO pass event to update_plane().. */ +WARN_ON(cstate->event); ret = plane->funcs->update_plane(plane, crtc, pstate->fb, pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h, @@ -443,7 +493,7 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) }
} else if (pstate->new_fb) {
ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
ret = crtc->funcs->page_flip(crtc, fb, cstate->event, a->flags); if (ret == 0) { /* * Warn if the driver hasn't properly updated the plane->fb
@@ -473,9 +523,10 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) * original code. */ swap_plane_state(plane, pstate->state);
if (cstate)
}cstate->event = NULL;
- if (fb) drm_framebuffer_unreference(fb); if (old_fb)
@@ -484,8 +535,182 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) return ret; }
+int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t val, void *blob_data)
+{
- struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
- if (IS_ERR(cstate))
return PTR_ERR(cstate);
- return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
+} +EXPORT_SYMBOL(drm_atomic_crtc_set_property);
+static void init_crtc_state(struct drm_crtc *crtc,
struct drm_crtc_state *cstate, struct drm_atomic_state *state)
+{
- /* snapshot current state: */
- *cstate = *crtc->state;
- cstate->state = state;
- if (cstate->connector_ids) {
int sz = cstate->num_connector_ids * sizeof(cstate->connector_ids[0]);
cstate->connector_ids = kmemdup(cstate->connector_ids, sz, GFP_KERNEL);
- }
- /* this should never happen.. but make sure! */
- WARN_ON(cstate->event);
- cstate->event = NULL;
+}
+struct drm_crtc_state * +drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a) +{
- struct drm_crtc_state *cstate;
- int ret;
- cstate = a->cstates[crtc->id];
- if (!cstate) {
ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
if (ret)
return ERR_PTR(ret);
cstate = drm_crtc_create_state(crtc);
if (!cstate)
return ERR_PTR(-ENOMEM);
init_crtc_state(crtc, cstate, a);
a->crtcs[crtc->id] = crtc;
a->cstates[crtc->id] = cstate;
/* we'll need it later, so make sure we have state
* for primary plane too:
*/
drm_atomic_get_plane_state(crtc->primary, a);
I haven't figured out why. With primary planes I don't really see a need for this. If we need it to implement the legacy setcrtc interface, then that should be done there, not here.
- }
- return cstate;
+} +EXPORT_SYMBOL(drm_atomic_get_crtc_state);
+static void +swap_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a) +{
- struct drm_crtc_state *cstate = a->cstates[crtc->id];
- struct drm_device *dev = crtc->dev;
- struct drm_pending_vblank_event *event = cstate->event;
- if (event) {
/* hrm, need to sort out a better way to send events for
* other-than-pageflip.. but modeset is not async, so:
*/
unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags);
drm_send_vblank_event(dev, crtc->id, event);
cstate->event = NULL;
spin_unlock_irqrestore(&dev->event_lock, flags);
- }
- /* clear transient state (only valid during atomic update): */
- cstate->set_config = false;
- cstate->connectors_change = false;
- swap(crtc->state, a->cstates[crtc->id]);
- crtc->base.propvals = &crtc->state->propvals;
+}
+static struct drm_connector **get_connector_set(struct drm_device *dev,
uint32_t *connector_ids, uint32_t num_connector_ids)
+{
- struct drm_connector **connector_set = NULL;
- int i;
- connector_set = kmalloc(num_connector_ids *
sizeof(struct drm_connector *),
GFP_KERNEL);
- if (!connector_set)
return NULL;
- for (i = 0; i < num_connector_ids; i++)
connector_set[i] = drm_connector_find(dev, connector_ids[i]);
- return connector_set;
+}
+static int set_config(struct drm_crtc *crtc, struct drm_crtc_state *cstate) +{
- struct drm_device *dev = crtc->dev;
- struct drm_plane_state *pstate =
drm_atomic_get_plane_state(crtc->primary, cstate->state);
- struct drm_framebuffer *fb = pstate->fb;
- struct drm_connector **connector_set = get_connector_set(crtc->dev,
cstate->connector_ids, cstate->num_connector_ids);
- struct drm_display_mode *mode = drm_crtc_get_mode(crtc, cstate);
- struct drm_mode_set set = {
.crtc = crtc,
.x = pstate->src_x >> 16,
.y = pstate->src_y >> 16,
.mode = mode,
.num_connectors = cstate->num_connector_ids,
.connectors = connector_set,
.fb = fb,
- };
- int ret;
- if (IS_ERR(mode)) {
ret = PTR_ERR(mode);
return ret;
- }
- if (fb)
drm_framebuffer_reference(fb);
- ret = drm_mode_set_config_internal(&set);
- if (!ret) {
swap_crtc_state(crtc, cstate->state);
pstate->new_fb = pstate->update_plane = false;
- }
- if (fb)
drm_framebuffer_unreference(fb);
- kfree(connector_set);
- if (mode)
drm_mode_destroy(dev, mode);
- return ret;
+}
+static int +commit_crtc_state(struct drm_crtc *crtc,
struct drm_crtc_state *cstate)
+{
- struct drm_plane_state *pstate =
drm_atomic_get_plane_state(crtc->primary, cstate->state);
- int ret = -EINVAL;
- if (cstate->set_config)
return set_config(crtc, cstate);
- if (!pstate->fb) {
/* disable */
struct drm_mode_set set = {
.crtc = crtc,
.fb = NULL,
};
ret = drm_mode_set_config_internal(&set);
if (!ret) {
swap_crtc_state(crtc, cstate->state);
}
- }
- return ret;
+}
const struct drm_atomic_funcs drm_atomic_funcs = { .check_plane_state = drm_plane_check_state, .commit_plane_state = commit_plane_state,
.check_crtc_state = drm_crtc_check_state,
.commit_crtc_state = commit_crtc_state,
}; EXPORT_SYMBOL(drm_atomic_funcs); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index b556a31..e14d517 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -689,10 +689,7 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); void drm_framebuffer_remove(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev;
struct drm_crtc *crtc; struct drm_plane *plane;
struct drm_mode_set set;
int ret;
WARN_ON(!list_empty(&fb->filp_head));
@@ -712,7 +709,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) * in this manner. */ if (atomic_read(&fb->refcount.refcount) > 1) {
void *state;
struct drm_atomic_state *state;
state = dev->driver->atomic_begin(dev, 0); if (IS_ERR(state)) {
@@ -720,24 +717,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) return; }
/* TODO once CRTC is converted to state/properties, we can push the
* locking down into drm_atomic_commit(), since that is where
* the actual changes take place..
*/
drm_modeset_lock_all(dev);
/* remove from any CRTC */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
if (crtc->primary->fb == fb) {
/* should turn off the crtc */
memset(&set, 0, sizeof(struct drm_mode_set));
set.crtc = crtc;
set.fb = NULL;
ret = drm_mode_set_config_internal(&set);
if (ret)
DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
}
}
list_for_each_entry(plane, &dev->mode_config.plane_list, head) { if (plane->fb == fb) drm_plane_force_disable(plane, state);/* remove from any plane */
@@ -750,8 +730,6 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) dev->driver->atomic_commit(dev, state);
dev->driver->atomic_end(dev, state);
drm_modeset_unlock_all(dev);
}
drm_framebuffer_unreference(fb);
@@ -782,9 +760,13 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, struct drm_mode_config *config = &dev->mode_config; int ret;
- /* this is now required: */
- WARN_ON(!funcs->set_property);
- crtc->dev = dev; crtc->funcs = funcs;
- crtc->invert_dimensions = false;
crtc->state = drm_crtc_create_state(crtc);
crtc->state->invert_dimensions = false;
drm_modeset_lock_all(dev); drm_modeset_lock_init(&crtc->mutex);
@@ -796,14 +778,17 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, goto out;
crtc->base.properties = &crtc->properties;
- crtc->base.propvals = &crtc->propvals;
crtc->base.propvals = &crtc->state->propvals;
list_add_tail(&crtc->head, &dev->mode_config.crtc_list); dev->mode_config.num_crtc++;
crtc->primary = primary; if (primary)
primary->possible_crtcs = 1 << drm_crtc_index(crtc);
primary->possible_crtcs = 1 << crtc->id;
drm_object_attach_property(&crtc->base, config->prop_mode, 0);
drm_object_attach_property(&crtc->base, config->prop_connector_ids, 0);
out: drm_modeset_unlock_all(dev);
@@ -832,31 +817,245 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) drm_mode_object_put(dev, &crtc->base); list_del(&crtc->head); dev->mode_config.num_crtc--;
- drm_crtc_destroy_state(crtc, crtc->state);
} EXPORT_SYMBOL(drm_crtc_cleanup);
-/**
- drm_crtc_index - find the index of a registered CRTC
- @crtc: CRTC to find index for
- Given a registered CRTC, return the index of that CRTC within a DRM
- device's list of CRTCs.
- */
-unsigned int drm_crtc_index(struct drm_crtc *crtc) +/* get display-mode from user-mode */ +struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc,
struct drm_crtc_state *cstate)
{
- unsigned int index = 0;
- struct drm_crtc *tmp;
- struct drm_display_mode *mode = NULL;
- if (cstate->mode_valid) {
struct drm_device *dev = crtc->dev;
int ret;
- list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
if (tmp == crtc)
return index;
mode = drm_mode_create(dev);
if (!mode)
return ERR_PTR(-ENOMEM);
ret = drm_crtc_convert_umode(mode, &cstate->mode);
if (ret) {
DRM_DEBUG_KMS("Invalid mode\n");
drm_mode_destroy(dev, mode);
return ERR_PTR(ret);
}
index++;
}drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
- return mode;
+}
- BUG();
+static int connector_idx(struct drm_crtc_state *state,
uint32_t connector_id)
+{
- int i;
- for (i = 0; i < state->num_connector_ids; i++)
if (state->connector_ids[i] == connector_id)
return i;
- return -1;
+}
+static int remove_connector(struct drm_crtc *ocrtc,
struct drm_crtc_state *ostate, struct drm_atomic_state *state,
int idx)
+{
- struct drm_mode_config *config = &ocrtc->dev->mode_config;
- uint32_t *new_connector_ids;
- int a, b;
- /* before deletion point: */
- a = idx * sizeof(ostate->connector_ids[0]);
- /* after deletion point: */
- b = (ostate->num_connector_ids - 1 - idx) *
sizeof(ostate->connector_ids[0]);
- new_connector_ids = kmalloc(a+b, GFP_KERNEL);
- if (!new_connector_ids)
return -ENOMEM;
- memcpy(new_connector_ids, ostate->connector_ids, a);
- memcpy(&new_connector_ids[idx],
&ostate->connector_ids[idx + 1], b);
- return drm_mode_crtc_set_obj_prop(ocrtc, state,
config->prop_connector_ids, a + b,
new_connector_ids);
} -EXPORT_SYMBOL(drm_crtc_index);
+static int check_connectors(struct drm_crtc *crtc,
struct drm_atomic_state *state, bool fix,
uint32_t *connector_ids, uint32_t num_connector_ids)
+{
- struct drm_mode_config *config = &crtc->dev->mode_config;
- struct drm_crtc *ocrtc; /* other connector */
- list_for_each_entry(ocrtc, &config->crtc_list, head) {
struct drm_crtc_state *ostate; /* other state */
unsigned i;
if (ocrtc == crtc)
continue;
ostate = drm_atomic_get_crtc_state(crtc, state);
if (IS_ERR(ostate))
return PTR_ERR(ostate);
for (i = 0; i < num_connector_ids; i++) {
struct drm_connector *connector;
uint32_t cid = connector_ids[i];
int idx;
+retry:
idx = connector_idx(ostate, cid);
if (idx < 0)
continue;
if (fix) {
int ret = remove_connector(ocrtc,
ostate, state, idx);
if (ret)
return ret;
goto retry;
}
connector = drm_connector_find(crtc->dev, cid);
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] already in use\n",
connector->base.id,
drm_get_connector_name(connector));
return -EINVAL;
}
- }
- return 0;
+}
+int drm_crtc_check_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
+{
- struct drm_plane *primary = crtc->primary;
- struct drm_plane_state *pstate =
drm_atomic_get_plane_state(primary, state->state);
- struct drm_framebuffer *fb = pstate->fb;
- int hdisplay, vdisplay;
- struct drm_display_mode *mode = drm_crtc_get_mode(crtc, state);
- unsigned x, y;
- if (IS_ERR(mode))
return PTR_ERR(mode);
- /* disabling the crtc is allowed: */
- if (!(fb && state->mode_valid))
return 0;
- hdisplay = state->mode.hdisplay;
- vdisplay = state->mode.vdisplay;
- if (mode && drm_mode_is_stereo(mode)) {
struct drm_display_mode adjusted = *mode;
drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE);
hdisplay = adjusted.crtc_hdisplay;
vdisplay = adjusted.crtc_vdisplay;
- }
- if (state->invert_dimensions)
swap(hdisplay, vdisplay);
- x = pstate->src_x >> 16;
- y = pstate->src_y >> 16;
- if (hdisplay > fb->width ||
vdisplay > fb->height ||
x > fb->width - hdisplay ||
y > fb->height - vdisplay) {
DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
fb->width, fb->height, hdisplay, vdisplay,
x, y, state->invert_dimensions ? " (inverted)" : "");
return -ENOSPC;
- }
- if (crtc->enabled && !state->set_config) {
if (primary->state->fb->pixel_format != fb->pixel_format) {
DRM_DEBUG_KMS("Page flip is not allowed to "
"change frame buffer format.\n");
return -EINVAL;
}
- }
- if (state->num_connector_ids == 0) {
DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
return -EINVAL;
- }
- if (state->connectors_change) {
int ret = check_connectors(crtc, state->state, false,
state->connector_ids, state->num_connector_ids);
if (ret)
return ret;
- }
- if (mode)
drm_mode_destroy(crtc->dev, mode);
- return 0;
+} +EXPORT_SYMBOL(drm_crtc_check_state);
+void drm_crtc_commit_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
+{
- crtc->state = state;
- crtc->base.propvals = &state->propvals;
+} +EXPORT_SYMBOL(drm_crtc_commit_state);
+int drm_crtc_set_property(struct drm_crtc *crtc,
struct drm_crtc_state *state,
struct drm_property *property,
uint64_t value, void *blob_data)
+{
- struct drm_device *dev = crtc->dev;
- struct drm_mode_config *config = &dev->mode_config;
- /* grab primary plane state now, to ensure locks are held, etc. */
- drm_atomic_get_plane_state(crtc->primary, state->state);
- drm_object_property_set_value(&crtc->base,
&state->propvals, property, value, blob_data);
- if (property == config->prop_mode) {
if (!blob_data) {
memset(&state->mode, 0, sizeof(state->mode));
state->mode_valid = false;
} else {
/* check size: */
if (value < sizeof(struct drm_mode_modeinfo))
return -EINVAL;
state->mode = *(struct drm_mode_modeinfo *)blob_data;
state->mode_valid = true;
}
state->set_config = true;
- } else if (property == config->prop_connector_ids) {
/* if connector-id's changing, we need to have all the locks: */
struct drm_atomic_state *a = state->state;
int ret = drm_modeset_lock_all_crtcs(crtc->dev, &a->acquire_ctx);
if (ret)
return ret;
state->connectors_change = true;
state->num_connector_ids = value / sizeof(state->connector_ids[0]);
kfree(state->connector_ids);
state->connector_ids = blob_data;
state->set_config = true;
- } else {
return -EINVAL;
- }
- return 0;
+} +EXPORT_SYMBOL(drm_crtc_set_property);
/*
- drm_mode_remove - remove and free a mode
@@ -1239,6 +1438,10 @@ int drm_plane_check_state(struct drm_plane *plane, if (!fb) return 0;
- /* we'll need this later during commit: */
- if (state->crtc)
drm_atomic_get_crtc_state(state->crtc, state->state);
- fb_width = fb->width << 16; fb_height = fb->height << 16;
@@ -1465,6 +1668,16 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev) return -ENOMEM; dev->mode_config.prop_crtc_id = prop;
- prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0);
- if (!prop)
return -ENOMEM;
- dev->mode_config.prop_connector_ids = prop;
- prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0);
- if (!prop)
return -ENOMEM;
- dev->mode_config.prop_mode = prop;
- return 0;
}
@@ -1754,7 +1967,7 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
- Returns:
- Zero on success, errno on failure.
*/ -static int drm_crtc_convert_umode(struct drm_display_mode *out, +int drm_crtc_convert_umode(struct drm_display_mode *out, const struct drm_mode_modeinfo *in) { if (in->clock > INT_MAX || in->vrefresh > INT_MAX) @@ -2001,8 +2214,8 @@ int drm_mode_getcrtc(struct drm_device *dev, goto out; }
- crtc_resp->x = crtc->x;
- crtc_resp->y = crtc->y;
- crtc_resp->x = crtc->primary->state->src_x >> 16;
- crtc_resp->y = crtc->primary->state->src_y >> 16; crtc_resp->gamma_size = crtc->gamma_size; if (crtc->primary->fb) crtc_resp->fb_id = crtc->primary->fb->base.id;
@@ -2495,7 +2708,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc, vdisplay = adjusted.crtc_vdisplay; }
- if (crtc->invert_dimensions)
if (crtc->state->invert_dimensions) swap(hdisplay, vdisplay);
if (hdisplay > fb->width ||
@@ -2504,7 +2717,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc, y > fb->height - vdisplay) { DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", fb->width, fb->height, hdisplay, vdisplay, x, y,
crtc->invert_dimensions ? " (inverted)" : "");
return -ENOSPC; }crtc->state->invert_dimensions ? " (inverted)" : "");
@@ -2531,22 +2744,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_mode_config *config = &dev->mode_config; struct drm_mode_crtc *crtc_req = data; struct drm_crtc *crtc;
- struct drm_connector **connector_set = NULL, *connector;
- struct drm_framebuffer *fb = NULL;
- struct drm_display_mode *mode = NULL;
- struct drm_mode_set set;
- uint32_t __user *set_connectors_ptr;
uint32_t fb_id = -1;
uint32_t *connector_ids = NULL;
struct drm_atomic_state *state = NULL; int ret; int i;
if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL;
- /* For some reason crtc x/y offsets are signed internally. */
- if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)
return -ERANGE;
- drm_modeset_lock_all(dev); crtc = drm_crtc_find(dev, crtc_req->crtc_id); if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
@@ -2564,55 +2770,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ret = -EINVAL; goto out; }
fb = crtc->primary->fb;
/* Make refcounting symmetric with the lookup path. */
drm_framebuffer_reference(fb);
} else {fb_id = crtc->primary->fb->base.id;
fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
if (!fb) {
DRM_DEBUG_KMS("Unknown FB ID%d\n",
crtc_req->fb_id);
ret = -ENOENT;
goto out;
}
}
mode = drm_mode_create(dev);
if (!mode) {
ret = -ENOMEM;
goto out;
}fb_id = crtc_req->fb_id;
ret = drm_crtc_convert_umode(mode, &crtc_req->mode);
if (ret) {
DRM_DEBUG_KMS("Invalid mode\n");
goto out;
}
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
mode, fb);
if (ret)
goto out;
}
if (crtc_req->count_connectors == 0 && mode) {
DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
ret = -EINVAL;
goto out;
}
if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
crtc_req->count_connectors);
ret = -EINVAL;
goto out;
}
if (crtc_req->count_connectors > 0) {
u32 out_id;
uint32_t __user *set_connectors_ptr =
(uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
/* Avoid unbounded kernel memory allocation */ if (crtc_req->count_connectors > config->num_connector) {
@@ -2620,52 +2786,65 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; }
connector_set = kmalloc(crtc_req->count_connectors *
sizeof(struct drm_connector *),
connector_ids = kmalloc(crtc_req->count_connectors *
sizeof(connector_ids[0]), GFP_KERNEL);
if (!connector_set) {
if (!connector_ids) { ret = -ENOMEM; goto out;
}
for (i = 0; i < crtc_req->count_connectors; i++) {
set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
u32 out_id;
if (get_user(out_id, &set_connectors_ptr[i])) { ret = -EFAULT; goto out; }
connector = drm_connector_find(dev, out_id);
if (!connector) {
DRM_DEBUG_KMS("Connector id %d unknown\n",
out_id);
ret = -ENOENT;
goto out;
}
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id,
drm_get_connector_name(connector));
connector_set[i] = connector;
} }connector_ids[i] = out_id;
- set.crtc = crtc;
- set.x = crtc_req->x;
- set.y = crtc_req->y;
- set.mode = mode;
- set.connectors = connector_set;
- set.num_connectors = crtc_req->count_connectors;
- set.fb = fb;
- ret = drm_mode_set_config_internal(&set);
+retry:
- state = dev->driver->atomic_begin(dev, 0);
- if (IS_ERR(state))
return PTR_ERR(state);
-out:
- if (fb)
drm_framebuffer_unreference(fb);
- /* If connectors change, we need to check if we need to steal one
* from another CRTC.. setcrtc makes this implicit, but atomic
* treats it as an error so we need to handle here:
*/
- ret = check_connectors(crtc, state, true,
connector_ids, crtc_req->count_connectors);
- if (ret)
goto out;
- kfree(connector_set);
- drm_mode_destroy(dev, mode);
- drm_modeset_unlock_all(dev);
- ret =
drm_mode_crtc_set_obj_prop(crtc, state,
config->prop_mode, sizeof(crtc_req->mode), &crtc_req->mode) ||
drm_mode_crtc_set_obj_prop(crtc, state,
config->prop_connector_ids,
crtc_req->count_connectors * sizeof(connector_ids[0]),
connector_ids) ||
drm_mode_plane_set_obj_prop(crtc->primary, state,
config->prop_crtc_id, crtc->base.id, NULL) ||
drm_mode_plane_set_obj_prop(crtc->primary, state,
config->prop_fb_id, fb_id, NULL) ||
drm_mode_plane_set_obj_prop(crtc->primary, state,
config->prop_src_x, crtc_req->x << 16, NULL) ||
drm_mode_plane_set_obj_prop(crtc->primary, state,
config->prop_src_y, crtc_req->y << 16, NULL) ||
dev->driver->atomic_check(dev, state);
- if (ret)
goto out;
- ret = dev->driver->atomic_commit(dev, state);
+out:
- if (state)
dev->driver->atomic_end(dev, state);
- if (ret == -EDEADLK)
return ret;goto retry;
}
@@ -4028,9 +4207,6 @@ int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, if (crtc->funcs->set_property) ret = crtc->funcs->set_property(crtc, state, property, value, blob_data);
if (!ret)
drm_object_property_set_value(&crtc->base, &crtc->propvals,
property, value, NULL);
return ret;
} @@ -4424,6 +4600,51 @@ out: return ret; }
+static struct drm_pending_vblank_event *create_vblank_event(
struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data)
+{
- struct drm_pending_vblank_event *e = NULL;
- unsigned long flags;
- spin_lock_irqsave(&dev->event_lock, flags);
- if (file_priv->event_space < sizeof e->event) {
spin_unlock_irqrestore(&dev->event_lock, flags);
goto out;
- }
- file_priv->event_space -= sizeof e->event;
- spin_unlock_irqrestore(&dev->event_lock, flags);
- e = kzalloc(sizeof *e, GFP_KERNEL);
- if (e == NULL) {
spin_lock_irqsave(&dev->event_lock, flags);
file_priv->event_space += sizeof e->event;
spin_unlock_irqrestore(&dev->event_lock, flags);
goto out;
- }
- e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
- e->event.base.length = sizeof e->event;
- e->event.user_data = user_data;
- e->base.event = &e->event.base;
- e->base.file_priv = file_priv;
- e->base.destroy =
(void (*) (struct drm_pending_event *)) kfree;
+out:
- return e;
+}
+static void destroy_vblank_event(struct drm_device *dev,
struct drm_file *file_priv, struct drm_pending_vblank_event *e)
+{
- unsigned long flags;
- spin_lock_irqsave(&dev->event_lock, flags);
- file_priv->event_space += sizeof e->event;
- spin_unlock_irqrestore(&dev->event_lock, flags);
- kfree(e);
+}
/**
- drm_mode_page_flip_ioctl - schedule an asynchronous fb update
- @dev: DRM device
@@ -4446,10 +4667,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_page_flip *page_flip = data;
- struct drm_mode_config *config = &dev->mode_config; struct drm_crtc *crtc;
- struct drm_framebuffer *fb = NULL, *old_fb = NULL; struct drm_pending_vblank_event *e = NULL;
- unsigned long flags;
struct drm_atomic_state *state; int ret = -EINVAL;
if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
@@ -4463,92 +4684,41 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (!crtc) return -ENOENT;
- drm_modeset_lock(&crtc->mutex, NULL);
- if (crtc->primary->fb == NULL) {
/* The framebuffer is currently unbound, presumably
* due to a hotplug event, that userspace has not
* yet discovered.
*/
ret = -EBUSY;
goto out;
- }
- if (crtc->funcs->page_flip == NULL)
goto out;
- fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
- if (!fb) {
ret = -ENOENT;
goto out;
- }
- ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
- if (ret)
goto out;
- if (crtc->primary->fb->pixel_format != fb->pixel_format) {
DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
ret = -EINVAL;
goto out;
- }
+retry:
state = dev->driver->atomic_begin(dev,
page_flip->flags | DRM_MODE_ATOMIC_NONBLOCK);
if (IS_ERR(state))
return PTR_ERR(state);
if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
ret = -ENOMEM;
spin_lock_irqsave(&dev->event_lock, flags);
if (file_priv->event_space < sizeof e->event) {
spin_unlock_irqrestore(&dev->event_lock, flags);
e = create_vblank_event(dev, file_priv, page_flip->user_data);
if (!e) {
}ret = -ENOMEM; goto out;
file_priv->event_space -= sizeof e->event;
spin_unlock_irqrestore(&dev->event_lock, flags);
e = kzalloc(sizeof *e, GFP_KERNEL);
if (e == NULL) {
spin_lock_irqsave(&dev->event_lock, flags);
file_priv->event_space += sizeof e->event;
spin_unlock_irqrestore(&dev->event_lock, flags);
ret = dev->driver->atomic_set_event(dev, state, &crtc->base, e);
}if (ret) { goto out;
e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
e->event.base.length = sizeof e->event;
e->event.user_data = page_flip->user_data;
e->base.event = &e->event.base;
e->base.file_priv = file_priv;
e->base.destroy =
(void (*) (struct drm_pending_event *)) kfree;
}
old_fb = crtc->primary->fb;
ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
if (ret) {
if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
spin_lock_irqsave(&dev->event_lock, flags);
file_priv->event_space += sizeof e->event;
spin_unlock_irqrestore(&dev->event_lock, flags);
kfree(e);
}
/* Keep the old fb, don't unref it. */
old_fb = NULL;
} else {
/*
* Warn if the driver hasn't properly updated the crtc->fb
* field to reflect that the new framebuffer is now used.
* Failing to do so will screw with the reference counting
* on framebuffers.
*/
WARN_ON(crtc->primary->fb != fb);
/* Unref only the old framebuffer. */
fb = NULL;
}
- ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
config->prop_fb_id, page_flip->fb_id, NULL);
- if (ret)
goto out;
-out:
- if (fb)
drm_framebuffer_unreference(fb);
- if (old_fb)
drm_framebuffer_unreference(old_fb);
- drm_modeset_unlock(&crtc->mutex);
- ret = dev->driver->atomic_check(dev, state);
- if (ret)
goto out;
- ret = dev->driver->atomic_commit(dev, state);
+out:
- if (ret && e)
destroy_vblank_event(dev, file_priv, e);
- dev->driver->atomic_end(dev, state);
- if (ret == -EDEADLK)
return ret;goto retry;
}
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index b73d3b0..4669e69 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -286,7 +286,7 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; struct drm_plane *plane; bool error = false;
- void *state;
struct drm_atomic_state *state; int i;
drm_warn_on_modeset_not_all_locked(dev);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 2a56973..f3c7e77 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -14,6 +14,7 @@
#include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h>
#include "exynos_drm_crtc.h" #include "exynos_drm_drv.h" @@ -289,6 +290,10 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct exynos_drm_private *dev_priv = dev->dev_private; struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
if (IS_ERR(cstate))
return PTR_ERR(cstate);
if (property == dev_priv->crtc_mode_property) { enum exynos_crtc_mode mode = val;
@@ -313,7 +318,7 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, return 0; }
- return -EINVAL;
- return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
}
static struct drm_crtc_funcs exynos_crtc_funcs = { diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 6672732..5b6eee9 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -989,6 +989,7 @@ const struct drm_crtc_funcs cdv_intel_crtc_funcs = { .cursor_move = gma_crtc_cursor_move, .gamma_set = gma_crtc_gamma_set, .set_config = gma_crtc_set_config,
- .set_property = drm_atomic_crtc_set_property, .destroy = gma_crtc_destroy,
};
diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index 87b50ba..79b5692 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c @@ -444,6 +444,7 @@ const struct drm_crtc_funcs psb_intel_crtc_funcs = { .cursor_move = gma_crtc_cursor_move, .gamma_set = gma_crtc_gamma_set, .set_config = gma_crtc_set_config,
- .set_property = drm_atomic_crtc_set_property, .destroy = gma_crtc_destroy,
};
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e9f6eb7..53b996f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -10430,6 +10430,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { .cursor_move = intel_crtc_cursor_move, .gamma_set = intel_crtc_gamma_set, .set_config = intel_crtc_set_config,
- .set_property = drm_atomic_crtc_set_property, .destroy = intel_crtc_destroy, .page_flip = intel_crtc_page_flip,
}; diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index a034ed4..ba9bd91 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -1296,6 +1296,7 @@ static const struct drm_crtc_funcs mga_crtc_funcs = { .cursor_move = mga_crtc_cursor_move, .gamma_set = mga_crtc_gamma_set, .set_config = drm_crtc_helper_set_config,
- .set_property = drm_atomic_crtc_set_property, .destroy = mga_crtc_destroy,
};
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index 7cf0f78..d0d8befd 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -471,8 +471,10 @@ static int mdp4_crtc_set_property(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) {
- // XXX
- return -EINVAL;
- struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
- if (IS_ERR(cstate))
return PTR_ERR(cstate);
- return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
}
#define CURSOR_WIDTH 64 diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 771390b..7f4ee99 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -389,8 +389,10 @@ static int mdp5_crtc_set_property(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) {
- // XXX
- return -EINVAL;
- struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
- if (IS_ERR(cstate))
return PTR_ERR(cstate);
- return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
}
static const struct drm_crtc_funcs mdp5_crtc_funcs = { diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index 41be342..9e24632 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -1086,6 +1086,7 @@ static const struct drm_crtc_funcs nv04_crtc_funcs = { .cursor_move = nv04_crtc_cursor_move, .gamma_set = nv_crtc_gamma_set, .set_config = nouveau_crtc_set_config,
- .set_property = drm_atomic_crtc_set_property, .page_flip = nouveau_crtc_page_flip, .destroy = nv_crtc_destroy,
}; diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 58af547..ecbffeb 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -1329,6 +1329,7 @@ static const struct drm_crtc_funcs nv50_crtc_func = { .cursor_move = nv50_crtc_cursor_move, .gamma_set = nv50_crtc_gamma_set, .set_config = nouveau_crtc_set_config,
- .set_property = drm_atomic_crtc_set_property, .destroy = nv50_crtc_destroy, .page_flip = nouveau_crtc_page_flip,
}; diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index a75934d..772687b 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -387,14 +387,22 @@ static int omap_crtc_set_property(struct drm_crtc *crtc, { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct omap_drm_private *priv = crtc->dev->dev_private;
struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
int ret;
if (IS_ERR(cstate))
return PTR_ERR(cstate);
if (property == priv->rotation_prop) {
crtc->invert_dimensions =
}cstate->invert_dimensions = !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
- return omap_plane_set_property(omap_crtc->plane, state,
- ret = omap_plane_set_property(omap_crtc->plane, state, property, val, blob_data);
- if (ret)
ret = drm_crtc_set_property(crtc, cstate, property, val, blob_data);
- return ret;
}
static const struct drm_crtc_funcs omap_crtc_funcs = { diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index da80bdc..3f64c47 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -579,7 +579,7 @@ static void dev_lastclose(struct drm_device *dev) */ for (i = 0; i < priv->num_crtcs; i++) { drm_object_property_set_value(&priv->crtcs[i]->base,
&priv->crtcs[i]->propvals,
}&priv->crtcs[i]->state->propvals, priv->rotation_prop, 0, NULL);
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index b54c970..25896a9 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -29,6 +29,7 @@ #include "qxl_drv.h" #include "qxl_object.h" #include "drm_crtc_helper.h" +#include "drm_atomic.h"
static bool qxl_head_enabled(struct qxl_head *head) { @@ -373,6 +374,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = { .cursor_set2 = qxl_crtc_cursor_set2, .cursor_move = qxl_crtc_cursor_move, .set_config = drm_crtc_helper_set_config,
- .set_property = drm_atomic_crtc_set_property, .destroy = qxl_crtc_destroy,
};
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 8d99d5e..cc86aac 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -32,6 +32,7 @@
#include <linux/pm_runtime.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include <drm/drm_edid.h>
#include <linux/gcd.h> @@ -546,6 +547,7 @@ static const struct drm_crtc_funcs radeon_crtc_funcs = { .cursor_move = radeon_crtc_cursor_move, .gamma_set = radeon_crtc_gamma_set, .set_config = radeon_crtc_set_config,
- .set_property = drm_atomic_crtc_set_property, .destroy = radeon_crtc_destroy, .page_flip = radeon_crtc_page_flip,
}; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 299267d..f5a3d55 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -17,6 +17,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
@@ -527,6 +528,7 @@ static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, static const struct drm_crtc_funcs crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_crtc_helper_set_config,
- .set_property = drm_atomic_crtc_set_property, .page_flip = rcar_du_crtc_page_flip,
};
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index 90e023a..0a5280c 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -17,6 +17,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
@@ -506,6 +507,7 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc, static const struct drm_crtc_funcs crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_crtc_helper_set_config,
- .set_property = drm_atomic_crtc_set_property, .page_flip = shmob_drm_crtc_page_flip,
};
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 92839ba..b07f116 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -411,6 +411,7 @@ static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, static const struct drm_crtc_funcs tilcdc_crtc_funcs = { .destroy = tilcdc_crtc_destroy, .set_config = drm_crtc_helper_set_config,
.page_flip = tilcdc_crtc_page_flip,.set_property = drm_atomic_crtc_set_property,
};
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index cddc4fc..36d0116 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -14,6 +14,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include "udl_drv.h"
/* @@ -383,6 +384,7 @@ static struct drm_crtc_helper_funcs udl_helper_funcs = {
static const struct drm_crtc_funcs udl_crtc_funcs = { .set_config = drm_crtc_helper_set_config,
- .set_property = drm_atomic_crtc_set_property, .destroy = udl_crtc_destroy,
};
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index b2b9bd2..0313b00 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -300,6 +300,7 @@ static struct drm_crtc_funcs vmw_legacy_crtc_funcs = { .cursor_move = vmw_du_crtc_cursor_move, .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_ldu_crtc_destroy,
- .set_property = drm_atomic_crtc_set_property, .set_config = vmw_ldu_crtc_set_config,
};
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index a95d3a0..b723e09 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -397,6 +397,7 @@ static struct drm_crtc_funcs vmw_screen_object_crtc_funcs = { .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_sou_crtc_destroy, .set_config = vmw_sou_crtc_set_config,
- .set_property = drm_atomic_crtc_set_property, .page_flip = vmw_du_page_flip,
};
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 78e93ec..7946b7f 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -70,6 +70,9 @@ struct drm_atomic_funcs { int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate); int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
- int (*check_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate);
- int (*commit_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate);
};
const extern struct drm_atomic_funcs drm_atomic_funcs; @@ -109,6 +112,30 @@ drm_atomic_commit_plane_state(struct drm_plane *plane, return funcs->commit_plane_state(plane, pstate); }
+int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t val, void *blob_data);
+struct drm_crtc_state *drm_atomic_get_crtc_state(struct drm_crtc *crtc,
struct drm_atomic_state *state);
+static inline int +drm_atomic_check_crtc_state(struct drm_crtc *crtc,
struct drm_crtc_state *cstate)
+{
- const struct drm_atomic_funcs *funcs =
crtc->dev->driver->atomic_funcs;
- return funcs->check_crtc_state(crtc, cstate);
+}
+static inline int +drm_atomic_commit_crtc_state(struct drm_crtc *crtc,
struct drm_crtc_state *cstate)
+{
- const struct drm_atomic_funcs *funcs =
crtc->dev->driver->atomic_funcs;
- return funcs->commit_crtc_state(crtc, cstate);
+}
/**
- struct drm_atomic_state - the state object used by atomic helpers
*/ @@ -118,6 +145,8 @@ struct drm_atomic_state { uint32_t flags; struct drm_plane **planes; struct drm_plane_state **pstates;
struct drm_crtc **crtcs;
struct drm_crtc_state **cstates;
bool committed; bool checked; /* just for debugging */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 58309cc..2fbf13a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -284,6 +284,10 @@ struct drm_crtc_funcs { struct drm_pending_vblank_event *event, uint32_t flags);
- struct drm_crtc_state *(*create_state)(struct drm_crtc *crtc);
- void (*destroy_state)(struct drm_crtc *crtc,
struct drm_crtc_state *cstate);
- int (*set_property)(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t val,
@@ -291,21 +295,52 @@ struct drm_crtc_funcs { };
/**
- drm_crtc_state - mutable crtc state
- @invert_dimensions: for purposes of error checking crtc vs fb sizes,
- invert the width/height of the crtc. This is used if the driver
- is performing 90 or 270 degree rotated scanout
- @mode_valid: a valid mode has been set
- @set_config: needs modeset (crtc->set_config())
- @connectors_change: the connector-ids array has changed
- @num_connector_ids: the number of connector-ids
- @connector_ids: array of connector ids
- @mode: current mode timings
- @event: pending pageflip event
- @propvals: property values
- @state: current global/toplevel state object (for atomic) while an
- update is in progress, NULL otherwise.
- */
+struct drm_crtc_state {
- bool invert_dimensions : 1;
- bool mode_valid : 1;
- /* transient state, only valid during atomic operation: */
- bool set_config : 1;
- bool connectors_change : 1;
- uint8_t num_connector_ids;
- uint32_t *connector_ids;
- struct drm_mode_modeinfo mode;
- struct drm_pending_vblank_event *event;
- struct drm_object_property_values propvals;
- struct drm_atomic_state *state;
+};
+/**
- drm_crtc - central CRTC control structure
- @dev: parent DRM device
- @head: list management
- @id: CRTC number, 0..n
- @mutex: per-CRTC locking
- @base: base KMS object for ID tracking etc.
- @primary: primary plane for this CRTC
- @cursor: cursor plane for this CRTC
- @state: the mutable state
- @enabled: is this CRTC enabled?
- @mode: current mode timings
- @hwmode: mode timings as programmed to hw regs
- @invert_dimensions: for purposes of error checking crtc vs fb sizes,
- invert the width/height of the crtc. This is used if the driver
- is performing 90 or 270 degree rotated scanout
- @x: x position on screen
- @y: y position on screen
- @funcs: CRTC control functions
- @gamma_size: size of gamma ramp
- @gamma_store: gamma ramp values
@@ -322,6 +357,8 @@ struct drm_crtc { struct drm_device *dev; struct list_head head;
- int id;
- /**
- crtc mutex
@@ -337,23 +374,19 @@ struct drm_crtc { struct drm_plane *primary; struct drm_plane *cursor;
struct drm_crtc_state *state;
/* Temporary tracking of the old fb while a modeset is ongoing. Used
- by drm_mode_set_config_internal to implement correct refcounting. */
struct drm_framebuffer *old_fb;
bool enabled;
/* Requested mode from modesetting. */
struct drm_display_mode mode;
/* Programmed mode in hw, after adjustments for encoders,
- crtc, panel scaling etc. Needed for timestamping etc.
*/ struct drm_display_mode hwmode;
bool invert_dimensions;
int x, y; const struct drm_crtc_funcs *funcs;
/* CRTC gamma size for reporting to userspace */
@@ -367,9 +400,15 @@ struct drm_crtc { void *helper_private;
struct drm_object_properties properties;
- struct drm_object_property_values propvals;
-};
- /* These are (temporary) duplicate information from what is in the
* drm_crtc_state struct.. keeping duplicate copy here makes the
* switch to atomic far less intrusive. Once all the drivers and
* the crtc/fb helpers are updated, then we can remove these:
*/
- int x, y;
- struct drm_display_mode mode;
+};
/**
- drm_connector_funcs - control connectors on a given device
@@ -875,6 +914,8 @@ struct drm_mode_config { struct drm_property *prop_crtc_h; struct drm_property *prop_fb_id; struct drm_property *prop_crtc_id;
- struct drm_property *prop_connector_ids;
- struct drm_property *prop_mode; struct drm_property *edid_property; struct drm_property *dpms_property; struct drm_property *plane_type_property;
@@ -935,7 +976,8 @@ extern int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, const struct drm_crtc_funcs *funcs); extern void drm_crtc_cleanup(struct drm_crtc *crtc); -extern unsigned int drm_crtc_index(struct drm_crtc *crtc); +struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc,
struct drm_crtc_state *cstate);
/**
- drm_crtc_mask - find the mask of a registered CRTC
@@ -946,9 +988,18 @@ extern unsigned int drm_crtc_index(struct drm_crtc *crtc); */ static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc) {
- return 1 << drm_crtc_index(crtc);
- return 1 << crtc->id;
}
+extern int drm_crtc_check_state(struct drm_crtc *crtc,
struct drm_crtc_state *state);
+extern void drm_crtc_commit_state(struct drm_crtc *crtc,
struct drm_crtc_state *state);
+extern int drm_crtc_set_property(struct drm_crtc *crtc,
struct drm_crtc_state *state,
struct drm_property *property,
uint64_t value, void *blob_data);
extern void drm_connector_ida_init(void); extern void drm_connector_ida_destroy(void); extern int drm_connector_init(struct drm_device *dev, @@ -1024,6 +1075,7 @@ extern const char *drm_get_tv_select_name(int val); extern void drm_fb_release(struct drm_file *file_priv); extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); extern void drm_mode_group_destroy(struct drm_mode_group *group); +extern int drm_crtc_convert_umode(struct drm_display_mode *out, const struct drm_mode_modeinfo *in); extern bool drm_probe_ddc(struct i2c_adapter *adapter); extern struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter); @@ -1251,6 +1303,25 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id) return mo ? obj_to_blob(mo) : NULL; }
+static inline struct drm_crtc_state * +drm_crtc_create_state(struct drm_crtc *crtc) +{
- if (crtc->funcs->create_state)
return crtc->funcs->create_state(crtc);
- return kzalloc(sizeof(struct drm_crtc_state), GFP_KERNEL);
+}
+static inline void +drm_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *cstate)
+{
- kfree(cstate->connector_ids);
- if (crtc->funcs->destroy_state)
crtc->funcs->destroy_state(crtc, cstate);
- else
kfree(cstate);
+}
static inline struct drm_plane_state * drm_plane_create_state(struct drm_plane *plane) { -- 1.9.0
On Mon, May 26, 2014 at 5:31 AM, Daniel Vetter daniel@ffwll.ch wrote:
+struct drm_crtc_state * +drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a) +{
struct drm_crtc_state *cstate;
int ret;
cstate = a->cstates[crtc->id];
if (!cstate) {
ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
if (ret)
return ERR_PTR(ret);
cstate = drm_crtc_create_state(crtc);
if (!cstate)
return ERR_PTR(-ENOMEM);
init_crtc_state(crtc, cstate, a);
a->crtcs[crtc->id] = crtc;
a->cstates[crtc->id] = cstate;
/* we'll need it later, so make sure we have state
* for primary plane too:
*/
drm_atomic_get_plane_state(crtc->primary, a);
I haven't figured out why. With primary planes I don't really see a need for this. If we need it to implement the legacy setcrtc interface, then that should be done there, not here.
well, if you sort out how to disable primary helper plane, then yes, you are right :-)
see commit_crtc_state()
BR, -R
On Mon, May 26, 2014 at 07:35:45AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 5:31 AM, Daniel Vetter daniel@ffwll.ch wrote:
+struct drm_crtc_state * +drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a) +{
struct drm_crtc_state *cstate;
int ret;
cstate = a->cstates[crtc->id];
if (!cstate) {
ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
if (ret)
return ERR_PTR(ret);
cstate = drm_crtc_create_state(crtc);
if (!cstate)
return ERR_PTR(-ENOMEM);
init_crtc_state(crtc, cstate, a);
a->crtcs[crtc->id] = crtc;
a->cstates[crtc->id] = cstate;
/* we'll need it later, so make sure we have state
* for primary plane too:
*/
drm_atomic_get_plane_state(crtc->primary, a);
I haven't figured out why. With primary planes I don't really see a need for this. If we need it to implement the legacy setcrtc interface, then that should be done there, not here.
well, if you sort out how to disable primary helper plane, then yes, you are right :-)
see commit_crtc_state()
Imo bail when we have a crtc with NULL primary plane in crtc_commit (and wont disable the entire crtc ofc).
Also I think your current commit_crtc should be pushed down into the crtc helpers - it's not going to do any good for i915. But I guess until we have a real user of the atomic interface (i.e. updates more than 1 crtc) like the fb helper code we don't need this yet.
But as soon as we update more than one crtc we _must_ push it down into the crtc helpers or the i915 machinery - if you try to enable crtcs which need resources from crtcs which aren't yet off this is simply going to fail. -Daniel
On Mon, May 26, 2014 at 10:56 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, May 26, 2014 at 07:35:45AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 5:31 AM, Daniel Vetter daniel@ffwll.ch wrote:
+struct drm_crtc_state * +drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a) +{
struct drm_crtc_state *cstate;
int ret;
cstate = a->cstates[crtc->id];
if (!cstate) {
ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
if (ret)
return ERR_PTR(ret);
cstate = drm_crtc_create_state(crtc);
if (!cstate)
return ERR_PTR(-ENOMEM);
init_crtc_state(crtc, cstate, a);
a->crtcs[crtc->id] = crtc;
a->cstates[crtc->id] = cstate;
/* we'll need it later, so make sure we have state
* for primary plane too:
*/
drm_atomic_get_plane_state(crtc->primary, a);
I haven't figured out why. With primary planes I don't really see a need for this. If we need it to implement the legacy setcrtc interface, then that should be done there, not here.
well, if you sort out how to disable primary helper plane, then yes, you are right :-)
see commit_crtc_state()
Imo bail when we have a crtc with NULL primary plane in crtc_commit (and wont disable the entire crtc ofc).
Also I think your current commit_crtc should be pushed down into the crtc helpers - it's not going to do any good for i915. But I guess until we have a real user of the atomic interface (i.e. updates more than 1 crtc) like the fb helper code we don't need this yet.
it is in helpers (if we rename drm_atomic_funcs -> drm_atomic_helper_funcs, which is basically what it is).
I suspect what you actually want is to split up atomic_commit() so you can do the loop over all the planes and crtcs internally.
BR, -R
But as soon as we update more than one crtc we _must_ push it down into the crtc helpers or the i915 machinery - if you try to enable crtcs which need resources from crtcs which aren't yet off this is simply going to fail.
-Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Mon, May 26, 2014 at 11:31:10AM +0200, Daniel Vetter wrote:
On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark wrote:
Break the mutable state of a crtc out into a separate structure and use atomic properties mechanism to set crtc attributes. This makes it easier to have some helpers for crtc->set_property() and for checking for invalid params. The idea is that individual drivers can wrap the state struct in their own struct which adds driver specific parameters, for easy build-up of state across multiple set_property() calls and for easy atomic commit or roll- back.
Signed-off-by: Rob Clark robdclark@gmail.com
Same comments about interface design as for the plane patch apply here. One additional comment below.
drivers/gpu/drm/armada/armada_crtc.c | 11 +- drivers/gpu/drm/ast/ast_mode.c | 1 + drivers/gpu/drm/cirrus/cirrus_mode.c | 1 + drivers/gpu/drm/drm_atomic.c | 231 ++++++++++- drivers/gpu/drm/drm_crtc.c | 598 ++++++++++++++++++----------- drivers/gpu/drm/drm_fb_helper.c | 2 +- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 7 +- drivers/gpu/drm/gma500/cdv_intel_display.c | 1 + drivers/gpu/drm/gma500/psb_intel_display.c | 1 + drivers/gpu/drm/i915/intel_display.c | 1 + drivers/gpu/drm/mgag200/mgag200_mode.c | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 6 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 6 +- drivers/gpu/drm/nouveau/dispnv04/crtc.c | 1 + drivers/gpu/drm/nouveau/nv50_display.c | 1 + drivers/gpu/drm/omapdrm/omap_crtc.c | 12 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/qxl/qxl_display.c | 2 + drivers/gpu/drm/radeon/radeon_display.c | 2 + drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 2 + drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 2 + drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 1 + drivers/gpu/drm/udl/udl_modeset.c | 2 + drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 1 + include/drm/drm_atomic.h | 29 ++ include/drm/drm_crtc.h | 103 ++++- 27 files changed, 785 insertions(+), 243 deletions(-)
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 7d3c649..6237af4 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -9,6 +9,7 @@ #include <linux/clk.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include "armada_crtc.h" #include "armada_drm.h" #include "armada_fb.h" @@ -966,7 +967,12 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc, { struct armada_private *priv = crtc->dev->dev_private; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state); bool update_csc = false;
int ret = 0;
if (IS_ERR(cstate))
return PTR_ERR(cstate);
if (property == priv->csc_yuv_prop) { dcrtc->csc_yuv_mode = val;
@@ -974,6 +980,9 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc, } else if (property == priv->csc_rgb_prop) { dcrtc->csc_rgb_mode = val; update_csc = true;
} else {
ret = drm_crtc_set_property(crtc, cstate, property,
val, blob_data);
}
if (update_csc) {
@@ -984,7 +993,7 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc, writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL); }
- return 0;
- return ret;
}
static struct drm_crtc_funcs armada_crtc_funcs = { diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 114aee9..c08e0e1 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -632,6 +632,7 @@ static const struct drm_crtc_funcs ast_crtc_funcs = { .cursor_move = ast_cursor_move, .reset = ast_crtc_reset, .set_config = drm_crtc_helper_set_config,
- .set_property = drm_atomic_crtc_set_property, .gamma_set = ast_crtc_gamma_set, .destroy = ast_crtc_destroy,
}; diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index 49332c5..3c4428c 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -366,6 +366,7 @@ static void cirrus_crtc_destroy(struct drm_crtc *crtc) static const struct drm_crtc_funcs cirrus_crtc_funcs = { .gamma_set = cirrus_crtc_gamma_set, .set_config = drm_crtc_helper_set_config,
- .set_property = drm_atomic_crtc_set_property, .destroy = cirrus_crtc_destroy,
};
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 403ffc5..863a0fe 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -48,11 +48,13 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, { struct drm_atomic_state *state; int nplanes = dev->mode_config.num_total_plane;
int ncrtcs = dev->mode_config.num_crtc; int sz; void *ptr;
sz = sizeof(*state); sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
sz += (sizeof(state->crtcs) + sizeof(state->cstates)) * ncrtcs;
ptr = kzalloc(sz, GFP_KERNEL);
@@ -74,6 +76,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, state->pstates = ptr; ptr = &state->pstates[nplanes];
- state->crtcs = ptr;
- ptr = &state->crtcs[ncrtcs];
- state->cstates = ptr;
- ptr = &state->cstates[ncrtcs];
- return state;
} EXPORT_SYMBOL(drm_atomic_begin); @@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev, struct drm_atomic_state *state, struct drm_mode_object *obj, struct drm_pending_vblank_event *event) {
- return -EINVAL; /* for now */
- switch (obj->type) {
- case DRM_MODE_OBJECT_CRTC: {
struct drm_crtc_state *cstate =
drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
if (IS_ERR(cstate))
return PTR_ERR(cstate);
cstate->event = event;
return 0;
- }
- default:
return -EINVAL;
- }
Hm, I think if we only want completion events on crtcs (which I agree on)
I don't. Unless you have a nice way of passing some kind of "fbs now available for rendering" list back to userland in the single crtc event. Last time I looked making the drm event stuff deal with variable length events looked more painful than just adding per plane events. But I must admit that I didn't really try to do it.
On Mon, May 26, 2014 at 06:23:05PM +0300, Ville Syrjälä wrote:
On Mon, May 26, 2014 at 11:31:10AM +0200, Daniel Vetter wrote:
On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark wrote:
@@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev, struct drm_atomic_state *state, struct drm_mode_object *obj, struct drm_pending_vblank_event *event) {
- return -EINVAL; /* for now */
- switch (obj->type) {
- case DRM_MODE_OBJECT_CRTC: {
struct drm_crtc_state *cstate =
drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
if (IS_ERR(cstate))
return PTR_ERR(cstate);
cstate->event = event;
return 0;
- }
- default:
return -EINVAL;
- }
Hm, I think if we only want completion events on crtcs (which I agree on)
I don't. Unless you have a nice way of passing some kind of "fbs now available for rendering" list back to userland in the single crtc event. Last time I looked making the drm event stuff deal with variable length events looked more painful than just adding per plane events. But I must admit that I didn't really try to do it.
Hm, why can't userspace keep a list of fb ids involved in a given crtc pageflip around and index those with the cookie we pass around?
I really don't see why the kernel has to implement a half-baked event multiplexer (which just copies the same thing n times), while userspace is perfectly capable of doing that itself?
Now if the timestamps would be genuinely different then I'd agree, but then that's not an atomic update. -Daniel
On Mon, May 26, 2014 at 11:37 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, May 26, 2014 at 06:23:05PM +0300, Ville Syrjälä wrote:
On Mon, May 26, 2014 at 11:31:10AM +0200, Daniel Vetter wrote:
On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark wrote:
@@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev, struct drm_atomic_state *state, struct drm_mode_object *obj, struct drm_pending_vblank_event *event) {
- return -EINVAL; /* for now */
- switch (obj->type) {
- case DRM_MODE_OBJECT_CRTC: {
struct drm_crtc_state *cstate =
drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
if (IS_ERR(cstate))
return PTR_ERR(cstate);
cstate->event = event;
return 0;
- }
- default:
return -EINVAL;
- }
Hm, I think if we only want completion events on crtcs (which I agree on)
I don't. Unless you have a nice way of passing some kind of "fbs now available for rendering" list back to userland in the single crtc event. Last time I looked making the drm event stuff deal with variable length events looked more painful than just adding per plane events. But I must admit that I didn't really try to do it.
Hm, why can't userspace keep a list of fb ids involved in a given crtc pageflip around and index those with the cookie we pass around?
I really don't see why the kernel has to implement a half-baked event multiplexer (which just copies the same thing n times), while userspace is perfectly capable of doing that itself?
Now if the timestamps would be genuinely different then I'd agree, but then that's not an atomic update.
I do kinda think userspace would want to know somehow if an fb is going to be really late and explicitly submit an older fb. If you were resizing, for example, I don't think the driver could make the call to leave one plane displaying an older fb.
But I do think we can bikeshed about that in parallel with getting the inital atomic infrastructure merged.
BR, -R
-Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Mon, May 26, 2014 at 05:37:57PM +0200, Daniel Vetter wrote:
On Mon, May 26, 2014 at 06:23:05PM +0300, Ville Syrjälä wrote:
On Mon, May 26, 2014 at 11:31:10AM +0200, Daniel Vetter wrote:
On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark wrote:
@@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev, struct drm_atomic_state *state, struct drm_mode_object *obj, struct drm_pending_vblank_event *event) {
- return -EINVAL; /* for now */
- switch (obj->type) {
- case DRM_MODE_OBJECT_CRTC: {
struct drm_crtc_state *cstate =
drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
if (IS_ERR(cstate))
return PTR_ERR(cstate);
cstate->event = event;
return 0;
- }
- default:
return -EINVAL;
- }
Hm, I think if we only want completion events on crtcs (which I agree on)
I don't. Unless you have a nice way of passing some kind of "fbs now available for rendering" list back to userland in the single crtc event. Last time I looked making the drm event stuff deal with variable length events looked more painful than just adding per plane events. But I must admit that I didn't really try to do it.
Hm, why can't userspace keep a list of fb ids involved in a given crtc pageflip around and index those with the cookie we pass around?
I really don't see why the kernel has to implement a half-baked event multiplexer (which just copies the same thing n times), while userspace is perfectly capable of doing that itself?
Now if the timestamps would be genuinely different then I'd agree, but then that's not an atomic update.
User space can't do it if flips get issued faster than the vrefresh rate. I still want my "moar fps" triple buffering mode.
On Mon, May 26, 2014 at 06:46:30PM +0300, Ville Syrjälä wrote:
On Mon, May 26, 2014 at 05:37:57PM +0200, Daniel Vetter wrote:
On Mon, May 26, 2014 at 06:23:05PM +0300, Ville Syrjälä wrote:
On Mon, May 26, 2014 at 11:31:10AM +0200, Daniel Vetter wrote:
On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark wrote:
@@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev, struct drm_atomic_state *state, struct drm_mode_object *obj, struct drm_pending_vblank_event *event) {
- return -EINVAL; /* for now */
- switch (obj->type) {
- case DRM_MODE_OBJECT_CRTC: {
struct drm_crtc_state *cstate =
drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
if (IS_ERR(cstate))
return PTR_ERR(cstate);
cstate->event = event;
return 0;
- }
- default:
return -EINVAL;
- }
Hm, I think if we only want completion events on crtcs (which I agree on)
I don't. Unless you have a nice way of passing some kind of "fbs now available for rendering" list back to userland in the single crtc event. Last time I looked making the drm event stuff deal with variable length events looked more painful than just adding per plane events. But I must admit that I didn't really try to do it.
Hm, why can't userspace keep a list of fb ids involved in a given crtc pageflip around and index those with the cookie we pass around?
I really don't see why the kernel has to implement a half-baked event multiplexer (which just copies the same thing n times), while userspace is perfectly capable of doing that itself?
Now if the timestamps would be genuinely different then I'd agree, but then that's not an atomic update.
User space can't do it if flips get issued faster than the vrefresh rate. I still want my "moar fps" triple buffering mode.
Hm right, I forget about this every time it comes up. Imo we should postpone this to when it actually gets implemented - usualy preemptive generalism just ends up hurting everyone for now real good. For the current logic we could ditch all the indirection and callbacks really and simply require that the ->commit hook sends out the crtc even if it's there.
And even if we add per-plane events we don't need new hooks imo. Just adding an event pointer to the plane state structure and firing off those events together with the crtc one in drivers should be good enough.
The proposed set_event callback plus implementation otoh are serious overkill. -Daniel
All the call-sites save one need locking. By pushing it down and adding a lockless flag, we can use the new spiffy atomic ww_mutex crtc locking and simplify all the call-sites.
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/armada/armada_fbdev.c | 4 +--- drivers/gpu/drm/drm_fb_cma_helper.c | 9 ++------- drivers/gpu/drm/drm_fb_helper.c | 17 ++++++++--------- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 4 +--- drivers/gpu/drm/gma500/psb_drv.c | 4 +--- drivers/gpu/drm/i915/intel_fbdev.c | 6 +----- drivers/gpu/drm/msm/msm_drv.c | 7 ++----- drivers/gpu/drm/omapdrm/omap_drv.c | 4 +--- drivers/gpu/drm/tegra/fb.c | 7 ++----- include/drm/drm_fb_helper.h | 3 ++- 10 files changed, 21 insertions(+), 44 deletions(-)
diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index 948cb14..9976125 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -181,10 +181,8 @@ void armada_fbdev_lastclose(struct drm_device *dev) { struct armada_private *priv = dev->dev_private;
- drm_modeset_lock_all(dev); if (priv->fbdev) - drm_fb_helper_restore_fbdev_mode(priv->fbdev); - drm_modeset_unlock_all(dev); + drm_fb_helper_restore_fbdev_mode(priv->fbdev, false); }
void armada_fbdev_fini(struct drm_device *dev) diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 65540b7..ccb7a9ac 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -429,13 +429,8 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); */ void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) { - if (fbdev_cma) { - struct drm_device *dev = fbdev_cma->fb_helper.dev; - - drm_modeset_lock_all(dev); - drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper); - drm_modeset_unlock_all(dev); - } + if (fbdev_cma) + drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper, false); } EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode);
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 4669e69..3815e1a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -276,12 +276,15 @@ EXPORT_SYMBOL(drm_fb_helper_debug_leave); /** * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration * @fb_helper: fbcon to restore + * @lockless: true in drm_fb_helper_force_kernel_mode() path, to avoid + * blocking in panic case, everywhere else should use false * * This should be called from driver's drm ->lastclose callback * when implementing an fbcon on top of kms using this helper. This ensures that * the user isn't greeted with a black screen when e.g. X dies. */ -bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) +bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper, + bool lockless) { struct drm_device *dev = fb_helper->dev; struct drm_plane *plane; @@ -289,9 +292,8 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) struct drm_atomic_state *state; int i;
- drm_warn_on_modeset_not_all_locked(dev); - - state = dev->driver->atomic_begin(dev, 0); + state = dev->driver->atomic_begin(dev, lockless ? + DRM_MODE_ATOMIC_NOLOCK : 0); if (IS_ERR(state)) { DRM_ERROR("failed to restore fbdev mode\n"); return true; @@ -355,7 +357,7 @@ static bool drm_fb_helper_force_kernel_mode(void) continue; }
- ret = drm_fb_helper_restore_fbdev_mode(helper); + ret = drm_fb_helper_restore_fbdev_mode(helper, true); if (ret) error = true;
@@ -840,7 +842,6 @@ EXPORT_SYMBOL(drm_fb_helper_check_var); int drm_fb_helper_set_par(struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; struct fb_var_screeninfo *var = &info->var;
if (var->pixclock != 0) { @@ -848,9 +849,7 @@ int drm_fb_helper_set_par(struct fb_info *info) return -EINVAL; }
- drm_modeset_lock_all(dev); - drm_fb_helper_restore_fbdev_mode(fb_helper); - drm_modeset_unlock_all(dev); + drm_fb_helper_restore_fbdev_mode(fb_helper, false);
if (fb_helper->delayed_hotplug) { fb_helper->delayed_hotplug = false; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 2371716..486b21f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -375,7 +375,5 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev) if (!private || !private->fb_helper) return;
- drm_modeset_lock_all(dev); - drm_fb_helper_restore_fbdev_mode(private->fb_helper); - drm_modeset_unlock_all(dev); + drm_fb_helper_restore_fbdev_mode(private->fb_helper, false); } diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 0180292..72a35e3 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -112,11 +112,9 @@ static void psb_driver_lastclose(struct drm_device *dev) struct drm_psb_private *dev_priv = dev->dev_private; struct psb_fbdev *fbdev = dev_priv->fbdev;
- drm_modeset_lock_all(dev); - ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); + ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper, false); if (ret) DRM_DEBUG("failed to restore crtc mode\n"); - drm_modeset_unlock_all(dev);
return; } diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index fce4a0d..28afd69 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -683,11 +683,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev) if (!dev_priv->fbdev) return;
- drm_modeset_lock_all(dev); - - ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper); + ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper, false); if (ret) DRM_DEBUG("failed to restore crtc mode\n"); - - drm_modeset_unlock_all(dev); } diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 46e0f29..d24ca45 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -382,11 +382,8 @@ static void msm_preclose(struct drm_device *dev, struct drm_file *file) static void msm_lastclose(struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private; - if (priv->fbdev) { - drm_modeset_lock_all(dev); - drm_fb_helper_restore_fbdev_mode(priv->fbdev); - drm_modeset_unlock_all(dev); - } + if (priv->fbdev) + drm_fb_helper_restore_fbdev_mode(priv->fbdev, false); }
static irqreturn_t msm_irq(int irq, void *arg) diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 3f64c47..ad082f4 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -590,9 +590,7 @@ static void dev_lastclose(struct drm_device *dev) } }
- drm_modeset_lock_all(dev); - ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev); - drm_modeset_unlock_all(dev); + ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev, false); if (ret) DBG("failed to restore crtc mode"); } diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index f7fca09..85aae37 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -346,11 +346,8 @@ static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev) { - if (fbdev) { - drm_modeset_lock_all(fbdev->base.dev); - drm_fb_helper_restore_fbdev_mode(&fbdev->base); - drm_modeset_unlock_all(fbdev->base.dev); - } + if (fbdev) + drm_fb_helper_restore_fbdev_mode(&fbdev->base, false); }
static void tegra_fb_output_poll_changed(struct drm_device *drm) diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 6e622f7..99598a0 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -108,7 +108,8 @@ int drm_fb_helper_set_par(struct fb_info *info); int drm_fb_helper_check_var(struct fb_var_screeninfo *var, struct fb_info *info);
-bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper); +bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper, + bool lockless); void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, uint32_t fb_width, uint32_t fb_height); void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
On Sat, May 24, 2014 at 02:30:22PM -0400, Rob Clark wrote:
All the call-sites save one need locking. By pushing it down and adding a lockless flag, we can use the new spiffy atomic ww_mutex crtc locking and simplify all the call-sites.
Signed-off-by: Rob Clark robdclark@gmail.com
Yeah, makes sense. But I don't like the bool lockless interface, especially since the only user of it is also in the fb helper.
Imo much better to add a static function with __ prefix internal to drm_fb_helper.c which is lockless and give drivers less rope to hang themselves. Since I just can't think of a good reason why they want to not have locking when force-restoring the fbdev emulation.
With that changed this is Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
drivers/gpu/drm/armada/armada_fbdev.c | 4 +--- drivers/gpu/drm/drm_fb_cma_helper.c | 9 ++------- drivers/gpu/drm/drm_fb_helper.c | 17 ++++++++--------- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 4 +--- drivers/gpu/drm/gma500/psb_drv.c | 4 +--- drivers/gpu/drm/i915/intel_fbdev.c | 6 +----- drivers/gpu/drm/msm/msm_drv.c | 7 ++----- drivers/gpu/drm/omapdrm/omap_drv.c | 4 +--- drivers/gpu/drm/tegra/fb.c | 7 ++----- include/drm/drm_fb_helper.h | 3 ++- 10 files changed, 21 insertions(+), 44 deletions(-)
diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index 948cb14..9976125 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -181,10 +181,8 @@ void armada_fbdev_lastclose(struct drm_device *dev) { struct armada_private *priv = dev->dev_private;
- drm_modeset_lock_all(dev); if (priv->fbdev)
drm_fb_helper_restore_fbdev_mode(priv->fbdev);
- drm_modeset_unlock_all(dev);
drm_fb_helper_restore_fbdev_mode(priv->fbdev, false);
}
void armada_fbdev_fini(struct drm_device *dev) diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 65540b7..ccb7a9ac 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -429,13 +429,8 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); */ void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) {
- if (fbdev_cma) {
struct drm_device *dev = fbdev_cma->fb_helper.dev;
drm_modeset_lock_all(dev);
drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper);
drm_modeset_unlock_all(dev);
- }
- if (fbdev_cma)
drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper, false);
} EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode);
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 4669e69..3815e1a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -276,12 +276,15 @@ EXPORT_SYMBOL(drm_fb_helper_debug_leave); /**
- drm_fb_helper_restore_fbdev_mode - restore fbdev configuration
- @fb_helper: fbcon to restore
- @lockless: true in drm_fb_helper_force_kernel_mode() path, to avoid
*/
- blocking in panic case, everywhere else should use false
- This should be called from driver's drm ->lastclose callback
- when implementing an fbcon on top of kms using this helper. This ensures that
- the user isn't greeted with a black screen when e.g. X dies.
-bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) +bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper,
bool lockless)
{ struct drm_device *dev = fb_helper->dev; struct drm_plane *plane; @@ -289,9 +292,8 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) struct drm_atomic_state *state; int i;
- drm_warn_on_modeset_not_all_locked(dev);
- state = dev->driver->atomic_begin(dev, 0);
- state = dev->driver->atomic_begin(dev, lockless ?
if (IS_ERR(state)) { DRM_ERROR("failed to restore fbdev mode\n"); return true;DRM_MODE_ATOMIC_NOLOCK : 0);
@@ -355,7 +357,7 @@ static bool drm_fb_helper_force_kernel_mode(void) continue; }
ret = drm_fb_helper_restore_fbdev_mode(helper);
if (ret) error = true;ret = drm_fb_helper_restore_fbdev_mode(helper, true);
@@ -840,7 +842,6 @@ EXPORT_SYMBOL(drm_fb_helper_check_var); int drm_fb_helper_set_par(struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par;
struct drm_device *dev = fb_helper->dev; struct fb_var_screeninfo *var = &info->var;
if (var->pixclock != 0) {
@@ -848,9 +849,7 @@ int drm_fb_helper_set_par(struct fb_info *info) return -EINVAL; }
- drm_modeset_lock_all(dev);
- drm_fb_helper_restore_fbdev_mode(fb_helper);
- drm_modeset_unlock_all(dev);
drm_fb_helper_restore_fbdev_mode(fb_helper, false);
if (fb_helper->delayed_hotplug) { fb_helper->delayed_hotplug = false;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 2371716..486b21f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -375,7 +375,5 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev) if (!private || !private->fb_helper) return;
- drm_modeset_lock_all(dev);
- drm_fb_helper_restore_fbdev_mode(private->fb_helper);
- drm_modeset_unlock_all(dev);
- drm_fb_helper_restore_fbdev_mode(private->fb_helper, false);
} diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 0180292..72a35e3 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -112,11 +112,9 @@ static void psb_driver_lastclose(struct drm_device *dev) struct drm_psb_private *dev_priv = dev->dev_private; struct psb_fbdev *fbdev = dev_priv->fbdev;
- drm_modeset_lock_all(dev);
- ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper);
- ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper, false); if (ret) DRM_DEBUG("failed to restore crtc mode\n");
drm_modeset_unlock_all(dev);
return;
} diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index fce4a0d..28afd69 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -683,11 +683,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev) if (!dev_priv->fbdev) return;
- drm_modeset_lock_all(dev);
- ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);
- ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper, false); if (ret) DRM_DEBUG("failed to restore crtc mode\n");
- drm_modeset_unlock_all(dev);
} diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 46e0f29..d24ca45 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -382,11 +382,8 @@ static void msm_preclose(struct drm_device *dev, struct drm_file *file) static void msm_lastclose(struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private;
- if (priv->fbdev) {
drm_modeset_lock_all(dev);
drm_fb_helper_restore_fbdev_mode(priv->fbdev);
drm_modeset_unlock_all(dev);
- }
- if (priv->fbdev)
drm_fb_helper_restore_fbdev_mode(priv->fbdev, false);
}
static irqreturn_t msm_irq(int irq, void *arg) diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 3f64c47..ad082f4 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -590,9 +590,7 @@ static void dev_lastclose(struct drm_device *dev) } }
- drm_modeset_lock_all(dev);
- ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev);
- drm_modeset_unlock_all(dev);
- ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev, false); if (ret) DBG("failed to restore crtc mode");
} diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index f7fca09..85aae37 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -346,11 +346,8 @@ static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev) {
- if (fbdev) {
drm_modeset_lock_all(fbdev->base.dev);
drm_fb_helper_restore_fbdev_mode(&fbdev->base);
drm_modeset_unlock_all(fbdev->base.dev);
- }
- if (fbdev)
drm_fb_helper_restore_fbdev_mode(&fbdev->base, false);
}
static void tegra_fb_output_poll_changed(struct drm_device *drm) diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 6e622f7..99598a0 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -108,7 +108,8 @@ int drm_fb_helper_set_par(struct fb_info *info); int drm_fb_helper_check_var(struct fb_var_screeninfo *var, struct fb_info *info);
-bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper); +bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper,
bool lockless);
void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, uint32_t fb_width, uint32_t fb_height); void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, -- 1.9.0
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 57 ++++++------ drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c | 6 ++ drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c | 5 -- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 56 ++++++------ drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 6 ++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 2 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 5 -- drivers/gpu/drm/msm/msm_atomic.c | 141 ++++++++++++++++++++++++++++++ drivers/gpu/drm/msm/msm_drv.c | 22 ++++- drivers/gpu/drm/msm/msm_drv.h | 7 ++ drivers/gpu/drm/msm/msm_gem.c | 24 +---- drivers/gpu/drm/msm/msm_gem.h | 13 +++ drivers/gpu/drm/msm/msm_kms.h | 1 + 15 files changed, 253 insertions(+), 94 deletions(-) create mode 100644 drivers/gpu/drm/msm/msm_atomic.c
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 5e1e6b0..dd12f56 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -27,6 +27,7 @@ msm-y := \ mdp/mdp5/mdp5_kms.o \ mdp/mdp5/mdp5_plane.o \ mdp/mdp5/mdp5_smp.o \ + msm_atomic.o \ msm_drv.o \ msm_fb.o \ msm_gem.o \ diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index d0d8befd..2fa6b75 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -52,7 +52,6 @@ struct mdp4_crtc {
/* if there is a pending flip, these will be non-null: */ struct drm_pending_vblank_event *event; - struct msm_fence_cb pageflip_cb;
#define PENDING_CURSOR 0x1 #define PENDING_FLIP 0x2 @@ -93,12 +92,16 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending) mdp_irq_register(&get_kms(crtc)->base, &mdp4_crtc->vblank); }
-static void crtc_flush(struct drm_crtc *crtc) +void mdp4_crtc_flush(struct drm_crtc *crtc) { + struct msm_drm_private *priv = crtc->dev->dev_private; struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); struct mdp4_kms *mdp4_kms = get_kms(crtc); uint32_t i, flush = 0;
+ if (priv->pending_crtcs & (1 << crtc->id)) + return; + for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) { struct drm_plane *plane = mdp4_crtc->planes[i]; if (plane) { @@ -142,7 +145,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) * so that we can safely queue unref to current fb (ie. next * vblank we know hw is done w/ previous scanout_fb). */ - crtc_flush(crtc); + mdp4_crtc_flush(crtc);
if (mdp4_crtc->scanout_fb) drm_flip_work_queue(&mdp4_crtc->unref_fb_work, @@ -177,21 +180,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) spin_unlock_irqrestore(&dev->event_lock, flags); }
-static void pageflip_cb(struct msm_fence_cb *cb) -{ - struct mdp4_crtc *mdp4_crtc = - container_of(cb, struct mdp4_crtc, pageflip_cb); - struct drm_crtc *crtc = &mdp4_crtc->base; - struct drm_framebuffer *fb = crtc->primary->fb; - - if (!fb) - return; - - drm_framebuffer_reference(fb); - mdp4_plane_set_scanout(mdp4_crtc->plane, fb); - update_scanout(crtc, fb); -} - static void unref_fb_worker(struct drm_flip_work *work, void *val) { struct mdp4_crtc *mdp4_crtc = @@ -406,7 +394,7 @@ static void mdp4_crtc_prepare(struct drm_crtc *crtc) static void mdp4_crtc_commit(struct drm_crtc *crtc) { mdp4_crtc_dpms(crtc, DRM_MODE_DPMS_ON); - crtc_flush(crtc); + mdp4_crtc_flush(crtc); /* drop the ref to mdp clk's that we got in prepare: */ mdp4_disable(get_kms(crtc)); } @@ -448,23 +436,27 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc, { struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); struct drm_device *dev = crtc->dev; - struct drm_gem_object *obj; unsigned long flags;
+ spin_lock_irqsave(&dev->event_lock, flags); if (mdp4_crtc->event) { dev_err(dev->dev, "already pending flip!\n"); + spin_unlock_irqrestore(&dev->event_lock, flags); return -EBUSY; }
- obj = msm_framebuffer_bo(new_fb, 0); - - spin_lock_irqsave(&dev->event_lock, flags); mdp4_crtc->event = event; spin_unlock_irqrestore(&dev->event_lock, flags);
update_fb(crtc, new_fb);
- return msm_gem_queue_inactive_cb(obj, &mdp4_crtc->pageflip_cb); + + /* grab extra ref for update_scanout() */ + drm_framebuffer_reference(new_fb); + mdp4_plane_set_scanout(mdp4_crtc->plane, new_fb); + update_scanout(crtc, new_fb); + + return 0; }
static int mdp4_crtc_set_property(struct drm_crtc *crtc, @@ -598,7 +590,7 @@ static int mdp4_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) mdp4_crtc->cursor.y = y; spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags);
- crtc_flush(crtc); + mdp4_crtc_flush(crtc); request_pending(crtc, PENDING_CURSOR);
return 0; @@ -635,8 +627,13 @@ static void mdp4_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus) pending = atomic_xchg(&mdp4_crtc->pending, 0);
if (pending & PENDING_FLIP) { - complete_flip(crtc, NULL); - drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq); + if (priv->pending_crtcs & (1 << crtc->id)) { + /* our update hasn't been flushed yet: */ + request_pending(crtc, PENDING_FLIP); + } else { + complete_flip(crtc, NULL); + drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq); + } }
if (pending & PENDING_CURSOR) { @@ -650,7 +647,7 @@ static void mdp4_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus) struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, err); struct drm_crtc *crtc = &mdp4_crtc->base; DBG("%s: error: %08x", mdp4_crtc->name, irqstatus); - crtc_flush(crtc); + mdp4_crtc_flush(crtc); }
uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc) @@ -730,7 +727,7 @@ static void set_attach(struct drm_crtc *crtc, enum mdp4_pipe pipe_id, mdp4_crtc->planes[pipe_id] = plane; blend_setup(crtc); if (mdp4_crtc->enabled && (plane != mdp4_crtc->plane)) - crtc_flush(crtc); + mdp4_crtc_flush(crtc); }
void mdp4_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane) @@ -792,8 +789,6 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev, ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64, "unref cursor", unref_cursor_worker);
- INIT_FENCE_CB(&mdp4_crtc->pageflip_cb, pageflip_cb); - drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp4_crtc_funcs); drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index 0bb4faa..af0bfb1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -131,6 +131,11 @@ static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate, return mdp4_dtv_round_pixclk(encoder, rate); }
+static void mdp4_flush(struct msm_kms *kms, struct drm_crtc *crtc) +{ + mdp4_crtc_flush(crtc); +} + static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file) { struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); @@ -162,6 +167,7 @@ static const struct mdp_kms_funcs kms_funcs = { .disable_vblank = mdp4_disable_vblank, .get_format = mdp_get_format, .round_pixclk = mdp4_round_pixclk, + .flush = mdp4_flush, .preclose = mdp4_preclose, .destroy = mdp4_destroy, }, diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h index 715520c5..834454c 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h @@ -184,6 +184,7 @@ enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane); struct drm_plane *mdp4_plane_init(struct drm_device *dev, enum mdp4_pipe pipe_id, bool private_plane);
+void mdp4_crtc_flush(struct drm_crtc *crtc); uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc); void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file); void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config); diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 4c92985..f35848d 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -48,11 +48,6 @@ static int mdp4_plane_update(struct drm_plane *plane,
mdp4_plane->enabled = true;
- if (plane->fb) - drm_framebuffer_unreference(plane->fb); - - drm_framebuffer_reference(fb); - return mdp4_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 7f4ee99..0e74317 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -35,7 +35,6 @@ struct mdp5_crtc {
/* if there is a pending flip, these will be non-null: */ struct drm_pending_vblank_event *event; - struct msm_fence_cb pageflip_cb;
#define PENDING_CURSOR 0x1 #define PENDING_FLIP 0x2 @@ -73,13 +72,17 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending) mdp_irq_register(&get_kms(crtc)->base, &mdp5_crtc->vblank); }
-static void crtc_flush(struct drm_crtc *crtc) +void mdp5_crtc_flush(struct drm_crtc *crtc) { + struct msm_drm_private *priv = crtc->dev->dev_private; struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct mdp5_kms *mdp5_kms = get_kms(crtc); int id = mdp5_crtc->id; uint32_t i, flush = 0;
+ if (priv->pending_crtcs & (1 << crtc->id)) + return; + for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) { struct drm_plane *plane = mdp5_crtc->planes[i]; if (plane) { @@ -124,7 +127,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) * so that we can safely queue unref to current fb (ie. next * vblank we know hw is done w/ previous scanout_fb). */ - crtc_flush(crtc); + mdp5_crtc_flush(crtc);
if (mdp5_crtc->scanout_fb) drm_flip_work_queue(&mdp5_crtc->unref_fb_work, @@ -165,21 +168,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) } }
-static void pageflip_cb(struct msm_fence_cb *cb) -{ - struct mdp5_crtc *mdp5_crtc = - container_of(cb, struct mdp5_crtc, pageflip_cb); - struct drm_crtc *crtc = &mdp5_crtc->base; - struct drm_framebuffer *fb = mdp5_crtc->fb; - - if (!fb) - return; - - drm_framebuffer_reference(fb); - mdp5_plane_set_scanout(mdp5_crtc->plane, fb); - update_scanout(crtc, fb); -} - static void unref_fb_worker(struct drm_flip_work *work, void *val) { struct mdp5_crtc *mdp5_crtc = @@ -324,7 +312,7 @@ static void mdp5_crtc_prepare(struct drm_crtc *crtc) static void mdp5_crtc_commit(struct drm_crtc *crtc) { mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_ON); - crtc_flush(crtc); + mdp5_crtc_flush(crtc); /* drop the ref to mdp clk's that we got in prepare: */ mdp5_disable(get_kms(crtc)); } @@ -366,23 +354,26 @@ static int mdp5_crtc_page_flip(struct drm_crtc *crtc, { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct drm_device *dev = crtc->dev; - struct drm_gem_object *obj; unsigned long flags;
+ spin_lock_irqsave(&dev->event_lock, flags); if (mdp5_crtc->event) { dev_err(dev->dev, "already pending flip!\n"); + spin_unlock_irqrestore(&dev->event_lock, flags); return -EBUSY; }
- obj = msm_framebuffer_bo(new_fb, 0); - - spin_lock_irqsave(&dev->event_lock, flags); mdp5_crtc->event = event; spin_unlock_irqrestore(&dev->event_lock, flags);
update_fb(crtc, new_fb);
- return msm_gem_queue_inactive_cb(obj, &mdp5_crtc->pageflip_cb); + /* grab extra ref for update_scanout() */ + drm_framebuffer_reference(new_fb); + mdp5_plane_set_scanout(mdp5_crtc->plane, new_fb); + update_scanout(crtc, new_fb); + + return 0; }
static int mdp5_crtc_set_property(struct drm_crtc *crtc, @@ -424,8 +415,13 @@ static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus) pending = atomic_xchg(&mdp5_crtc->pending, 0);
if (pending & PENDING_FLIP) { - complete_flip(crtc, NULL); - drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq); + if (priv->pending_crtcs & (1 << crtc->id)) { + /* our update hasn't been flushed yet: */ + request_pending(crtc, PENDING_FLIP); + } else { + complete_flip(crtc, NULL); + drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq); + } } }
@@ -434,7 +430,7 @@ static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus) struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, err); struct drm_crtc *crtc = &mdp5_crtc->base; DBG("%s: error: %08x", mdp5_crtc->name, irqstatus); - crtc_flush(crtc); + mdp5_crtc_flush(crtc); }
uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc) @@ -501,7 +497,7 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf, MDP5_CTL_OP_MODE(MODE_NONE) | MDP5_CTL_OP_INTF_NUM(intfnum[intf]));
- crtc_flush(crtc); + mdp5_crtc_flush(crtc); }
static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id, @@ -517,7 +513,7 @@ static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id, mdp5_crtc->planes[pipe_id] = plane; blend_setup(crtc); if (mdp5_crtc->enabled && (plane != mdp5_crtc->plane)) - crtc_flush(crtc); + mdp5_crtc_flush(crtc); }
void mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane) @@ -563,8 +559,6 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, if (ret) goto fail;
- INIT_FENCE_CB(&mdp5_crtc->pageflip_cb, pageflip_cb); - drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs); drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index ee8446c..01c3d70 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -91,6 +91,11 @@ static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate, return rate; }
+static void mdp5_flush(struct msm_kms *kms, struct drm_crtc *crtc) +{ + mdp5_crtc_flush(crtc); +} + static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); @@ -118,6 +123,7 @@ static const struct mdp_kms_funcs kms_funcs = { .disable_vblank = mdp5_disable_vblank, .get_format = mdp_get_format, .round_pixclk = mdp5_round_pixclk, + .flush = mdp5_flush, .preclose = mdp5_preclose, .destroy = mdp5_destroy, }, diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index c8b1a25..18b031b 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -197,8 +197,8 @@ enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane); struct drm_plane *mdp5_plane_init(struct drm_device *dev, enum mdp5_pipe pipe, bool private_plane);
+void mdp5_crtc_flush(struct drm_crtc *crtc); uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc); - void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file); void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf, enum mdp5_intf intf_id); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 53cc8c6..f1bf3c2 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -48,11 +48,6 @@ static int mdp5_plane_update(struct drm_plane *plane,
mdp5_plane->enabled = true;
- if (plane->fb) - drm_framebuffer_unreference(plane->fb); - - drm_framebuffer_reference(fb); - return mdp5_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h); diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c new file mode 100644 index 0000000..b231377 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark robdclark@gmail.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see http://www.gnu.org/licenses/. + */ + +#include "msm_drv.h" +#include "msm_kms.h" +#include "msm_gem.h" + +struct msm_async_commit { + struct drm_atomic_state *state; + uint32_t fence; + struct msm_fence_cb fence_cb; +}; + +static void fence_cb(struct msm_fence_cb *cb); +static int commit_sync(struct drm_device *dev, void *state, bool unlocked); + +static struct msm_async_commit *new_commit(struct drm_atomic_state *state) +{ + struct msm_async_commit *c = kzalloc(sizeof(*c), GFP_KERNEL); + + if (!c) + return NULL; + + drm_atomic_state_reference(state); + c->state = state; + INIT_FENCE_CB(&c->fence_cb, fence_cb); + + return c; +} +static void free_commit(struct msm_async_commit *c) +{ + drm_atomic_state_unreference(c->state); + kfree(c); +} + +static void fence_cb(struct msm_fence_cb *cb) +{ + struct msm_async_commit *c = + container_of(cb, struct msm_async_commit, fence_cb); + commit_sync(c->state->dev, c->state, true); + free_commit(c); +} + +static void add_fb(struct msm_async_commit *c, struct drm_crtc *crtc, + struct drm_framebuffer *fb) +{ + struct drm_gem_object *obj = msm_framebuffer_bo(fb, 0); + c->fence = max(c->fence, msm_gem_fence(to_msm_bo(obj), MSM_PREP_READ)); +} + +static int wait_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb) +{ + // XXX TODO wait.. + return 0; +} + +#define pending_fb(state) ((state) && (state)->fb && (state)->new_fb) + +static int commit_sync(struct drm_device *dev, void *state, bool unlocked) +{ + struct drm_atomic_state *a = state; + struct msm_drm_private *priv = dev->dev_private; + struct msm_kms *kms = priv->kms; + int ncrtcs = dev->mode_config.num_crtc; + uint32_t pending_crtcs = 0; + int i, ret; + + for (i = 0; i < ncrtcs; i++) + if (a->crtcs[i]) + pending_crtcs |= (1 << a->crtcs[i]->id); + + mutex_lock(&dev->struct_mutex); + WARN_ON(priv->pending_crtcs & pending_crtcs); + priv->pending_crtcs |= pending_crtcs; + mutex_unlock(&dev->struct_mutex); + + if (unlocked) + ret = drm_atomic_commit_unlocked(dev, state); + else + ret = drm_atomic_commit(dev, state); + + mutex_lock(&dev->struct_mutex); + priv->pending_crtcs &= ~pending_crtcs; + mutex_unlock(&dev->struct_mutex); + + if (ret) + return ret; + + for (i = 0; i < ncrtcs; i++) + if (a->crtcs[i]) + kms->funcs->flush(kms, a->crtcs[i]); + + return 0; +} + +int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state) +{ + struct drm_atomic_state *a = state; + int nplanes = dev->mode_config.num_total_plane; + int i; + + if (a->flags & DRM_MODE_ATOMIC_NONBLOCK) { + /* non-block mode: defer commit until fb's are ready */ + struct msm_async_commit *c = new_commit(state); + + if (!c) + return -ENOMEM; + + for (i = 0; i < nplanes; i++) + if (pending_fb(a->pstates[i])) + add_fb(c, a->pstates[i]->crtc, a->pstates[i]->fb); + + return msm_queue_fence_cb(dev, &c->fence_cb, c->fence); + } else { + /* blocking mode: wait until fb's are ready */ + int ret = 0; + + for (i = 0; i < nplanes && !ret; i++) + if (pending_fb(a->pstates[i])) + ret = wait_fb(a->pstates[i]->crtc, a->pstates[i]->fb); + + if (ret) + return ret; + + return commit_sync(dev, state, false); + } +} diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index d24ca45..bcee218 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -600,6 +600,26 @@ int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence, return ret; }
+int msm_queue_fence_cb(struct drm_device *dev, + struct msm_fence_cb *cb, uint32_t fence) +{ + struct msm_drm_private *priv = dev->dev_private; + int ret = 0; + + mutex_lock(&dev->struct_mutex); + if (!list_empty(&cb->work.entry)) { + ret = -EINVAL; + } else if (fence > priv->completed_fence) { + cb->fence = fence; + list_add_tail(&cb->work.entry, &priv->fence_cbs); + } else { + queue_work(priv->wq, &cb->work); + } + mutex_unlock(&dev->struct_mutex); + + return ret; +} + /* called from workqueue */ void msm_update_fence(struct drm_device *dev, uint32_t fence) { @@ -815,7 +835,7 @@ static struct drm_driver msm_driver = { .atomic_begin = drm_atomic_begin, .atomic_set_event = drm_atomic_set_event, .atomic_check = drm_atomic_check, - .atomic_commit = drm_atomic_commit, + .atomic_commit = msm_atomic_commit, .atomic_end = drm_atomic_end, .atomic_funcs = &drm_atomic_funcs, #ifdef CONFIG_DEBUG_FS diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index afc990e..5e1e7da 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -110,6 +110,9 @@ struct msm_drm_private { unsigned int num_connectors; struct drm_connector *connectors[8];
+ /* crtc's pending atomic update: */ + uint32_t pending_crtcs; + /* VRAM carveout, used when no IOMMU: */ struct { unsigned long size; @@ -143,11 +146,15 @@ int msm_register_mmu(struct drm_device *dev, struct msm_mmu *mmu);
int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence, struct timespec *timeout); +int msm_queue_fence_cb(struct drm_device *dev, + struct msm_fence_cb *cb, uint32_t fence); void msm_update_fence(struct drm_device *dev, uint32_t fence);
int msm_ioctl_gem_submit(struct drm_device *dev, void *data, struct drm_file *file);
+int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state); + int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma); int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index bb8026d..90cb138 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -392,23 +392,11 @@ void *msm_gem_vaddr(struct drm_gem_object *obj) int msm_gem_queue_inactive_cb(struct drm_gem_object *obj, struct msm_fence_cb *cb) { - struct drm_device *dev = obj->dev; - struct msm_drm_private *priv = dev->dev_private; struct msm_gem_object *msm_obj = to_msm_bo(obj); - int ret = 0; + uint32_t fence = msm_gem_fence(msm_obj, + MSM_PREP_READ | MSM_PREP_WRITE);
- mutex_lock(&dev->struct_mutex); - if (!list_empty(&cb->work.entry)) { - ret = -EINVAL; - } else if (is_active(msm_obj)) { - cb->fence = max(msm_obj->read_fence, msm_obj->write_fence); - list_add_tail(&cb->work.entry, &priv->fence_cbs); - } else { - queue_work(priv->wq, &cb->work); - } - mutex_unlock(&dev->struct_mutex); - - return ret; + return msm_queue_fence_cb(obj->dev, cb, fence); }
void msm_gem_move_to_active(struct drm_gem_object *obj, @@ -447,12 +435,8 @@ int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, int ret = 0;
if (is_active(msm_obj)) { - uint32_t fence = 0; + uint32_t fence = msm_gem_fence(msm_obj, op);
- if (op & MSM_PREP_READ) - fence = msm_obj->write_fence; - if (op & MSM_PREP_WRITE) - fence = max(fence, msm_obj->read_fence); if (op & MSM_PREP_NOSYNC) timeout = NULL;
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index 3246bb4..41a60cb 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -70,6 +70,19 @@ static inline bool is_active(struct msm_gem_object *msm_obj) return msm_obj->gpu != NULL; }
+static inline uint32_t msm_gem_fence(struct msm_gem_object *msm_obj, + uint32_t op) +{ + uint32_t fence = 0; + + if (op & MSM_PREP_READ) + fence = msm_obj->write_fence; + if (op & MSM_PREP_WRITE) + fence = max(fence, msm_obj->read_fence); + + return fence; +} + #define MAX_CMDS 4
/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc, diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index 0643774..91ddffc 100644 --- a/drivers/gpu/drm/msm/msm_kms.h +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -42,6 +42,7 @@ struct msm_kms_funcs { const struct msm_format *(*get_format)(struct msm_kms *kms, uint32_t format); long (*round_pixclk)(struct msm_kms *kms, unsigned long rate, struct drm_encoder *encoder); + void (*flush)(struct msm_kms *kms, struct drm_crtc *crtc); /* cleanup: */ void (*preclose)(struct msm_kms *kms, struct drm_file *file); void (*destroy)(struct msm_kms *kms);
On Sat, May 24, 2014 at 02:30:23PM -0400, Rob Clark wrote:
Signed-off-by: Rob Clark robdclark@gmail.com
drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 57 ++++++------ drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c | 6 ++ drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c | 5 -- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 56 ++++++------ drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 6 ++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 2 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 5 -- drivers/gpu/drm/msm/msm_atomic.c | 141 ++++++++++++++++++++++++++++++ drivers/gpu/drm/msm/msm_drv.c | 22 ++++- drivers/gpu/drm/msm/msm_drv.h | 7 ++ drivers/gpu/drm/msm/msm_gem.c | 24 +---- drivers/gpu/drm/msm/msm_gem.h | 13 +++ drivers/gpu/drm/msm/msm_kms.h | 1 + 15 files changed, 253 insertions(+), 94 deletions(-) create mode 100644 drivers/gpu/drm/msm/msm_atomic.c
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 5e1e6b0..dd12f56 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -27,6 +27,7 @@ msm-y := \ mdp/mdp5/mdp5_kms.o \ mdp/mdp5/mdp5_plane.o \ mdp/mdp5/mdp5_smp.o \
- msm_atomic.o \ msm_drv.o \ msm_fb.o \ msm_gem.o \
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index d0d8befd..2fa6b75 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -52,7 +52,6 @@ struct mdp4_crtc {
/* if there is a pending flip, these will be non-null: */ struct drm_pending_vblank_event *event;
- struct msm_fence_cb pageflip_cb;
#define PENDING_CURSOR 0x1 #define PENDING_FLIP 0x2 @@ -93,12 +92,16 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending) mdp_irq_register(&get_kms(crtc)->base, &mdp4_crtc->vblank); }
-static void crtc_flush(struct drm_crtc *crtc) +void mdp4_crtc_flush(struct drm_crtc *crtc) {
struct msm_drm_private *priv = crtc->dev->dev_private; struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); struct mdp4_kms *mdp4_kms = get_kms(crtc); uint32_t i, flush = 0;
if (priv->pending_crtcs & (1 << crtc->id))
return;
for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) { struct drm_plane *plane = mdp4_crtc->planes[i]; if (plane) {
@@ -142,7 +145,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) * so that we can safely queue unref to current fb (ie. next * vblank we know hw is done w/ previous scanout_fb). */
- crtc_flush(crtc);
mdp4_crtc_flush(crtc);
if (mdp4_crtc->scanout_fb) drm_flip_work_queue(&mdp4_crtc->unref_fb_work,
@@ -177,21 +180,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) spin_unlock_irqrestore(&dev->event_lock, flags); }
-static void pageflip_cb(struct msm_fence_cb *cb) -{
- struct mdp4_crtc *mdp4_crtc =
container_of(cb, struct mdp4_crtc, pageflip_cb);
- struct drm_crtc *crtc = &mdp4_crtc->base;
- struct drm_framebuffer *fb = crtc->primary->fb;
- if (!fb)
return;
- drm_framebuffer_reference(fb);
- mdp4_plane_set_scanout(mdp4_crtc->plane, fb);
- update_scanout(crtc, fb);
-}
static void unref_fb_worker(struct drm_flip_work *work, void *val) { struct mdp4_crtc *mdp4_crtc = @@ -406,7 +394,7 @@ static void mdp4_crtc_prepare(struct drm_crtc *crtc) static void mdp4_crtc_commit(struct drm_crtc *crtc) { mdp4_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
- crtc_flush(crtc);
- mdp4_crtc_flush(crtc); /* drop the ref to mdp clk's that we got in prepare: */ mdp4_disable(get_kms(crtc));
} @@ -448,23 +436,27 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc, { struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); struct drm_device *dev = crtc->dev;
- struct drm_gem_object *obj; unsigned long flags;
- spin_lock_irqsave(&dev->event_lock, flags); if (mdp4_crtc->event) { dev_err(dev->dev, "already pending flip!\n");
return -EBUSY; }spin_unlock_irqrestore(&dev->event_lock, flags);
obj = msm_framebuffer_bo(new_fb, 0);
spin_lock_irqsave(&dev->event_lock, flags); mdp4_crtc->event = event; spin_unlock_irqrestore(&dev->event_lock, flags);
update_fb(crtc, new_fb);
return msm_gem_queue_inactive_cb(obj, &mdp4_crtc->pageflip_cb);
- /* grab extra ref for update_scanout() */
- drm_framebuffer_reference(new_fb);
- mdp4_plane_set_scanout(mdp4_crtc->plane, new_fb);
- update_scanout(crtc, new_fb);
- return 0;
}
static int mdp4_crtc_set_property(struct drm_crtc *crtc, @@ -598,7 +590,7 @@ static int mdp4_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) mdp4_crtc->cursor.y = y; spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags);
- crtc_flush(crtc);
mdp4_crtc_flush(crtc); request_pending(crtc, PENDING_CURSOR);
return 0;
@@ -635,8 +627,13 @@ static void mdp4_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus) pending = atomic_xchg(&mdp4_crtc->pending, 0);
if (pending & PENDING_FLIP) {
complete_flip(crtc, NULL);
drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq);
if (priv->pending_crtcs & (1 << crtc->id)) {
/* our update hasn't been flushed yet: */
request_pending(crtc, PENDING_FLIP);
} else {
complete_flip(crtc, NULL);
drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq);
}
}
if (pending & PENDING_CURSOR) {
@@ -650,7 +647,7 @@ static void mdp4_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus) struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, err); struct drm_crtc *crtc = &mdp4_crtc->base; DBG("%s: error: %08x", mdp4_crtc->name, irqstatus);
- crtc_flush(crtc);
- mdp4_crtc_flush(crtc);
}
uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc) @@ -730,7 +727,7 @@ static void set_attach(struct drm_crtc *crtc, enum mdp4_pipe pipe_id, mdp4_crtc->planes[pipe_id] = plane; blend_setup(crtc); if (mdp4_crtc->enabled && (plane != mdp4_crtc->plane))
crtc_flush(crtc);
mdp4_crtc_flush(crtc);
}
void mdp4_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane) @@ -792,8 +789,6 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev, ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64, "unref cursor", unref_cursor_worker);
- INIT_FENCE_CB(&mdp4_crtc->pageflip_cb, pageflip_cb);
- drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp4_crtc_funcs); drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index 0bb4faa..af0bfb1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -131,6 +131,11 @@ static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate, return mdp4_dtv_round_pixclk(encoder, rate); }
+static void mdp4_flush(struct msm_kms *kms, struct drm_crtc *crtc) +{
- mdp4_crtc_flush(crtc);
+}
static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file) { struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); @@ -162,6 +167,7 @@ static const struct mdp_kms_funcs kms_funcs = { .disable_vblank = mdp4_disable_vblank, .get_format = mdp_get_format, .round_pixclk = mdp4_round_pixclk,
.preclose = mdp4_preclose, .destroy = mdp4_destroy, },.flush = mdp4_flush,
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h index 715520c5..834454c 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h @@ -184,6 +184,7 @@ enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane); struct drm_plane *mdp4_plane_init(struct drm_device *dev, enum mdp4_pipe pipe_id, bool private_plane);
+void mdp4_crtc_flush(struct drm_crtc *crtc); uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc); void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file); void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config); diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 4c92985..f35848d 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -48,11 +48,6 @@ static int mdp4_plane_update(struct drm_plane *plane,
mdp4_plane->enabled = true;
- if (plane->fb)
drm_framebuffer_unreference(plane->fb);
- drm_framebuffer_reference(fb);
- return mdp4_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 7f4ee99..0e74317 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -35,7 +35,6 @@ struct mdp5_crtc {
/* if there is a pending flip, these will be non-null: */ struct drm_pending_vblank_event *event;
- struct msm_fence_cb pageflip_cb;
#define PENDING_CURSOR 0x1 #define PENDING_FLIP 0x2 @@ -73,13 +72,17 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending) mdp_irq_register(&get_kms(crtc)->base, &mdp5_crtc->vblank); }
-static void crtc_flush(struct drm_crtc *crtc) +void mdp5_crtc_flush(struct drm_crtc *crtc) {
struct msm_drm_private *priv = crtc->dev->dev_private; struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct mdp5_kms *mdp5_kms = get_kms(crtc); int id = mdp5_crtc->id; uint32_t i, flush = 0;
if (priv->pending_crtcs & (1 << crtc->id))
return;
for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) { struct drm_plane *plane = mdp5_crtc->planes[i]; if (plane) {
@@ -124,7 +127,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) * so that we can safely queue unref to current fb (ie. next * vblank we know hw is done w/ previous scanout_fb). */
- crtc_flush(crtc);
mdp5_crtc_flush(crtc);
if (mdp5_crtc->scanout_fb) drm_flip_work_queue(&mdp5_crtc->unref_fb_work,
@@ -165,21 +168,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) } }
-static void pageflip_cb(struct msm_fence_cb *cb) -{
- struct mdp5_crtc *mdp5_crtc =
container_of(cb, struct mdp5_crtc, pageflip_cb);
- struct drm_crtc *crtc = &mdp5_crtc->base;
- struct drm_framebuffer *fb = mdp5_crtc->fb;
- if (!fb)
return;
- drm_framebuffer_reference(fb);
- mdp5_plane_set_scanout(mdp5_crtc->plane, fb);
- update_scanout(crtc, fb);
-}
static void unref_fb_worker(struct drm_flip_work *work, void *val) { struct mdp5_crtc *mdp5_crtc = @@ -324,7 +312,7 @@ static void mdp5_crtc_prepare(struct drm_crtc *crtc) static void mdp5_crtc_commit(struct drm_crtc *crtc) { mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
- crtc_flush(crtc);
- mdp5_crtc_flush(crtc); /* drop the ref to mdp clk's that we got in prepare: */ mdp5_disable(get_kms(crtc));
} @@ -366,23 +354,26 @@ static int mdp5_crtc_page_flip(struct drm_crtc *crtc, { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct drm_device *dev = crtc->dev;
- struct drm_gem_object *obj; unsigned long flags;
- spin_lock_irqsave(&dev->event_lock, flags); if (mdp5_crtc->event) { dev_err(dev->dev, "already pending flip!\n");
return -EBUSY; }spin_unlock_irqrestore(&dev->event_lock, flags);
obj = msm_framebuffer_bo(new_fb, 0);
spin_lock_irqsave(&dev->event_lock, flags); mdp5_crtc->event = event; spin_unlock_irqrestore(&dev->event_lock, flags);
update_fb(crtc, new_fb);
return msm_gem_queue_inactive_cb(obj, &mdp5_crtc->pageflip_cb);
- /* grab extra ref for update_scanout() */
- drm_framebuffer_reference(new_fb);
- mdp5_plane_set_scanout(mdp5_crtc->plane, new_fb);
- update_scanout(crtc, new_fb);
- return 0;
}
static int mdp5_crtc_set_property(struct drm_crtc *crtc, @@ -424,8 +415,13 @@ static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus) pending = atomic_xchg(&mdp5_crtc->pending, 0);
if (pending & PENDING_FLIP) {
complete_flip(crtc, NULL);
drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq);
if (priv->pending_crtcs & (1 << crtc->id)) {
/* our update hasn't been flushed yet: */
request_pending(crtc, PENDING_FLIP);
} else {
complete_flip(crtc, NULL);
drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq);
}}
}
@@ -434,7 +430,7 @@ static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus) struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, err); struct drm_crtc *crtc = &mdp5_crtc->base; DBG("%s: error: %08x", mdp5_crtc->name, irqstatus);
- crtc_flush(crtc);
- mdp5_crtc_flush(crtc);
}
uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc) @@ -501,7 +497,7 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf, MDP5_CTL_OP_MODE(MODE_NONE) | MDP5_CTL_OP_INTF_NUM(intfnum[intf]));
- crtc_flush(crtc);
- mdp5_crtc_flush(crtc);
}
static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id, @@ -517,7 +513,7 @@ static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id, mdp5_crtc->planes[pipe_id] = plane; blend_setup(crtc); if (mdp5_crtc->enabled && (plane != mdp5_crtc->plane))
crtc_flush(crtc);
mdp5_crtc_flush(crtc);
}
void mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane) @@ -563,8 +559,6 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, if (ret) goto fail;
- INIT_FENCE_CB(&mdp5_crtc->pageflip_cb, pageflip_cb);
- drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs); drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index ee8446c..01c3d70 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -91,6 +91,11 @@ static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate, return rate; }
+static void mdp5_flush(struct msm_kms *kms, struct drm_crtc *crtc) +{
- mdp5_crtc_flush(crtc);
+}
static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); @@ -118,6 +123,7 @@ static const struct mdp_kms_funcs kms_funcs = { .disable_vblank = mdp5_disable_vblank, .get_format = mdp_get_format, .round_pixclk = mdp5_round_pixclk,
.preclose = mdp5_preclose, .destroy = mdp5_destroy, },.flush = mdp5_flush,
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index c8b1a25..18b031b 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -197,8 +197,8 @@ enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane); struct drm_plane *mdp5_plane_init(struct drm_device *dev, enum mdp5_pipe pipe, bool private_plane);
+void mdp5_crtc_flush(struct drm_crtc *crtc); uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc);
void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file); void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf, enum mdp5_intf intf_id); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 53cc8c6..f1bf3c2 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -48,11 +48,6 @@ static int mdp5_plane_update(struct drm_plane *plane,
mdp5_plane->enabled = true;
- if (plane->fb)
drm_framebuffer_unreference(plane->fb);
- drm_framebuffer_reference(fb);
- return mdp5_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h);
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c new file mode 100644 index 0000000..b231377 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -0,0 +1,141 @@ +/*
- Copyright (C) 2013 Red Hat
- Author: Rob Clark robdclark@gmail.com
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 as published by
- the Free Software Foundation.
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- You should have received a copy of the GNU General Public License along with
- this program. If not, see http://www.gnu.org/licenses/.
- */
+#include "msm_drv.h" +#include "msm_kms.h" +#include "msm_gem.h"
+struct msm_async_commit {
- struct drm_atomic_state *state;
- uint32_t fence;
- struct msm_fence_cb fence_cb;
+};
+static void fence_cb(struct msm_fence_cb *cb); +static int commit_sync(struct drm_device *dev, void *state, bool unlocked);
+static struct msm_async_commit *new_commit(struct drm_atomic_state *state) +{
- struct msm_async_commit *c = kzalloc(sizeof(*c), GFP_KERNEL);
- if (!c)
return NULL;
- drm_atomic_state_reference(state);
- c->state = state;
- INIT_FENCE_CB(&c->fence_cb, fence_cb);
- return c;
+} +static void free_commit(struct msm_async_commit *c) +{
- drm_atomic_state_unreference(c->state);
- kfree(c);
+}
+static void fence_cb(struct msm_fence_cb *cb) +{
- struct msm_async_commit *c =
container_of(cb, struct msm_async_commit, fence_cb);
- commit_sync(c->state->dev, c->state, true);
- free_commit(c);
+}
+static void add_fb(struct msm_async_commit *c, struct drm_crtc *crtc,
struct drm_framebuffer *fb)
+{
- struct drm_gem_object *obj = msm_framebuffer_bo(fb, 0);
- c->fence = max(c->fence, msm_gem_fence(to_msm_bo(obj), MSM_PREP_READ));
+}
+static int wait_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb) +{
- // XXX TODO wait..
- return 0;
+}
+#define pending_fb(state) ((state) && (state)->fb && (state)->new_fb)
+static int commit_sync(struct drm_device *dev, void *state, bool unlocked) +{
- struct drm_atomic_state *a = state;
- struct msm_drm_private *priv = dev->dev_private;
- struct msm_kms *kms = priv->kms;
- int ncrtcs = dev->mode_config.num_crtc;
- uint32_t pending_crtcs = 0;
- int i, ret;
- for (i = 0; i < ncrtcs; i++)
if (a->crtcs[i])
pending_crtcs |= (1 << a->crtcs[i]->id);
- mutex_lock(&dev->struct_mutex);
- WARN_ON(priv->pending_crtcs & pending_crtcs);
- priv->pending_crtcs |= pending_crtcs;
- mutex_unlock(&dev->struct_mutex);
- if (unlocked)
ret = drm_atomic_commit_unlocked(dev, state);
- else
ret = drm_atomic_commit(dev, state);
- mutex_lock(&dev->struct_mutex);
- priv->pending_crtcs &= ~pending_crtcs;
- mutex_unlock(&dev->struct_mutex);
- if (ret)
return ret;
- for (i = 0; i < ncrtcs; i++)
if (a->crtcs[i])
kms->funcs->flush(kms, a->crtcs[i]);
- return 0;
+}
+int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state) +{
- struct drm_atomic_state *a = state;
- int nplanes = dev->mode_config.num_total_plane;
- int i;
- if (a->flags & DRM_MODE_ATOMIC_NONBLOCK) {
/* non-block mode: defer commit until fb's are ready */
struct msm_async_commit *c = new_commit(state);
if (!c)
return -ENOMEM;
for (i = 0; i < nplanes; i++)
if (pending_fb(a->pstates[i]))
add_fb(c, a->pstates[i]->crtc, a->pstates[i]->fb);
return msm_queue_fence_cb(dev, &c->fence_cb, c->fence);
- } else {
/* blocking mode: wait until fb's are ready */
int ret = 0;
for (i = 0; i < nplanes && !ret; i++)
if (pending_fb(a->pstates[i]))
ret = wait_fb(a->pstates[i]->crtc, a->pstates[i]->fb);
if (ret)
return ret;
return commit_sync(dev, state, false);
- }
+}
Ok, I think I should have read your msm implementation a _lot_ earlier. Explains your desing choices neatly.
Two observations:
- A GO bit makes nuclear pageflips ridiculously easy to implement, presuming the hardware actually works. And it's the sane model, so imo a good one to wrap the atomic helpers around.
But reality is often a lot more ugly, especially if you're employed by Intel. Which is why I'm harping so much on this helpers-vs-core interface issues ... We really need the full state transition in one piece to do anything useful.
- msm doesn't have any resource sharing going on for modeset operations (which I mean lighting up crtcs to feed pixel streams to connectors). Which means the simplistic "loop over all crtcs and call the old ->setcrtc" approach actually works.
The problem I see here is that if you have hardware with more elaborate needs (e.g. shared dplls), but otherwise sanity for plane updates (i.e. a GO bit) then your current atomic helpers will make it rather hard to mix this. So I think we should pimp the crtc helpers a bit to be atomic compliant (i.e. kill all outputs first before enabling anything new) and try to integrate this with the atomic helpers used for GO bit style updates.
i915 has dpll sharing on ivb/hsw, but doesn't use the the crtc helpers anymore. But the radone eyefinity (or whatever the damn thing is called) I have lying around here fits the bill: It has 5 DP+ outputs but only 2 dplls. So you can drive e.g. 3 DP screens and then switch to 2 HDMI screens atomically and it should all pan out.
But since your current helpers just loop over all crtcs without any regard to ordering constraints this will fall over if the 2 HDMI outputs get enabled before the 3 DP outputs get disabled all disabled.
So with all that in mind I have 3 sanity checks for the atomic interfaces and helpers:
1. The atomic helpers should make enabling sane hw with a GO bit easy for nuclear pageflips. Benchmark would be sane hw like msm or omap.
2. It should cooperate with the crtc helpers such that hw with some shared resources (like dplls) works for atomic modesets. That pretty much means "disable everything (including crtc/connetor pipelines that only changed some parts) before enabling anything". Benchmark would be a platform with shared dplls.
3. The core->driver interface should be powerful enough to support insanity like i915, but no more. Which means all the code that's share (i.e. the set_prop code I've been harping all over the place about) should be done in the core, without any means for drivers to override. Currently the drm core also takes care of a bunch of things like basic locking and refcounting, and that's kinda the spirit for this. i915 is the obvious benchmark here.
I think we can roll this out piecemeal (and it's probably better to do it that way), but I also think that until we've resolved the requirements of 2&3 we should try to minimize subsystem wide changes as much as possible by making them opt-in and the vfuncs optional.
If you compare this approach with my review for Matt's universal plane patches it's exactly the same song.
I hope this elaboration of my thinking clarifies all my review comments a bit and explains what I'm aiming for.
Cheers, Daniel
On Mon, May 26, 2014 at 1:54 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 02:30:23PM -0400, Rob Clark wrote:
Signed-off-by: Rob Clark robdclark@gmail.com
drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 57 ++++++------ drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c | 6 ++ drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c | 5 -- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 56 ++++++------ drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 6 ++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 2 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 5 -- drivers/gpu/drm/msm/msm_atomic.c | 141 ++++++++++++++++++++++++++++++ drivers/gpu/drm/msm/msm_drv.c | 22 ++++- drivers/gpu/drm/msm/msm_drv.h | 7 ++ drivers/gpu/drm/msm/msm_gem.c | 24 +---- drivers/gpu/drm/msm/msm_gem.h | 13 +++ drivers/gpu/drm/msm/msm_kms.h | 1 + 15 files changed, 253 insertions(+), 94 deletions(-) create mode 100644 drivers/gpu/drm/msm/msm_atomic.c
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 5e1e6b0..dd12f56 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -27,6 +27,7 @@ msm-y := \ mdp/mdp5/mdp5_kms.o \ mdp/mdp5/mdp5_plane.o \ mdp/mdp5/mdp5_smp.o \
msm_atomic.o \ msm_drv.o \ msm_fb.o \ msm_gem.o \
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index d0d8befd..2fa6b75 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -52,7 +52,6 @@ struct mdp4_crtc {
/* if there is a pending flip, these will be non-null: */ struct drm_pending_vblank_event *event;
struct msm_fence_cb pageflip_cb;
#define PENDING_CURSOR 0x1 #define PENDING_FLIP 0x2 @@ -93,12 +92,16 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending) mdp_irq_register(&get_kms(crtc)->base, &mdp4_crtc->vblank); }
-static void crtc_flush(struct drm_crtc *crtc) +void mdp4_crtc_flush(struct drm_crtc *crtc) {
struct msm_drm_private *priv = crtc->dev->dev_private; struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); struct mdp4_kms *mdp4_kms = get_kms(crtc); uint32_t i, flush = 0;
if (priv->pending_crtcs & (1 << crtc->id))
return;
for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) { struct drm_plane *plane = mdp4_crtc->planes[i]; if (plane) {
@@ -142,7 +145,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) * so that we can safely queue unref to current fb (ie. next * vblank we know hw is done w/ previous scanout_fb). */
crtc_flush(crtc);
mdp4_crtc_flush(crtc); if (mdp4_crtc->scanout_fb) drm_flip_work_queue(&mdp4_crtc->unref_fb_work,
@@ -177,21 +180,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) spin_unlock_irqrestore(&dev->event_lock, flags); }
-static void pageflip_cb(struct msm_fence_cb *cb) -{
struct mdp4_crtc *mdp4_crtc =
container_of(cb, struct mdp4_crtc, pageflip_cb);
struct drm_crtc *crtc = &mdp4_crtc->base;
struct drm_framebuffer *fb = crtc->primary->fb;
if (!fb)
return;
drm_framebuffer_reference(fb);
mdp4_plane_set_scanout(mdp4_crtc->plane, fb);
update_scanout(crtc, fb);
-}
static void unref_fb_worker(struct drm_flip_work *work, void *val) { struct mdp4_crtc *mdp4_crtc = @@ -406,7 +394,7 @@ static void mdp4_crtc_prepare(struct drm_crtc *crtc) static void mdp4_crtc_commit(struct drm_crtc *crtc) { mdp4_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
crtc_flush(crtc);
mdp4_crtc_flush(crtc); /* drop the ref to mdp clk's that we got in prepare: */ mdp4_disable(get_kms(crtc));
} @@ -448,23 +436,27 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc, { struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); struct drm_device *dev = crtc->dev;
struct drm_gem_object *obj; unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags); if (mdp4_crtc->event) { dev_err(dev->dev, "already pending flip!\n");
spin_unlock_irqrestore(&dev->event_lock, flags); return -EBUSY; }
obj = msm_framebuffer_bo(new_fb, 0);
spin_lock_irqsave(&dev->event_lock, flags); mdp4_crtc->event = event; spin_unlock_irqrestore(&dev->event_lock, flags); update_fb(crtc, new_fb);
return msm_gem_queue_inactive_cb(obj, &mdp4_crtc->pageflip_cb);
/* grab extra ref for update_scanout() */
drm_framebuffer_reference(new_fb);
mdp4_plane_set_scanout(mdp4_crtc->plane, new_fb);
update_scanout(crtc, new_fb);
return 0;
}
static int mdp4_crtc_set_property(struct drm_crtc *crtc, @@ -598,7 +590,7 @@ static int mdp4_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) mdp4_crtc->cursor.y = y; spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags);
crtc_flush(crtc);
mdp4_crtc_flush(crtc); request_pending(crtc, PENDING_CURSOR); return 0;
@@ -635,8 +627,13 @@ static void mdp4_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus) pending = atomic_xchg(&mdp4_crtc->pending, 0);
if (pending & PENDING_FLIP) {
complete_flip(crtc, NULL);
drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq);
if (priv->pending_crtcs & (1 << crtc->id)) {
/* our update hasn't been flushed yet: */
request_pending(crtc, PENDING_FLIP);
} else {
complete_flip(crtc, NULL);
drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq);
} } if (pending & PENDING_CURSOR) {
@@ -650,7 +647,7 @@ static void mdp4_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus) struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, err); struct drm_crtc *crtc = &mdp4_crtc->base; DBG("%s: error: %08x", mdp4_crtc->name, irqstatus);
crtc_flush(crtc);
mdp4_crtc_flush(crtc);
}
uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc) @@ -730,7 +727,7 @@ static void set_attach(struct drm_crtc *crtc, enum mdp4_pipe pipe_id, mdp4_crtc->planes[pipe_id] = plane; blend_setup(crtc); if (mdp4_crtc->enabled && (plane != mdp4_crtc->plane))
crtc_flush(crtc);
mdp4_crtc_flush(crtc);
}
void mdp4_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane) @@ -792,8 +789,6 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev, ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64, "unref cursor", unref_cursor_worker);
INIT_FENCE_CB(&mdp4_crtc->pageflip_cb, pageflip_cb);
drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp4_crtc_funcs); drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index 0bb4faa..af0bfb1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -131,6 +131,11 @@ static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate, return mdp4_dtv_round_pixclk(encoder, rate); }
+static void mdp4_flush(struct msm_kms *kms, struct drm_crtc *crtc) +{
mdp4_crtc_flush(crtc);
+}
static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file) { struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); @@ -162,6 +167,7 @@ static const struct mdp_kms_funcs kms_funcs = { .disable_vblank = mdp4_disable_vblank, .get_format = mdp_get_format, .round_pixclk = mdp4_round_pixclk,
.flush = mdp4_flush, .preclose = mdp4_preclose, .destroy = mdp4_destroy, },
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h index 715520c5..834454c 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h @@ -184,6 +184,7 @@ enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane); struct drm_plane *mdp4_plane_init(struct drm_device *dev, enum mdp4_pipe pipe_id, bool private_plane);
+void mdp4_crtc_flush(struct drm_crtc *crtc); uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc); void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file); void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config); diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 4c92985..f35848d 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -48,11 +48,6 @@ static int mdp4_plane_update(struct drm_plane *plane,
mdp4_plane->enabled = true;
if (plane->fb)
drm_framebuffer_unreference(plane->fb);
drm_framebuffer_reference(fb);
return mdp4_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 7f4ee99..0e74317 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -35,7 +35,6 @@ struct mdp5_crtc {
/* if there is a pending flip, these will be non-null: */ struct drm_pending_vblank_event *event;
struct msm_fence_cb pageflip_cb;
#define PENDING_CURSOR 0x1 #define PENDING_FLIP 0x2 @@ -73,13 +72,17 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending) mdp_irq_register(&get_kms(crtc)->base, &mdp5_crtc->vblank); }
-static void crtc_flush(struct drm_crtc *crtc) +void mdp5_crtc_flush(struct drm_crtc *crtc) {
struct msm_drm_private *priv = crtc->dev->dev_private; struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct mdp5_kms *mdp5_kms = get_kms(crtc); int id = mdp5_crtc->id; uint32_t i, flush = 0;
if (priv->pending_crtcs & (1 << crtc->id))
return;
for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) { struct drm_plane *plane = mdp5_crtc->planes[i]; if (plane) {
@@ -124,7 +127,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) * so that we can safely queue unref to current fb (ie. next * vblank we know hw is done w/ previous scanout_fb). */
crtc_flush(crtc);
mdp5_crtc_flush(crtc); if (mdp5_crtc->scanout_fb) drm_flip_work_queue(&mdp5_crtc->unref_fb_work,
@@ -165,21 +168,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) } }
-static void pageflip_cb(struct msm_fence_cb *cb) -{
struct mdp5_crtc *mdp5_crtc =
container_of(cb, struct mdp5_crtc, pageflip_cb);
struct drm_crtc *crtc = &mdp5_crtc->base;
struct drm_framebuffer *fb = mdp5_crtc->fb;
if (!fb)
return;
drm_framebuffer_reference(fb);
mdp5_plane_set_scanout(mdp5_crtc->plane, fb);
update_scanout(crtc, fb);
-}
static void unref_fb_worker(struct drm_flip_work *work, void *val) { struct mdp5_crtc *mdp5_crtc = @@ -324,7 +312,7 @@ static void mdp5_crtc_prepare(struct drm_crtc *crtc) static void mdp5_crtc_commit(struct drm_crtc *crtc) { mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
crtc_flush(crtc);
mdp5_crtc_flush(crtc); /* drop the ref to mdp clk's that we got in prepare: */ mdp5_disable(get_kms(crtc));
} @@ -366,23 +354,26 @@ static int mdp5_crtc_page_flip(struct drm_crtc *crtc, { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct drm_device *dev = crtc->dev;
struct drm_gem_object *obj; unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags); if (mdp5_crtc->event) { dev_err(dev->dev, "already pending flip!\n");
spin_unlock_irqrestore(&dev->event_lock, flags); return -EBUSY; }
obj = msm_framebuffer_bo(new_fb, 0);
spin_lock_irqsave(&dev->event_lock, flags); mdp5_crtc->event = event; spin_unlock_irqrestore(&dev->event_lock, flags); update_fb(crtc, new_fb);
return msm_gem_queue_inactive_cb(obj, &mdp5_crtc->pageflip_cb);
/* grab extra ref for update_scanout() */
drm_framebuffer_reference(new_fb);
mdp5_plane_set_scanout(mdp5_crtc->plane, new_fb);
update_scanout(crtc, new_fb);
return 0;
}
static int mdp5_crtc_set_property(struct drm_crtc *crtc, @@ -424,8 +415,13 @@ static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus) pending = atomic_xchg(&mdp5_crtc->pending, 0);
if (pending & PENDING_FLIP) {
complete_flip(crtc, NULL);
drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq);
if (priv->pending_crtcs & (1 << crtc->id)) {
/* our update hasn't been flushed yet: */
request_pending(crtc, PENDING_FLIP);
} else {
complete_flip(crtc, NULL);
drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq);
} }
}
@@ -434,7 +430,7 @@ static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus) struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, err); struct drm_crtc *crtc = &mdp5_crtc->base; DBG("%s: error: %08x", mdp5_crtc->name, irqstatus);
crtc_flush(crtc);
mdp5_crtc_flush(crtc);
}
uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc) @@ -501,7 +497,7 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf, MDP5_CTL_OP_MODE(MODE_NONE) | MDP5_CTL_OP_INTF_NUM(intfnum[intf]));
crtc_flush(crtc);
mdp5_crtc_flush(crtc);
}
static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id, @@ -517,7 +513,7 @@ static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id, mdp5_crtc->planes[pipe_id] = plane; blend_setup(crtc); if (mdp5_crtc->enabled && (plane != mdp5_crtc->plane))
crtc_flush(crtc);
mdp5_crtc_flush(crtc);
}
void mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane) @@ -563,8 +559,6 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, if (ret) goto fail;
INIT_FENCE_CB(&mdp5_crtc->pageflip_cb, pageflip_cb);
drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs); drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index ee8446c..01c3d70 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -91,6 +91,11 @@ static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate, return rate; }
+static void mdp5_flush(struct msm_kms *kms, struct drm_crtc *crtc) +{
mdp5_crtc_flush(crtc);
+}
static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); @@ -118,6 +123,7 @@ static const struct mdp_kms_funcs kms_funcs = { .disable_vblank = mdp5_disable_vblank, .get_format = mdp_get_format, .round_pixclk = mdp5_round_pixclk,
.flush = mdp5_flush, .preclose = mdp5_preclose, .destroy = mdp5_destroy, },
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index c8b1a25..18b031b 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -197,8 +197,8 @@ enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane); struct drm_plane *mdp5_plane_init(struct drm_device *dev, enum mdp5_pipe pipe, bool private_plane);
+void mdp5_crtc_flush(struct drm_crtc *crtc); uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc);
void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file); void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf, enum mdp5_intf intf_id); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 53cc8c6..f1bf3c2 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -48,11 +48,6 @@ static int mdp5_plane_update(struct drm_plane *plane,
mdp5_plane->enabled = true;
if (plane->fb)
drm_framebuffer_unreference(plane->fb);
drm_framebuffer_reference(fb);
return mdp5_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h);
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c new file mode 100644 index 0000000..b231377 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -0,0 +1,141 @@ +/*
- Copyright (C) 2013 Red Hat
- Author: Rob Clark robdclark@gmail.com
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 as published by
- the Free Software Foundation.
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- You should have received a copy of the GNU General Public License along with
- this program. If not, see http://www.gnu.org/licenses/.
- */
+#include "msm_drv.h" +#include "msm_kms.h" +#include "msm_gem.h"
+struct msm_async_commit {
struct drm_atomic_state *state;
uint32_t fence;
struct msm_fence_cb fence_cb;
+};
+static void fence_cb(struct msm_fence_cb *cb); +static int commit_sync(struct drm_device *dev, void *state, bool unlocked);
+static struct msm_async_commit *new_commit(struct drm_atomic_state *state) +{
struct msm_async_commit *c = kzalloc(sizeof(*c), GFP_KERNEL);
if (!c)
return NULL;
drm_atomic_state_reference(state);
c->state = state;
INIT_FENCE_CB(&c->fence_cb, fence_cb);
return c;
+} +static void free_commit(struct msm_async_commit *c) +{
drm_atomic_state_unreference(c->state);
kfree(c);
+}
+static void fence_cb(struct msm_fence_cb *cb) +{
struct msm_async_commit *c =
container_of(cb, struct msm_async_commit, fence_cb);
commit_sync(c->state->dev, c->state, true);
free_commit(c);
+}
+static void add_fb(struct msm_async_commit *c, struct drm_crtc *crtc,
struct drm_framebuffer *fb)
+{
struct drm_gem_object *obj = msm_framebuffer_bo(fb, 0);
c->fence = max(c->fence, msm_gem_fence(to_msm_bo(obj), MSM_PREP_READ));
+}
+static int wait_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb) +{
// XXX TODO wait..
return 0;
+}
+#define pending_fb(state) ((state) && (state)->fb && (state)->new_fb)
+static int commit_sync(struct drm_device *dev, void *state, bool unlocked) +{
struct drm_atomic_state *a = state;
struct msm_drm_private *priv = dev->dev_private;
struct msm_kms *kms = priv->kms;
int ncrtcs = dev->mode_config.num_crtc;
uint32_t pending_crtcs = 0;
int i, ret;
for (i = 0; i < ncrtcs; i++)
if (a->crtcs[i])
pending_crtcs |= (1 << a->crtcs[i]->id);
mutex_lock(&dev->struct_mutex);
WARN_ON(priv->pending_crtcs & pending_crtcs);
priv->pending_crtcs |= pending_crtcs;
mutex_unlock(&dev->struct_mutex);
if (unlocked)
ret = drm_atomic_commit_unlocked(dev, state);
else
ret = drm_atomic_commit(dev, state);
mutex_lock(&dev->struct_mutex);
priv->pending_crtcs &= ~pending_crtcs;
mutex_unlock(&dev->struct_mutex);
if (ret)
return ret;
for (i = 0; i < ncrtcs; i++)
if (a->crtcs[i])
kms->funcs->flush(kms, a->crtcs[i]);
return 0;
+}
+int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state) +{
struct drm_atomic_state *a = state;
int nplanes = dev->mode_config.num_total_plane;
int i;
if (a->flags & DRM_MODE_ATOMIC_NONBLOCK) {
/* non-block mode: defer commit until fb's are ready */
struct msm_async_commit *c = new_commit(state);
if (!c)
return -ENOMEM;
for (i = 0; i < nplanes; i++)
if (pending_fb(a->pstates[i]))
add_fb(c, a->pstates[i]->crtc, a->pstates[i]->fb);
return msm_queue_fence_cb(dev, &c->fence_cb, c->fence);
} else {
/* blocking mode: wait until fb's are ready */
int ret = 0;
for (i = 0; i < nplanes && !ret; i++)
if (pending_fb(a->pstates[i]))
ret = wait_fb(a->pstates[i]->crtc, a->pstates[i]->fb);
if (ret)
return ret;
return commit_sync(dev, state, false);
}
+}
Ok, I think I should have read your msm implementation a _lot_ earlier. Explains your desing choices neatly.
Two observations:
A GO bit makes nuclear pageflips ridiculously easy to implement, presuming the hardware actually works. And it's the sane model, so imo a good one to wrap the atomic helpers around.
But reality is often a lot more ugly, especially if you're employed by Intel. Which is why I'm harping so much on this helpers-vs-core interface issues ... We really need the full state transition in one piece to do anything useful.
msm doesn't have any resource sharing going on for modeset operations (which I mean lighting up crtcs to feed pixel streams to connectors). Which means the simplistic "loop over all crtcs and call the old ->setcrtc" approach actually works.
we do actually have some shared resources on mdp5 generation (the "smp" blocks, basically a shared buffer which we need to allocate fifo space from for each plane)..
I'm mostly ignoring this at the moment, because we don't support enough crtc's yet to run out of smp blocks. But hooking in at the current ->atomic_commit() would be enough for me, I think. Tbh, it is something that should be handled at the ->atomic_check() stage so I hadn't given much though to ->atomic_commit() stage.
That all said, I've no problem with adding one more hook, after ->atomic_commit() and lock magic, before loop over planes/crtcs, so drivers that need to can replace the loops and do their own thing. Current state should be reasonably good for sane hw. I'm just waiting for patches for i915 to add whatever it needs.
The problem I see here is that if you have hardware with more elaborate needs (e.g. shared dplls), but otherwise sanity for plane updates (i.e. a GO bit) then your current atomic helpers will make it rather hard to mix this. So I think we should pimp the crtc helpers a bit to be atomic compliant (i.e. kill all outputs first before enabling anything new) and try to integrate this with the atomic helpers used for GO bit style updates.
Not really, I don't think. You can still do whatever shared resource setup in ->atomic_commit(). For drivers which want to defer commit (ie. doing commit after fb's ready from cpu, rather than from gpu), it would probably be more convenient to split up atomic_commit() so drivers have a post lock hook. I think it is just a few line patch, and I think that it probably isn't really needed until i915 is ready. I'm pretty sure we can change this to do what i915 needs, while at the same time keeping it simple for 'GO bit' drivers.
The bit about crtc helpers, I'll have to think about. I'm not sure that we want to require that 'atomic' (modeset or pageflip) completely *replaces* current state. So disabling unrelated crtcs doesn't seem like the right thing. But we have some time to decide about the semantics of an atomic modeset before we expose to userspace.
i915 has dpll sharing on ivb/hsw, but doesn't use the the crtc helpers anymore. But the radone eyefinity (or whatever the damn thing is called) I have lying around here fits the bill: It has 5 DP+ outputs but only 2 dplls. So you can drive e.g. 3 DP screens and then switch to 2 HDMI screens atomically and it should all pan out.
But since your current helpers just loop over all crtcs without any regard to ordering constraints this will fall over if the 2 HDMI outputs get enabled before the 3 DP outputs get disabled all disabled.
the driver should have rejected the config before it gets to commit stage, if it were an impossible config.
So with all that in mind I have 3 sanity checks for the atomic interfaces and helpers:
- The atomic helpers should make enabling sane hw with a GO bit easy for
nuclear pageflips. Benchmark would be sane hw like msm or omap.
- It should cooperate with the crtc helpers such that hw with some shared
resources (like dplls) works for atomic modesets. That pretty much means "disable everything (including crtc/connetor pipelines that only changed some parts) before enabling anything". Benchmark would be a platform with shared dplls.
btw, not something I'd thought about before, but shared dplls seem common enough, that it is something core could help with. (Assuming they are all related to pixel clk and not some derived thing that core wouldn't know about.)
I think we do need to decide what partial state updates me in the context of modeset or pageflip. I kinda think the right thing is different for modeset vs pageflip. Maybe we want to define an atomic flag to mean "disable/discard everything else".. at any rate, we only need to sort that before exposing the API to userspace.
- The core->driver interface should be powerful enough to support
insanity like i915, but no more. Which means all the code that's share (i.e. the set_prop code I've been harping all over the place about) should be done in the core, without any means for drivers to override. Currently the drm core also takes care of a bunch of things like basic locking and refcounting, and that's kinda the spirit for this. i915 is the obvious benchmark here.
The more I think about it, the more I think we should leave set_prop as it is (although possibly with drm_atomic_state -> drm_{plane,crtc,etc}_state).
In the past, before primary plane, I really needed this. And I expect having a convenient way to 'sniff' changing properties as they go by will be useful in some cases.
I think we can roll this out piecemeal (and it's probably better to do it that way), but I also think that until we've resolved the requirements of 2&3 we should try to minimize subsystem wide changes as much as possible by making them opt-in and the vfuncs optional.
the new mandatory vfuncs don't amount to too much churn.. not as much as the extra param for crtc lock/unlock fxns. Maybe if I was planning on keeping this alive on a branch for so long in the beginning I would have arranged a few things slightly different, but meh.
A very early iteration of atomic preserved the old pageflip, setcrtc, setplane, etc paths for drivers not implementing atomic fxns. But that was at least a bit ugly. I like the current approach since the code paths are very much the same for atomic and non-atomic.
BR, -R
If you compare this approach with my review for Matt's universal plane patches it's exactly the same song.
I hope this elaboration of my thinking clarifies all my review comments a bit and explains what I'm aiming for.
Cheers, Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Tue, May 27, 2014 at 11:58:33AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 1:54 PM, Daniel Vetter daniel@ffwll.ch wrote:
Ok, I think I should have read your msm implementation a _lot_ earlier. Explains your desing choices neatly.
Two observations:
A GO bit makes nuclear pageflips ridiculously easy to implement, presuming the hardware actually works. And it's the sane model, so imo a good one to wrap the atomic helpers around.
But reality is often a lot more ugly, especially if you're employed by Intel. Which is why I'm harping so much on this helpers-vs-core interface issues ... We really need the full state transition in one piece to do anything useful.
msm doesn't have any resource sharing going on for modeset operations (which I mean lighting up crtcs to feed pixel streams to connectors). Which means the simplistic "loop over all crtcs and call the old ->setcrtc" approach actually works.
we do actually have some shared resources on mdp5 generation (the "smp" blocks, basically a shared buffer which we need to allocate fifo space from for each plane)..
I'm mostly ignoring this at the moment, because we don't support enough crtc's yet to run out of smp blocks. But hooking in at the current ->atomic_commit() would be enough for me, I think. Tbh, it is something that should be handled at the ->atomic_check() stage so I hadn't given much though to ->atomic_commit() stage.
That all said, I've no problem with adding one more hook, after ->atomic_commit() and lock magic, before loop over planes/crtcs, so drivers that need to can replace the loops and do their own thing. Current state should be reasonably good for sane hw. I'm just waiting for patches for i915 to add whatever it needs.
I don't think that will be enough for you. Example, slightly hypothetical:
- Configuration A: crtc 0 displays a small screen, crtc 1 a giant screen. Together they just barely fit into your fifo space.
- Configuration B: crtc 0 drives a giant screen, crtc 1 a tiny one. Presume different outputs here so that no implicit output stealing happens upon mode switching.
Atomic switch should work, but don't since if you just loop over crtcs you have the intermediate stage where you try to drive 2 giant screens, which will run out of fifo resources. And I think it's really common to have such implicit resource sharing, maybe except for the most beefy desktop gpu which simply can't run out of memory bw with today's screen.
So afaics you really need to push a bit of smarts into the crtc helpers to make atomic possible, which then leaves you with interaction issues between the atomic stuff for GO bit capable hw for plane updates and the modeset ordering hell.
The problem I see here is that if you have hardware with more elaborate needs (e.g. shared dplls), but otherwise sanity for plane updates (i.e. a GO bit) then your current atomic helpers will make it rather hard to mix this. So I think we should pimp the crtc helpers a bit to be atomic compliant (i.e. kill all outputs first before enabling anything new) and try to integrate this with the atomic helpers used for GO bit style updates.
Not really, I don't think. You can still do whatever shared resource setup in ->atomic_commit(). For drivers which want to defer commit (ie. doing commit after fb's ready from cpu, rather than from gpu), it would probably be more convenient to split up atomic_commit() so drivers have a post lock hook. I think it is just a few line patch, and I think that it probably isn't really needed until i915 is ready. I'm pretty sure we can change this to do what i915 needs, while at the same time keeping it simple for 'GO bit' drivers.
The bit about crtc helpers, I'll have to think about. I'm not sure that we want to require that 'atomic' (modeset or pageflip) completely *replaces* current state. So disabling unrelated crtcs doesn't seem like the right thing. But we have some time to decide about the semantics of an atomic modeset before we expose to userspace.
I'm not talking about replacing unrelated crtcs. It's also not about underspecified state or about enabling/changing global resources.
It is _only_ about ordering of the various operations: If both the current and the desired new configuration are at the limit of what the hw can do you can't switch to the new configuration by looping over all crtcs. The fact that this doesn't work is the entire point of atomic modesets. And if we have helpers which aren't cut out for the task at hand for any hw where we need to have atomic modesets to make such changes possible without userspace going nuts, then the helpers are imo simply not useful
i915 has dpll sharing on ivb/hsw, but doesn't use the the crtc helpers anymore. But the radone eyefinity (or whatever the damn thing is called) I have lying around here fits the bill: It has 5 DP+ outputs but only 2 dplls. So you can drive e.g. 3 DP screens and then switch to 2 HDMI screens atomically and it should all pan out.
But since your current helpers just loop over all crtcs without any regard to ordering constraints this will fall over if the 2 HDMI outputs get enabled before the 3 DP outputs get disabled all disabled.
the driver should have rejected the config before it gets to commit stage, if it were an impossible config.
The configuration _is_ possible. We simply have to be somewhat careful with transitioning to it, since not _all_ intermediate states are possible. Your current helpers presume that's the case, which afaik isn't the case on any hw where we have global limits. For modesets.
Nuclear pageflips are a completely different thing, as long as your hw has a GO bit.
So with all that in mind I have 3 sanity checks for the atomic interfaces and helpers:
- The atomic helpers should make enabling sane hw with a GO bit easy for
nuclear pageflips. Benchmark would be sane hw like msm or omap.
- It should cooperate with the crtc helpers such that hw with some shared
resources (like dplls) works for atomic modesets. That pretty much means "disable everything (including crtc/connetor pipelines that only changed some parts) before enabling anything". Benchmark would be a platform with shared dplls.
btw, not something I'd thought about before, but shared dplls seem common enough, that it is something core could help with. (Assuming they are all related to pixel clk and not some derived thing that core wouldn't know about.)
Yup, that's what I'm trying to say ;-) But it was just an example, I believe that atm your helpers don't help for any shared modeset resources at all.
I think we do need to decide what partial state updates me in the context of modeset or pageflip. I kinda think the right thing is different for modeset vs pageflip. Maybe we want to define an atomic flag to mean "disable/discard everything else".. at any rate, we only need to sort that before exposing the API to userspace.
Yeah, I still strongly support this split in the api itself. For i915 my plan is to have separate configuration structures for modeset state and pageflip/plane config state. When we do an atomic modeset we compute both (maybe with some shortcut if we know that the pipe config didn't change at all). Then comes the big switch:
- If we have the same pipe config, we simply to a vblank synce nuclear flip to the new config.
- If pipe configs change it will be much more elaborate:
1. Do a vblank synced nuclear flip to black on all pipes that need to go off (whether they get disabled or reconfigured doesn't matter for now).
2. Disable pipes.
3. Commit new state on the sw side.
4. Enable all pipes with the new config, but only scanning out black.
5. Do a vblank-synced nuclear flip to the new config. This would also be the one that would signale the drm events that the atomic update completed.
For fastboot we might need some hacks to fuse this all together, e.g for some panel fitter changes we don't need to disable the pipe completely. But that's the advanced stuff really.
I think modelling the crtc helpers after this model could work. But that means that the crtc helpers and the nuclear flip atomic helpers for GO bit capable hw need to be rather tightly integrated, while still allowing drivers to override the nuclear flip parts.
- The core->driver interface should be powerful enough to support
insanity like i915, but no more. Which means all the code that's share (i.e. the set_prop code I've been harping all over the place about) should be done in the core, without any means for drivers to override. Currently the drm core also takes care of a bunch of things like basic locking and refcounting, and that's kinda the spirit for this. i915 is the obvious benchmark here.
The more I think about it, the more I think we should leave set_prop as it is (although possibly with drm_atomic_state -> drm_{plane,crtc,etc}_state).
In the past, before primary plane, I really needed this. And I expect having a convenient way to 'sniff' changing properties as they go by will be useful in some cases.
I actually really like the addition of the state object to ->set_prop. At least for i915 (which already has a fair pile of additional properties) that looks like the perfect way to prep the stage.
But at least for the transition we should keep the impact minimal. So no manadatory callbacks and don't enforce the usage of the state object until the drm core is fully converted to always follow a set_prop with a ->commit. Since currently we have internal mode_set calls in all i915 set_prop functions and we need to convert them all. But we can't do that until all the core stuff uses the atomic interface for all legacy ioctl ops.
I think we can roll this out piecemeal (and it's probably better to do it that way), but I also think that until we've resolved the requirements of 2&3 we should try to minimize subsystem wide changes as much as possible by making them opt-in and the vfuncs optional.
the new mandatory vfuncs don't amount to too much churn.. not as much as the extra param for crtc lock/unlock fxns. Maybe if I was planning on keeping this alive on a branch for so long in the beginning I would have arranged a few things slightly different, but meh.
A very early iteration of atomic preserved the old pageflip, setcrtc, setplane, etc paths for drivers not implementing atomic fxns. But that was at least a bit ugly. I like the current approach since the code paths are very much the same for atomic and non-atomic.
I'm not advertising for keeping the old driver interfaces, that would be much worse. I'm only saying that we'll likely change this a few times, and we will transition fairly slowly anyway. Reducing the overall churn and especially reducing the amount of code we might need to fix up if we change our minds seems like a good approach to me. -Daniel
On Tue, May 27, 2014 at 1:50 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, May 27, 2014 at 11:58:33AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 1:54 PM, Daniel Vetter daniel@ffwll.ch wrote:
Ok, I think I should have read your msm implementation a _lot_ earlier. Explains your desing choices neatly.
Two observations:
A GO bit makes nuclear pageflips ridiculously easy to implement, presuming the hardware actually works. And it's the sane model, so imo a good one to wrap the atomic helpers around.
But reality is often a lot more ugly, especially if you're employed by Intel. Which is why I'm harping so much on this helpers-vs-core interface issues ... We really need the full state transition in one piece to do anything useful.
msm doesn't have any resource sharing going on for modeset operations (which I mean lighting up crtcs to feed pixel streams to connectors). Which means the simplistic "loop over all crtcs and call the old ->setcrtc" approach actually works.
we do actually have some shared resources on mdp5 generation (the "smp" blocks, basically a shared buffer which we need to allocate fifo space from for each plane)..
I'm mostly ignoring this at the moment, because we don't support enough crtc's yet to run out of smp blocks. But hooking in at the current ->atomic_commit() would be enough for me, I think. Tbh, it is something that should be handled at the ->atomic_check() stage so I hadn't given much though to ->atomic_commit() stage.
That all said, I've no problem with adding one more hook, after ->atomic_commit() and lock magic, before loop over planes/crtcs, so drivers that need to can replace the loops and do their own thing. Current state should be reasonably good for sane hw. I'm just waiting for patches for i915 to add whatever it needs.
I don't think that will be enough for you. Example, slightly hypothetical:
Configuration A: crtc 0 displays a small screen, crtc 1 a giant screen. Together they just barely fit into your fifo space.
Configuration B: crtc 0 drives a giant screen, crtc 1 a tiny one. Presume different outputs here so that no implicit output stealing happens upon mode switching.
Atomic switch should work, but don't since if you just loop over crtcs you have the intermediate stage where you try to drive 2 giant screens, which will run out of fifo resources. And I think it's really common to have such implicit resource sharing, maybe except for the most beefy desktop gpu which simply can't run out of memory bw with today's screen.
Well, this situation is a bit problematic in other similar cases.. moving plane between crtc's which needs to wait on a vblank is another vaguely similar case. I was kinda thinking of punting on that one (ie. -EBUSY and userspace tries again on next frame). Maybe for modeset that doesn't work out so well, since frame N+1 you'll still be at configuration A and have the same problem.
Would be kinda nice if helpers could order things according to what decreases resource requirements vs what increases. Although we could probably get a lot of mileage out of just making the 'atomic loop over things helper' apply config for crtcs/planes which will be disabled first, before the ones which will be enabled at the end of the commit. Hmm.
Either way, if you have to replace 'atomic loop over things helper' with your own i915 specific thing, it shouldn't make much difference to you. And I don't really think we need to solve all the worlds problems in the first version. But seems like there could be some value in making helpers a bit more aware of shared resource constraints.
So afaics you really need to push a bit of smarts into the crtc helpers to make atomic possible, which then leaves you with interaction issues between the atomic stuff for GO bit capable hw for plane updates and the modeset ordering hell.
The problem I see here is that if you have hardware with more elaborate needs (e.g. shared dplls), but otherwise sanity for plane updates (i.e. a GO bit) then your current atomic helpers will make it rather hard to mix this. So I think we should pimp the crtc helpers a bit to be atomic compliant (i.e. kill all outputs first before enabling anything new) and try to integrate this with the atomic helpers used for GO bit style updates.
Not really, I don't think. You can still do whatever shared resource setup in ->atomic_commit(). For drivers which want to defer commit (ie. doing commit after fb's ready from cpu, rather than from gpu), it would probably be more convenient to split up atomic_commit() so drivers have a post lock hook. I think it is just a few line patch, and I think that it probably isn't really needed until i915 is ready. I'm pretty sure we can change this to do what i915 needs, while at the same time keeping it simple for 'GO bit' drivers.
The bit about crtc helpers, I'll have to think about. I'm not sure that we want to require that 'atomic' (modeset or pageflip) completely *replaces* current state. So disabling unrelated crtcs doesn't seem like the right thing. But we have some time to decide about the semantics of an atomic modeset before we expose to userspace.
I'm not talking about replacing unrelated crtcs. It's also not about underspecified state or about enabling/changing global resources.
It is _only_ about ordering of the various operations: If both the current and the desired new configuration are at the limit of what the hw can do you can't switch to the new configuration by looping over all crtcs. The fact that this doesn't work is the entire point of atomic modesets. And if we have helpers which aren't cut out for the task at hand for any hw where we need to have atomic modesets to make such changes possible without userspace going nuts, then the helpers are imo simply not useful
i915 has dpll sharing on ivb/hsw, but doesn't use the the crtc helpers anymore. But the radone eyefinity (or whatever the damn thing is called) I have lying around here fits the bill: It has 5 DP+ outputs but only 2 dplls. So you can drive e.g. 3 DP screens and then switch to 2 HDMI screens atomically and it should all pan out.
But since your current helpers just loop over all crtcs without any regard to ordering constraints this will fall over if the 2 HDMI outputs get enabled before the 3 DP outputs get disabled all disabled.
the driver should have rejected the config before it gets to commit stage, if it were an impossible config.
The configuration _is_ possible. We simply have to be somewhat careful with transitioning to it, since not _all_ intermediate states are possible. Your current helpers presume that's the case, which afaik isn't the case on any hw where we have global limits. For modesets.
Nuclear pageflips are a completely different thing, as long as your hw has a GO bit.
So with all that in mind I have 3 sanity checks for the atomic interfaces and helpers:
- The atomic helpers should make enabling sane hw with a GO bit easy for
nuclear pageflips. Benchmark would be sane hw like msm or omap.
- It should cooperate with the crtc helpers such that hw with some shared
resources (like dplls) works for atomic modesets. That pretty much means "disable everything (including crtc/connetor pipelines that only changed some parts) before enabling anything". Benchmark would be a platform with shared dplls.
btw, not something I'd thought about before, but shared dplls seem common enough, that it is something core could help with. (Assuming they are all related to pixel clk and not some derived thing that core wouldn't know about.)
Yup, that's what I'm trying to say ;-) But it was just an example, I believe that atm your helpers don't help for any shared modeset resources at all.
no, not at all (other than the ww_mutex stuff which should be useful for shared resources and more fine grained locking within driver). It wasn't really a goal.
But having some knowledge about shared resources seems like it could make core helpers more useful when I get closer to exploiting the limits of the hw I have.. I suspect i915 is just further down that path than the other drivers.
I think we do need to decide what partial state updates me in the context of modeset or pageflip. I kinda think the right thing is different for modeset vs pageflip. Maybe we want to define an atomic flag to mean "disable/discard everything else".. at any rate, we only need to sort that before exposing the API to userspace.
Yeah, I still strongly support this split in the api itself. For i915 my plan is to have separate configuration structures for modeset state and pageflip/plane config state. When we do an atomic modeset we compute both (maybe with some shortcut if we know that the pipe config didn't change at all). Then comes the big switch:
If we have the same pipe config, we simply to a vblank synce nuclear flip to the new config.
If pipe configs change it will be much more elaborate:
- Do a vblank synced nuclear flip to black on all pipes that need to go
off (whether they get disabled or reconfigured doesn't matter for now).
Disable pipes.
Commit new state on the sw side.
Enable all pipes with the new config, but only scanning out black.
Do a vblank-synced nuclear flip to the new config. This would also be
the one that would signale the drm events that the atomic update completed.
For fastboot we might need some hacks to fuse this all together, e.g for some panel fitter changes we don't need to disable the pipe completely. But that's the advanced stuff really.
I think modelling the crtc helpers after this model could work. But that means that the crtc helpers and the nuclear flip atomic helpers for GO bit capable hw need to be rather tightly integrated, while still allowing drivers to override the nuclear flip parts.
- The core->driver interface should be powerful enough to support
insanity like i915, but no more. Which means all the code that's share (i.e. the set_prop code I've been harping all over the place about) should be done in the core, without any means for drivers to override. Currently the drm core also takes care of a bunch of things like basic locking and refcounting, and that's kinda the spirit for this. i915 is the obvious benchmark here.
The more I think about it, the more I think we should leave set_prop as it is (although possibly with drm_atomic_state -> drm_{plane,crtc,etc}_state).
In the past, before primary plane, I really needed this. And I expect having a convenient way to 'sniff' changing properties as they go by will be useful in some cases.
I actually really like the addition of the state object to ->set_prop. At least for i915 (which already has a fair pile of additional properties) that looks like the perfect way to prep the stage.
But at least for the transition we should keep the impact minimal. So no manadatory callbacks and don't enforce the usage of the state object until the drm core is fully converted to always follow a set_prop with a ->commit. Since currently we have internal mode_set calls in all i915 set_prop functions and we need to convert them all. But we can't do that until all the core stuff uses the atomic interface for all legacy ioctl ops.
fwiw, at least all set_prop ioctl stuff from core follows up with a atomic_commit now. There are one or two more places doing mode_set un-atomically. And I'm not sure if you call set_prop anywhere internally from i915.
BR, -R
On Tue, May 27, 2014 at 02:48:41PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 1:50 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, May 27, 2014 at 11:58:33AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 1:54 PM, Daniel Vetter daniel@ffwll.ch wrote:
Ok, I think I should have read your msm implementation a _lot_ earlier. Explains your desing choices neatly.
Two observations:
A GO bit makes nuclear pageflips ridiculously easy to implement, presuming the hardware actually works. And it's the sane model, so imo a good one to wrap the atomic helpers around.
But reality is often a lot more ugly, especially if you're employed by Intel. Which is why I'm harping so much on this helpers-vs-core interface issues ... We really need the full state transition in one piece to do anything useful.
msm doesn't have any resource sharing going on for modeset operations (which I mean lighting up crtcs to feed pixel streams to connectors). Which means the simplistic "loop over all crtcs and call the old ->setcrtc" approach actually works.
we do actually have some shared resources on mdp5 generation (the "smp" blocks, basically a shared buffer which we need to allocate fifo space from for each plane)..
I'm mostly ignoring this at the moment, because we don't support enough crtc's yet to run out of smp blocks. But hooking in at the current ->atomic_commit() would be enough for me, I think. Tbh, it is something that should be handled at the ->atomic_check() stage so I hadn't given much though to ->atomic_commit() stage.
That all said, I've no problem with adding one more hook, after ->atomic_commit() and lock magic, before loop over planes/crtcs, so drivers that need to can replace the loops and do their own thing. Current state should be reasonably good for sane hw. I'm just waiting for patches for i915 to add whatever it needs.
I don't think that will be enough for you. Example, slightly hypothetical:
Configuration A: crtc 0 displays a small screen, crtc 1 a giant screen. Together they just barely fit into your fifo space.
Configuration B: crtc 0 drives a giant screen, crtc 1 a tiny one. Presume different outputs here so that no implicit output stealing happens upon mode switching.
Atomic switch should work, but don't since if you just loop over crtcs you have the intermediate stage where you try to drive 2 giant screens, which will run out of fifo resources. And I think it's really common to have such implicit resource sharing, maybe except for the most beefy desktop gpu which simply can't run out of memory bw with today's screen.
Well, this situation is a bit problematic in other similar cases.. moving plane between crtc's which needs to wait on a vblank is another vaguely similar case. I was kinda thinking of punting on that one (ie. -EBUSY and userspace tries again on next frame). Maybe for modeset that doesn't work out so well, since frame N+1 you'll still be at configuration A and have the same problem.
Would be kinda nice if helpers could order things according to what decreases resource requirements vs what increases. Although we could probably get a lot of mileage out of just making the 'atomic loop over things helper' apply config for crtcs/planes which will be disabled first, before the ones which will be enabled at the end of the commit. Hmm.
Yeah, that one should go a long way, but only for modeset changes. If we do this for plane updates it will look _really_ bad ;-)
So for the "move plane from crtc to crtc" I think we need a separate step. Presuming we don't have any modeset operations we should be able to group all the plane updates per crtc. This ofc presumes a sane hw with GO bit. Then the helper could figure out which crtc it needs to nuclear flip first to be able to move the plane.
At least with the current atomic ioctl proposal we can't reject this with -EBUSY since with a full modeset (which takes longer than one vblank anyway) it's possible. Otoh we _should_ reject it when userspace expects a vblank synced update.
Which is another reason for why I think we really should have a flag somewhere for vblank synced atomic updates vs. atomic updates which take longer than one vblank and might even involved a few msec of blank screens for switching.
Either way, if you have to replace 'atomic loop over things helper' with your own i915 specific thing, it shouldn't make much difference to you. And I don't really think we need to solve all the worlds problems in the first version. But seems like there could be some value in making helpers a bit more aware of shared resource constraints.
This isn't about i915, but about all the drivers using crtc helpers. Correct ordering of the crtc helper hooks should pretty much solve this, without the need to track global resources (i.e. disable everything before we start enabling). At least for modeset-like atomic updates.
i915 will roll their own, but not because atomic updates aren't possible with the crtc helpers, but because the crtc helpers are inadequate for our hw. For modeset updates atomic or not doesn't factor in here.
And imo if we can make the crtc helpers work, we should do that. Otherwise there won't be a whole lot of use behind the atomic modeset updates imo.
So afaics you really need to push a bit of smarts into the crtc helpers to make atomic possible, which then leaves you with interaction issues between the atomic stuff for GO bit capable hw for plane updates and the modeset ordering hell.
The problem I see here is that if you have hardware with more elaborate needs (e.g. shared dplls), but otherwise sanity for plane updates (i.e. a GO bit) then your current atomic helpers will make it rather hard to mix this. So I think we should pimp the crtc helpers a bit to be atomic compliant (i.e. kill all outputs first before enabling anything new) and try to integrate this with the atomic helpers used for GO bit style updates.
Not really, I don't think. You can still do whatever shared resource setup in ->atomic_commit(). For drivers which want to defer commit (ie. doing commit after fb's ready from cpu, rather than from gpu), it would probably be more convenient to split up atomic_commit() so drivers have a post lock hook. I think it is just a few line patch, and I think that it probably isn't really needed until i915 is ready. I'm pretty sure we can change this to do what i915 needs, while at the same time keeping it simple for 'GO bit' drivers.
The bit about crtc helpers, I'll have to think about. I'm not sure that we want to require that 'atomic' (modeset or pageflip) completely *replaces* current state. So disabling unrelated crtcs doesn't seem like the right thing. But we have some time to decide about the semantics of an atomic modeset before we expose to userspace.
I'm not talking about replacing unrelated crtcs. It's also not about underspecified state or about enabling/changing global resources.
It is _only_ about ordering of the various operations: If both the current and the desired new configuration are at the limit of what the hw can do you can't switch to the new configuration by looping over all crtcs. The fact that this doesn't work is the entire point of atomic modesets. And if we have helpers which aren't cut out for the task at hand for any hw where we need to have atomic modesets to make such changes possible without userspace going nuts, then the helpers are imo simply not useful
i915 has dpll sharing on ivb/hsw, but doesn't use the the crtc helpers anymore. But the radone eyefinity (or whatever the damn thing is called) I have lying around here fits the bill: It has 5 DP+ outputs but only 2 dplls. So you can drive e.g. 3 DP screens and then switch to 2 HDMI screens atomically and it should all pan out.
But since your current helpers just loop over all crtcs without any regard to ordering constraints this will fall over if the 2 HDMI outputs get enabled before the 3 DP outputs get disabled all disabled.
the driver should have rejected the config before it gets to commit stage, if it were an impossible config.
The configuration _is_ possible. We simply have to be somewhat careful with transitioning to it, since not _all_ intermediate states are possible. Your current helpers presume that's the case, which afaik isn't the case on any hw where we have global limits. For modesets.
Nuclear pageflips are a completely different thing, as long as your hw has a GO bit.
So with all that in mind I have 3 sanity checks for the atomic interfaces and helpers:
- The atomic helpers should make enabling sane hw with a GO bit easy for
nuclear pageflips. Benchmark would be sane hw like msm or omap.
- It should cooperate with the crtc helpers such that hw with some shared
resources (like dplls) works for atomic modesets. That pretty much means "disable everything (including crtc/connetor pipelines that only changed some parts) before enabling anything". Benchmark would be a platform with shared dplls.
btw, not something I'd thought about before, but shared dplls seem common enough, that it is something core could help with. (Assuming they are all related to pixel clk and not some derived thing that core wouldn't know about.)
Yup, that's what I'm trying to say ;-) But it was just an example, I believe that atm your helpers don't help for any shared modeset resources at all.
no, not at all (other than the ww_mutex stuff which should be useful for shared resources and more fine grained locking within driver). It wasn't really a goal.
But having some knowledge about shared resources seems like it could make core helpers more useful when I get closer to exploiting the limits of the hw I have.. I suspect i915 is just further down that path than the other drivers.
I'm repeating myself, but simply ordering updates correctly should already solve it. At least if the driver provides checks to make sure the new config doesn't go over limits (e.g. by counting plls or required fifo space). If we don't have that, the helpers are imo not sufficiently validated as generally useful. And I have seen _way_ too much single use code in the drm core from the old ums/dri1 days.
I think we do need to decide what partial state updates me in the context of modeset or pageflip. I kinda think the right thing is different for modeset vs pageflip. Maybe we want to define an atomic flag to mean "disable/discard everything else".. at any rate, we only need to sort that before exposing the API to userspace.
Yeah, I still strongly support this split in the api itself. For i915 my plan is to have separate configuration structures for modeset state and pageflip/plane config state. When we do an atomic modeset we compute both (maybe with some shortcut if we know that the pipe config didn't change at all). Then comes the big switch:
If we have the same pipe config, we simply to a vblank synce nuclear flip to the new config.
If pipe configs change it will be much more elaborate:
- Do a vblank synced nuclear flip to black on all pipes that need to go
off (whether they get disabled or reconfigured doesn't matter for now).
Disable pipes.
Commit new state on the sw side.
Enable all pipes with the new config, but only scanning out black.
Do a vblank-synced nuclear flip to the new config. This would also be
the one that would signale the drm events that the atomic update completed.
For fastboot we might need some hacks to fuse this all together, e.g for some panel fitter changes we don't need to disable the pipe completely. But that's the advanced stuff really.
I think modelling the crtc helpers after this model could work. But that means that the crtc helpers and the nuclear flip atomic helpers for GO bit capable hw need to be rather tightly integrated, while still allowing drivers to override the nuclear flip parts.
- The core->driver interface should be powerful enough to support
insanity like i915, but no more. Which means all the code that's share (i.e. the set_prop code I've been harping all over the place about) should be done in the core, without any means for drivers to override. Currently the drm core also takes care of a bunch of things like basic locking and refcounting, and that's kinda the spirit for this. i915 is the obvious benchmark here.
The more I think about it, the more I think we should leave set_prop as it is (although possibly with drm_atomic_state -> drm_{plane,crtc,etc}_state).
In the past, before primary plane, I really needed this. And I expect having a convenient way to 'sniff' changing properties as they go by will be useful in some cases.
I actually really like the addition of the state object to ->set_prop. At least for i915 (which already has a fair pile of additional properties) that looks like the perfect way to prep the stage.
But at least for the transition we should keep the impact minimal. So no manadatory callbacks and don't enforce the usage of the state object until the drm core is fully converted to always follow a set_prop with a ->commit. Since currently we have internal mode_set calls in all i915 set_prop functions and we need to convert them all. But we can't do that until all the core stuff uses the atomic interface for all legacy ioctl ops.
fwiw, at least all set_prop ioctl stuff from core follows up with a atomic_commit now. There are one or two more places doing mode_set un-atomically. And I'm not sure if you call set_prop anywhere internally from i915.
We do modesets internally, but as long as those aren't externally visible (which they shouldn't be if we grab locks before checking the state) it should be all fine. Also from my pov the ->set_prop stuff is just interface to construct the in-kernel representation of the desired config. The real magic happens in the check/commit hooks (which is the same level i915-internal modeset changes happens at).
I think one excellent use-case we get for free (almost) without the ioctl would be fbcon. It very much wants to do an atomic update, so converting that over to the atomic interface would be good imo. -Daniel
On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, May 27, 2014 at 02:48:41PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 1:50 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, May 27, 2014 at 11:58:33AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 1:54 PM, Daniel Vetter daniel@ffwll.ch wrote:
Ok, I think I should have read your msm implementation a _lot_ earlier. Explains your desing choices neatly.
Two observations:
A GO bit makes nuclear pageflips ridiculously easy to implement, presuming the hardware actually works. And it's the sane model, so imo a good one to wrap the atomic helpers around.
But reality is often a lot more ugly, especially if you're employed by Intel. Which is why I'm harping so much on this helpers-vs-core interface issues ... We really need the full state transition in one piece to do anything useful.
msm doesn't have any resource sharing going on for modeset operations (which I mean lighting up crtcs to feed pixel streams to connectors). Which means the simplistic "loop over all crtcs and call the old ->setcrtc" approach actually works.
we do actually have some shared resources on mdp5 generation (the "smp" blocks, basically a shared buffer which we need to allocate fifo space from for each plane)..
I'm mostly ignoring this at the moment, because we don't support enough crtc's yet to run out of smp blocks. But hooking in at the current ->atomic_commit() would be enough for me, I think. Tbh, it is something that should be handled at the ->atomic_check() stage so I hadn't given much though to ->atomic_commit() stage.
That all said, I've no problem with adding one more hook, after ->atomic_commit() and lock magic, before loop over planes/crtcs, so drivers that need to can replace the loops and do their own thing. Current state should be reasonably good for sane hw. I'm just waiting for patches for i915 to add whatever it needs.
I don't think that will be enough for you. Example, slightly hypothetical:
Configuration A: crtc 0 displays a small screen, crtc 1 a giant screen. Together they just barely fit into your fifo space.
Configuration B: crtc 0 drives a giant screen, crtc 1 a tiny one. Presume different outputs here so that no implicit output stealing happens upon mode switching.
Atomic switch should work, but don't since if you just loop over crtcs you have the intermediate stage where you try to drive 2 giant screens, which will run out of fifo resources. And I think it's really common to have such implicit resource sharing, maybe except for the most beefy desktop gpu which simply can't run out of memory bw with today's screen.
Well, this situation is a bit problematic in other similar cases.. moving plane between crtc's which needs to wait on a vblank is another vaguely similar case. I was kinda thinking of punting on that one (ie. -EBUSY and userspace tries again on next frame). Maybe for modeset that doesn't work out so well, since frame N+1 you'll still be at configuration A and have the same problem.
Would be kinda nice if helpers could order things according to what decreases resource requirements vs what increases. Although we could probably get a lot of mileage out of just making the 'atomic loop over things helper' apply config for crtcs/planes which will be disabled first, before the ones which will be enabled at the end of the commit. Hmm.
Yeah, that one should go a long way, but only for modeset changes. If we do this for plane updates it will look _really_ bad ;-)
So for the "move plane from crtc to crtc" I think we need a separate step. Presuming we don't have any modeset operations we should be able to group all the plane updates per crtc. This ofc presumes a sane hw with GO bit. Then the helper could figure out which crtc it needs to nuclear flip first to be able to move the plane.
At least with the current atomic ioctl proposal we can't reject this with -EBUSY since with a full modeset (which takes longer than one vblank anyway) it's possible. Otoh we _should_ reject it when userspace expects a vblank synced update.
Which is another reason for why I think we really should have a flag somewhere for vblank synced atomic updates vs. atomic updates which take longer than one vblank and might even involved a few msec of blank screens for switching.
Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we should hang so much off of that one flag.
Either way, if you have to replace 'atomic loop over things helper' with your own i915 specific thing, it shouldn't make much difference to you. And I don't really think we need to solve all the worlds problems in the first version. But seems like there could be some value in making helpers a bit more aware of shared resource constraints.
This isn't about i915, but about all the drivers using crtc helpers. Correct ordering of the crtc helper hooks should pretty much solve this, without the need to track global resources (i.e. disable everything before we start enabling). At least for modeset-like atomic updates.
i915 will roll their own, but not because atomic updates aren't possible with the crtc helpers, but because the crtc helpers are inadequate for our hw. For modeset updates atomic or not doesn't factor in here.
And imo if we can make the crtc helpers work, we should do that. Otherwise there won't be a whole lot of use behind the atomic modeset updates imo.
So afaics you really need to push a bit of smarts into the crtc helpers to make atomic possible, which then leaves you with interaction issues between the atomic stuff for GO bit capable hw for plane updates and the modeset ordering hell.
The problem I see here is that if you have hardware with more elaborate needs (e.g. shared dplls), but otherwise sanity for plane updates (i.e. a GO bit) then your current atomic helpers will make it rather hard to mix this. So I think we should pimp the crtc helpers a bit to be atomic compliant (i.e. kill all outputs first before enabling anything new) and try to integrate this with the atomic helpers used for GO bit style updates.
Not really, I don't think. You can still do whatever shared resource setup in ->atomic_commit(). For drivers which want to defer commit (ie. doing commit after fb's ready from cpu, rather than from gpu), it would probably be more convenient to split up atomic_commit() so drivers have a post lock hook. I think it is just a few line patch, and I think that it probably isn't really needed until i915 is ready. I'm pretty sure we can change this to do what i915 needs, while at the same time keeping it simple for 'GO bit' drivers.
The bit about crtc helpers, I'll have to think about. I'm not sure that we want to require that 'atomic' (modeset or pageflip) completely *replaces* current state. So disabling unrelated crtcs doesn't seem like the right thing. But we have some time to decide about the semantics of an atomic modeset before we expose to userspace.
I'm not talking about replacing unrelated crtcs. It's also not about underspecified state or about enabling/changing global resources.
It is _only_ about ordering of the various operations: If both the current and the desired new configuration are at the limit of what the hw can do you can't switch to the new configuration by looping over all crtcs. The fact that this doesn't work is the entire point of atomic modesets. And if we have helpers which aren't cut out for the task at hand for any hw where we need to have atomic modesets to make such changes possible without userspace going nuts, then the helpers are imo simply not useful
i915 has dpll sharing on ivb/hsw, but doesn't use the the crtc helpers anymore. But the radone eyefinity (or whatever the damn thing is called) I have lying around here fits the bill: It has 5 DP+ outputs but only 2 dplls. So you can drive e.g. 3 DP screens and then switch to 2 HDMI screens atomically and it should all pan out.
But since your current helpers just loop over all crtcs without any regard to ordering constraints this will fall over if the 2 HDMI outputs get enabled before the 3 DP outputs get disabled all disabled.
the driver should have rejected the config before it gets to commit stage, if it were an impossible config.
The configuration _is_ possible. We simply have to be somewhat careful with transitioning to it, since not _all_ intermediate states are possible. Your current helpers presume that's the case, which afaik isn't the case on any hw where we have global limits. For modesets.
Nuclear pageflips are a completely different thing, as long as your hw has a GO bit.
So with all that in mind I have 3 sanity checks for the atomic interfaces and helpers:
- The atomic helpers should make enabling sane hw with a GO bit easy for
nuclear pageflips. Benchmark would be sane hw like msm or omap.
- It should cooperate with the crtc helpers such that hw with some shared
resources (like dplls) works for atomic modesets. That pretty much means "disable everything (including crtc/connetor pipelines that only changed some parts) before enabling anything". Benchmark would be a platform with shared dplls.
btw, not something I'd thought about before, but shared dplls seem common enough, that it is something core could help with. (Assuming they are all related to pixel clk and not some derived thing that core wouldn't know about.)
Yup, that's what I'm trying to say ;-) But it was just an example, I believe that atm your helpers don't help for any shared modeset resources at all.
no, not at all (other than the ww_mutex stuff which should be useful for shared resources and more fine grained locking within driver). It wasn't really a goal.
But having some knowledge about shared resources seems like it could make core helpers more useful when I get closer to exploiting the limits of the hw I have.. I suspect i915 is just further down that path than the other drivers.
I'm repeating myself, but simply ordering updates correctly should already solve it. At least if the driver provides checks to make sure the new config doesn't go over limits (e.g. by counting plls or required fifo space). If we don't have that, the helpers are imo not sufficiently validated as generally useful. And I have seen _way_ too much single use code in the drm core from the old ums/dri1 days.
I think we do need to decide what partial state updates me in the context of modeset or pageflip. I kinda think the right thing is different for modeset vs pageflip. Maybe we want to define an atomic flag to mean "disable/discard everything else".. at any rate, we only need to sort that before exposing the API to userspace.
Yeah, I still strongly support this split in the api itself. For i915 my plan is to have separate configuration structures for modeset state and pageflip/plane config state. When we do an atomic modeset we compute both (maybe with some shortcut if we know that the pipe config didn't change at all). Then comes the big switch:
If we have the same pipe config, we simply to a vblank synce nuclear flip to the new config.
If pipe configs change it will be much more elaborate:
- Do a vblank synced nuclear flip to black on all pipes that need to go
off (whether they get disabled or reconfigured doesn't matter for now).
Disable pipes.
Commit new state on the sw side.
Enable all pipes with the new config, but only scanning out black.
Do a vblank-synced nuclear flip to the new config. This would also be
the one that would signale the drm events that the atomic update completed.
For fastboot we might need some hacks to fuse this all together, e.g for some panel fitter changes we don't need to disable the pipe completely. But that's the advanced stuff really.
I think modelling the crtc helpers after this model could work. But that means that the crtc helpers and the nuclear flip atomic helpers for GO bit capable hw need to be rather tightly integrated, while still allowing drivers to override the nuclear flip parts.
- The core->driver interface should be powerful enough to support
insanity like i915, but no more. Which means all the code that's share (i.e. the set_prop code I've been harping all over the place about) should be done in the core, without any means for drivers to override. Currently the drm core also takes care of a bunch of things like basic locking and refcounting, and that's kinda the spirit for this. i915 is the obvious benchmark here.
The more I think about it, the more I think we should leave set_prop as it is (although possibly with drm_atomic_state -> drm_{plane,crtc,etc}_state).
In the past, before primary plane, I really needed this. And I expect having a convenient way to 'sniff' changing properties as they go by will be useful in some cases.
I actually really like the addition of the state object to ->set_prop. At least for i915 (which already has a fair pile of additional properties) that looks like the perfect way to prep the stage.
But at least for the transition we should keep the impact minimal. So no manadatory callbacks and don't enforce the usage of the state object until the drm core is fully converted to always follow a set_prop with a ->commit. Since currently we have internal mode_set calls in all i915 set_prop functions and we need to convert them all. But we can't do that until all the core stuff uses the atomic interface for all legacy ioctl ops.
fwiw, at least all set_prop ioctl stuff from core follows up with a atomic_commit now. There are one or two more places doing mode_set un-atomically. And I'm not sure if you call set_prop anywhere internally from i915.
We do modesets internally, but as long as those aren't externally visible (which they shouldn't be if we grab locks before checking the state) it should be all fine. Also from my pov the ->set_prop stuff is just interface to construct the in-kernel representation of the desired config. The real magic happens in the check/commit hooks (which is the same level i915-internal modeset changes happens at).
I think one excellent use-case we get for free (almost) without the ioctl would be fbcon. It very much wants to do an atomic update, so converting that over to the atomic interface would be good imo.
Yes, iirc the remaining non-atomic paths are fbcon related. In principle it should be a simple matter to increment the refcnt on fbcon state object and re-apply it. Although at the moment we keep track of *how* to apply the state (ie. page_flip vs set_config, etc) as the state object is built up.. which isn't very conducive to re-committing an existing state object. Which is part of the reason I wanted to deprecate the various existing ->page_flip/->update_plane/->set_config/etc and introduce per object ->commit()'s. (Which could either be called by helpers, or called internally by driver or completely ignored by driver)
I've been a bit reluctant so far to do too much additional refactoring on top of atomic, since I'm about at the limit of what I have time to repeatedly rebase each kernel version. This is why I'm a bit anxious to start merging some of atomic, even if it doesn't do absolutely everything yet.
BR, -R
-Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter daniel@ffwll.ch wrote:
[snip]
Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we should hang so much off of that one flag.
Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also want non-blocking modesets.
[snip]
I think one excellent use-case we get for free (almost) without the ioctl would be fbcon. It very much wants to do an atomic update, so converting that over to the atomic interface would be good imo.
Yes, iirc the remaining non-atomic paths are fbcon related. In principle it should be a simple matter to increment the refcnt on fbcon state object and re-apply it. Although at the moment we keep track of *how* to apply the state (ie. page_flip vs set_config, etc) as the state object is built up.. which isn't very conducive to re-committing an existing state object. Which is part of the reason I wanted to deprecate the various existing ->page_flip/->update_plane/->set_config/etc and introduce per object ->commit()'s. (Which could either be called by helpers, or called internally by driver or completely ignored by driver)
Yeah, I think the approach in here with a few helpers to bend atomic ->commit to the old hooks (somewhat-ish) is good. And with the crtc helpers we should be able to move most drivers away from the old hooks quickly. The exception will be pageflips/cursors, but that requires a lot of driver-specific work, and probably first a full conversion to universal planes (which atm don't support everything even for the primary plane due to the arbitrary restriction to rgbx8888).
I've been a bit reluctant so far to do too much additional refactoring on top of atomic, since I'm about at the limit of what I have time to repeatedly rebase each kernel version. This is why I'm a bit anxious to start merging some of atomic, even if it doesn't do absolutely everything yet.
I understand that, which is why I suggested a bunch of things to split out already so we can get them in.
On top of that I think with the split-up mode_config.mutex like I've just proposed in an RFC we have a clear path for the locking issues, too. So could go ahead an merge the w/w conversion, too.
That leaves the set_prop refactoring which is still under discussion. Those three pieces hopefully help a lot with reducing rebasing pain.
On top of that I think we should look at cutting away functional pieces of the conversion, e.g. ignore planes at first and only look at atomic modeset. Or ignore atomic modesets and only look at plane updates, rejecting anything that changes connectors or crtcs. By cutting out slices we should be able to get patches into shape step-by-step. -Daniel
On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter daniel@ffwll.ch wrote:
[snip]
Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we should hang so much off of that one flag.
Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also want non-blocking modesets.
random-diverging-from-original-topic-thought.. seems like userspace just wants a deadline/timeout (hopefully in units of vblanks).. at least to the level of "I want this to happen on the next vblank (after rendering completes), or give me an error" vs "I want this to complete atomically even if it takes a few extra vblanks for things to sort out"..
I guess that amounts to what you mean by VBLANK_SYNCED flag, but VBLANKED_SYNCED might not be a good name.. at least for some hw all you can do is vblank sync'd..
BR, -R
On Tue, May 27, 2014 at 07:32:46PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter daniel@ffwll.ch wrote:
[snip]
Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we should hang so much off of that one flag.
Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also want non-blocking modesets.
random-diverging-from-original-topic-thought.. seems like userspace just wants a deadline/timeout (hopefully in units of vblanks).. at least to the level of "I want this to happen on the next vblank (after rendering completes), or give me an error" vs "I want this to complete atomically even if it takes a few extra vblanks for things to sort out"..
I guess that amounts to what you mean by VBLANK_SYNCED flag, but VBLANKED_SYNCED might not be a good name.. at least for some hw all you can do is vblank sync'd..
Hm, we might as well go full monty and allow userspace to request a specific vblank counter. Would be useful for e.g. queuing up a bunch of video frames, which some hw can do. But that would then require cancelling of existing flips, so I guess for now a simple VBLANK_SYNCED flag which emulates pageflip behaviour should be good enough.
That would also be useful if userspace attempts an atomic update on drivers which only support atomic modeset (through the crtc helpers), but can't do vblank synced updates. Then they could easily reject those. After all current drivers also often lack a pageflip implementation ... -Daniel
On Wed, May 28, 2014 at 03:21:41PM +0200, Daniel Vetter wrote:
On Tue, May 27, 2014 at 07:32:46PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter daniel@ffwll.ch wrote:
[snip]
Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we should hang so much off of that one flag.
Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also want non-blocking modesets.
random-diverging-from-original-topic-thought.. seems like userspace just wants a deadline/timeout (hopefully in units of vblanks).. at least to the level of "I want this to happen on the next vblank (after rendering completes), or give me an error" vs "I want this to complete atomically even if it takes a few extra vblanks for things to sort out"..
I guess that amounts to what you mean by VBLANK_SYNCED flag, but VBLANKED_SYNCED might not be a good name.. at least for some hw all you can do is vblank sync'd..
Hm, we might as well go full monty and allow userspace to request a specific vblank counter. Would be useful for e.g. queuing up a bunch of video frames, which some hw can do. But that would then require cancelling of existing flips,
The hard part there would be rolling back the user visible state. But if we were to disallow it for anything but simple page flips, we could rather easily keep the fb pointers around in the structure that tracks the progress of the operation.
Otherwise cancelling should be trivial when using mmio instead of the cs.
But we could certainly do the target vblank thing without allowing multiple queued flips initially, and without a cancel capability.
However using vblank counter is a bit problematic with multiple crtcs. But I guess userspace can try to do the something_else->vbl conversion itself if it wants to use some other units.
There's also the question whether we should allow a target vbl count for each crtc, or just one of them.
We could make it so that you can specify the target vblank count only for a single crtc, and the rest of the crtcs will flip soon before or soon after. The reason for allowing the "soon before" case is because it'll make the implementation much simpler. We just have to perform all the register writes somewhere between 'target_vbl-1 - target_vbl' of the specified crtc. The order in which the flips actually happen then depends on the vblank period and phase of each crtc.
If user space wants target counts for all crtcs, it could issue separate nuclear flips for each crtc, although that does raise the issue that we can't check the entire target state, so we can't guarantee that all of the nuclear flips will succeed. So that's a bit bad.
So I guess we could allow a target vbl count for all the crtcs, just need to convey that information inside another array in the ioctl to avoid imposing a limit in the number of crtcs. But then there's the question what happens if only a subset of the involved crtcs have a target count. Return an error? Or just pick one of the crtcs that did get a target vblank and flip the rest at the same time? I guess the latter option is better to allow one crtc to act as the master clock for the whole thing, and the rest just hop along as best they can.
On Wed, May 28, 2014 at 05:14:21PM +0300, Ville Syrjälä wrote:
On Wed, May 28, 2014 at 03:21:41PM +0200, Daniel Vetter wrote:
On Tue, May 27, 2014 at 07:32:46PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter daniel@ffwll.ch wrote:
[snip]
Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we should hang so much off of that one flag.
Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also want non-blocking modesets.
random-diverging-from-original-topic-thought.. seems like userspace just wants a deadline/timeout (hopefully in units of vblanks).. at least to the level of "I want this to happen on the next vblank (after rendering completes), or give me an error" vs "I want this to complete atomically even if it takes a few extra vblanks for things to sort out"..
I guess that amounts to what you mean by VBLANK_SYNCED flag, but VBLANKED_SYNCED might not be a good name.. at least for some hw all you can do is vblank sync'd..
Hm, we might as well go full monty and allow userspace to request a specific vblank counter. Would be useful for e.g. queuing up a bunch of video frames, which some hw can do. But that would then require cancelling of existing flips,
The hard part there would be rolling back the user visible state. But if we were to disallow it for anything but simple page flips, we could rather easily keep the fb pointers around in the structure that tracks the progress of the operation.
Otherwise cancelling should be trivial when using mmio instead of the cs.
But we could certainly do the target vblank thing without allowing multiple queued flips initially, and without a cancel capability.
However using vblank counter is a bit problematic with multiple crtcs. But I guess userspace can try to do the something_else->vbl conversion itself if it wants to use some other units.
There's also the question whether we should allow a target vbl count for each crtc, or just one of them.
We could make it so that you can specify the target vblank count only for a single crtc, and the rest of the crtcs will flip soon before or soon after. The reason for allowing the "soon before" case is because it'll make the implementation much simpler. We just have to perform all the register writes somewhere between 'target_vbl-1 - target_vbl' of the specified crtc. The order in which the flips actually happen then depends on the vblank period and phase of each crtc.
If user space wants target counts for all crtcs, it could issue separate nuclear flips for each crtc, although that does raise the issue that we can't check the entire target state, so we can't guarantee that all of the nuclear flips will succeed. So that's a bit bad.
So I guess we could allow a target vbl count for all the crtcs, just need to convey that information inside another array in the ioctl to avoid imposing a limit in the number of crtcs. But then there's the question what happens if only a subset of the involved crtcs have a target count. Return an error? Or just pick one of the crtcs that did get a target vblank and flip the rest at the same time? I guess the latter option is better to allow one crtc to act as the master clock for the whole thing, and the rest just hop along as best they can.
I guess if userspace asks for a target count on more than one crtc we'd reject the request. That leaves a bit a window unfortunately where userspace partially submitted a request and then the kernel starts to tell it that it's too late. But then I don't think we can really avoid this situation without going completely nuts on the implementation side.
So a best effort (maybe with the guarantee that if we're too late it wont happen so that userspace could try to render the next frame and display that for smoothness) approach should be good enough here.
Anyway, let's not get too distracted with fancy ideas before we have the basics ;-)
Cheers, Daniel
On Wed, May 28, 2014 at 9:21 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, May 27, 2014 at 07:32:46PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter daniel@ffwll.ch wrote:
[snip]
Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we should hang so much off of that one flag.
Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also want non-blocking modesets.
random-diverging-from-original-topic-thought.. seems like userspace just wants a deadline/timeout (hopefully in units of vblanks).. at least to the level of "I want this to happen on the next vblank (after rendering completes), or give me an error" vs "I want this to complete atomically even if it takes a few extra vblanks for things to sort out"..
I guess that amounts to what you mean by VBLANK_SYNCED flag, but VBLANKED_SYNCED might not be a good name.. at least for some hw all you can do is vblank sync'd..
Hm, we might as well go full monty and allow userspace to request a specific vblank counter. Would be useful for e.g. queuing up a bunch of video frames, which some hw can do. But that would then require cancelling of existing flips, so I guess for now a simple VBLANK_SYNCED flag which emulates pageflip behaviour should be good enough.
The full on vblank counter and queue stuff up could be interesting.. not entirely sure how we'd manage ->atomic_check() against a not yet applied base state. I suppose it is just a matter of copying the previous-state values for new state objects from the not-yet applied state, rather than {plane,crtc,etc}->state..
And since state objects are refcnt'd I guess forming a linked list out of 'em would not be hard.
Probably we should not allow queuing in the beginning. But does kinda seem like some magic is possible to handle this.
That would also be useful if userspace attempts an atomic update on drivers which only support atomic modeset (through the crtc helpers), but can't do vblank synced updates. Then they could easily reject those. After all current drivers also often lack a pageflip implementation ...
yeah.. but I guess this mainly the server chips and old stuff? I wonder how many drivers support multiple crtcs but not pageflip?
BR, -R
-Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter daniel@ffwll.ch wrote:
[snip]
Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we should hang so much off of that one flag.
Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also want non-blocking modesets.
[snip]
I think one excellent use-case we get for free (almost) without the ioctl would be fbcon. It very much wants to do an atomic update, so converting that over to the atomic interface would be good imo.
Yes, iirc the remaining non-atomic paths are fbcon related. In principle it should be a simple matter to increment the refcnt on fbcon state object and re-apply it. Although at the moment we keep track of *how* to apply the state (ie. page_flip vs set_config, etc) as the state object is built up.. which isn't very conducive to re-committing an existing state object. Which is part of the reason I wanted to deprecate the various existing ->page_flip/->update_plane/->set_config/etc and introduce per object ->commit()'s. (Which could either be called by helpers, or called internally by driver or completely ignored by driver)
Yeah, I think the approach in here with a few helpers to bend atomic ->commit to the old hooks (somewhat-ish) is good. And with the crtc helpers we should be able to move most drivers away from the old hooks quickly. The exception will be pageflips/cursors, but that requires a lot of driver-specific work, and probably first a full conversion to universal planes (which atm don't support everything even for the primary plane due to the arbitrary restriction to rgbx8888).
btw, I guess/hope most SoC drivers (ie. the ones that want to do crazy things with overlays) would be moving rapidly away from using primary plane helpers. I think native primary planes fits better most of the non-trivial mobile display controller blocks..
I've been a bit reluctant so far to do too much additional refactoring on top of atomic, since I'm about at the limit of what I have time to repeatedly rebase each kernel version. This is why I'm a bit anxious to start merging some of atomic, even if it doesn't do absolutely everything yet.
I understand that, which is why I suggested a bunch of things to split out already so we can get them in.
On top of that I think with the split-up mode_config.mutex like I've just proposed in an RFC we have a clear path for the locking issues, too. So could go ahead an merge the w/w conversion, too.
I suppose now that I split modeset acquire ctx out into it's own thing (originally it was inline w/ drm_atomic_state), I could reshuffle the patches and move ww_mutex stuff below atomic.. will take a bit of patch surgery since some parts of that patch would have to move to later patches, but I can sort it out
That leaves the set_prop refactoring which is still under discussion. Those three pieces hopefully help a lot with reducing rebasing pain.
On top of that I think we should look at cutting away functional pieces of the conversion, e.g. ignore planes at first and only look at atomic modeset. Or ignore atomic modesets and only look at plane updates, rejecting anything that changes connectors or crtcs. By cutting out slices we should be able to get patches into shape step-by-step.
hmm, well, not sure how much value it is splitting up like that. I suppose the only-plane approach would sidestep/punt some issues. But once locking and set_prop are in place / agreed, it isn't that much more from a churn perspective.
tbh, I think the worse problems are when we actually expose atomic ioctl to userspace. Right now we are rather restricted in the updates triggered via atomic in that there are fixed well defined entry points to atomic. Ie. only pageflip() sets _NONBLOCK flag, and it does not set mode property, so you won't ever see a _NONBLOCK modeset.
BR, -R
-Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Tue, May 27, 2014 at 07:47:42PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter daniel@ffwll.ch wrote:
[snip]
Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we should hang so much off of that one flag.
Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also want non-blocking modesets.
[snip]
I think one excellent use-case we get for free (almost) without the ioctl would be fbcon. It very much wants to do an atomic update, so converting that over to the atomic interface would be good imo.
Yes, iirc the remaining non-atomic paths are fbcon related. In principle it should be a simple matter to increment the refcnt on fbcon state object and re-apply it. Although at the moment we keep track of *how* to apply the state (ie. page_flip vs set_config, etc) as the state object is built up.. which isn't very conducive to re-committing an existing state object. Which is part of the reason I wanted to deprecate the various existing ->page_flip/->update_plane/->set_config/etc and introduce per object ->commit()'s. (Which could either be called by helpers, or called internally by driver or completely ignored by driver)
Yeah, I think the approach in here with a few helpers to bend atomic ->commit to the old hooks (somewhat-ish) is good. And with the crtc helpers we should be able to move most drivers away from the old hooks quickly. The exception will be pageflips/cursors, but that requires a lot of driver-specific work, and probably first a full conversion to universal planes (which atm don't support everything even for the primary plane due to the arbitrary restriction to rgbx8888).
btw, I guess/hope most SoC drivers (ie. the ones that want to do crazy things with overlays) would be moving rapidly away from using primary plane helpers. I think native primary planes fits better most of the non-trivial mobile display controller blocks..
Fully agreed. See my comment about the VBLANK_SYNCED flag, I think without a driver implementation we should simply aggressively reject such updates. Same with disabling the primary plane, at least until we've reworked the interfaces a bit.
I've been a bit reluctant so far to do too much additional refactoring on top of atomic, since I'm about at the limit of what I have time to repeatedly rebase each kernel version. This is why I'm a bit anxious to start merging some of atomic, even if it doesn't do absolutely everything yet.
I understand that, which is why I suggested a bunch of things to split out already so we can get them in.
On top of that I think with the split-up mode_config.mutex like I've just proposed in an RFC we have a clear path for the locking issues, too. So could go ahead an merge the w/w conversion, too.
I suppose now that I split modeset acquire ctx out into it's own thing (originally it was inline w/ drm_atomic_state), I could reshuffle the patches and move ww_mutex stuff below atomic.. will take a bit of patch surgery since some parts of that patch would have to move to later patches, but I can sort it out
below = earlier in the series or later? I'll assume earlier since that what git log shows ;-)
As mentioned on irc I think we could also throw per-plane locks into the mix to really validate the locking side of this. One thing we didn't discuss that much in the last few emails is accessing the obj->state data and other stuff which is autodetect (e.g. whether the sink is audio capable or not). I still think we need one additional plain mutex to protect that. It would sit between the mode_config.mutex and all the ww mutexes in the locking hierarchy.
But we can look at that once the mode_config.mutex mess is untangled and clear.
That leaves the set_prop refactoring which is still under discussion. Those three pieces hopefully help a lot with reducing rebasing pain.
On top of that I think we should look at cutting away functional pieces of the conversion, e.g. ignore planes at first and only look at atomic modeset. Or ignore atomic modesets and only look at plane updates, rejecting anything that changes connectors or crtcs. By cutting out slices we should be able to get patches into shape step-by-step.
hmm, well, not sure how much value it is splitting up like that. I suppose the only-plane approach would sidestep/punt some issues. But once locking and set_prop are in place / agreed, it isn't that much more from a churn perspective.
Hm, could be that it's not worth it, was just an idea.
tbh, I think the worse problems are when we actually expose atomic ioctl to userspace. Right now we are rather restricted in the updates triggered via atomic in that there are fixed well defined entry points to atomic. Ie. only pageflip() sets _NONBLOCK flag, and it does not set mode property, so you won't ever see a _NONBLOCK modeset.
I guess we could lift limits step-by-step, e.g. reject any NONBLOCK updates if they change crtc/connector properties. And reject any NONBLOCK which isn't also vsynced.
Wrt earlier testing fbcon should give us basic in-kernel validation, since it really wants to kill all the planes and everything and put a new config into place in one go. Another good one would be suspend/resume. It needs vt-switchless resume enabled (which is a per-driver knob and only i915 has it for now, but it's easy to do). Then we could suspend/resume a full X session with overlays, cursors and all and see whether the atomic engine can cope and restore it all.
Once that's proven the ioctl shouldn't be that bad really any more. For i915 I'll demand lots of tests for corner-cases, but the easiest to implement workload might be the vt restore in the ddx. If we can do that with atomic you can fire up a bunch of ridiculous (and conflicting) X configs with cursors and overlays and the vt-switch between them like mad. That should give us good testing I hope. -Daniel
I find myself making this change locally whenever debugging FB reference counting. Which seems a bit silly.
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/drm_crtc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index e14d517..972af76 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -584,7 +584,7 @@ EXPORT_SYMBOL(drm_framebuffer_lookup); */ void drm_framebuffer_unreference(struct drm_framebuffer *fb) { - DRM_DEBUG("FB ID: %d\n", fb->base.id); + DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, fb->refcount.refcount.counter); kref_put(&fb->refcount, drm_framebuffer_free); } EXPORT_SYMBOL(drm_framebuffer_unreference); @@ -597,7 +597,7 @@ EXPORT_SYMBOL(drm_framebuffer_unreference); */ void drm_framebuffer_reference(struct drm_framebuffer *fb) { - DRM_DEBUG("FB ID: %d\n", fb->base.id); + DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, fb->refcount.refcount.counter); kref_get(&fb->refcount); } EXPORT_SYMBOL(drm_framebuffer_reference); @@ -609,7 +609,7 @@ static void drm_framebuffer_free_bug(struct kref *kref)
static void __drm_framebuffer_unreference(struct drm_framebuffer *fb) { - DRM_DEBUG("FB ID: %d\n", fb->base.id); + DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, fb->refcount.refcount.counter); kref_put(&fb->refcount, drm_framebuffer_free_bug); }
TODO possibly just squash this back into "convert crtc/plane to atomic"..
This reintroduces drm_modeset_lock_all() in a places that had not been converted to more fine grained locking (setcrtc, setplane, etc). This makes the locking equivalent to before.
Note that since mode_config.mutex is a drm_modeset_lock, the lock is automatically dropped at the end of the atomic update.
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/drm_crtc.c | 21 +++++++++++++++++++++ drivers/gpu/drm/drm_fb_helper.c | 18 +++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 972af76..4e3b527 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -709,14 +709,22 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) * in this manner. */ if (atomic_read(&fb->refcount.refcount) > 1) { + struct drm_mode_config *config = &dev->mode_config; struct drm_atomic_state *state; + int ret;
+retry: state = dev->driver->atomic_begin(dev, 0); if (IS_ERR(state)) { DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n"); return; }
+ ret = drm_modeset_lock(&config->mutex, &state->acquire_ctx) || + drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx); + if (ret) + goto out; + /* remove from any plane */ list_for_each_entry(plane, &dev->mode_config.plane_list, head) { if (plane->fb == fb) @@ -729,7 +737,10 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) else dev->driver->atomic_commit(dev, state);
+out: dev->driver->atomic_end(dev, state); + if (ret == -EDEADLK) + goto retry; }
drm_framebuffer_unreference(fb); @@ -2595,6 +2606,11 @@ retry: if (IS_ERR(state)) return PTR_ERR(state);
+ ret = drm_modeset_lock(&config->mutex, &state->acquire_ctx) || + drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx); + if (ret) + goto out; + plane = drm_plane_find(dev, plane_req->plane_id); if (!plane) { DRM_DEBUG_KMS("Unknown plane ID %d\n", @@ -2810,6 +2826,11 @@ retry: if (IS_ERR(state)) return PTR_ERR(state);
+ ret = drm_modeset_lock(&config->mutex, &state->acquire_ctx) || + drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx); + if (ret) + goto out; + /* 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: diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 3815e1a..4dc22cf 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -36,6 +36,7 @@ #include <linux/module.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> +#include <drm/drm_atomic.h> #include <drm/drm_fb_helper.h> #include <drm/drm_crtc_helper.h>
@@ -287,11 +288,13 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper, bool lockless) { struct drm_device *dev = fb_helper->dev; + struct drm_mode_config *config = &dev->mode_config; struct drm_plane *plane; bool error = false; struct drm_atomic_state *state; - int i; + int ret, i;
+retry: state = dev->driver->atomic_begin(dev, lockless ? DRM_MODE_ATOMIC_NOLOCK : 0); if (IS_ERR(state)) { @@ -299,6 +302,11 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper, return true; }
+ ret = drm_modeset_lock(&config->mutex, &state->acquire_ctx) || + drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx); + if (ret) + goto out; + list_for_each_entry(plane, &dev->mode_config.plane_list, head) if (plane->type != DRM_PLANE_TYPE_PRIMARY) drm_plane_force_disable(plane, state); @@ -309,7 +317,12 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper, else dev->driver->atomic_commit(dev, state);
+out: dev->driver->atomic_end(dev, state); + if (ret == -EDEADLK) + goto retry; + + drm_modeset_lock_all(dev);
for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; @@ -326,6 +339,9 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper, if (ret) error = true; } + + drm_modeset_unlock_all(dev); + return error; } EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);
From: Sean Paul seanpaul@chromium.org
BUG=chromium:336809 TEST=Tested on snow & peppy
Change-Id: Icd864ac52da9c973202f3ac4629824ef5eec2ac9 Signed-off-by: Sean Paul seanpaul@chromium.org Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/drm_atomic.c | 4 ++++ drivers/gpu/drm/drm_crtc.c | 12 +++++++++--- include/drm/drm_crtc.h | 1 + 3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 863a0fe..79fbb2c 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -613,6 +613,7 @@ swap_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a) /* clear transient state (only valid during atomic update): */ cstate->set_config = false; cstate->connectors_change = false; + cstate->commit_state = false;
swap(crtc->state, a->cstates[crtc->id]); crtc->base.propvals = &crtc->state->propvals; @@ -687,6 +688,9 @@ commit_crtc_state(struct drm_crtc *crtc, drm_atomic_get_plane_state(crtc->primary, 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 4e3b527..03dddbb 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -792,7 +792,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, crtc->base.propvals = &crtc->state->propvals;
list_add_tail(&crtc->head, &dev->mode_config.crtc_list); - dev->mode_config.num_crtc++; + crtc->id = dev->mode_config.num_crtc++;
crtc->primary = primary; if (primary) @@ -910,7 +910,7 @@ static int check_connectors(struct drm_crtc *crtc, if (ocrtc == crtc) continue;
- ostate = drm_atomic_get_crtc_state(crtc, state); + ostate = drm_atomic_get_crtc_state(ocrtc, state); if (IS_ERR(ostate)) return PTR_ERR(ostate);
@@ -939,7 +939,6 @@ retry: return -EINVAL; } } - return 0; }
@@ -961,6 +960,10 @@ int drm_crtc_check_state(struct drm_crtc *crtc, if (!(fb && state->mode_valid)) return 0;
+ /* We're not committing this state, ignore */ + if (!state->commit_state) + return 0; + hdisplay = state->mode.hdisplay; vdisplay = state->mode.vdisplay;
@@ -1034,6 +1037,7 @@ int drm_crtc_set_property(struct drm_crtc *crtc, /* grab primary plane state now, to ensure locks are held, etc. */ drm_atomic_get_plane_state(crtc->primary, state->state);
+ state->commit_state = true; drm_object_property_set_value(&crtc->base, &state->propvals, property, value, blob_data);
@@ -1045,6 +1049,7 @@ int drm_crtc_set_property(struct drm_crtc *crtc, /* check size: */ if (value < sizeof(struct drm_mode_modeinfo)) return -EINVAL; + state->mode = *(struct drm_mode_modeinfo *)blob_data; state->mode_valid = true; } @@ -1538,6 +1543,7 @@ int drm_plane_set_property(struct drm_plane *plane, } else if (property == config->prop_crtc_id) { struct drm_mode_object *obj = drm_property_get_obj(property, value); struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL; + /* take the lock of the incoming crtc as well, moving * plane between crtcs is synchronized on both incoming * and outgoing crtc. diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 2fbf13a..009b76f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -317,6 +317,7 @@ struct drm_crtc_state { /* transient state, only valid during atomic operation: */ bool set_config : 1; bool connectors_change : 1; + bool commit_state : 1;
uint8_t num_connector_ids; uint32_t *connector_ids;
On Sat, May 24, 2014 at 02:30:09PM -0400, Rob Clark wrote:
One more time, with feeling..
Previous revision of series: http://lists.freedesktop.org/archives/dri-devel/2014-March/055806.html
And if you prefer, in git form: http://cgit.freedesktop.org/~robclark/linux/log/?h=cold-fusion git://people.freedesktop.org/~robclark/linux cold-fusion
This series does not include the actual atomic ioctl, but it does include all the needed infrastructure changes.
Compared to previous revision, I've split out drm_modeset_acquire_ctx from drm_atomic_state, so that we can ww acquire mode_config and crtc locks outside of atomic transactions. (Which keeps lockdep happy wrt. drm_modeset_lock_all().) I also got to the point in juggling things around where I wanted to let the compiler type checking do it's job, so 's/void *state/struct drm_atomic_state *state/g'.
At this point, I've tested this on i915 (few different generation laptops), radeon, and msm. And of course w/ ww debug (deadlock in- jection) enabled. So I think all the locking related paths should be covered.
I believe Thierry has tested a slighly older revision on tegra. I would of course appreciate more testing on other drivers for which I don't have the hw. But I think it is pretty much ready to go. I do still owe some docs updates, I will send some patches for that in the near future, but didn't want to hold up giving others a chance to start banging on this.
Ok, I've done a fairly cursory look at this only and tried to concentrate on grasping the high-level details. Patches contain a bunch of comments on the details, but here is the big stuff.
First things first there's a bunch of prep work and refactoring sprinkled throughout the series. Imo we should pick those out and rebase them on top of drm-next asap to get them in before the actual interfaces. I hope I've catched them all and commented everywhere about what imo is still missing for merging. With that out of the way the real atomic review below.
I like the overall design. It will be a royal pain to convert i915 over to this since thus far we've simply tucked away the staged state into obj->new_foo pointers. Which means we get to change the entire driver again ;-)
But I think the interfaces to driver and overall api design need some good polish:
- Imo way too much is done in driver callbacks, which then again call helpers. Parsing of the core properties for plane/crtcs should be done in the core imo. This way drivers only need to register ->set_prop callbacks if there's a driver-specific property they support.
- Imo those obj->set_prop callbacks should take the object-specific drm_obj_state object, not the global atomic state structure. Will lead to _much_ less lookup code duplicated all over the place. If we then also add a state->obj pointer we could also ditch that parameter. Ofc obj would be specific to the state at hand.
- One downside of that is that drivers won't be able to interfere the allocation step any more. I think we'd need an additional ->alloc_atomic_state call so that drivers can still easily subclass some objects with their own type.
- There's imo a bit a confusion between what's helper and what's driver api. My big gripe here is with the set_prop stuff which imo really should be core and not helpers. The default ->commit/check implementations otoh should be more clearly delineated as helper implementations that drivers can/should overwrite. I think we should split drm_atomic.c into drm_atomic.c (with the official pieces) and drm_atomic_helper.c (with the suggested standard/transitional implemenations for commit/check). Helper functions should have the drm_atomic_helper_ prefix.
- I also think we should split the vfuncs like we do with the crtc helpers. Imo the core interface should just be an ->alloc_state and ->set_prop on all kms objects, plus a global ->prep/check/commit at the driver level. The object-specific ->check/commit hooks otoh should be part of the atomic helper library and in some separate atomic_helper_funcs structure. Imo we could either extend the sturcture used by the crtc helpers (hackish imo) or change the type of that to make it clear it's for the crtc helpers and add a new pointer for atomic helpers. E.g. in i915 I expect that we'll implement our very own ->check/commit hooks using our own compute_config infrastructure. Maybe we could switch to the ->check hooks of the atomic helper eventually, but that will be a lot of work. And in any case we won't ever switch to the ->commit hook as you have it in your helpers since for truly atomic flips/modesets we need to interleave all the updates of all objects in various phases.
The patches are also rather big, which makes them a bit a pain to review. Matt's approach for the primary planes was much easier: 1) Add the new core interfaces for the new world. 2) Implement helpers for drivers to transition and convert drivers. 3) Implement ioctls using the new driver interfaces.
That approach would also help a lot in figuring out what's helper code and so optional, and what's core stuff.
The other big problem I see still is with the locking:
- Imo switching mode_config.mutex to a ww_mutex won't work. I know that the magic rules of w/w locking are _really_ tempting. But I don't think it will actually solve anything and instead only cause havoc.
- I think we need ww mutexes for crtcs, but they will be fairly useless without ww mutexes on plane. Not for i915, but for all those drivers which can switch planes around between different crtcs. Imo we should do this as a first step (before getting all the atomic interfaces in), and pimp the set_plane locking a bit already like I've described in some mail.
- There's a pile of state that's currently not really protected by anything in your patches. One piece is all the obj->state properties. Another one is state detected at runtime like e.g. whether a hdmi sink supports audio or what kind of link bw parameters a dp sink supports. Thus far this was all protected by mode_config.mutex, but we can't take this one in the generic atomic ioctls for pretty obvious reasons.
I think we need a new lock here, e.g. dev->mode_config.state_lock. It's going to be painful, since we need to roll this out over _all_ driver callbacks which can change the meaning of some property. E.g. all the ->detect callbacks in i915. The important part is that no one is allowed to do any costly operations why holding this lock (i.e. anything like reading EDIDs or doing load-detect), it is only for updating/reading this state.
- To avoid nasties with locking inversion between the plain mode_config.mutex, the state_mutex and the various ww mutexes we need to rather completely rework the locking sequence for atomic updates compared to what you have. Since in the driver's detect callbacks we first grab the mode_config.mutex and then potentiall ww mutexes (only i915) and maybe also the state_mutex (for updating autodetected properties). But for atomic updates we first need to grab the state_mutex (so that we can get at the current state) and then ww mutexes and the mode_config.mutex. And for the later we're not even good at predicting the order we want to acquire them.
Stage 1: Create the new state We grab _just_ the mode_config.state_mutex and allocate all the state objects. This will allow us to grab a copy of the current state of everything without races or inconsistencies.
As a consequence ->set_prop hooks may not touch anything outside of the date/state protected by the state_mutex.
Stage 2: check/commit stage We drop the state_lock since it would nest the wrong way round again with mode_config.mutex. We should have a complete copy of all state already, and if something races against us (2nd ioctl or a output probe call) we don't really care.
Only then will the check/commit hooks grab all the locks. For the mode_config.mutex we could add a bit to the global state the we need it (e.g. when the connector->crtc links change or for some other driver-private need).
That only leaves races with a 2nd concurrent atomic modeset ioctl. Which can be avoided by grabbing yet another lock, which we drop after the driver has acquired all real locks. For that we could mandate the ->check callback and require that it grabs all ww mutexes plus the mode_config.mutex.
For implementing this madness (if we agree it's the right approach) we can go step-by-step: - Convert crtc->mutex to a ww mutex. - Add plane ww mutexes, make set_plane locking more fine-grained. - Add the mode_config.state_lock, roll it out across all driver's ->detect callbacks and add it to modeset_lock_all (so that set_prop calls dtrt). - Use all this correctly in the atomic stuff.
Ok, that's the two big comments on this work from my side. Now reality check: How much off the mark am I?
Cheers, Daniel
On Mon, May 26, 2014 at 6:40 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 02:30:09PM -0400, Rob Clark wrote:
One more time, with feeling..
Previous revision of series: http://lists.freedesktop.org/archives/dri-devel/2014-March/055806.html
And if you prefer, in git form: http://cgit.freedesktop.org/~robclark/linux/log/?h=cold-fusion git://people.freedesktop.org/~robclark/linux cold-fusion
This series does not include the actual atomic ioctl, but it does include all the needed infrastructure changes.
Compared to previous revision, I've split out drm_modeset_acquire_ctx from drm_atomic_state, so that we can ww acquire mode_config and crtc locks outside of atomic transactions. (Which keeps lockdep happy wrt. drm_modeset_lock_all().) I also got to the point in juggling things around where I wanted to let the compiler type checking do it's job, so 's/void *state/struct drm_atomic_state *state/g'.
At this point, I've tested this on i915 (few different generation laptops), radeon, and msm. And of course w/ ww debug (deadlock in- jection) enabled. So I think all the locking related paths should be covered.
I believe Thierry has tested a slighly older revision on tegra. I would of course appreciate more testing on other drivers for which I don't have the hw. But I think it is pretty much ready to go. I do still owe some docs updates, I will send some patches for that in the near future, but didn't want to hold up giving others a chance to start banging on this.
Ok, I've done a fairly cursory look at this only and tried to concentrate on grasping the high-level details. Patches contain a bunch of comments on the details, but here is the big stuff.
First things first there's a bunch of prep work and refactoring sprinkled throughout the series. Imo we should pick those out and rebase them on top of drm-next asap to get them in before the actual interfaces. I hope I've catched them all and commented everywhere about what imo is still missing for merging. With that out of the way the real atomic review below.
I like the overall design. It will be a royal pain to convert i915 over to this since thus far we've simply tucked away the staged state into obj->new_foo pointers. Which means we get to change the entire driver again ;-)
But I think the interfaces to driver and overall api design need some good polish:
- Imo way too much is done in driver callbacks, which then again call helpers. Parsing of the core properties for plane/crtcs should be done in the core imo. This way drivers only need to register ->set_prop callbacks if there's a driver-specific property they support.
Pretty universally, the approach I took was to provide default fxns (for ->atomic_xyz(), ->set_property(), ->create_state(), etc), and let drivers wrap them as needed. We might need to tweak drm_atomic_begin() a bit for the first driver that needs to wrap drm_atomic_state, but I guess we can tackle that as the need arises.
- Imo those obj->set_prop callbacks should take the object-specific drm_obj_state object, not the global atomic state structure. Will lead to _much_ less lookup code duplicated all over the place. If we then also add a state->obj pointer we could also ditch that parameter. Ofc obj would be specific to the state at hand.
The problem is if those callbacks need to take other driver shared locks.. that plus initially I was trying to keep state as opaque as possible (in case sooner or later i915 decided to re-implement :-P)
Eventually I decided keeping state opaque was too much pita, and that if driver wanted to do something different, it should instead subclass. So I guess these days they could chase [pc]state->state to get back at the global state.
- One downside of that is that drivers won't be able to interfere the allocation step any more. I think we'd need an additional ->alloc_atomic_state call so that drivers can still easily subclass some objects with their own type.
the ->create_state() is already split out as a vfunc for planes and crtcs. Which I think should be enough to cover most of what drivers need. If driver needs to subclass global state too, then we need to split up drm_atomic_begin(), but I guess that should be a relatively small change that we can make when it becomes needed.
- There's imo a bit a confusion between what's helper and what's driver api. My big gripe here is with the set_prop stuff which imo really should be core and not helpers. The default ->commit/check implementations otoh should be more clearly delineated as helper implementations that drivers can/should overwrite. I think we should split drm_atomic.c into drm_atomic.c (with the official pieces) and drm_atomic_helper.c (with the suggested standard/transitional implemenations for commit/check). Helper functions should have the drm_atomic_helper_ prefix.
Hmm, I think nearly *everything* could be overridden.. maybe it could be more clear what to override vs outright replace. But otoh I'm not quite sure yet what drivers will need to override (other than some obvious ones, which are already broken out into vfuncs, like {plane,crtc}->create_state() to handle driver custom properties).
So I expect some tweaks as other drivers start adding native atomic support. I basically added what I needed for msm, and what I thought was pretty safe bet that other drivers would need. This at least gives us something other drivers could start trying to use. Then see what is missing and add it as we go. At least that was my line of thinking.
- I also think we should split the vfuncs like we do with the crtc helpers. Imo the core interface should just be an ->alloc_state and ->set_prop on all kms objects, plus a global ->prep/check/commit at the driver level. The object-specific ->check/commit hooks otoh should be part of the atomic helper library and in some separate atomic_helper_funcs structure.
this is in fact the way it is, although 'struct drm_atomic_funcs' maybe should have "helper" in the name.
Imo we could either extend the sturcture used by the crtc helpers (hackish imo) or change the type of that to make it clear it's for the crtc helpers and add a new pointer for atomic helpers. E.g. in i915 I expect that we'll implement our very own ->check/commit hooks using our own compute_config infrastructure. Maybe we could switch to the ->check hooks of the atomic helper eventually, but that will be a lot of work. And in any case we won't ever switch to the ->commit hook as you have it in your helpers since for truly atomic flips/modesets we need to interleave all the updates of all objects in various phases.
not sure if it would work for you, but in msm I have a per-crtc "don't actually commit yet" bit, which gets set for each crtc involved in atomic update. Although in my case it is mainly a matter of not touching the "GO" bits until everything is setup..
If not, I guess it shouldn't be a big deal to, for example, split up atomic_commit() into part that did the locking dance and then call vfunc ->atomic_apply_state() or something like that.
Basically figure out where you need to hook in for i915 as you add native atomic support. By the time you and maybe one or two other drivers have added atomic support we should have it pretty well nailed down. But until then, there might be some small adjustments here/there.
The patches are also rather big, which makes them a bit a pain to review. Matt's approach for the primary planes was much easier:
- Add the new core interfaces for the new world.
- Implement helpers for drivers to transition and convert drivers.
- Implement ioctls using the new driver interfaces.
not so far different from what I did. Although I suppose I could have split up adding atomic support for plane/crtc vs converting over existing ioctls to use it. Not sure if it is worth doing at this stage or not. Not really sure that it would help in sorting out what is helper vs not.
That approach would also help a lot in figuring out what's helper code and so optional, and what's core stuff.
The other big problem I see still is with the locking:
- Imo switching mode_config.mutex to a ww_mutex won't work. I know that the magic rules of w/w locking are _really_ tempting. But I don't think it will actually solve anything and instead only cause havoc.
are you actually getting lockdep splats? If so pls send them my way and I'll have a look.
In non-atomic cases we are still using these as bare mutex's (other than the one special case where we needed nested_lock before). So I think if it worked before, it should continue to work.
I think we need ww mutexes for crtcs, but they will be fairly useless without ww mutexes on plane. Not for i915, but for all those drivers which can switch planes around between different crtcs. Imo we should do this as a first step (before getting all the atomic interfaces in), and pimp the set_plane locking a bit already like I've described in some mail.
There's a pile of state that's currently not really protected by anything in your patches. One piece is all the obj->state properties. Another one is state detected at runtime like e.g. whether a hdmi sink supports audio or what kind of link bw parameters a dp sink supports. Thus far this was all protected by mode_config.mutex, but we can't take this one in the generic atomic ioctls for pretty obvious reasons.
we are still taking mode_config.mutex in places where we did before (setcrtc, setplane, etc). So it shouldn't be a problem *yet*.
Runtime internal params about hw state, as opposed to things userspace is asking for, should perhaps not be in obj->state? Or maybe I'm misunderstanding you.
I think we need a new lock here, e.g. dev->mode_config.state_lock. It's going to be painful, since we need to roll this out over _all_ driver callbacks which can change the meaning of some property. E.g. all the ->detect callbacks in i915. The important part is that no one is allowed to do any costly operations why holding this lock (i.e. anything like reading EDIDs or doing load-detect), it is only for updating/reading this state.
- To avoid nasties with locking inversion between the plain mode_config.mutex, the state_mutex and the various ww mutexes we need to rather completely rework the locking sequence for atomic updates compared to what you have. Since in the driver's detect callbacks we first grab the mode_config.mutex and then potentiall ww mutexes (only i915) and maybe also the state_mutex (for updating autodetected properties). But for atomic updates we first need to grab the state_mutex (so that we can get at the current state) and then ww mutexes and the mode_config.mutex. And for the later we're not even good at predicting the order we want to acquire them.
so I think I need to understand the call sequence for the detect thing in i915, since atm I'm not quite sure what the problem is, and why it worked before..
Stage 1: Create the new state We grab _just_ the mode_config.state_mutex and allocate all the state objects. This will allow us to grab a copy of the current state of everything without races or inconsistencies.
As a consequence ->set_prop hooks may not touch anything outside of the date/state protected by the state_mutex.
Stage 2: check/commit stage We drop the state_lock since it would nest the wrong way round again with mode_config.mutex. We should have a complete copy of all state already, and if something races against us (2nd ioctl or a output probe call) we don't really care.
Only then will the check/commit hooks grab all the locks. For the mode_config.mutex we could add a bit to the global state the we need it (e.g. when the connector->crtc links change or for some other driver-private need).
That only leaves races with a 2nd concurrent atomic modeset ioctl. Which can be avoided by grabbing yet another lock, which we drop after the driver has acquired all real locks. For that we could mandate the ->check callback and require that it grabs all ww mutexes plus the mode_config.mutex.
For implementing this madness (if we agree it's the right approach) we can go step-by-step:
- Convert crtc->mutex to a ww mutex.
- Add plane ww mutexes, make set_plane locking more fine-grained.
- Add the mode_config.state_lock, roll it out across all driver's ->detect callbacks and add it to modeset_lock_all (so that set_prop calls dtrt).
- Use all this correctly in the atomic stuff.
Ok, that's the two big comments on this work from my side. Now reality check: How much off the mark am I?
well, I think in summary, it is (a) might need to split few things up or re-arrange things slightly as i915 and other drivers start adding native atomic support.. but this doesn't scare me too much, and I think we can take it as we go.
And (b) possible locking fun.. this I think actually does need to be sorted first. Although I'm not quite sure I understand why it is actually a problem.
BR, -R
Cheers, Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Mon, May 26, 2014 at 08:48:52AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 6:40 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 02:30:09PM -0400, Rob Clark wrote:
One more time, with feeling..
Previous revision of series: http://lists.freedesktop.org/archives/dri-devel/2014-March/055806.html
And if you prefer, in git form: http://cgit.freedesktop.org/~robclark/linux/log/?h=cold-fusion git://people.freedesktop.org/~robclark/linux cold-fusion
This series does not include the actual atomic ioctl, but it does include all the needed infrastructure changes.
Compared to previous revision, I've split out drm_modeset_acquire_ctx from drm_atomic_state, so that we can ww acquire mode_config and crtc locks outside of atomic transactions. (Which keeps lockdep happy wrt. drm_modeset_lock_all().) I also got to the point in juggling things around where I wanted to let the compiler type checking do it's job, so 's/void *state/struct drm_atomic_state *state/g'.
At this point, I've tested this on i915 (few different generation laptops), radeon, and msm. And of course w/ ww debug (deadlock in- jection) enabled. So I think all the locking related paths should be covered.
I believe Thierry has tested a slighly older revision on tegra. I would of course appreciate more testing on other drivers for which I don't have the hw. But I think it is pretty much ready to go. I do still owe some docs updates, I will send some patches for that in the near future, but didn't want to hold up giving others a chance to start banging on this.
Ok, I've done a fairly cursory look at this only and tried to concentrate on grasping the high-level details. Patches contain a bunch of comments on the details, but here is the big stuff.
First things first there's a bunch of prep work and refactoring sprinkled throughout the series. Imo we should pick those out and rebase them on top of drm-next asap to get them in before the actual interfaces. I hope I've catched them all and commented everywhere about what imo is still missing for merging. With that out of the way the real atomic review below.
I like the overall design. It will be a royal pain to convert i915 over to this since thus far we've simply tucked away the staged state into obj->new_foo pointers. Which means we get to change the entire driver again ;-)
But I think the interfaces to driver and overall api design need some good polish:
- Imo way too much is done in driver callbacks, which then again call helpers. Parsing of the core properties for plane/crtcs should be done in the core imo. This way drivers only need to register ->set_prop callbacks if there's a driver-specific property they support.
Pretty universally, the approach I took was to provide default fxns (for ->atomic_xyz(), ->set_property(), ->create_state(), etc), and let drivers wrap them as needed. We might need to tweak drm_atomic_begin() a bit for the first driver that needs to wrap drm_atomic_state, but I guess we can tackle that as the need arises.
- Imo those obj->set_prop callbacks should take the object-specific drm_obj_state object, not the global atomic state structure. Will lead to _much_ less lookup code duplicated all over the place. If we then also add a state->obj pointer we could also ditch that parameter. Ofc obj would be specific to the state at hand.
The problem is if those callbacks need to take other driver shared locks.. that plus initially I was trying to keep state as opaque as possible (in case sooner or later i915 decided to re-implement :-P)
Eventually I decided keeping state opaque was too much pita, and that if driver wanted to do something different, it should instead subclass. So I guess these days they could chase [pc]state->state to get back at the global state.
I don't think keeping state opaque is a feature. Imo we _want_ to have some common structures to track the common set of properties, for otherwise insanity will rule. Drivers can add whatever they whish by subclassing the existing state structs and add whatever they need to track their state. I don't have any intentions to implement something newfangled/different for i915, but instead want to port our current infrastructure over to this.
Having clear prepare&check (we call it compute) and commit semantics is imo the right way to do kms. Think of i915 as the poc vehicle that gets to pay the bill of rewriting the driver twice.
So I'm advocating to move even further avoid from void* everywhere than you've moved already.
Wrt helpers I think at least for modeset operations we can pimp the crtc helpers and mostly reuse the hooks we already have, maybe augmented with per-object ->check hooks. But for nuclear pageflips I really don't see a chance but a single driver-gloabl ->commit (and fwiw check) hook. Maybe per-crtc at best, but that will already run into planes changing crtcs.
- One downside of that is that drivers won't be able to interfere the allocation step any more. I think we'd need an additional ->alloc_atomic_state call so that drivers can still easily subclass some objects with their own type.
the ->create_state() is already split out as a vfunc for planes and crtcs. Which I think should be enough to cover most of what drivers need. If driver needs to subclass global state too, then we need to split up drm_atomic_begin(), but I guess that should be a relatively small change that we can make when it becomes needed.
I guess i915 will need it, to keep track of shared resources like plls. Atm we carry them around in dev_priv, without any precompute/commit semantics. Still something that needs to be fixed ...
Wrt ->create_state: I simply missed that in all the patches. But if you have that I don't quite understand why the various ->set_property callbacks have checks whether the object-specific state exists already and allocate it if not through the get_plane_state and similar functions. Imo that kind of stuff should be handled in the core. Error handling code is really hard to get correct ime, and we depend upon it's correctness here (at least with the current locking scheme) for ww mutexes to work.
- There's imo a bit a confusion between what's helper and what's driver api. My big gripe here is with the set_prop stuff which imo really should be core and not helpers. The default ->commit/check implementations otoh should be more clearly delineated as helper implementations that drivers can/should overwrite. I think we should split drm_atomic.c into drm_atomic.c (with the official pieces) and drm_atomic_helper.c (with the suggested standard/transitional implemenations for commit/check). Helper functions should have the drm_atomic_helper_ prefix.
Hmm, I think nearly *everything* could be overridden.. maybe it could be more clear what to override vs outright replace. But otoh I'm not quite sure yet what drivers will need to override (other than some obvious ones, which are already broken out into vfuncs, like {plane,crtc}->create_state() to handle driver custom properties).
Like I've said above we should imo try to unify the world a bit more again. And for the state tracking that should be possible, so I don't see a need to hand drivers too much rope.
The check/commit stuff otoh should be fully overrideable, since i915 will need that.
So I expect some tweaks as other drivers start adding native atomic support. I basically added what I needed for msm, and what I thought was pretty safe bet that other drivers would need. This at least gives us something other drivers could start trying to use. Then see what is missing and add it as we go. At least that was my line of thinking.
Like I've said I don't think there's much value beyong making crtc helpers atomic capable for modeset changes. Atomic pageflips pretty much need driver-specific magic (less so if there's a "GO" bit like on sane hardware).
- I also think we should split the vfuncs like we do with the crtc helpers. Imo the core interface should just be an ->alloc_state and ->set_prop on all kms objects, plus a global ->prep/check/commit at the driver level. The object-specific ->check/commit hooks otoh should be part of the atomic helper library and in some separate atomic_helper_funcs structure.
this is in fact the way it is, although 'struct drm_atomic_funcs' maybe should have "helper" in the name.
Yeah, I'm mostly just asking for sprinkling helper all over the stuff that clearly is helper code. And moving everything else into the core and making it non-overrideable.
Imo we could either extend the sturcture used by the crtc helpers (hackish imo) or change the type of that to make it clear it's for the crtc helpers and add a new pointer for atomic helpers. E.g. in i915 I expect that we'll implement our very own ->check/commit hooks using our own compute_config infrastructure. Maybe we could switch to the ->check hooks of the atomic helper eventually, but that will be a lot of work. And in any case we won't ever switch to the ->commit hook as you have it in your helpers since for truly atomic flips/modesets we need to interleave all the updates of all objects in various phases.
not sure if it would work for you, but in msm I have a per-crtc "don't actually commit yet" bit, which gets set for each crtc involved in atomic update. Although in my case it is mainly a matter of not touching the "GO" bits until everything is setup..
If not, I guess it shouldn't be a big deal to, for example, split up atomic_commit() into part that did the locking dance and then call vfunc ->atomic_apply_state() or something like that.
Basically figure out where you need to hook in for i915 as you add native atomic support. By the time you and maybe one or two other drivers have added atomic support we should have it pretty well nailed down. But until then, there might be some small adjustments here/there.
For modeset changes we need to munge everything through the full state precompute and then commit to hw machinery. Not yet fully there (since the shared dpll stuff doesn't work yet). For nuclear pageflips we're only just starting to merge all of Ville's infrastructure, but will probably be the same. So imo the only hand-off point between the core and drm is the single, global ->commit. ->check would be the identical code, except for the final commit.
All the ->set_prop callbacks would do nothing else but store data into structures.
The patches are also rather big, which makes them a bit a pain to review. Matt's approach for the primary planes was much easier:
- Add the new core interfaces for the new world.
- Implement helpers for drivers to transition and convert drivers.
- Implement ioctls using the new driver interfaces.
not so far different from what I did. Although I suppose I could have split up adding atomic support for plane/crtc vs converting over existing ioctls to use it. Not sure if it is worth doing at this stage or not. Not really sure that it would help in sorting out what is helper vs not.
It's mostly for reviewing - silly me got lost countless times in your patches.
That approach would also help a lot in figuring out what's helper code and so optional, and what's core stuff.
The other big problem I see still is with the locking:
- Imo switching mode_config.mutex to a ww_mutex won't work. I know that the magic rules of w/w locking are _really_ tempting. But I don't think it will actually solve anything and instead only cause havoc.
are you actually getting lockdep splats? If so pls send them my way and I'll have a look.
In non-atomic cases we are still using these as bare mutex's (other than the one special case where we needed nested_lock before). So I think if it worked before, it should continue to work.
Didn't run it, but pretty sure. I think the other mail conversation cleared that up - mixing w/w nesting and static nesting just don't work: Static nesting means locks are filed into different (static) groups, w/w nesting means you have one single locking class, but ordered dynamically with the backoff magic.
I think we need ww mutexes for crtcs, but they will be fairly useless without ww mutexes on plane. Not for i915, but for all those drivers which can switch planes around between different crtcs. Imo we should do this as a first step (before getting all the atomic interfaces in), and pimp the set_plane locking a bit already like I've described in some mail.
There's a pile of state that's currently not really protected by anything in your patches. One piece is all the obj->state properties. Another one is state detected at runtime like e.g. whether a hdmi sink supports audio or what kind of link bw parameters a dp sink supports. Thus far this was all protected by mode_config.mutex, but we can't take this one in the generic atomic ioctls for pretty obvious reasons.
we are still taking mode_config.mutex in places where we did before (setcrtc, setplane, etc). So it shouldn't be a problem *yet*.
Runtime internal params about hw state, as opposed to things userspace is asking for, should perhaps not be in obj->state? Or maybe I'm misunderstanding you.
Afaict we copy obj->state and ->set_prop (at least in i915) also looks at a bunch of other connector state. And at that point we don't hold mode_config.mutex, and I couldn't spot anything else. Thus far all the ->set_prop stuff _did_ hold mode_config.mutex.
I think we need a new lock here, e.g. dev->mode_config.state_lock. It's going to be painful, since we need to roll this out over _all_ driver callbacks which can change the meaning of some property. E.g. all the ->detect callbacks in i915. The important part is that no one is allowed to do any costly operations why holding this lock (i.e. anything like reading EDIDs or doing load-detect), it is only for updating/reading this state.
- To avoid nasties with locking inversion between the plain mode_config.mutex, the state_mutex and the various ww mutexes we need to rather completely rework the locking sequence for atomic updates compared to what you have. Since in the driver's detect callbacks we first grab the mode_config.mutex and then potentiall ww mutexes (only i915) and maybe also the state_mutex (for updating autodetected properties). But for atomic updates we first need to grab the state_mutex (so that we can get at the current state) and then ww mutexes and the mode_config.mutex. And for the later we're not even good at predicting the order we want to acquire them.
so I think I need to understand the call sequence for the detect thing in i915, since atm I'm not quite sure what the problem is, and why it worked before..
Stage 1: Create the new state We grab _just_ the mode_config.state_mutex and allocate all the state objects. This will allow us to grab a copy of the current state of everything without races or inconsistencies.
As a consequence ->set_prop hooks may not touch anything outside of the date/state protected by the state_mutex.
Stage 2: check/commit stage We drop the state_lock since it would nest the wrong way round again with mode_config.mutex. We should have a complete copy of all state already, and if something races against us (2nd ioctl or a output probe call) we don't really care.
Only then will the check/commit hooks grab all the locks. For the mode_config.mutex we could add a bit to the global state the we need it (e.g. when the connector->crtc links change or for some other driver-private need).
That only leaves races with a 2nd concurrent atomic modeset ioctl. Which can be avoided by grabbing yet another lock, which we drop after the driver has acquired all real locks. For that we could mandate the ->check callback and require that it grabs all ww mutexes plus the mode_config.mutex.
For implementing this madness (if we agree it's the right approach) we can go step-by-step:
- Convert crtc->mutex to a ww mutex.
- Add plane ww mutexes, make set_plane locking more fine-grained.
- Add the mode_config.state_lock, roll it out across all driver's ->detect callbacks and add it to modeset_lock_all (so that set_prop calls dtrt).
- Use all this correctly in the atomic stuff.
Ok, that's the two big comments on this work from my side. Now reality check: How much off the mark am I?
well, I think in summary, it is (a) might need to split few things up or re-arrange things slightly as i915 and other drivers start adding native atomic support.. but this doesn't scare me too much, and I think we can take it as we go.
The problem I see here is massive churn over drivers. If we try to make set_prop hooks and similar stuff optional we'd avoid that, and would actually gain flexibility with fixing the interfaces down the road. If we enforce default callbacks for everyone that will only bloat diffs.
And (b) possible locking fun.. this I think actually does need to be sorted first. Although I'm not quite sure I understand why it is actually a problem.
Plan B for this might be to go full bore on ww mutexes and also protect connectors with them. Then atomic updates would never have a need for mode_config.mutex, and the role of that lock would be restricted to some detect fun. Less complicated locking scheme, but even more code to audit. -Daniel
On Mon, May 26, 2014 at 11:24 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, May 26, 2014 at 08:48:52AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 6:40 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 02:30:09PM -0400, Rob Clark wrote:
One more time, with feeling..
Previous revision of series: http://lists.freedesktop.org/archives/dri-devel/2014-March/055806.html
And if you prefer, in git form: http://cgit.freedesktop.org/~robclark/linux/log/?h=cold-fusion git://people.freedesktop.org/~robclark/linux cold-fusion
This series does not include the actual atomic ioctl, but it does include all the needed infrastructure changes.
Compared to previous revision, I've split out drm_modeset_acquire_ctx from drm_atomic_state, so that we can ww acquire mode_config and crtc locks outside of atomic transactions. (Which keeps lockdep happy wrt. drm_modeset_lock_all().) I also got to the point in juggling things around where I wanted to let the compiler type checking do it's job, so 's/void *state/struct drm_atomic_state *state/g'.
At this point, I've tested this on i915 (few different generation laptops), radeon, and msm. And of course w/ ww debug (deadlock in- jection) enabled. So I think all the locking related paths should be covered.
I believe Thierry has tested a slighly older revision on tegra. I would of course appreciate more testing on other drivers for which I don't have the hw. But I think it is pretty much ready to go. I do still owe some docs updates, I will send some patches for that in the near future, but didn't want to hold up giving others a chance to start banging on this.
Ok, I've done a fairly cursory look at this only and tried to concentrate on grasping the high-level details. Patches contain a bunch of comments on the details, but here is the big stuff.
First things first there's a bunch of prep work and refactoring sprinkled throughout the series. Imo we should pick those out and rebase them on top of drm-next asap to get them in before the actual interfaces. I hope I've catched them all and commented everywhere about what imo is still missing for merging. With that out of the way the real atomic review below.
I like the overall design. It will be a royal pain to convert i915 over to this since thus far we've simply tucked away the staged state into obj->new_foo pointers. Which means we get to change the entire driver again ;-)
But I think the interfaces to driver and overall api design need some good polish:
- Imo way too much is done in driver callbacks, which then again call helpers. Parsing of the core properties for plane/crtcs should be done in the core imo. This way drivers only need to register ->set_prop callbacks if there's a driver-specific property they support.
Pretty universally, the approach I took was to provide default fxns (for ->atomic_xyz(), ->set_property(), ->create_state(), etc), and let drivers wrap them as needed. We might need to tweak drm_atomic_begin() a bit for the first driver that needs to wrap drm_atomic_state, but I guess we can tackle that as the need arises.
- Imo those obj->set_prop callbacks should take the object-specific drm_obj_state object, not the global atomic state structure. Will lead to _much_ less lookup code duplicated all over the place. If we then also add a state->obj pointer we could also ditch that parameter. Ofc obj would be specific to the state at hand.
The problem is if those callbacks need to take other driver shared locks.. that plus initially I was trying to keep state as opaque as possible (in case sooner or later i915 decided to re-implement :-P)
Eventually I decided keeping state opaque was too much pita, and that if driver wanted to do something different, it should instead subclass. So I guess these days they could chase [pc]state->state to get back at the global state.
I don't think keeping state opaque is a feature. Imo we _want_ to have some common structures to track the common set of properties, for otherwise insanity will rule. Drivers can add whatever they whish by subclassing the existing state structs and add whatever they need to track their state. I don't have any intentions to implement something newfangled/different for i915, but instead want to port our current infrastructure over to this.
Having clear prepare&check (we call it compute) and commit semantics is imo the right way to do kms. Think of i915 as the poc vehicle that gets to pay the bill of rewriting the driver twice.
So I'm advocating to move even further avoid from void* everywhere than you've moved already.
Wrt helpers I think at least for modeset operations we can pimp the crtc helpers and mostly reuse the hooks we already have, maybe augmented with per-object ->check hooks. But for nuclear pageflips I really don't see a chance but a single driver-gloabl ->commit (and fwiw check) hook. Maybe per-crtc at best, but that will already run into planes changing crtcs.
The per-object hooks will work with at least three display controller blocks that I know of (two in msm, and omapdrm), so I guess it is not *that* uncommon.
We'll need to split up an existing fxn or two so that other drivers can do an all-at-once commit more easily. That seems like a fairly small tweak, and I think it would be ok to merge along w/ i915 atomic support.
- One downside of that is that drivers won't be able to interfere the allocation step any more. I think we'd need an additional ->alloc_atomic_state call so that drivers can still easily subclass some objects with their own type.
the ->create_state() is already split out as a vfunc for planes and crtcs. Which I think should be enough to cover most of what drivers need. If driver needs to subclass global state too, then we need to split up drm_atomic_begin(), but I guess that should be a relatively small change that we can make when it becomes needed.
I guess i915 will need it, to keep track of shared resources like plls. Atm we carry them around in dev_priv, without any precompute/commit semantics. Still something that needs to be fixed ...
it would seem tempting to put in i915_atomic_state, but then how does it work with multiple parallel but independent atomic updates, or should that not be allowed?
Wrt ->create_state: I simply missed that in all the patches. But if you have that I don't quite understand why the various ->set_property callbacks have checks whether the object-specific state exists already and allocate it if not through the get_plane_state and similar functions. Imo that kind of stuff should be handled in the core. Error handling code is really hard to get correct ime, and we depend upon it's correctness here (at least with the current locking scheme) for ww mutexes to work.
Well, the current approach gave the driver more flexibility about what locks to grab, etc.. although I suppose some of this might be less needed now with primary planes. Formerly I had to fwd some properties from crtc to my own internal primary plane, which required the flexibility that the current approach gives.
- There's imo a bit a confusion between what's helper and what's driver api. My big gripe here is with the set_prop stuff which imo really should be core and not helpers. The default ->commit/check implementations otoh should be more clearly delineated as helper implementations that drivers can/should overwrite. I think we should split drm_atomic.c into drm_atomic.c (with the official pieces) and drm_atomic_helper.c (with the suggested standard/transitional implemenations for commit/check). Helper functions should have the drm_atomic_helper_ prefix.
Hmm, I think nearly *everything* could be overridden.. maybe it could be more clear what to override vs outright replace. But otoh I'm not quite sure yet what drivers will need to override (other than some obvious ones, which are already broken out into vfuncs, like {plane,crtc}->create_state() to handle driver custom properties).
Like I've said above we should imo try to unify the world a bit more again. And for the state tracking that should be possible, so I don't see a need to hand drivers too much rope.
The check/commit stuff otoh should be fully overrideable, since i915 will need that.
So I expect some tweaks as other drivers start adding native atomic support. I basically added what I needed for msm, and what I thought was pretty safe bet that other drivers would need. This at least gives us something other drivers could start trying to use. Then see what is missing and add it as we go. At least that was my line of thinking.
Like I've said I don't think there's much value beyong making crtc helpers atomic capable for modeset changes. Atomic pageflips pretty much need driver-specific magic (less so if there's a "GO" bit like on sane hardware).
right, there is a very tiny bit of driver magic in msm_atomic_commit()..
anyways, I don't disagree that we probably need to add something for some drivers to be able to hook in to ->atomic_commit() after locking magic but before loop over planes/crtcs. I think that would simply be addition to the api, ie. new fxn ptr a driver could populate. I wouldn't need it for msm.
- I also think we should split the vfuncs like we do with the crtc helpers. Imo the core interface should just be an ->alloc_state and ->set_prop on all kms objects, plus a global ->prep/check/commit at the driver level. The object-specific ->check/commit hooks otoh should be part of the atomic helper library and in some separate atomic_helper_funcs structure.
this is in fact the way it is, although 'struct drm_atomic_funcs' maybe should have "helper" in the name.
Yeah, I'm mostly just asking for sprinkling helper all over the stuff that clearly is helper code. And moving everything else into the core and making it non-overrideable.
Imo we could either extend the sturcture used by the crtc helpers (hackish imo) or change the type of that to make it clear it's for the crtc helpers and add a new pointer for atomic helpers. E.g. in i915 I expect that we'll implement our very own ->check/commit hooks using our own compute_config infrastructure. Maybe we could switch to the ->check hooks of the atomic helper eventually, but that will be a lot of work. And in any case we won't ever switch to the ->commit hook as you have it in your helpers since for truly atomic flips/modesets we need to interleave all the updates of all objects in various phases.
not sure if it would work for you, but in msm I have a per-crtc "don't actually commit yet" bit, which gets set for each crtc involved in atomic update. Although in my case it is mainly a matter of not touching the "GO" bits until everything is setup..
If not, I guess it shouldn't be a big deal to, for example, split up atomic_commit() into part that did the locking dance and then call vfunc ->atomic_apply_state() or something like that.
Basically figure out where you need to hook in for i915 as you add native atomic support. By the time you and maybe one or two other drivers have added atomic support we should have it pretty well nailed down. But until then, there might be some small adjustments here/there.
For modeset changes we need to munge everything through the full state precompute and then commit to hw machinery. Not yet fully there (since the shared dpll stuff doesn't work yet). For nuclear pageflips we're only just starting to merge all of Ville's infrastructure, but will probably be the same. So imo the only hand-off point between the core and drm is the single, global ->commit. ->check would be the identical code, except for the final commit.
All the ->set_prop callbacks would do nothing else but store data into structures.
I suspect I'll want to see which core properties change eventually.. when I have a chance to implement YUV and scaling, I'd want to flag that scaling/csc coefficients need updating, etc.
It doesn't seem unreasonable to pass plane/crtc state rather than global state. (Although we would need connector_state.. which is something I've not found a good use for yet.) But other than that, I'd kinda like to leave the ->set_property() as it is.
BR, -R
The patches are also rather big, which makes them a bit a pain to review. Matt's approach for the primary planes was much easier:
- Add the new core interfaces for the new world.
- Implement helpers for drivers to transition and convert drivers.
- Implement ioctls using the new driver interfaces.
not so far different from what I did. Although I suppose I could have split up adding atomic support for plane/crtc vs converting over existing ioctls to use it. Not sure if it is worth doing at this stage or not. Not really sure that it would help in sorting out what is helper vs not.
It's mostly for reviewing - silly me got lost countless times in your patches.
That approach would also help a lot in figuring out what's helper code and so optional, and what's core stuff.
The other big problem I see still is with the locking:
- Imo switching mode_config.mutex to a ww_mutex won't work. I know that the magic rules of w/w locking are _really_ tempting. But I don't think it will actually solve anything and instead only cause havoc.
are you actually getting lockdep splats? If so pls send them my way and I'll have a look.
In non-atomic cases we are still using these as bare mutex's (other than the one special case where we needed nested_lock before). So I think if it worked before, it should continue to work.
Didn't run it, but pretty sure. I think the other mail conversation cleared that up - mixing w/w nesting and static nesting just don't work: Static nesting means locks are filed into different (static) groups, w/w nesting means you have one single locking class, but ordered dynamically with the backoff magic.
I think we need ww mutexes for crtcs, but they will be fairly useless without ww mutexes on plane. Not for i915, but for all those drivers which can switch planes around between different crtcs. Imo we should do this as a first step (before getting all the atomic interfaces in), and pimp the set_plane locking a bit already like I've described in some mail.
There's a pile of state that's currently not really protected by anything in your patches. One piece is all the obj->state properties. Another one is state detected at runtime like e.g. whether a hdmi sink supports audio or what kind of link bw parameters a dp sink supports. Thus far this was all protected by mode_config.mutex, but we can't take this one in the generic atomic ioctls for pretty obvious reasons.
we are still taking mode_config.mutex in places where we did before (setcrtc, setplane, etc). So it shouldn't be a problem *yet*.
Runtime internal params about hw state, as opposed to things userspace is asking for, should perhaps not be in obj->state? Or maybe I'm misunderstanding you.
Afaict we copy obj->state and ->set_prop (at least in i915) also looks at a bunch of other connector state. And at that point we don't hold mode_config.mutex, and I couldn't spot anything else. Thus far all the ->set_prop stuff _did_ hold mode_config.mutex.
I think we need a new lock here, e.g. dev->mode_config.state_lock. It's going to be painful, since we need to roll this out over _all_ driver callbacks which can change the meaning of some property. E.g. all the ->detect callbacks in i915. The important part is that no one is allowed to do any costly operations why holding this lock (i.e. anything like reading EDIDs or doing load-detect), it is only for updating/reading this state.
- To avoid nasties with locking inversion between the plain mode_config.mutex, the state_mutex and the various ww mutexes we need to rather completely rework the locking sequence for atomic updates compared to what you have. Since in the driver's detect callbacks we first grab the mode_config.mutex and then potentiall ww mutexes (only i915) and maybe also the state_mutex (for updating autodetected properties). But for atomic updates we first need to grab the state_mutex (so that we can get at the current state) and then ww mutexes and the mode_config.mutex. And for the later we're not even good at predicting the order we want to acquire them.
so I think I need to understand the call sequence for the detect thing in i915, since atm I'm not quite sure what the problem is, and why it worked before..
Stage 1: Create the new state We grab _just_ the mode_config.state_mutex and allocate all the state objects. This will allow us to grab a copy of the current state of everything without races or inconsistencies.
As a consequence ->set_prop hooks may not touch anything outside of the date/state protected by the state_mutex.
Stage 2: check/commit stage We drop the state_lock since it would nest the wrong way round again with mode_config.mutex. We should have a complete copy of all state already, and if something races against us (2nd ioctl or a output probe call) we don't really care.
Only then will the check/commit hooks grab all the locks. For the mode_config.mutex we could add a bit to the global state the we need it (e.g. when the connector->crtc links change or for some other driver-private need).
That only leaves races with a 2nd concurrent atomic modeset ioctl. Which can be avoided by grabbing yet another lock, which we drop after the driver has acquired all real locks. For that we could mandate the ->check callback and require that it grabs all ww mutexes plus the mode_config.mutex.
For implementing this madness (if we agree it's the right approach) we can go step-by-step:
- Convert crtc->mutex to a ww mutex.
- Add plane ww mutexes, make set_plane locking more fine-grained.
- Add the mode_config.state_lock, roll it out across all driver's ->detect callbacks and add it to modeset_lock_all (so that set_prop calls dtrt).
- Use all this correctly in the atomic stuff.
Ok, that's the two big comments on this work from my side. Now reality check: How much off the mark am I?
well, I think in summary, it is (a) might need to split few things up or re-arrange things slightly as i915 and other drivers start adding native atomic support.. but this doesn't scare me too much, and I think we can take it as we go.
The problem I see here is massive churn over drivers. If we try to make set_prop hooks and similar stuff optional we'd avoid that, and would actually gain flexibility with fixing the interfaces down the road. If we enforce default callbacks for everyone that will only bloat diffs.
And (b) possible locking fun.. this I think actually does need to be sorted first. Although I'm not quite sure I understand why it is actually a problem.
Plan B for this might be to go full bore on ww mutexes and also protect connectors with them. Then atomic updates would never have a need for mode_config.mutex, and the role of that lock would be restricted to some detect fun. Less complicated locking scheme, but even more code to audit.
-Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Mon, May 26, 2014 at 6:12 PM, Rob Clark robdclark@gmail.com wrote:
On Mon, May 26, 2014 at 11:24 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, May 26, 2014 at 08:48:52AM -0400, Rob Clark wrote:
On Mon, May 26, 2014 at 6:40 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sat, May 24, 2014 at 02:30:09PM -0400, Rob Clark wrote:
One more time, with feeling..
Previous revision of series: http://lists.freedesktop.org/archives/dri-devel/2014-March/055806.html
And if you prefer, in git form: http://cgit.freedesktop.org/~robclark/linux/log/?h=cold-fusion git://people.freedesktop.org/~robclark/linux cold-fusion
This series does not include the actual atomic ioctl, but it does include all the needed infrastructure changes.
Compared to previous revision, I've split out drm_modeset_acquire_ctx from drm_atomic_state, so that we can ww acquire mode_config and crtc locks outside of atomic transactions. (Which keeps lockdep happy wrt. drm_modeset_lock_all().) I also got to the point in juggling things around where I wanted to let the compiler type checking do it's job, so 's/void *state/struct drm_atomic_state *state/g'.
At this point, I've tested this on i915 (few different generation laptops), radeon, and msm. And of course w/ ww debug (deadlock in- jection) enabled. So I think all the locking related paths should be covered.
I believe Thierry has tested a slighly older revision on tegra. I would of course appreciate more testing on other drivers for which I don't have the hw. But I think it is pretty much ready to go. I do still owe some docs updates, I will send some patches for that in the near future, but didn't want to hold up giving others a chance to start banging on this.
Ok, I've done a fairly cursory look at this only and tried to concentrate on grasping the high-level details. Patches contain a bunch of comments on the details, but here is the big stuff.
First things first there's a bunch of prep work and refactoring sprinkled throughout the series. Imo we should pick those out and rebase them on top of drm-next asap to get them in before the actual interfaces. I hope I've catched them all and commented everywhere about what imo is still missing for merging. With that out of the way the real atomic review below.
I like the overall design. It will be a royal pain to convert i915 over to this since thus far we've simply tucked away the staged state into obj->new_foo pointers. Which means we get to change the entire driver again ;-)
But I think the interfaces to driver and overall api design need some good polish:
- Imo way too much is done in driver callbacks, which then again call helpers. Parsing of the core properties for plane/crtcs should be done in the core imo. This way drivers only need to register ->set_prop callbacks if there's a driver-specific property they support.
Pretty universally, the approach I took was to provide default fxns (for ->atomic_xyz(), ->set_property(), ->create_state(), etc), and let drivers wrap them as needed. We might need to tweak drm_atomic_begin() a bit for the first driver that needs to wrap drm_atomic_state, but I guess we can tackle that as the need arises.
- Imo those obj->set_prop callbacks should take the object-specific drm_obj_state object, not the global atomic state structure. Will lead to _much_ less lookup code duplicated all over the place. If we then also add a state->obj pointer we could also ditch that parameter. Ofc obj would be specific to the state at hand.
The problem is if those callbacks need to take other driver shared locks.. that plus initially I was trying to keep state as opaque as possible (in case sooner or later i915 decided to re-implement :-P)
Eventually I decided keeping state opaque was too much pita, and that if driver wanted to do something different, it should instead subclass. So I guess these days they could chase [pc]state->state to get back at the global state.
I don't think keeping state opaque is a feature. Imo we _want_ to have some common structures to track the common set of properties, for otherwise insanity will rule. Drivers can add whatever they whish by subclassing the existing state structs and add whatever they need to track their state. I don't have any intentions to implement something newfangled/different for i915, but instead want to port our current infrastructure over to this.
Having clear prepare&check (we call it compute) and commit semantics is imo the right way to do kms. Think of i915 as the poc vehicle that gets to pay the bill of rewriting the driver twice.
So I'm advocating to move even further avoid from void* everywhere than you've moved already.
Wrt helpers I think at least for modeset operations we can pimp the crtc helpers and mostly reuse the hooks we already have, maybe augmented with per-object ->check hooks. But for nuclear pageflips I really don't see a chance but a single driver-gloabl ->commit (and fwiw check) hook. Maybe per-crtc at best, but that will already run into planes changing crtcs.
The per-object hooks will work with at least three display controller blocks that I know of (two in msm, and omapdrm), so I guess it is not *that* uncommon.
We'll need to split up an existing fxn or two so that other drivers can do an all-at-once commit more easily. That seems like a fairly small tweak, and I think it would be ok to merge along w/ i915 atomic support.
I'm working on a reply to your msm patch. I think it's better we'll move that discussion over there, where we have more context with actual code.
- One downside of that is that drivers won't be able to interfere the allocation step any more. I think we'd need an additional ->alloc_atomic_state call so that drivers can still easily subclass some objects with their own type.
the ->create_state() is already split out as a vfunc for planes and crtcs. Which I think should be enough to cover most of what drivers need. If driver needs to subclass global state too, then we need to split up drm_atomic_begin(), but I guess that should be a relatively small change that we can make when it becomes needed.
I guess i915 will need it, to keep track of shared resources like plls. Atm we carry them around in dev_priv, without any precompute/commit semantics. Still something that needs to be fixed ...
it would seem tempting to put in i915_atomic_state, but then how does it work with multiple parallel but independent atomic updates, or should that not be allowed?
Once we've entered the check/commit hooks all locks should have been acquired (or will get acquired at the beginning of the check hook), so there's no bothersome parallelism I think. Wrt the i915 implementation I think the approach I'll try is:
1. In the set_prop stage _only_ store data in the <obj>_state structures and nowhere else, not touching any data reachable from a 2nd thread.
2. In the check hook put all the state into <obj>->new_state pointers. At that point we hold all the locks so can do that.
3. Run the ->compute_config hooks over all relevant objects. We need to change all the code from directly looking into its object's structure to go through the new_state indirection (e.g. for figuring out whether we should enable hdmi audio or similar stuff).
4. Bail out and undo al new_foo pointer changes when only checking, run the full hw commit code when doing a real atomic update.
That approach should hug the current implementation closely wrt the ->new_foo handling, but still tightly integrate with the free-floating state construction atomic wants. The conversion will not be pretty though :(
Wrt ->create_state: I simply missed that in all the patches. But if you have that I don't quite understand why the various ->set_property callbacks have checks whether the object-specific state exists already and allocate it if not through the get_plane_state and similar functions. Imo that kind of stuff should be handled in the core. Error handling code is really hard to get correct ime, and we depend upon it's correctness here (at least with the current locking scheme) for ww mutexes to work.
Well, the current approach gave the driver more flexibility about what locks to grab, etc.. although I suppose some of this might be less needed now with primary planes. Formerly I had to fwd some properties from crtc to my own internal primary plane, which required the flexibility that the current approach gives.
I think if there's any private ww mutex locks drivers need to acquire, they should do that in their check/commit hook. Not while parsing properties. From that point on it's all under the control of the driver, so might not even need a full ww mutex, but a plain lock that nests within all the kms ww mutexes might be good enough.
- There's imo a bit a confusion between what's helper and what's driver api. My big gripe here is with the set_prop stuff which imo really should be core and not helpers. The default ->commit/check implementations otoh should be more clearly delineated as helper implementations that drivers can/should overwrite. I think we should split drm_atomic.c into drm_atomic.c (with the official pieces) and drm_atomic_helper.c (with the suggested standard/transitional implemenations for commit/check). Helper functions should have the drm_atomic_helper_ prefix.
Hmm, I think nearly *everything* could be overridden.. maybe it could be more clear what to override vs outright replace. But otoh I'm not quite sure yet what drivers will need to override (other than some obvious ones, which are already broken out into vfuncs, like {plane,crtc}->create_state() to handle driver custom properties).
Like I've said above we should imo try to unify the world a bit more again. And for the state tracking that should be possible, so I don't see a need to hand drivers too much rope.
The check/commit stuff otoh should be fully overrideable, since i915 will need that.
So I expect some tweaks as other drivers start adding native atomic support. I basically added what I needed for msm, and what I thought was pretty safe bet that other drivers would need. This at least gives us something other drivers could start trying to use. Then see what is missing and add it as we go. At least that was my line of thinking.
Like I've said I don't think there's much value beyong making crtc helpers atomic capable for modeset changes. Atomic pageflips pretty much need driver-specific magic (less so if there's a "GO" bit like on sane hardware).
right, there is a very tiny bit of driver magic in msm_atomic_commit()..
anyways, I don't disagree that we probably need to add something for some drivers to be able to hook in to ->atomic_commit() after locking magic but before loop over planes/crtcs. I think that would simply be addition to the api, ie. new fxn ptr a driver could populate. I wouldn't need it for msm.
If you mean new functions for the atomic helper, I agree (and don't really care). If you mean new functions for the core->driver interface (i.e. what i915 gets to implement on it's own) then if the check/commit hooks really should carry all the information we need. In the end this boils down to my request to more clearly separate core->driver interface from helper code.
- I also think we should split the vfuncs like we do with the crtc helpers. Imo the core interface should just be an ->alloc_state and ->set_prop on all kms objects, plus a global ->prep/check/commit at the driver level. The object-specific ->check/commit hooks otoh should be part of the atomic helper library and in some separate atomic_helper_funcs structure.
this is in fact the way it is, although 'struct drm_atomic_funcs' maybe should have "helper" in the name.
Yeah, I'm mostly just asking for sprinkling helper all over the stuff that clearly is helper code. And moving everything else into the core and making it non-overrideable.
Imo we could either extend the sturcture used by the crtc helpers (hackish imo) or change the type of that to make it clear it's for the crtc helpers and add a new pointer for atomic helpers. E.g. in i915 I expect that we'll implement our very own ->check/commit hooks using our own compute_config infrastructure. Maybe we could switch to the ->check hooks of the atomic helper eventually, but that will be a lot of work. And in any case we won't ever switch to the ->commit hook as you have it in your helpers since for truly atomic flips/modesets we need to interleave all the updates of all objects in various phases.
not sure if it would work for you, but in msm I have a per-crtc "don't actually commit yet" bit, which gets set for each crtc involved in atomic update. Although in my case it is mainly a matter of not touching the "GO" bits until everything is setup..
If not, I guess it shouldn't be a big deal to, for example, split up atomic_commit() into part that did the locking dance and then call vfunc ->atomic_apply_state() or something like that.
Basically figure out where you need to hook in for i915 as you add native atomic support. By the time you and maybe one or two other drivers have added atomic support we should have it pretty well nailed down. But until then, there might be some small adjustments here/there.
For modeset changes we need to munge everything through the full state precompute and then commit to hw machinery. Not yet fully there (since the shared dpll stuff doesn't work yet). For nuclear pageflips we're only just starting to merge all of Ville's infrastructure, but will probably be the same. So imo the only hand-off point between the core and drm is the single, global ->commit. ->check would be the identical code, except for the final commit.
All the ->set_prop callbacks would do nothing else but store data into structures.
I suspect I'll want to see which core properties change eventually.. when I have a chance to implement YUV and scaling, I'd want to flag that scaling/csc coefficients need updating, etc.
Hm, my idea was that drivers would implement any state diffing in their ->check hooks. Only then we - hold all the locks, so know nothing relevant will change - have the entire picture of all states (and the often depend on each another in funny ways).
So imo all the set_prop code should _only_ deal with creating the in-kernel representation of the desired state, not with checking it or computing a whole lot of derived date (like which parts need to be updated in hw).
It doesn't seem unreasonable to pass plane/crtc state rather than global state. (Although we would need connector_state.. which is something I've not found a good use for yet.) But other than that, I'd kinda like to leave the ->set_property() as it is.
Oh, we'll need that definitely. At least on i915 we have a pile of connector properties which we need to track somehow, and most of those need a full modeset to put them into the hw (mostly disable display pipe, frob hw, reenable display pipe with the same state). -Daniel
dri-devel@lists.freedesktop.org