<title>KMS API Functions</title>
 !Edrivers/gpu/drm/drm_crtc.c
     </sect2>
+    <sect2>
+      <title>KMS Locking</title>
+!Pdrivers/gpu/drm/drm_modeset_lock.c kms locking
+!Iinclude/drm/drm_modeset_lock.h
+!Edrivers/gpu/drm/drm_modeset_lock.c
+    </sect2>
   </sect1>
 
   <!-- Internals: kms helper functions -->
 
                drm_crtc.o drm_modes.o drm_edid.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_rect.o drm_vma_manager.o drm_flip_work.o \
+               drm_modeset_lock.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
 
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_modeset_lock.h>
 
 #include "drm_crtc_internal.h"
 
  */
 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;
 
-       mutex_lock(&dev->mode_config.connection_mutex);
+       mutex_lock(&config->mutex);
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-               mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
+       drm_modeset_acquire_init(ctx, 0);
+
+retry:
+       ret = drm_modeset_lock(&config->connection_mutex, ctx);
+       if (ret)
+               goto fail;
+       ret = drm_modeset_lock_all_crtcs(dev, ctx);
+       if (ret)
+               goto fail;
+
+       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;
+
+       drm_warn_on_modeset_not_all_locked(dev);
+
+       return;
+
+fail:
+       if (ret == -EDEADLK) {
+               drm_modeset_backoff(ctx);
+               goto retry;
+       }
 }
 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);
+       drm_modeset_acquire_fini(ctx);
 
-       mutex_unlock(&dev->mode_config.connection_mutex);
+       kfree(ctx);
 
        mutex_unlock(&dev->mode_config.mutex);
 }
                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.connection_mutex));
+       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
        WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
 }
 EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
 }
 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.
                              void *cursor,
                              const struct drm_crtc_funcs *funcs)
 {
+       struct drm_mode_config *config = &dev->mode_config;
        int ret;
 
        crtc->dev = dev;
        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)
 
        crtc->base.properties = &crtc->properties;
 
-       list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
-       dev->mode_config.num_crtc++;
+       list_add_tail(&crtc->head, &config->crtc_list);
+       config->num_crtc++;
 
        crtc->primary = primary;
        if (primary)
        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--;
        DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
 
        mutex_lock(&dev->mode_config.mutex);
-       mutex_lock(&dev->mode_config.connection_mutex);
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
 
        connector = drm_connector_find(dev, out_resp->connector_id);
        if (!connector) {
        out_resp->count_encoders = encoders_count;
 
 out:
-       mutex_unlock(&dev->mode_config.connection_mutex);
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
        mutex_unlock(&dev->mode_config.mutex);
 
        return ret;
                return -ENOENT;
        }
 
-       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;
                }
        }
 out:
-       mutex_unlock(&crtc->mutex);
+       drm_modeset_unlock(&crtc->mutex);
 
        return ret;
 
        if (!crtc)
                return -ENOENT;
 
-       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
                drm_framebuffer_unreference(fb);
        if (old_fb)
                drm_framebuffer_unreference(old_fb);
-       mutex_unlock(&crtc->mutex);
+       drm_modeset_unlock(&crtc->mutex);
 
        return ret;
 }
 void drm_mode_config_init(struct drm_device *dev)
 {
        mutex_init(&dev->mode_config.mutex);
-       mutex_init(&dev->mode_config.connection_mutex);
+       drm_modeset_lock_init(&dev->mode_config.connection_mutex);
        mutex_init(&dev->mode_config.idr_mutex);
        mutex_init(&dev->mode_config.fb_lock);
        INIT_LIST_HEAD(&dev->mode_config.fb_list);
        }
 
        idr_destroy(&dev->mode_config.crtc_idr);
+       drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
 }
 EXPORT_SYMBOL(drm_mode_config_cleanup);
 
        struct drm_device *dev = encoder->dev;
 
        WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
-       WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
-
+       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
        list_for_each_entry(connector, &dev->mode_config.connector_list, head)
                if (connector->encoder == encoder)
                        return true;
 
                if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
                        continue;
 
