diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index b9ae4280de9d..c2c6d61d30cf 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -16,7 +16,8 @@ drm-y       :=	drm_auth.o drm_bufs.o drm_cache.o \
 		drm_framebuffer.o drm_connector.o drm_blend.o \
 		drm_encoder.o drm_mode_object.o drm_property.o \
 		drm_plane.o drm_color_mgmt.o drm_print.o \
-		drm_dumb_buffers.o drm_mode_config.o
+		drm_dumb_buffers.o drm_mode_config.o \
+		drm_lease.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_auth.c b/drivers/gpu/drm/drm_auth.c
index 6b143514a566..1db4f63860d1 100644
--- a/drivers/gpu/drm/drm_auth.c
+++ b/drivers/gpu/drm/drm_auth.c
@@ -31,6 +31,7 @@
 #include <drm/drmP.h>
 #include "drm_internal.h"
 #include "drm_legacy.h"
+#include <drm/drm_lease.h>
 
 /**
  * DOC: master and authentication
@@ -93,7 +94,7 @@ int drm_authmagic(struct drm_device *dev, void *data,
 	return file ? 0 : -EINVAL;
 }
 
-static struct drm_master *drm_master_create(struct drm_device *dev)
+struct drm_master *drm_master_create(struct drm_device *dev)
 {
 	struct drm_master *master;
 
@@ -107,6 +108,14 @@ static struct drm_master *drm_master_create(struct drm_device *dev)
 	idr_init(&master->magic_map);
 	master->dev = dev;
 
+	/* initialize the tree of output resource lessees */
+	master->lessor = NULL;
+	master->lessee_id = 0;
+	INIT_LIST_HEAD(&master->lessees);
+	INIT_LIST_HEAD(&master->lessee_list);
+	idr_init(&master->leases);
+	idr_init(&master->lessee_idr);
+
 	return master;
 }
 
@@ -189,6 +198,12 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
 		goto out_unlock;
 	}
 
+	if (file_priv->master->lessor != NULL) {
+		DRM_DEBUG_LEASE("Attempt to set lessee %d as master\n", file_priv->master->lessee_id);
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
 	ret = drm_set_master(dev, file_priv, false);
 out_unlock:
 	mutex_unlock(&dev->master_mutex);
@@ -310,12 +325,17 @@ static void drm_master_destroy(struct kref *kref)
 	struct drm_master *master = container_of(kref, struct drm_master, refcount);
 	struct drm_device *dev = master->dev;
 
+	drm_lease_destroy(master);
+
 	if (dev->driver->master_destroy)
 		dev->driver->master_destroy(dev, master);
 
 	drm_legacy_master_rmmaps(dev, master);
 
 	idr_destroy(&master->magic_map);
+
+	idr_destroy(&master->lessee_idr);
+
 	kfree(master->unique);
 	kfree(master);
 }
diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c
new file mode 100644
index 000000000000..782005c7706d
--- /dev/null
+++ b/drivers/gpu/drm/drm_lease.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#include <drm/drmP.h>
+#include "drm_internal.h"
+#include "drm_legacy.h"
+#include "drm_crtc_internal.h"
+#include <drm/drm_lease.h>
+#include <drm/drm_auth.h>
+
+#define drm_for_each_lessee(lessee, lessor) \
+	list_for_each_entry((lessee), &(lessor)->lessees, lessee_list)
+
+/*
+ * drm_lease_owner - return ancestor owner drm_master
+ * @master: drm_master somewhere within tree of lessees and lessors
+ *
+ * RETURN:
+ *
+ * drm_master at the top of the tree (i.e, with lessor NULL
+ */
+struct drm_master *drm_lease_owner(struct drm_master *master) {
+	while (master->lessor != NULL)
+		master = master->lessor;
+	return master;
+}
+EXPORT_SYMBOL(drm_lease_owner);
+
+/**
+ * _drm_lease_find: Look up an object and check who holds the lease
+ * @master: the master to look in
+ * @id: the id to check
+ *
+ * Check if 'master' holds a lease (or owns) 'id' and has not
+ * sub-leased it to another master. Return value:
+ *
+ *	object		'master' holds lease and has not sub-leased
+ *	-EACCESS	Some other master holds the lease
+ *	-ENOENT		'id' is not a valid DRM object for this device
+ *	-EBUSY		'master' holds lease, but has sub-leased
+ *
+ * Must be called with mode_config.idr_mutex held
+ */
+static struct drm_mode_object *_drm_lease_find(struct drm_master *master, int id)
+{
+	struct drm_master *lessee;
+	struct drm_mode_object *object;
+
+	DRM_DEBUG_LEASE("master %d object %d\n", master->lessee_id, id);
+	BUG_ON(__mutex_owner(&master->dev->mode_config.idr_mutex) != current);
+
+	object = idr_find(&master->dev->mode_config.crtc_idr, id);
+	if (IS_ERR_OR_NULL(object)) {
+		DRM_DEBUG_LEASE("no such object %d\n", id);
+		return ERR_PTR(-ENOENT);
+	}
+
+	if (master->lessor) {
+		/*
+		 * If the lessor is not the owner, then make sure they
+		 * are the current lease holder for the object
+		 */
+		if (idr_find(&master->leases, id) == NULL) {
+			DRM_DEBUG_LEASE("%d is not lessee for %d\n", master->lessee_id, id);
+			return ERR_PTR(-EACCES);
+		}
+	}
+
+	/* Now make sure no other lessee already holds a lease which masks this object */
+	drm_for_each_lessee(lessee, master) {
+		if (lessee->mask_lease && idr_find(&lessee->leases, id) != NULL) {
+			DRM_DEBUG_LEASE("id %d is held by %d\n", id, lessee->lessee_id);
+			return ERR_PTR(-EBUSY);
+		}
+	}
+
+	DRM_DEBUG_LEASE("id %d is held by us\n", id);
+	return object;
+}
+
+struct drm_mode_object *drm_lease_find(struct drm_master *master, int id)
+{
+	struct drm_mode_object *object;
+
+	BUG_ON(__mutex_owner(&master->dev->mode_config.idr_mutex) == current);
+	mutex_lock(&master->dev->mode_config.idr_mutex);
+	object = _drm_lease_find(master, id);
+	mutex_unlock(&master->dev->mode_config.idr_mutex);
+	if (IS_ERR(object))
+		DRM_DEBUG_LEASE("find %d from master %d failed %ld\n", id, master->lessee_id, PTR_ERR(object));
+	else
+		DRM_DEBUG_LEASE("find %d from master %d %p\n", id, master->lessee_id, object);
+	return object;
+}
+EXPORT_SYMBOL(drm_lease_find);
+
+static inline int _drm_lease_check(struct drm_master *master, int id) {
+	struct drm_mode_object *object = _drm_lease_find(master, id);
+	if (IS_ERR(object))
+		return PTR_ERR(object);
+	return 0;
+}
+
+/**
+ * drm_lease_hoder - find current lease holder for a resource
+ * @master: Some drm_master for the device
+ * @id: the resource id
+ *
+ * Walks down the lease holder tree looking for the drm_master who holds
+ * a lease and is not sub-leasing to another drm_master.
+ *
+ * Returns the owner if no lessee holds the resource
+ */
+static struct drm_master *_drm_lease_holder(struct drm_master *master, int id)
+{
+	struct drm_master *lessor = drm_lease_owner(master);
+
+	for (;;) {
+		struct drm_master *lessee;
+		struct drm_master *found;
+
+		found = NULL;
+		drm_for_each_lessee(lessee, lessor) {
+			if (idr_find(&lessee->leases, id) != NULL) {
+				found = lessee;
+				break;
+			}
+		}
+		if (found == NULL)
+			return lessor;
+		lessor = found;
+	}
+}
+
+/**
+ * drm_lessee_is_descendant - check hierarchy structure
+ * @lessee: lessee
+ * @lessor: lessor
+ *
+ * Checks whether 'lessor' is in control of 'lessee' resources, which
+ * happens when lessee is lessor or lessee is a lessee of lessor or
+ * one of lessors lessees (recursively)
+ */
+static bool
+drm_lessee_is_descendant(struct drm_master *lessee, struct drm_master *lessor)
+{
+	/* Walk up the chain to the owner */
+	while (lessee) {
+
+		/* return true if we found lessor on the way up */
+		if (lessee == lessor)
+			return true;
+		lessee = lessee->lessor;
+	}
+
+	/* return false if lessor is not 'above' lessee */
+	return false;
+}
+
+/**
+ * drm_lessee_get - Get a reference on a lessee_id in a lease sub-tree
+ * @top: the top of the sub-tree to search in
+ * @id: the lessee_id to look for
+ */
+static struct drm_master *
+drm_lessee_get(struct drm_master *top, int lessee_id)
+{
+	struct drm_master *owner;
+	struct drm_master *lessee;
+
+	BUG_ON(__mutex_owner(&top->dev->master_mutex) != current);
+
+	DRM_DEBUG_LEASE("%d\n", lessee_id);
+
+	owner = drm_lease_owner(top);
+	if (lessee_id == 0)
+		lessee = owner;
+	else
+		lessee = idr_find(&owner->lessee_idr, lessee_id);
+
+	DRM_DEBUG_LEASE("%d -> %p (top %p)\n", lessee_id, lessee, top);
+	if (!lessee || !drm_lessee_is_descendant(lessee, top)) {
+		DRM_DEBUG_LEASE("null lessee or not descendant\n");
+		return NULL;
+	}
+
+	return drm_master_get(lessee);
+}
+
+enum drm_lease_walk {
+	drm_lease_walk_stop,
+	drm_lease_walk_dont_descend,
+	drm_lease_walk_descend
+};
+
+/**
+ * drm_lease_walk - walk a subtree of leases
+ * @master: the top of the sub-tree to walk
+ * @func: the function to call on each lease
+ * @closure: data to pass to the function
+ */
+static struct drm_master *
+drm_lease_walk(struct drm_master *top,
+	       enum drm_lease_walk (*func)(struct drm_master *lessee, void *closure),
+	       void *closure)
+{
+	struct drm_master *master = top;
+	enum drm_lease_walk result;
+
+	for (;;) {
+		result = (*func)(master, closure);
+		if (result == drm_lease_walk_stop)
+			return master;
+		if (result == drm_lease_walk_descend && !list_empty(&master->lessees)) {
+			master = list_first_entry(&master->lessees, struct drm_master, lessee_list);
+		} else {
+			while (master != top && list_is_last(&master->lessee_list,
+							     &master->lessor->lessees))
+				master = master->lessor;
+			if (master == top)
+				return NULL;
+			master = list_next_entry(master, lessee_list);
+		}
+	}
+}
+
+/**
+ * drm_lease_filter_crtcs - remove unleased crtcs from set
+ * @master: drm_master of requestor
+ * @crtcs: bitmask of crtcs to check
+ */
+uint32_t drm_lease_filter_crtcs(struct drm_master *master, uint32_t crtcs)
+{
+	struct drm_device *dev = master->dev;
+	struct drm_crtc *crtc;
+	int count;
+
+	BUG_ON(__mutex_owner(&master->dev->mode_config.idr_mutex) == current);
+	mutex_lock(&master->dev->mode_config.idr_mutex);
+	count = -1;
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+		uint32_t	mask = 1ul << count;
+		count++;
+
+		if ((crtcs & mask) != 0)
+			if (IS_ERR(_drm_lease_find(master, crtc->base.id)))
+				crtcs &= ~mask;
+	}
+	mutex_unlock(&master->dev->mode_config.idr_mutex);
+	return crtcs;
+}
+EXPORT_SYMBOL(drm_lease_filter_crtcs);
+
+/**
+ * drm_lease_filter_encoders - remove unleased encoders from set
+ * @master: drm_master of requestor
+ * @encoders: bitmask of encoders to check
+ */
+uint32_t drm_lease_filter_encoders(struct drm_master *master, uint32_t encoders)
+{
+	struct drm_device *dev = master->dev;
+	struct drm_encoder *encoder;
+	int count;
+
+	BUG_ON(__mutex_owner(&master->dev->mode_config.idr_mutex) == current);
+	mutex_lock(&master->dev->mode_config.idr_mutex);
+	count = -1;
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		uint32_t	mask = 1ul << count;
+		count++;
+
+		if ((encoders & mask) != 0)
+			if (IS_ERR(_drm_lease_find(master, encoder->base.id)))
+				encoders &= ~mask;
+	}
+	mutex_unlock(&master->dev->mode_config.idr_mutex);
+	return encoders;
+}
+EXPORT_SYMBOL(drm_lease_filter_encoders);
+
+/*
+ * drm_lease_create - create a new drm_master with leased objects
+ * @lessor: lease holder (or owner) of objects
+ * @leases: objects to lease to the new drm_master
+ *
+ * Uses drm_master_create to allocate a new drm_master, then checks to
+ * make sure all of the desired objects can be leased, atomically
+ * leasing them to the new drmmaster.
+ *
+ * 	ERR_PTR(-EACCESS)	some other master holds the title to any object
+ * 	ERR_PTR(-ENOENT)	some object is not a valid DRM object for this device
+ * 	ERR_PTR(-EBUSY)		some other lessee holds title to this object
+ *	ERR_PTR(-EEXIST)	same object specified more than once in the provided list
+ *	ERR_PTR(-ENOMEM)	allocation failed
+ */
+static struct drm_master *drm_lease_create(struct drm_master *lessor, bool mask_lease, struct idr *leases)
+{
+	int error;
+	struct drm_master *lessee;
+	int object;
+	int id;
+	void *entry;
+
+	DRM_DEBUG_LEASE("lessor %d\n", lessor->lessee_id);
+
+	BUG_ON(__mutex_owner(&lessor->dev->master_mutex) != current);
+
+	lessee = drm_master_create(lessor->dev);
+	if (!lessee) {
+		DRM_DEBUG_LEASE("drm_master_create failed\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	/* Insert the new lessee into the tree */
+
+	id = idr_alloc(&(drm_lease_owner(lessor)->lessee_idr), lessee, 1, 0, GFP_KERNEL);
+	if (id < 0) {
+		DRM_DEBUG_LEASE("lessee_id allocation failed %d\n", id);
+		drm_master_put(&lessee);
+		return ERR_PTR(id);
+	}
+
+	lessee->lessee_id = id;
+	lessee->lessor = drm_master_get(lessor);
+	list_add_tail(&lessee->lessee_list, &lessor->lessees);
+
+	/* Lock the mode object mutex to make the check and allocation atomic */
+
+	mutex_lock(&lessor->dev->mode_config.idr_mutex);
+	idr_for_each_entry(leases, entry, object) {
+		error = _drm_lease_check(lessor, object);
+		if (error) {
+			mutex_unlock(&lessor->dev->mode_config.idr_mutex);
+			drm_master_put(&lessee);
+			DRM_DEBUG_LEASE("_drm_lease_check %d failed %d\n", object, error);
+			return ERR_PTR(error);
+		}
+	}
+
+	/* Move the leases over */
+	lessee->leases = *leases;
+	lessee->mask_lease = mask_lease;
+	idr_init(leases);
+	mutex_unlock(&lessor->dev->mode_config.idr_mutex);
+
+	DRM_DEBUG_LEASE("new lessee %d %p, lessor %d %p\n", lessee->lessee_id, lessee, lessor->lessee_id, lessor);
+
+	return lessee;
+}
+
+static enum drm_lease_walk drm_lease_validate(struct drm_master *lessee, void *v_lessor)
+{
+	struct drm_master *lessor = v_lessor;
+	struct drm_mode_object *entry;
+	int object;
+	int to_remove;
+	bool any_removed = false;
+
+	if (lessee == lessor)
+		return drm_lease_walk_descend;
+
+	for (;;) {
+		to_remove = 0;
+
+		/* it's not safe to call idr_remove inside the idr_for_each_entry loop,
+		 * so find the first invalid object, remove that, and then try again
+		 */
+		idr_for_each_entry(&lessee->leases, entry, object) {
+			if (idr_find(&lessor->leases, object) == NULL) {
+				to_remove = object;
+				break;
+			}
+		}
+		if (to_remove == 0)
+			break;
+		idr_remove(&lessee->leases, to_remove);
+		any_removed = true;
+	}
+	if (any_removed)
+		return drm_lease_walk_descend;
+	else
+		return drm_lease_walk_dont_descend;
+}
+
+/**
+ * drm_lease_change - change the resources held in a specific lease
+ * @lessor: lessor of the resources
+ * @lessee_id: unique id of the lessee
+ * @leases: new set of resources to be held in the lease
+ */
+int
+drm_lease_change(struct drm_master *lessor, int lessee_id, bool mask_lease, struct idr *leases)
+{
+	struct drm_master *lessee;
+	int ret;
+	struct drm_mode_object *entry;
+	int object;
+
+	/* Find the lessee and hold a reference while we mess around */
+	lessee = drm_lessee_get(lessor, lessee_id);
+	if (!lessee)
+		return -ENOENT;
+	if (lessee == lessor)
+		return -EINVAL;
+
+	/*
+	 * Check to make sure the updated lease terms are valid, which
+	 * means that each resource is leased by the lessor and either
+	 * not sub-leased, or leased to the lessee
+	 */
+
+	mutex_lock(&lessor->dev->mode_config.idr_mutex);
+	idr_for_each_entry(leases, entry, object) {
+		entry = _drm_lease_find(lessor, object);
+		if (IS_ERR(entry)) {
+			ret = PTR_ERR(entry);
+			if (ret != -EBUSY ||
+			    _drm_lease_holder(lessor, object) != lessee)
+			{
+				mutex_unlock(&lessor->dev->mode_config.idr_mutex);
+				drm_master_put(&lessee);
+				return ret;
+			}
+		}
+	}
+
+	/* Free the existing leases and move the new ones in */
+	idr_destroy(&lessee->leases);
+	lessee->leases = *leases;
+	lessee->mask_lease = mask_lease;
+	idr_init(leases);
+
+	drm_lease_walk(lessee, drm_lease_validate, lessee);
+
+	mutex_unlock(&lessor->dev->mode_config.idr_mutex);
+
+	drm_master_put(&lessee);
+	return 0;
+}
+
+/**
+ * drm_lease_destroy - a master is going away
+ * @master: the drm_master being destroyed
+ *
+ * Revoke all leases held by this master from it and all lessees
+ * Orphan all lessees. They'll go away when closed.
+ */
+void drm_lease_destroy(struct drm_master *master)
+{
+	struct drm_device *dev = master->dev;
+
+	BUG_ON(__mutex_owner(&dev->master_mutex) != current);
+
+	DRM_DEBUG_LEASE("drm_lease_destroy %d\n", master->lessee_id);
+
+	/* This master is referenced by all lessees, hence it cannot be destroyed
+	 * until all of them have been
+	 */
+
+	BUG_ON(!list_empty(&master->lessees));
+
+	/* Remove this master from the lessee idr in the owner */
+	if (master->lessee_id != 0) {
+		DRM_DEBUG_LEASE("remove master %d from device list of lessees\n", master->lessee_id);
+		idr_remove(&(drm_lease_owner(master)->lessee_idr), master->lessee_id);
+	}
+
+	/* Remove this master from any lessee list it may be on */
+	list_del(&master->lessee_list);
+	if (master->lessor)
+		drm_master_put(&master->lessor);
+
+	DRM_DEBUG_LEASE("drm_lease_destroy done %d\n", master->lessee_id);
+}
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 304a22c87999..b468bc9cf318 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -340,6 +340,7 @@ typedef int drm_ioctl_compat_t(struct file *filp, unsigned int cmd,
 #define DRM_CONTROL_ALLOW 0x8
 #define DRM_UNLOCKED	0x10
 #define DRM_RENDER_ALLOW 0x20
+#define DRM_ANY_MASTER	0x40
 
 struct drm_ioctl_desc {
 	unsigned int cmd;
diff --git a/include/drm/drm_auth.h b/include/drm/drm_auth.h
index 610223b0481b..e0e2af09d3af 100644
--- a/include/drm/drm_auth.h
+++ b/include/drm/drm_auth.h
@@ -50,10 +50,38 @@ struct drm_master {
 	struct idr magic_map;
 	struct drm_lock_data lock;
 	void *driver_priv;
+
+	/* Tree of display resource leases, each of which is a drm_master struct
+	 * All of these get activated simultaneously, so drm_device master points
+	 * at the top of the tree (for which lessor is NULL)
+	 */
+
+	/** Lease holder */
+	struct drm_master *lessor;
+
+	/** id for lessees. Owners always have id 0 */
+	int	lessee_id;
+
+	/** other lessees of the same master */
+	struct list_head lessee_list;
+
+	/** drm_masters leasing from this one */
+	struct list_head lessees;
+
+	/** Objects leased to this drm_master. */
+	struct idr leases;
+
+	/** All lessees under this owner (only used where lessor == NULL) */
+	struct idr lessee_idr;
+
+	/** Indicates that the leased objects should be hidden from the lessor */
+	bool mask_lease;
 };
 
 struct drm_master *drm_master_get(struct drm_master *master);
 void drm_master_put(struct drm_master **master);
 bool drm_is_current_master(struct drm_file *fpriv);
 
+struct drm_master *drm_master_create(struct drm_device *dev);
+
 #endif
diff --git a/include/drm/drm_lease.h b/include/drm/drm_lease.h
new file mode 100644
index 000000000000..e02adf3e42fd
--- /dev/null
+++ b/include/drm/drm_lease.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifndef _DRM_LEASE_H_
+#define _DRM_LEASE_H_
+
+struct drm_file;
+struct drm_device;
+
+struct drm_master *drm_lease_owner(struct drm_master *master);
+
+struct drm_master *drm_lessee_find(struct drm_master *top, int lessee_id);
+
+void drm_lease_destroy(struct drm_master *lessee);
+
+struct drm_mode_object *drm_lease_find(struct drm_master *master, int id);
+
+/**
+ * drm_lease_check - check drm_mode_object lease status
+ * @master: the drm_master
+ * @id: the object id
+ *
+ * Checks if the specified master holds a lease on the object
+ * and also whether it has been leased to some lessee of the
+ * specified master. Return value:
+ *
+ *	0		'master' holds a lease (or owns) and has not leased
+ *	-EACCESS	Some other master holds the lease
+ *	-ENOENT		'id' is not a valid DRM object for this device
+ *	-EBUSY		'master' holds lease, but has sub-leased
+ */
+
+static inline int drm_lease_check(struct drm_master *master, int id) {
+	struct drm_mode_object *object = drm_lease_find(master, id);
+	if (IS_ERR(object))
+		return PTR_ERR(object);
+	return 0;
+}
+
+#endif /* _DRM_LEASE_H_ */
