diff --git a/drivers/gpu/drm/vkms/vkms_configfs.c b/drivers/gpu/drm/vkms/vkms_configfs.c
index aabc832836266668c8adc60d7d5284ee1f385f31..a410c9be4f2bbf7b2651245747eb357fcf32d1f2 100644
--- a/drivers/gpu/drm/vkms/vkms_configfs.c
+++ b/drivers/gpu/drm/vkms/vkms_configfs.c
@@ -5,6 +5,7 @@
 #include <drm/drm_print.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/generic-radix-tree.h>
 
 #include "vkms_configfs.h"
 #include "vkms_drv.h"
@@ -395,6 +396,84 @@ static const struct config_item_type subgroup_plane = {
 	.ct_owner	= THIS_MODULE,
 };
 
+static const struct config_item_type crtc_item_type;
+static const struct config_item_type planes_item_type;
+
+static int possible_crtcs_allow_link(struct config_item *src,
+				     struct config_item *target)
+{
+	struct vkms_configfs_device *vkms_configfs = plane_possible_crtc_src_item_to_vkms_configfs_device(src);
+	struct vkms_config_crtc *crtc;
+
+	mutex_lock(&vkms_configfs->lock);
+
+	if (target->ci_type != &crtc_item_type) {
+		mutex_unlock(&vkms_configfs->lock);
+		return -EINVAL;
+	}
+
+	crtc = crtc_item_to_vkms_configfs_crtc(target)->vkms_config_crtc;
+	struct vkms_config_plane *plane = plane_possible_crtc_src_item_to_vkms_configfs_plane(src)->vkms_config_plane;
+
+	struct vkms_config_crtc *crtc_entry;
+	unsigned long idx = 0;
+
+	xa_for_each(&plane->possible_crtcs, idx, crtc_entry) {
+		if (crtc_entry == crtc) {
+			mutex_unlock(&vkms_configfs->lock);
+			return -EINVAL;
+		}
+	}
+
+	if (vkms_config_plane_attach_crtc(plane, crtc))
+		return -EINVAL;
+
+	mutex_unlock(&vkms_configfs->lock);
+
+	return 0;
+}
+
+static void possible_crtcs_drop_link(struct config_item *src,
+				     struct config_item *target)
+{
+	struct vkms_config_crtc *crtc;
+	struct vkms_configfs_device *vkms_configfs = plane_possible_crtc_src_item_to_vkms_configfs_device(src);
+
+	mutex_lock(&vkms_configfs->lock);
+
+	crtc = crtc_item_to_vkms_configfs_crtc(target)->vkms_config_crtc;
+	struct vkms_config_plane *plane = plane_possible_crtc_src_item_to_vkms_configfs_plane(src)->vkms_config_plane;
+
+	struct vkms_config_crtc  *crtc_entry;
+	struct vkms_config_plane *plane_entry;
+	unsigned long crtc_idx  = -1;
+
+	xa_for_each(&plane->possible_crtcs, crtc_idx, crtc_entry) {
+		if (crtc_entry == crtc)
+			break;
+	}
+	unsigned long plane_idx = -1;
+
+	xa_erase(&plane->possible_crtcs, crtc_idx);
+	xa_for_each(&crtc->possible_planes, plane_idx, plane_entry) {
+		if (plane_entry == plane)
+			break;
+	}
+	xa_erase(&crtc->possible_planes, plane_idx);
+
+	mutex_unlock(&vkms_configfs->lock);
+}
+
+static struct configfs_item_operations plane_possible_crtcs_item_ops = {
+	.allow_link = &possible_crtcs_allow_link,
+	.drop_link = &possible_crtcs_drop_link,
+};
+
+static struct config_item_type plane_possible_crtcs_group_type = {
+	.ct_item_ops = &plane_possible_crtcs_item_ops,
+	.ct_owner = THIS_MODULE,
+};
+
 static struct config_group *planes_make_group(struct config_group *config_group,
 					      const char *name)
 {
@@ -419,10 +498,7 @@ static struct config_group *planes_make_group(struct config_group *config_group,
 
 	if (list_count_nodes(&vkms_configfs->vkms_config->planes) == 1)
 		vkms_configfs_plane->vkms_config_plane->type = DRM_PLANE_TYPE_PRIMARY;
-
-	if (!vkms_configfs_plane->vkms_config_plane ||
-	    vkms_config_plane_attach_crtc(vkms_configfs_plane->vkms_config_plane,
-					  vkms_configfs->vkms_config_crtc)) {
+	if (!vkms_configfs_plane->vkms_config_plane) {
 		kfree(vkms_configfs_plane);
 		mutex_unlock(&vkms_configfs->lock);
 		return ERR_PTR(-ENOMEM);
@@ -439,7 +515,12 @@ static struct config_group *planes_make_group(struct config_group *config_group,
 
 	config_group_init_type_name(&vkms_configfs_plane->group, name, &subgroup_plane);
 
+	config_group_init_type_name(&vkms_configfs_plane->possible_crtc_group, "possible_crtcs",
+				    &plane_possible_crtcs_group_type);
+	configfs_add_default_group(&vkms_configfs_plane->possible_crtc_group,
+				   &vkms_configfs_plane->group);
 	vkms_configfs_plane->vkms_configfs_device = vkms_configfs;
+
 	mutex_unlock(&vkms_configfs->lock);
 
 	return &vkms_configfs_plane->group;
@@ -454,6 +535,283 @@ static const struct config_item_type planes_item_type = {
 	.ct_owner	= THIS_MODULE,
 };
 
+static void crtc_release(struct config_item *item)
+{
+	struct vkms_configfs_crtc *vkms_configfs_crtc = crtc_item_to_vkms_configfs_crtc(item);
+
+	mutex_lock(&vkms_configfs_crtc->vkms_configfs_device->lock);
+	vkms_config_delete_crtc(vkms_configfs_crtc->vkms_config_crtc,
+				vkms_configfs_crtc->vkms_configfs_device->vkms_config);
+	mutex_unlock(&vkms_configfs_crtc->vkms_configfs_device->lock);
+
+	kfree(vkms_configfs_crtc);
+}
+
+static struct configfs_item_operations crtc_item_operations = {
+	.release = crtc_release,
+};
+
+static const struct config_item_type crtc_item_type = {
+	.ct_owner	= THIS_MODULE,
+	.ct_item_ops	= &crtc_item_operations,
+};
+
+static struct config_group *crtcs_make_group(struct config_group *config_group,
+					     const char *name)
+{
+	struct config_item *root_item = config_group->cg_item.ci_parent;
+	struct vkms_configfs_device *vkms_configfs = config_item_to_vkms_configfs_device(root_item);
+	struct vkms_configfs_crtc *vkms_configfs_crtc;
+
+	vkms_configfs_crtc = kzalloc(sizeof(*vkms_configfs_crtc), GFP_KERNEL);
+
+	if (!vkms_configfs_crtc)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_lock(&vkms_configfs->lock);
+	vkms_configfs_crtc->vkms_configfs_device = vkms_configfs;
+
+	if (vkms_configfs->enabled) {
+		kfree(vkms_configfs_crtc);
+		mutex_unlock(&vkms_configfs->lock);
+		return ERR_PTR(-EINVAL);
+	}
+
+	vkms_configfs_crtc->vkms_config_crtc = vkms_config_create_crtc(vkms_configfs->vkms_config);
+
+	if (!vkms_configfs_crtc->vkms_config_crtc) {
+		kfree(vkms_configfs_crtc);
+		mutex_unlock(&vkms_configfs->lock);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	vkms_configfs_crtc->vkms_config_crtc->name = kzalloc(strlen(name) + 1, GFP_KERNEL);
+	if (!vkms_configfs_crtc->vkms_config_crtc->name) {
+		kfree(vkms_configfs_crtc->vkms_config_crtc);
+		kfree(vkms_configfs_crtc);
+		mutex_unlock(&vkms_configfs->lock);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	vkms_configfs_crtc->vkms_configfs_device = vkms_configfs;
+
+	strscpy(vkms_configfs_crtc->vkms_config_crtc->name, name, strlen(name) + 1);
+	config_group_init_type_name(&vkms_configfs_crtc->group, name,
+				    &crtc_item_type);
+
+	mutex_unlock(&vkms_configfs->lock);
+
+	return &vkms_configfs_crtc->group;
+}
+
+static struct configfs_group_operations crtcs_group_operations = {
+	.make_group	= &crtcs_make_group,
+};
+
+static const struct config_item_type crtcs_item_type = {
+	.ct_group_ops    = &crtcs_group_operations,
+	.ct_owner        = THIS_MODULE,
+};
+
+static int encoder_possible_crtcs_allow_link(struct config_item *src,
+					     struct config_item *target)
+{
+	struct vkms_config_crtc *crtc;
+	struct vkms_configfs_device *vkms_configfs = encoder_possible_crtc_src_item_to_vkms_configfs_device(src);
+
+	mutex_lock(&vkms_configfs->lock);
+
+	if (target->ci_type != &crtc_item_type) {
+		DRM_ERROR("Unable to link non-CRTCs.\n");
+		mutex_unlock(&vkms_configfs->lock);
+		return -EINVAL;
+	}
+
+	crtc = crtc_item_to_vkms_configfs_crtc(target)->vkms_config_crtc;
+	struct vkms_config_encoder *encoder = encoder_possible_crtc_src_item_to_vkms_configfs_encoder(src)->vkms_config_encoder;
+
+	struct vkms_config_crtc *crtc_entry;
+	unsigned long idx = 0;
+
+	xa_for_each(&encoder->possible_crtcs, idx, crtc_entry) {
+		if (crtc_entry == crtc) {
+			pr_err("Tried to add two symlinks to the same CRTC from the same object.\n");
+			mutex_unlock(&vkms_configfs->lock);
+			return -EINVAL;
+		}
+	}
+
+	if (vkms_config_encoder_attach_crtc(encoder, crtc))
+		return -EINVAL;
+
+	mutex_unlock(&vkms_configfs->lock);
+
+	return 0;
+}
+
+static void encoder_possible_crtcs_drop_link(struct config_item *src,
+					     struct config_item *target)
+{
+	struct vkms_config_crtc *crtc;
+	struct vkms_configfs_device *vkms_configfs = encoder_possible_crtc_src_item_to_vkms_configfs_device(src);
+
+	mutex_lock(&vkms_configfs->lock);
+
+	crtc = crtc_item_to_vkms_configfs_crtc(target)->vkms_config_crtc;
+	struct vkms_config_encoder *encoder = encoder_possible_crtc_src_item_to_vkms_configfs_encoder(src)->vkms_config_encoder;
+
+	struct vkms_config_encoder *encoder_entry;
+	struct vkms_config_crtc *crtc_entry;
+	unsigned long encoder_idx = -1;
+	unsigned long crtc_idx = -1;
+
+	xa_for_each(&encoder->possible_crtcs, crtc_idx, crtc_entry) {
+		if (crtc_entry == crtc)
+			break;
+	}
+	xa_erase(&encoder->possible_crtcs, crtc_idx);
+	xa_for_each(&crtc->possible_encoders, encoder_idx, encoder_entry) {
+		if (encoder_entry == encoder)
+			break;
+	}
+	xa_erase(&crtc->possible_encoders, encoder_idx);
+
+	mutex_unlock(&vkms_configfs->lock);
+}
+
+static struct configfs_item_operations encoder_possible_crtcs_item_operations = {
+	.allow_link	= &encoder_possible_crtcs_allow_link,
+	.drop_link	= &encoder_possible_crtcs_drop_link,
+};
+
+static struct config_item_type encoder_possible_crtcs_item_type = {
+	.ct_item_ops	= &encoder_possible_crtcs_item_operations,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void encoder_release(struct config_item *item)
+{
+	struct vkms_configfs_encoder *vkms_configfs_encoder = encoder_item_to_vkms_configfs_encoder(item);
+
+	mutex_lock(&vkms_configfs_encoder->vkms_configfs_device->lock);
+	vkms_config_delete_encoder(vkms_configfs_encoder->vkms_config_encoder,
+				   vkms_configfs_encoder->vkms_configfs_device->vkms_config);
+	mutex_unlock(&vkms_configfs_encoder->vkms_configfs_device->lock);
+
+	kfree(vkms_configfs_encoder);
+}
+
+static struct configfs_item_operations encoder_item_operations = {
+	.release	= encoder_release,
+};
+
+static const struct config_item_type encoder_item_type = {
+	.ct_item_ops	= &encoder_item_operations,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *encoder_make_group(struct config_group *config_group,
+					       const char *name)
+{
+	struct vkms_configfs_device *vkms_configfs = encoder_item_to_vkms_configfs_device(&config_group->cg_item);
+	struct vkms_configfs_encoder *vkms_configfs_encoder;
+
+	vkms_configfs_encoder = kzalloc(sizeof(*vkms_configfs_encoder), GFP_KERNEL);
+
+	if (!vkms_configfs_encoder)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_lock(&vkms_configfs->lock);
+
+	if (vkms_configfs->enabled) {
+		kfree(vkms_configfs_encoder);
+		mutex_unlock(&vkms_configfs->lock);
+		return ERR_PTR(-EINVAL);
+	}
+
+	vkms_configfs_encoder->vkms_config_encoder = vkms_config_create_encoder(vkms_configfs->vkms_config);
+
+	if (!vkms_configfs_encoder->vkms_config_encoder) {
+		kfree(vkms_configfs_encoder);
+		mutex_unlock(&vkms_configfs->lock);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	vkms_configfs_encoder->vkms_config_encoder->name = kzalloc(strlen(name) + 1, GFP_KERNEL);
+	if (!vkms_configfs_encoder->vkms_config_encoder->name) {
+		kfree(vkms_configfs_encoder->vkms_config_encoder);
+		kfree(vkms_configfs_encoder);
+		mutex_unlock(&vkms_configfs->lock);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	strscpy(vkms_configfs_encoder->vkms_config_encoder->name, name, strlen(name) + 1);
+	config_group_init_type_name(&vkms_configfs_encoder->group, name,
+				    &encoder_item_type);
+
+	config_group_init_type_name(&vkms_configfs_encoder->possible_crtc_group, "possible_crtcs",
+				    &encoder_possible_crtcs_item_type);
+	configfs_add_default_group(&vkms_configfs_encoder->possible_crtc_group,
+				   &vkms_configfs_encoder->group);
+	vkms_configfs_encoder->vkms_configfs_device = vkms_configfs;
+
+	mutex_unlock(&vkms_configfs->lock);
+
+	return &vkms_configfs_encoder->group;
+}
+
+static struct configfs_group_operations encoder_group_operations = {
+	.make_group	= &encoder_make_group,
+};
+
+static const struct config_item_type encoders_item_type = {
+	.ct_group_ops	= &encoder_group_operations,
+	.ct_owner	= THIS_MODULE,
+};
+
+/**
+ * configfs_lock_dependencies() - In order to forbid the userspace to delete items when the
+ * device is enabled, mark all configfs items as dependent
+ *
+ * @vkms_configfs_device: Device to lock
+ */
+static void configfs_lock_dependencies(struct vkms_configfs_device *vkms_configfs_device)
+{
+	/* Lock the group itself */
+	configfs_depend_item_unlocked(vkms_configfs_device->group.cg_subsys,
+				      &vkms_configfs_device->group.cg_item);
+	/* Lock the planes elements */
+	struct config_item *item;
+
+	list_for_each_entry(item, &vkms_configfs_device->plane_group.cg_children, ci_entry) {
+		configfs_depend_item_unlocked(vkms_configfs_device->plane_group.cg_subsys,
+					      item);
+	}
+	list_for_each_entry(item, &vkms_configfs_device->crtc_group.cg_children, ci_entry) {
+		configfs_depend_item_unlocked(vkms_configfs_device->crtc_group.cg_subsys,
+					      item);
+	}
+}
+
+/**
+ * configfs_unlock_dependencies() - Once the device is disable, its configuration can be modified.
+ *
+ * @vkms_configfs_device: Device to unlock
+ */
+static void configfs_unlock_dependencies(struct vkms_configfs_device *vkms_configfs_device)
+{
+	struct config_item *item;
+
+	configfs_undepend_item_unlocked(&vkms_configfs_device->group.cg_item);
+
+	list_for_each_entry(item, &vkms_configfs_device->plane_group.cg_children, ci_entry) {
+		configfs_undepend_item_unlocked(item);
+	}
+	list_for_each_entry(item, &vkms_configfs_device->crtc_group.cg_children, ci_entry) {
+		configfs_undepend_item_unlocked(item);
+	}
+}
+
 static ssize_t device_enable_show(struct config_item *item, char *page)
 {
 	return sprintf(page, "%d\n",
@@ -474,13 +832,25 @@ static ssize_t device_enable_store(struct config_item *item,
 		return -EINVAL;
 
 	mutex_lock(&vkms_configfs_device->lock);
+	if (vkms_configfs_device->enabled == value) {
+		mutex_unlock(&vkms_configfs_device->lock);
+		return (ssize_t)count;
+	}
+
+	if (value && !vkms_config_is_valid(vkms_configfs_device->vkms_config)) {
+		mutex_unlock(&vkms_configfs_device->lock);
+		return -EINVAL;
+	}
 
 	vkms_configfs_device->enabled = value;
 
-	if (value)
+	if (value) {
+		configfs_lock_dependencies(vkms_configfs_device);
 		vkms_create(vkms_configfs_device->vkms_config);
-	else
+	} else {
+		configfs_unlock_dependencies(vkms_configfs_device);
 		vkms_destroy(vkms_configfs_device->vkms_config);
+	}
 
 	mutex_unlock(&vkms_configfs_device->lock);
 
@@ -519,9 +889,6 @@ static const struct config_item_type device_item_type = {
 static struct config_group *root_make_group(struct config_group *group,
 					    const char *name)
 {
-	struct vkms_config_plane *plane;
-	struct vkms_config_crtc *crtc;
-	struct vkms_config_encoder *encoder;
 	struct vkms_configfs_device *configfs = kzalloc(sizeof(*configfs), GFP_KERNEL);
 
 	if (!configfs)
@@ -536,22 +903,18 @@ static struct config_group *root_make_group(struct config_group *group,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	configfs->vkms_config_crtc = vkms_config_create_crtc(configfs->vkms_config);
-	configfs->vkms_config_encoder = vkms_config_create_encoder(configfs->vkms_config);
-	if (!configfs->vkms_config_crtc || !configfs->vkms_config_encoder ||
-	    vkms_config_encoder_attach_crtc(configfs->vkms_config_encoder,
-					    configfs->vkms_config_crtc)) {
-		vkms_config_destroy(configfs->vkms_config);
-		kfree(configfs);
-		return ERR_PTR(-ENOMEM);
-	}
-
 	config_group_init_type_name(&configfs->group, name,
 				    &device_item_type);
 
 	config_group_init_type_name(&configfs->plane_group, "planes", &planes_item_type);
 	configfs_add_default_group(&configfs->plane_group, &configfs->group);
 
+	config_group_init_type_name(&configfs->crtc_group, "crtcs", &crtcs_item_type);
+	configfs_add_default_group(&configfs->crtc_group, &configfs->group);
+
+	config_group_init_type_name(&configfs->encoder_group, "encoders", &encoders_item_type);
+	configfs_add_default_group(&configfs->encoder_group, &configfs->group);
+
 	return &configfs->group;
 }
 
diff --git a/drivers/gpu/drm/vkms/vkms_configfs.h b/drivers/gpu/drm/vkms/vkms_configfs.h
index 6dc4d34a9e44ed4beb115ad3c86b759e28f5d0ef..df743e0107f40cd10433bdb638108d266f9c83a6 100644
--- a/drivers/gpu/drm/vkms/vkms_configfs.h
+++ b/drivers/gpu/drm/vkms/vkms_configfs.h
@@ -20,34 +20,84 @@ struct vkms_configfs_device {
 	struct config_group group;
 
 	struct config_group plane_group;
+	struct config_group crtc_group;
+	struct config_group encoder_group;
 
 	struct mutex lock;
 	bool enabled;
 
 	struct vkms_config *vkms_config;
-	struct vkms_config_crtc *vkms_config_crtc;
-	struct vkms_config_encoder *vkms_config_encoder;
 };
 
 struct vkms_configfs_plane {
 	struct config_group group;
+	struct config_group possible_crtc_group;
 
 	struct vkms_configfs_device *vkms_configfs_device;
 	struct vkms_config_plane *vkms_config_plane;
 };
 
+struct vkms_configfs_crtc {
+	struct config_group group;
+
+	struct vkms_configfs_device *vkms_configfs_device;
+	struct vkms_config_crtc *vkms_config_crtc;
+};
+
+struct vkms_configfs_encoder {
+	/* must be first because it is a krefcounted stuff */
+	struct config_group group;
+
+	struct config_group possible_crtc_group;
+	struct vkms_configfs_device *vkms_configfs_device;
+	struct vkms_config_encoder *vkms_config_encoder;
+};
+
 #define config_item_to_vkms_configfs_device(item) \
 	container_of(to_config_group((item)), struct vkms_configfs_device, group)
 
 #define planes_item_to_vkms_configfs_device(item) \
 	config_item_to_vkms_configfs_device((item)->ci_parent)
 
+#define encoders_item_to_vkms_configfs_device(item) \
+	config_item_to_vkms_configfs_device((item)->ci_parent)
+
+#define crtc_item_to_vkms_configfs_crtc(item) \
+	container_of(to_config_group((item)), struct vkms_configfs_crtc, group)
+
+#define encoder_item_to_vkms_configfs_encoder(item) \
+	container_of(to_config_group((item)), struct vkms_configfs_encoder, group)
+
 #define plane_item_to_vkms_configfs_device(item) \
 	planes_item_to_vkms_configfs_device((item)->ci_parent)
 
 #define plane_item_to_vkms_configfs_plane(item) \
 	container_of(to_config_group((item)), struct vkms_configfs_plane, group)
 
+#define plane_possible_crtc_src_item_to_vkms_configfs_device(item) \
+	plane_item_to_vkms_configfs_device((item)->ci_parent)
+
+#define plane_possible_crtc_src_item_to_vkms_configfs_plane(item) \
+	plane_item_to_vkms_configfs_plane((item)->ci_parent)
+
+#define crtc_item_to_vkms_configfs_device(item) \
+	config_item_to_vkms_configfs_device((item)->ci_parent)
+
+#define crtc_child_item_to_vkms_configfs_device(item) \
+	crtc_item_to_vkms_configfs_device((item)->ci_parent)
+
+#define encoder_item_to_vkms_configfs_device(item) \
+	config_item_to_vkms_configfs_device((item)->ci_parent)
+
+#define encoder_child_item_to_vkms_configfs_device(item) \
+	encoder_item_to_vkms_configfs_device((item)->ci_parent)
+
+#define encoder_possible_crtc_src_item_to_vkms_configfs_device(item) \
+	encoder_child_item_to_vkms_configfs_device((item)->ci_parent)
+
+#define encoder_possible_crtc_src_item_to_vkms_configfs_encoder(item) \
+	encoder_item_to_vkms_configfs_encoder((item)->ci_parent)
+
 /* ConfigFS Support */
 int vkms_init_configfs(void);
 void vkms_unregister_configfs(void);