+               /* 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)) {
                        error = true;
                        continue;
 
--- /dev/null
+/*
+ * 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>
+
+/**
+ * DOC: kms locking
+ *
+ * As KMS moves toward more fine grained locking, and atomic ioctl where
+ * userspace can indirectly control locking order, it becomes necessary
+ * to use ww_mutex and acquire-contexts to avoid deadlocks.  But because
+ * the locking is more distributed around the driver code, we want a bit
+ * of extra utility/tracking out of our acquire-ctx.  This is provided
+ * by drm_modeset_lock / drm_modeset_acquire_ctx.
+ *
+ * For basic principles of ww_mutex, see: Documentation/ww-mutex-design.txt
+ *
+ * The basic usage pattern is to:
+ *
+ *     drm_modeset_acquire_init(&ctx)
+ *   retry:
+ *     foreach (lock in random_ordered_set_of_locks) {
+ *       ret = drm_modeset_lock(lock, &ctx)
+ *       if (ret == -EDEADLK) {
+ *          drm_modeset_backoff(&ctx);
+ *          goto retry;
+ *       }
+ *     }
+ *
+ *     ... do stuff ...
+ *
+ *     drm_modeset_drop_locks(&ctx);
+ *     drm_modeset_acquire_fini(&ctx);
+ */
+
+
+/**
+ * drm_modeset_acquire_init - initialize acquire context
+ * @ctx: the acquire context
+ * @flags: for future
+ */
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
+               uint32_t flags)
+{
+       ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
+       INIT_LIST_HEAD(&ctx->locked);
+}
+EXPORT_SYMBOL(drm_modeset_acquire_init);
+
+/**
+ * drm_modeset_acquire_fini - cleanup acquire context
+ * @ctx: the acquire context
+ */
+void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
+{
+       ww_acquire_fini(&ctx->ww_ctx);
+}
+EXPORT_SYMBOL(drm_modeset_acquire_fini);
+
+/**
+ * drm_modeset_drop_locks - drop all locks
+ * @ctx: the acquire context
+ *
+ * Drop all locks currently held against this acquire context.
+ */
+void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
+{
+       WARN_ON(ctx->contended);
+       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);
+       }
+}
+EXPORT_SYMBOL(drm_modeset_drop_locks);
+
+static inline int modeset_lock(struct drm_modeset_lock *lock,
+               struct drm_modeset_acquire_ctx *ctx,
+               bool interruptible, bool slow)
+{
+       int ret;
+
+       WARN_ON(ctx->contended);
+
+       if (interruptible && slow) {
+               ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
+       } else if (interruptible) {
+               ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
+       } else if (slow) {
+               ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
+               ret = 0;
+       } else {
+               ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
+       }
+       if (!ret) {
+               WARN_ON(!list_empty(&lock->head));
+               list_add(&lock->head, &ctx->locked);
+       } else if (ret == -EALREADY) {
+               /* we already hold the lock.. this is fine.  For atomic
+                * we will need to be able to drm_modeset_lock() things
+                * without having to keep track of what is already locked
+                * or not.
+                */
+               ret = 0;
+       } else if (ret == -EDEADLK) {
+               ctx->contended = lock;
+       }
+
+       return ret;
+}
+
+static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
+               bool interruptible)
+{
+       struct drm_modeset_lock *contended = ctx->contended;
+
+       ctx->contended = NULL;
+
+       if (WARN_ON(!contended))
+               return 0;
+
+       drm_modeset_drop_locks(ctx);
+
+       return modeset_lock(contended, ctx, interruptible, true);
+}
+
+/**
+ * drm_modeset_backoff - deadlock avoidance backoff
+ * @ctx: the acquire context
+ *
+ * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
+ * you must call this function to drop all currently held locks and
+ * block until the contended lock becomes available.
+ */
+void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
+{
+       modeset_backoff(ctx, false);
+}
+EXPORT_SYMBOL(drm_modeset_backoff);
+
+/**
+ * drm_modeset_backoff_interruptible - deadlock avoidance backoff
+ * @ctx: the acquire context
+ *
+ * Interruptible version of drm_modeset_backoff()
+ */
+int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
+{
+       return modeset_backoff(ctx, true);
+}
+EXPORT_SYMBOL(drm_modeset_backoff_interruptible);
+
+/**
+ * drm_modeset_lock - take modeset lock
+ * @lock: lock to take
+ * @ctx: acquire ctx
+ *
+ * If ctx is not NULL, then its ww acquire context is used and the
+ * lock will be tracked by the context and can be released by calling
+ * drm_modeset_drop_locks().  If -EDEADLK is returned, this means a
+ * deadlock scenario has been detected and it is an error to attempt
+ * to take any more locks without first calling drm_modeset_backoff().
+ */
+int drm_modeset_lock(struct drm_modeset_lock *lock,
+               struct drm_modeset_acquire_ctx *ctx)
+{
+       if (ctx)
+               return modeset_lock(lock, ctx, false, false);
+
+       ww_mutex_lock(&lock->mutex, NULL);
+       return 0;
+}
+EXPORT_SYMBOL(drm_modeset_lock);
+
+/**
+ * drm_modeset_lock_interruptible - take modeset lock
+ * @lock: lock to take
+ * @ctx: acquire ctx
+ *
+ * Interruptible version of 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, false);
+
+       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);
+       ww_mutex_unlock(&lock->mutex);
+}
+EXPORT_SYMBOL(drm_modeset_unlock);
+
+/* Temporary.. until we have sufficiently fine grained locking, there
+ * are a couple scenarios where it is convenient to grab all crtc locks.
+ * It is planned to remove this:
+ */
+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);
 
         * need to grab the connection_mutex here to be able to make these
         * checks.
         */
-       WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
+       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
 
        list_for_each_entry(connector, &dev->mode_config.connector_list, head)
                if (connector->encoder && connector->encoder->crtc == crtc) {
 
        enum intel_display_power_domain power_domain;
        enum drm_connector_status status;
        struct intel_load_detect_pipe tmp;
+       struct drm_modeset_acquire_ctx ctx;
 
        intel_runtime_pm_get(dev_priv);
 
        }
 
        /* for pre-945g platforms use load detect */
-       if (intel_get_load_detect_pipe(connector, NULL, &tmp)) {
+       if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) {
                if (intel_crt_detect_ddc(connector))
                        status = connector_status_connected;
                else
                        status = intel_crt_load_detect(crt);
-               intel_release_load_detect_pipe(connector, &tmp);
+               intel_release_load_detect_pipe(connector, &tmp, &ctx);
        } else
                status = connector_status_unknown;
 
 
        for_each_crtc(dev, crtc) {
                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
                                                               crtc->primary->fb,
                                                               crtc->x,
                                                               crtc->y);
-               mutex_unlock(&crtc->mutex);
+               drm_modeset_unlock(&crtc->mutex);
        }
 }
 
 
 bool intel_get_load_detect_pipe(struct drm_connector *connector,
                                struct drm_display_mode *mode,
-                               struct intel_load_detect_pipe *old)
+                               struct intel_load_detect_pipe *old,
+                               struct drm_modeset_acquire_ctx *ctx)
 {
        struct intel_crtc *intel_crtc;
        struct intel_encoder *intel_encoder =
        struct drm_crtc *crtc = NULL;
        struct drm_device *dev = encoder->dev;
        struct drm_framebuffer *fb;
-       int i = -1;
+       struct drm_mode_config *config = &dev->mode_config;
+       int ret, i = -1;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
                      connector->base.id, connector->name,
                      encoder->base.id, encoder->name);
 
-       mutex_lock(&dev->mode_config.connection_mutex);
+       drm_modeset_acquire_init(ctx, 0);
+
+retry:
+       ret = drm_modeset_lock(&config->connection_mutex, ctx);
+       if (ret)
+               goto fail_unlock;
 
        /*
         * Algorithm gets a little messy:
        if (encoder->crtc) {
                crtc = encoder->crtc;
 
-               mutex_lock(&crtc->mutex);
+               ret = drm_modeset_lock(&crtc->mutex, ctx);
+               if (ret)
+                       goto fail_unlock;
 
                old->dpms_mode = connector->dpms;
                old->load_detect_temp = false;
         */
        if (!crtc) {
                DRM_DEBUG_KMS("no pipe available for load-detect\n");
-               goto fail_unlock_connector;
+               goto fail_unlock;
        }
 
-       mutex_lock(&crtc->mutex);
+       ret = drm_modeset_lock(&crtc->mutex, ctx);
+       if (ret)
+               goto fail_unlock;
        intel_encoder->new_crtc = to_intel_crtc(crtc);
        to_intel_connector(connector)->new_encoder = intel_encoder;
 
                intel_crtc->new_config = &intel_crtc->config;
        else
                intel_crtc->new_config = NULL;
-       mutex_unlock(&crtc->mutex);
-fail_unlock_connector:
-       mutex_unlock(&dev->mode_config.connection_mutex);
+fail_unlock:
+       if (ret == -EDEADLK) {
+               drm_modeset_backoff(ctx);
+               goto retry;
+       }
+
+       drm_modeset_drop_locks(ctx);
+       drm_modeset_acquire_fini(ctx);
 
        return false;
 }
 
 void intel_release_load_detect_pipe(struct drm_connector *connector,
-                                   struct intel_load_detect_pipe *old)
+                                   struct intel_load_detect_pipe *old,
+                                   struct drm_modeset_acquire_ctx *ctx)
 {
        struct intel_encoder *intel_encoder =
                intel_attached_encoder(connector);
                        drm_framebuffer_unreference(old->release_fb);
                }
 
-               mutex_unlock(&crtc->mutex);
-               mutex_unlock(&connector->dev->mode_config.connection_mutex);
+               goto unlock;
                return;
        }
 
        if (old->dpms_mode != DRM_MODE_DPMS_ON)
                connector->funcs->dpms(connector, old->dpms_mode);
 
-       mutex_unlock(&crtc->mutex);
-       mutex_unlock(&connector->dev->mode_config.connection_mutex);
+unlock:
+       drm_modeset_drop_locks(ctx);
+       drm_modeset_acquire_fini(ctx);
 }
 
 static int i9xx_pll_refclk(struct drm_device *dev,
        struct drm_encoder *encoder = connector->base.encoder;
        struct drm_device *dev = connector->base.dev;
 
-       WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
+       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
 
        if (!encoder)
                return INVALID_PIPE;
        struct intel_connector *connector;
        struct drm_connector *crt = NULL;
        struct intel_load_detect_pipe load_detect_temp;
+       struct drm_modeset_acquire_ctx ctx;
 
        /* We can't just switch on the pipe A, we need to set things up with a
         * proper mode and output configuration. As a gross hack, enable pipe A
        if (!crt)
                return;
 
-       if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp))
-               intel_release_load_detect_pipe(crt, &load_detect_temp);
+       if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, &ctx))
+               intel_release_load_detect_pipe(crt, &load_detect_temp, &ctx);
 
 
 }
 
        u32 pp;
        u32 pp_stat_reg, pp_ctrl_reg;
 
-       WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
+       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
 
        if (!intel_dp->want_panel_vdd && edp_have_panel_vdd(intel_dp)) {
                struct intel_digital_port *intel_dig_port =
                                                 struct intel_dp, panel_vdd_work);
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
 
-       mutex_lock(&dev->mode_config.connection_mutex);
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
        edp_panel_vdd_off_sync(intel_dp);
-       mutex_unlock(&dev->mode_config.connection_mutex);
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
 }
 
 static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
        drm_encoder_cleanup(encoder);
        if (is_edp(intel_dp)) {
                cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
-               mutex_lock(&dev->mode_config.connection_mutex);
+               drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
                edp_panel_vdd_off_sync(intel_dp);
-               mutex_unlock(&dev->mode_config.connection_mutex);
+               drm_modeset_unlock(&dev->mode_config.connection_mutex);
        }
        kfree(intel_dig_port);
 }
                drm_dp_aux_unregister(&intel_dp->aux);
                if (is_edp(intel_dp)) {
                        cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
-                       mutex_lock(&dev->mode_config.connection_mutex);
+                       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
                        edp_panel_vdd_off_sync(intel_dp);
-                       mutex_unlock(&dev->mode_config.connection_mutex);
+                       drm_modeset_unlock(&dev->mode_config.connection_mutex);
                }
                drm_sysfs_connector_remove(connector);
                drm_connector_cleanup(connector);
 
                         struct intel_digital_port *dport);
 bool intel_get_load_detect_pipe(struct drm_connector *connector,
                                struct drm_display_mode *mode,
-                               struct intel_load_detect_pipe *old);
+                               struct intel_load_detect_pipe *old,
+                               struct drm_modeset_acquire_ctx *ctx);
 void intel_release_load_detect_pipe(struct drm_connector *connector,
-                                   struct intel_load_detect_pipe *old);
+                                   struct intel_load_detect_pipe *old,
+                                   struct drm_modeset_acquire_ctx *ctx);
 int intel_pin_and_fence_fb_obj(struct drm_device *dev,
                               struct drm_i915_gem_object *obj,
                               struct intel_engine_cs *pipelined);
 
        if (bclp > 255)
                return ASLC_BACKLIGHT_FAILED;
 
-       mutex_lock(&dev->mode_config.connection_mutex);
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
 
        /*
         * Update backlight on all connectors that support backlight (usually
                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.connection_mutex);
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
 
 
        return 0;
 
        u32 swidth, swidthsw, sheight, ostride;
 
        BUG_ON(!mutex_is_locked(&dev->struct_mutex));
-       BUG_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
+       BUG_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
        BUG_ON(!overlay);
 
        ret = intel_overlay_release_old_vid(overlay);
        int ret;
 
        BUG_ON(!mutex_is_locked(&dev->struct_mutex));
-       BUG_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
+       BUG_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
 
        ret = intel_overlay_recover_from_interrupt(overlay);
        if (ret != 0)
 
        struct intel_connector *connector = bl_get_data(bd);
        struct drm_device *dev = connector->base.dev;
 
-       mutex_lock(&dev->mode_config.connection_mutex);
+       drm_modeset_lock(&dev->mode_config.connection_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.connection_mutex);
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
        return 0;
 }
 
        int ret;
 
        intel_runtime_pm_get(dev_priv);
-       mutex_lock(&dev->mode_config.connection_mutex);
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
        ret = intel_panel_get_backlight(connector);
-       mutex_unlock(&dev->mode_config.connection_mutex);
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
        intel_runtime_pm_put(dev_priv);
 
        return ret;
 
        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)
 
 
        if (force) {
                struct intel_load_detect_pipe tmp;
+               struct drm_modeset_acquire_ctx ctx;
 
-               if (intel_get_load_detect_pipe(connector, &mode, &tmp)) {
+               if (intel_get_load_detect_pipe(connector, &mode, &tmp, &ctx)) {
                        type = intel_tv_detect_type(intel_tv, connector);
-                       intel_release_load_detect_pipe(connector, &tmp);
+                       intel_release_load_detect_pipe(connector, &tmp, &ctx);
                } else
                        return connector_status_unknown;
        } else
 
        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);
         * 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();
 
        /*
 
 out:
        dispc_runtime_put();
-       mutex_unlock(&crtc->mutex);
+       drm_modeset_unlock(&crtc->mutex);
 }
 
 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)
 
         * 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 */
        ret = 0;
 out:
        drm_modeset_unlock_all(dev_priv->dev);
-       mutex_lock(&crtc->mutex);
+       drm_modeset_lock(&crtc->mutex, NULL);
 
        return ret;
 }
         * 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,
                                   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;
 }
 
        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;
 
 #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;
        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;
  * 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
         * 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;
 
  */
 struct drm_mode_config {
        struct mutex mutex; /* protects configuration (mode lists etc.) */
-       struct mutex connection_mutex; /* protects connector->encoder and encoder->crtc links */
+       struct drm_modeset_lock connection_mutex; /* protects connector->encoder and encoder->crtc links */
+       struct drm_modeset_acquire_ctx *acquire_ctx; /* for legacy _lock_all() / _unlock_all() */
        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 */
        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,
 
--- /dev/null
+/*
+ * 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_lock;
+
+/**
+ * drm_modeset_acquire_ctx - locking context (see ww_acquire_ctx)
+ * @ww_ctx: base acquire ctx
+ * @contended: used internally for -EDEADLK handling
+ * @locked: list of held locks
+ *
+ * Each thread competing for a set of locks must use one acquire
+ * ctx.  And if any lock fxn returns -EDEADLK, it must backoff and
+ * retry.
+ */
+struct drm_modeset_acquire_ctx {
+
+       struct ww_acquire_ctx ww_ctx;
+
+       /**
+        * Contended lock: if a lock is contended you should only call
+        * drm_modeset_backoff() which drops locks and slow-locks the
+        * contended lock.
+        */
+       struct drm_modeset_lock *contended;
+
+       /**
+        * list of held locks (drm_modeset_lock)
+        */
+       struct list_head locked;
+};
+
+/**
+ * drm_modeset_lock - used for locking modeset resources.
+ * @mutex: resource locking
+ * @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;
+
+       /**
+        * 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;
+};
+
+extern struct ww_class crtc_ww_class;
+
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
+               uint32_t flags);
+void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
+void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx);
+void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx);
+int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx);
+
+/**
+ * drm_modeset_lock_init - initialize lock
+ * @lock: lock to init
+ */
+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);
+}
+
+/**
+ * drm_modeset_lock_fini - cleanup lock
+ * @lock: lock to cleanup
+ */
+static inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock)
+{
+       WARN_ON(!list_empty(&lock->head));
+}
+
+/**
+ * drm_modeset_is_locked - equivalent to mutex_is_locked()
+ * @lock: lock to check
+ */
+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_ */