The current VKMS driver can only uses one CRTC and one encoder. This patch
introduce in the same time CRTC and encoders as they are tighly linked.
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
drivers/gpu/drm/vkms/vkms_config.c | 266 +++++++++++++++++++++++++++++++++----
drivers/gpu/drm/vkms/vkms_config.h | 102 +++++++++++++-
drivers/gpu/drm/vkms/vkms_crtc.c | 20 ++-
drivers/gpu/drm/vkms/vkms_crtc.h | 3 +-
drivers/gpu/drm/vkms/vkms_drv.c | 87 ++++++------
5 files changed, 405 insertions(+), 73 deletions(-)
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index e9e30974523a..fcd4a128c21b 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -14,6 +14,8 @@ struct vkms_config *vkms_config_create(void)
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&config->planes);
+ INIT_LIST_HEAD(&config->crtcs);
+ INIT_LIST_HEAD(&config->encoders);
return config;
}
@@ -22,12 +24,24 @@ struct vkms_config *vkms_config_alloc_default(bool enable_writeback, bool enable
bool enable_cursor)
{
struct vkms_config_plane *plane;
+ struct vkms_config_encoder *encoder;
+ struct vkms_config_crtc *crtc;
struct vkms_config *vkms_config = vkms_config_create();
if (IS_ERR(vkms_config))
return vkms_config;
- vkms_config->writeback = enable_writeback;
+ crtc = vkms_config_create_crtc(vkms_config);
+ if (!crtc)
+ goto err_alloc;
+ crtc->writeback = enable_writeback;
+
+ encoder = vkms_config_create_encoder(vkms_config);
+ if (!encoder)
+ goto err_alloc;
+
+ if (vkms_config_encoder_attach_crtc(encoder, crtc))
+ goto err_alloc;
plane = vkms_config_create_plane(vkms_config);
if (!plane)
@@ -39,6 +53,9 @@ struct vkms_config *vkms_config_alloc_default(bool enable_writeback, bool enable
goto err_alloc;
sprintf(plane->name, "primary");
+ if (vkms_config_plane_attach_crtc(plane, crtc))
+ goto err_alloc;
+
if (enable_overlay) {
for (int i = 0; i < NUM_OVERLAY_PLANES; i++) {
plane = vkms_config_create_plane(vkms_config);
@@ -49,6 +66,9 @@ struct vkms_config *vkms_config_alloc_default(bool enable_writeback, bool enable
if (!plane->name)
goto err_alloc;
snprintf(plane->name, 10, "plane-%d", i);
+
+ if (vkms_config_plane_attach_crtc(plane, crtc))
+ goto err_alloc;
}
}
if (enable_cursor) {
@@ -60,6 +80,9 @@ struct vkms_config *vkms_config_alloc_default(bool enable_writeback, bool enable
if (!plane->name)
goto err_alloc;
sprintf(plane->name, "cursor");
+
+ if (vkms_config_plane_attach_crtc(plane, crtc))
+ goto err_alloc;
}
return vkms_config;
@@ -89,38 +112,196 @@ struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *vkms_conf
vkms_config_overlay->supported_color_range = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
BIT(DRM_COLOR_YCBCR_FULL_RANGE);
vkms_config_overlay->default_color_range = DRM_COLOR_YCBCR_FULL_RANGE;
+ xa_init_flags(&vkms_config_overlay->possible_crtcs, XA_FLAGS_ALLOC);
list_add(&vkms_config_overlay->link, &vkms_config->planes);
return vkms_config_overlay;
}
-void vkms_config_delete_plane(struct vkms_config_plane *vkms_config_overlay)
+struct vkms_config_crtc *vkms_config_create_crtc(struct vkms_config *vkms_config)
{
- if (!vkms_config_overlay)
+ if (!vkms_config)
+ return NULL;
+
+ struct vkms_config_crtc *vkms_config_crtc = kzalloc(sizeof(*vkms_config_crtc),
+ GFP_KERNEL);
+
+ if (!vkms_config_crtc)
+ return NULL;
+
+ list_add(&vkms_config_crtc->link, &vkms_config->crtcs);
+ xa_init_flags(&vkms_config_crtc->possible_planes, XA_FLAGS_ALLOC);
+ xa_init_flags(&vkms_config_crtc->possible_encoders, XA_FLAGS_ALLOC);
+
+ return vkms_config_crtc;
+}
+
+struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *vkms_config)
+{
+ if (!vkms_config)
+ return NULL;
+
+ struct vkms_config_encoder *vkms_config_encoder = kzalloc(sizeof(*vkms_config_encoder),
+ GFP_KERNEL);
+
+ if (!vkms_config_encoder)
+ return NULL;
+
+ list_add(&vkms_config_encoder->link, &vkms_config->encoders);
+ xa_init_flags(&vkms_config_encoder->possible_crtcs, XA_FLAGS_ALLOC);
+
+ return vkms_config_encoder;
+}
+
+void vkms_config_delete_plane(struct vkms_config_plane *vkms_config_plane,
+ struct vkms_config *vkms_config)
+{
+ struct vkms_config_crtc *crtc_config;
+ struct vkms_config_plane *plane;
+
+ if (!vkms_config_plane)
+ return;
+ list_del(&vkms_config_plane->link);
+ xa_destroy(&vkms_config_plane->possible_crtcs);
+
+ list_for_each_entry(crtc_config, &vkms_config->crtcs, link) {
+ unsigned long idx = 0;
+
+ xa_for_each(&crtc_config->possible_planes, idx, plane) {
+ if (plane == vkms_config_plane)
+ xa_erase(&crtc_config->possible_planes, idx);
+ }
+ }
+
+ kfree(vkms_config_plane->name);
+ kfree(vkms_config_plane);
+}
+
+void vkms_config_delete_crtc(struct vkms_config_crtc *vkms_config_crtc,
+ struct vkms_config *vkms_config)
+{
+ struct vkms_config_crtc *crtc_config;
+ struct vkms_config_plane *plane_config;
+ struct vkms_config_encoder *encoder_config;
+
+ if (!vkms_config_crtc)
+ return;
+ list_del(&vkms_config_crtc->link);
+ xa_destroy(&vkms_config_crtc->possible_planes);
+ xa_destroy(&vkms_config_crtc->possible_encoders);
+
+ list_for_each_entry(plane_config, &vkms_config->planes, link) {
+ unsigned long idx = 0;
+
+ xa_for_each(&plane_config->possible_crtcs, idx, crtc_config) {
+ if (crtc_config == vkms_config_crtc)
+ xa_erase(&plane_config->possible_crtcs, idx);
+ }
+ }
+
+ list_for_each_entry(encoder_config, &vkms_config->encoders, link) {
+ unsigned long idx = 0;
+
+ xa_for_each(&encoder_config->possible_crtcs, idx, crtc_config) {
+ if (crtc_config == vkms_config_crtc)
+ xa_erase(&encoder_config->possible_crtcs, idx);
+ }
+ }
+
+ kfree(vkms_config_crtc);
+}
+
+void vkms_config_delete_encoder(struct vkms_config_encoder *vkms_config_encoder,
+ struct vkms_config *vkms_config)
+{
+ if (!vkms_config_encoder)
return;
- list_del(&vkms_config_overlay->link);
- kfree(vkms_config_overlay->name);
- kfree(vkms_config_overlay);
+ list_del(&vkms_config_encoder->link);
+ xa_destroy(&vkms_config_encoder->possible_crtcs);
+
+ struct vkms_config_crtc *crtc_config;
+ struct vkms_config_encoder *encoder;
+
+ list_for_each_entry(crtc_config, &vkms_config->crtcs, link) {
+ unsigned long idx = 0;
+
+ xa_for_each(&crtc_config->possible_encoders, idx, encoder) {
+ if (encoder == vkms_config_encoder)
+ xa_erase(&crtc_config->possible_encoders, idx);
+ }
+ }
+
+ kfree(vkms_config_encoder);
}
void vkms_config_destroy(struct vkms_config *config)
{
struct vkms_config_plane *vkms_config_plane, *tmp_plane;
+ struct vkms_config_encoder *vkms_config_encoder, *tmp_encoder;
+ struct vkms_config_crtc *vkms_config_crtc, *tmp_crtc;
+
list_for_each_entry_safe(vkms_config_plane, tmp_plane, &config->planes, link) {
- vkms_config_delete_plane(vkms_config_plane);
+ vkms_config_delete_plane(vkms_config_plane, config);
+ }
+ list_for_each_entry_safe(vkms_config_encoder, tmp_encoder, &config->encoders, link) {
+ vkms_config_delete_encoder(vkms_config_encoder, config);
+ }
+ list_for_each_entry_safe(vkms_config_crtc, tmp_crtc, &config->crtcs, link) {
+ vkms_config_delete_crtc(vkms_config_crtc, config);
}
kfree(config);
}
+int __must_check vkms_config_plane_attach_crtc(struct vkms_config_plane *vkms_config_plane,
+ struct vkms_config_crtc *vkms_config_crtc)
+{
+ u32 crtc_idx, encoder_idx;
+ int ret;
+
+ ret = xa_alloc(&vkms_config_plane->possible_crtcs, &crtc_idx, vkms_config_crtc,
+ xa_limit_32b, GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ ret = xa_alloc(&vkms_config_crtc->possible_planes, &encoder_idx, vkms_config_plane,
+ xa_limit_32b, GFP_KERNEL);
+ if (ret) {
+ xa_erase(&vkms_config_plane->possible_crtcs, crtc_idx);
+ return ret;
+ }
+
+ return ret;
+}
+
+int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *vkms_config_encoder,
+ struct vkms_config_crtc *vkms_config_crtc)
+{
+ u32 crtc_idx, encoder_idx;
+ int ret;
+
+ ret = xa_alloc(&vkms_config_encoder->possible_crtcs, &crtc_idx, vkms_config_crtc,
+ xa_limit_32b, GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ ret = xa_alloc(&vkms_config_crtc->possible_encoders, &encoder_idx, vkms_config_encoder,
+ xa_limit_32b, GFP_KERNEL);
+ if (ret) {
+ xa_erase(&vkms_config_encoder->possible_crtcs, crtc_idx);
+ return ret;
+ }
+
+ return ret;
+}
+
bool vkms_config_is_valid(struct vkms_config *config)
{
struct vkms_config_plane *config_plane;
-
- bool has_cursor = false;
- bool has_primary = false;
+ struct vkms_config_crtc *config_crtc;
+ struct vkms_config_encoder *config_encoder;
list_for_each_entry(config_plane, &config->planes, link) {
// Default rotation not in supported rotations
@@ -141,22 +322,47 @@ bool vkms_config_is_valid(struct vkms_config *config)
BIT(config_plane->default_color_range))
return false;
- if (config_plane->type == DRM_PLANE_TYPE_PRIMARY) {
- // Multiple primary planes for only one CRTC
- if (has_primary)
- return false;
- has_primary = true;
- }
- if (config_plane->type == DRM_PLANE_TYPE_CURSOR) {
- // Multiple cursor planes for only one CRTC
- if (has_cursor)
- return false;
- has_cursor = true;
- }
+ // No CRTC linked to this plane
+ if (xa_empty(&config_plane->possible_crtcs))
+ return false;
+ }
+
+ list_for_each_entry(config_encoder, &config->encoders, link) {
+ // No CRTC linked to this encoder
+ if (xa_empty(&config_encoder->possible_crtcs))
+ return false;
}
- if (!has_primary)
- return false;
+ list_for_each_entry(config_crtc, &config->crtcs, link) {
+ bool has_primary = false;
+ bool has_cursor = false;
+ unsigned long idx = 0;
+
+ // No encoder attached to this CRTC
+ if (xa_empty(&config_crtc->possible_encoders))
+ return false;
+
+ xa_for_each(&config_crtc->possible_planes, idx, config_plane) {
+ if (config_plane->type == DRM_PLANE_TYPE_PRIMARY) {
+ // Multiple primary planes for only one CRTC
+ if (has_primary)
+ return false;
+
+ has_primary = true;
+ }
+ if (config_plane->type == DRM_PLANE_TYPE_CURSOR) {
+ // Multiple cursor planes for only one CRTC
+ if (has_cursor)
+ return false;
+
+ has_cursor = true;
+ }
+ }
+
+ // No primary plane for this CRTC
+ if (!has_primary)
+ return false;
+ }
return true;
}
@@ -167,8 +373,9 @@ static int vkms_config_show(struct seq_file *m, void *data)
struct drm_device *dev = entry->dev;
struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
struct vkms_config_plane *config_plane;
+ struct vkms_config_crtc *config_crtc;
+ struct vkms_config_encoder *config_encoder;
- seq_printf(m, "writeback=%d\n", vkmsdev->config->writeback);
list_for_each_entry(config_plane, &vkmsdev->config->planes, link) {
seq_puts(m, "plane:\n");
seq_printf(m, "\tname: %s\n", config_plane->name);
@@ -185,6 +392,15 @@ static int vkms_config_show(struct seq_file *m, void *data)
config_plane->default_color_range);
}
+ list_for_each_entry(config_encoder, &vkmsdev->config->encoders, link) {
+ seq_puts(m, "encoder:\n");
+ }
+
+ list_for_each_entry(config_crtc, &vkmsdev->config->crtcs, link) {
+ seq_puts(m, "crtc:\n");
+ seq_printf(m, "\twriteback: %d\n", config_crtc->writeback);
+ }
+
return 0;
}
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index d668aeab9d26..8f247fc09373 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -9,16 +9,59 @@
/**
* struct vkms_config - General configuration for VKMS driver
*
- * @writeback: If true, a writeback buffer can be attached to the CRTC
* @planes: List of planes configured for this device. They are created by the function
* vkms_config_create_plane().
+ * @crtcs: List of crtcs configured for this device. They are created by the function
+ * vkms_config_create_crtc().
+ * @encoders: List of encoders configured for this device. They are created by the function
+ * vkms_config_create_encoder().
* @dev: Used to store the current vkms device. Only set when the device is instancied.
*/
struct vkms_config {
- bool writeback;
struct vkms_device *dev;
struct list_head planes;
+ struct list_head crtcs;
+ struct list_head encoders;
+};
+
+/**
+ * struct vkms_config_crtc
+ *
+ * @link: Link to the others CRTCs
+ * @possible_planes: List of planes that can be used with this CRTC
+ * @possible_encoders: List of encoders that can be used with this CRTC
+ * @crtc: Internal usage. This pointer should never be considered as valid. It can be used to
+ * store a temporary reference to a vkms crtc during device creation. This pointer is
+ * not managed by the configuration and must be managed by other means.
+ */
+struct vkms_config_crtc {
+ struct list_head link;
+
+ bool writeback;
+ struct xarray possible_planes;
+ struct xarray possible_encoders;
+
+ /* Internal usage */
+ struct vkms_crtc *crtc;
+};
+
+/**
+ * struct vkms_config_encoder
+ *
+ * @link: Link to the others encoders
+ * @possible_crtcs: List of CRTC that can be used with this encoder
+ * @encoder: Internal usage. This pointer should never be considered as valid. It can be used to
+ * store a temporary reference to a vkms encoder during device creation. This pointer is
+ * not managed by the configuration and must be managed by other means.
+ */
+struct vkms_config_encoder {
+ struct list_head link;
+
+ struct xarray possible_crtcs;
+
+ /* Internal usage */
+ struct drm_encoder *encoder;
};
/**
@@ -34,6 +77,7 @@ struct vkms_config {
* @supported_color_encoding: Color encoding that this plane will support
* @default_color_range: Default color range that should be used by this plane
* @supported_color_range: Color range that this plane will support
+ * @possible_crtcs: List of CRTC that can be used with this plane.
* @plane: Internal usage. This pointer should never be considered as valid. It can be used to
* store a temporary reference to a vkms plane during device creation. This pointer is
* not managed by the configuration and must be managed by other means.
@@ -50,6 +94,7 @@ struct vkms_config_plane {
enum drm_color_range default_color_range;
unsigned int supported_color_range;
+ struct xarray possible_crtcs;
/* Internal usage */
struct vkms_plane *plane;
};
@@ -84,14 +129,63 @@ bool vkms_config_is_valid(struct vkms_config *vkms_config);
*/
struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *vkms_config);
+/**
+ * vkms_config_create_crtc() - Create a crtc configuration
+ *
+ * This will allocate and add a new crtc configuration to @vkms_config.
+ * @vkms_config: Configuration where to insert new crtc configuration
+ */
+struct vkms_config_crtc *vkms_config_create_crtc(struct vkms_config *vkms_config);
+
+/**
+ * vkms_config_create_encoder() - Create an encoder configuration
+ *
+ * This will allocate and add a new encoder configuration to @vkms_config.
+ * @vkms_config: Configuration where to insert new encoder configuration
+ */
+struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *vkms_config);
+
+int __must_check vkms_config_plane_attach_crtc(struct vkms_config_plane *vkms_config_plane,
+ struct vkms_config_crtc *vkms_config_crtc);
+int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *vkms_config_encoder,
+ struct vkms_config_crtc *vkms_config_crtc);
+
/**
* vkms_config_delete_plane() - Remove a plane configuration and frees its memory
*
* This will delete a plane configuration from the parent configuration. This will NOT
- * cleanup and frees the vkms_plane that can be stored in @vkms_config_plane.
+ * cleanup and frees the vkms_plane that can be stored in @vkms_config_plane. It will also remove
+ * any reference to this plane in @vkms_config.
+ *
* @vkms_config_plane: Plane configuration to cleanup
+ * @vkms_config: Parent configuration
+ */
+void vkms_config_delete_plane(struct vkms_config_plane *vkms_config_plane,
+ struct vkms_config *vkms_config);
+/**
+ * vkms_config_delete_crtc() - Remove a CRTC configuration and frees its memory
+ *
+ * This will delete a CRTC configuration from the parent configuration. This will NOT
+ * cleanup and frees the vkms_crtc that can be stored in @vkms_config_crtc. It will also remove
+ * any reference to this CRTC in @vkms_config.
+ *
+ * @vkms_config_crtc: Plane configuration to cleanup
+ * @vkms_config: Parent configuration
+ */
+void vkms_config_delete_crtc(struct vkms_config_crtc *vkms_config_crtc,
+ struct vkms_config *vkms_config);
+/**
+ * vkms_config_delete_encoder() - Remove an encoder configuration and frees its memory
+ *
+ * This will delete an encoder configuration from the parent configuration. This will NOT
+ * cleanup and frees the vkms_encoder that can be stored in @vkms_config_encoder. It will also
+ * remove any reference to this CRTC in @vkms_config.
+ *
+ * @vkms_config_encoder: Plane configuration to cleanup
+ * @vkms_config: Parent configuration
*/
-void vkms_config_delete_plane(struct vkms_config_plane *vkms_config_plane);
+void vkms_config_delete_encoder(struct vkms_config_encoder *vkms_config_encoder,
+ struct vkms_config *vkms_config);
/**
* vkms_config_alloc_default() - Allocate the configuration for the default device
diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c
index 6f6d3118b2f2..654238dbba7f 100644
--- a/drivers/gpu/drm/vkms/vkms_crtc.c
+++ b/drivers/gpu/drm/vkms/vkms_crtc.c
@@ -281,9 +281,17 @@ static void vkms_crtc_destroy_workqueue(struct drm_device *dev, void *raw_vkms_c
destroy_workqueue(vkms_crtc->composer_workq);
}
+static void vkms_crtc_cleanup_writeback_connector(struct drm_device *dev, void *data)
+{
+ struct vkms_crtc *vkms_crtc = data;
+
+ drm_writeback_connector_cleanup(&vkms_crtc->wb_connector);
+}
+
struct vkms_crtc *vkms_crtc_init(struct vkms_device *vkmsdev,
struct drm_plane *primary,
- struct drm_plane *cursor)
+ struct drm_plane *cursor,
+ struct vkms_config_crtc *config)
{
struct drm_device *dev = &vkmsdev->drm;
struct vkms_crtc *vkms_crtc;
@@ -317,5 +325,15 @@ struct vkms_crtc *vkms_crtc_init(struct vkms_device *vkmsdev,
if (ret)
return ERR_PTR(ret);
+ if (config->writeback) {
+ ret = vkms_enable_writeback_connector(vkmsdev, vkms_crtc);
+ if (ret)
+ return ERR_PTR(ret);
+ ret = drmm_add_action_or_reset(&vkmsdev->drm, vkms_crtc_cleanup_writeback_connector,
+ vkms_crtc);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
return vkms_crtc;
}
diff --git a/drivers/gpu/drm/vkms/vkms_crtc.h b/drivers/gpu/drm/vkms/vkms_crtc.h
index dde28884a0a5..a271e95f1cfe 100644
--- a/drivers/gpu/drm/vkms/vkms_crtc.h
+++ b/drivers/gpu/drm/vkms/vkms_crtc.h
@@ -86,5 +86,6 @@ struct vkms_crtc {
*/
struct vkms_crtc *vkms_crtc_init(struct vkms_device *vkmsdev,
struct drm_plane *primary,
- struct drm_plane *cursor);
+ struct drm_plane *cursor,
+ struct vkms_config_crtc *config);
#endif //_VKMS_CRTC_H
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
index 403006a0bb61..6deff5099322 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -153,13 +153,12 @@ static const struct drm_connector_helper_funcs vkms_conn_helper_funcs = {
static int vkms_output_init(struct vkms_device *vkmsdev)
{
- struct vkms_config_plane *config_plane;
+ struct vkms_config_encoder *config_encoder;
struct drm_device *dev = &vkmsdev->drm;
+ struct vkms_config_plane *config_plane;
+ struct vkms_config_crtc *config_crtc;
struct drm_connector *connector;
- struct drm_encoder *encoder;
- struct vkms_crtc *crtc;
- struct drm_plane *plane;
- struct vkms_plane *primary, *cursor = NULL;
+ unsigned long idx;
int ret;
int writeback;
@@ -169,22 +168,30 @@ static int vkms_output_init(struct vkms_device *vkmsdev)
ret = PTR_ERR(config_plane->plane);
return ret;
}
-
- if (config_plane->type == DRM_PLANE_TYPE_PRIMARY)
- primary = config_plane->plane;
- else if (config_plane->type == DRM_PLANE_TYPE_CURSOR)
- cursor = config_plane->plane;
}
- /* [1]: Initialize the crtc component */
- crtc = vkms_crtc_init(vkmsdev, &primary->base,
- cursor ? &cursor->base : NULL);
- if (IS_ERR(crtc))
- return PTR_ERR(crtc);
+ list_for_each_entry(config_crtc, &vkmsdev->config->crtcs, link) {
+ struct drm_plane *primary = NULL, *cursor = NULL;
+
+ xa_for_each(&config_crtc->possible_planes, idx, config_plane) {
+ if (config_plane->type == DRM_PLANE_TYPE_PRIMARY)
+ primary = &config_plane->plane->base;
+ else if (config_plane->type == DRM_PLANE_TYPE_CURSOR)
+ cursor = &config_plane->plane->base;
+ }
+
+ config_crtc->crtc = vkms_crtc_init(vkmsdev, primary, cursor, config_crtc);
- /* Enable the output's CRTC for all the planes */
- drm_for_each_plane(plane, &vkmsdev->drm) {
- plane->possible_crtcs |= drm_crtc_mask(&crtc->base);
+ if (IS_ERR(config_crtc->crtc)) {
+ ret = PTR_ERR(config_crtc->crtc);
+ return ret;
+ }
+ }
+
+ list_for_each_entry(config_crtc, &vkmsdev->config->crtcs, link) {
+ xa_for_each(&config_crtc->possible_planes, idx, config_plane) {
+ config_plane->plane->base.possible_crtcs |= drm_crtc_mask(&config_crtc->crtc->base);
+ }
}
/* Initialize the connector component */
@@ -201,32 +208,28 @@ static int vkms_output_init(struct vkms_device *vkmsdev)
drm_connector_helper_add(connector, &vkms_conn_helper_funcs);
- /* Initialize the encoder component */
- encoder = drmm_kzalloc(&vkmsdev->drm, sizeof(*encoder), GFP_KERNEL);
- if (!encoder)
- return -ENOMEM;
-
- ret = drmm_encoder_init(dev, encoder, &vkms_encoder_funcs,
- DRM_MODE_ENCODER_VIRTUAL, NULL);
- if (ret) {
- DRM_ERROR("Failed to init encoder\n");
- return ret;
- }
-
- encoder->possible_crtcs = drm_crtc_mask(&crtc->base);
- /* Attach the encoder and the connector */
- ret = drm_connector_attach_encoder(connector, encoder);
- if (ret) {
- DRM_ERROR("Failed to attach connector to encoder\n");
- return ret;
- }
+ list_for_each_entry(config_encoder, &vkmsdev->config->encoders, link) {
+ config_encoder->encoder = drmm_kzalloc(&vkmsdev->drm,
+ sizeof(*config_encoder->encoder),
+ GFP_KERNEL);
+ if (!config_encoder->encoder)
+ return -ENOMEM;
+ ret = drmm_encoder_init(dev, config_encoder->encoder, &vkms_encoder_funcs,
+ DRM_MODE_ENCODER_VIRTUAL, NULL);
+ if (ret) {
+ DRM_ERROR("Failed to init encoder\n");
+ return ret;
+ }
- /* Initialize the writeback component */
- if (vkmsdev->config->writeback) {
- writeback = vkms_enable_writeback_connector(vkmsdev, crtc);
- if (writeback)
- DRM_ERROR("Failed to init writeback connector\n");
+ xa_for_each(&config_encoder->possible_crtcs, idx, config_crtc) {
+ config_encoder->encoder->possible_crtcs |= drm_crtc_mask(&config_crtc->crtc->base);
+ }
+ if (IS_ERR(config_encoder->encoder))
+ return PTR_ERR(config_encoder->encoder);
+ ret = drm_connector_attach_encoder(connector, config_encoder->encoder);
+ if (ret)
+ return ret;
}
drm_mode_config_reset(dev);
--
2.44.2
> The current VKMS driver can only uses one CRTC and one encoder. This patch
> introduce in the same time CRTC and encoders as they are tighly linked.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> ---
> drivers/gpu/drm/vkms/vkms_config.c | 266 +++++++++++++++++++++++++++++++++----
> drivers/gpu/drm/vkms/vkms_config.h | 102 +++++++++++++-
> drivers/gpu/drm/vkms/vkms_crtc.c | 20 ++-
> drivers/gpu/drm/vkms/vkms_crtc.h | 3 +-
> drivers/gpu/drm/vkms/vkms_drv.c | 87 ++++++------
> 5 files changed, 405 insertions(+), 73 deletions(-)
>
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
> index e9e30974523a..fcd4a128c21b 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.c
> +++ b/drivers/gpu/drm/vkms/vkms_config.c
> @@ -14,6 +14,8 @@ struct vkms_config *vkms_config_create(void)
> return ERR_PTR(-ENOMEM);
>
> INIT_LIST_HEAD(&config->planes);
> + INIT_LIST_HEAD(&config->crtcs);
> + INIT_LIST_HEAD(&config->encoders);
>
> return config;
> }
> @@ -22,12 +24,24 @@ struct vkms_config *vkms_config_alloc_default(bool enable_writeback, bool enable
> bool enable_cursor)
> {
> struct vkms_config_plane *plane;
> + struct vkms_config_encoder *encoder;
> + struct vkms_config_crtc *crtc;
> struct vkms_config *vkms_config = vkms_config_create();
>
> if (IS_ERR(vkms_config))
> return vkms_config;
>
> - vkms_config->writeback = enable_writeback;
> + crtc = vkms_config_create_crtc(vkms_config);
> + if (!crtc)
> + goto err_alloc;
> + crtc->writeback = enable_writeback;
> +
> + encoder = vkms_config_create_encoder(vkms_config);
> + if (!encoder)
> + goto err_alloc;
> +
> + if (vkms_config_encoder_attach_crtc(encoder, crtc))
> + goto err_alloc;
>
> plane = vkms_config_create_plane(vkms_config);
> if (!plane)
> @@ -39,6 +53,9 @@ struct vkms_config *vkms_config_alloc_default(bool enable_writeback, bool enable
> goto err_alloc;
> sprintf(plane->name, "primary");
>
> + if (vkms_config_plane_attach_crtc(plane, crtc))
> + goto err_alloc;
> +
> if (enable_overlay) {
> for (int i = 0; i < NUM_OVERLAY_PLANES; i++) {
> plane = vkms_config_create_plane(vkms_config);
> @@ -49,6 +66,9 @@ struct vkms_config *vkms_config_alloc_default(bool enable_writeback, bool enable
> if (!plane->name)
> goto err_alloc;
> snprintf(plane->name, 10, "plane-%d", i);
> +
> + if (vkms_config_plane_attach_crtc(plane, crtc))
> + goto err_alloc;
> }
> }
> if (enable_cursor) {
> @@ -60,6 +80,9 @@ struct vkms_config *vkms_config_alloc_default(bool enable_writeback, bool enable
> if (!plane->name)
> goto err_alloc;
> sprintf(plane->name, "cursor");
> +
> + if (vkms_config_plane_attach_crtc(plane, crtc))
> + goto err_alloc;
> }
>
> return vkms_config;
> @@ -89,38 +112,196 @@ struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *vkms_conf
> vkms_config_overlay->supported_color_range = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
> BIT(DRM_COLOR_YCBCR_FULL_RANGE);
> vkms_config_overlay->default_color_range = DRM_COLOR_YCBCR_FULL_RANGE;
> + xa_init_flags(&vkms_config_overlay->possible_crtcs, XA_FLAGS_ALLOC);
>
> list_add(&vkms_config_overlay->link, &vkms_config->planes);
>
> return vkms_config_overlay;
> }
>
> -void vkms_config_delete_plane(struct vkms_config_plane *vkms_config_overlay)
> +struct vkms_config_crtc *vkms_config_create_crtc(struct vkms_config *vkms_config)
> {
> - if (!vkms_config_overlay)
> + if (!vkms_config)
> + return NULL;
> +
> + struct vkms_config_crtc *vkms_config_crtc = kzalloc(sizeof(*vkms_config_crtc),
> + GFP_KERNEL);
> +
> + if (!vkms_config_crtc)
> + return NULL;
> +
> + list_add(&vkms_config_crtc->link, &vkms_config->crtcs);
> + xa_init_flags(&vkms_config_crtc->possible_planes, XA_FLAGS_ALLOC);
> + xa_init_flags(&vkms_config_crtc->possible_encoders, XA_FLAGS_ALLOC);
> +
> + return vkms_config_crtc;
> +}
> +
> +struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *vkms_config)
> +{
> + if (!vkms_config)
> + return NULL;
> +
> + struct vkms_config_encoder *vkms_config_encoder = kzalloc(sizeof(*vkms_config_encoder),
> + GFP_KERNEL);
> +
> + if (!vkms_config_encoder)
> + return NULL;
> +
> + list_add(&vkms_config_encoder->link, &vkms_config->encoders);
> + xa_init_flags(&vkms_config_encoder->possible_crtcs, XA_FLAGS_ALLOC);
> +
> + return vkms_config_encoder;
> +}
> +
> +void vkms_config_delete_plane(struct vkms_config_plane *vkms_config_plane,
> + struct vkms_config *vkms_config)
> +{
> + struct vkms_config_crtc *crtc_config;
> + struct vkms_config_plane *plane;
> +
> + if (!vkms_config_plane)
> + return;
> + list_del(&vkms_config_plane->link);
> + xa_destroy(&vkms_config_plane->possible_crtcs);
> +
> + list_for_each_entry(crtc_config, &vkms_config->crtcs, link) {
> + unsigned long idx = 0;
> +
> + xa_for_each(&crtc_config->possible_planes, idx, plane) {
> + if (plane == vkms_config_plane)
> + xa_erase(&crtc_config->possible_planes, idx);
> + }
> + }
> +
> + kfree(vkms_config_plane->name);
> + kfree(vkms_config_plane);
> +}
> +
> +void vkms_config_delete_crtc(struct vkms_config_crtc *vkms_config_crtc,
> + struct vkms_config *vkms_config)
> +{
> + struct vkms_config_crtc *crtc_config;
> + struct vkms_config_plane *plane_config;
> + struct vkms_config_encoder *encoder_config;
> +
> + if (!vkms_config_crtc)
> + return;
> + list_del(&vkms_config_crtc->link);
> + xa_destroy(&vkms_config_crtc->possible_planes);
> + xa_destroy(&vkms_config_crtc->possible_encoders);
> +
> + list_for_each_entry(plane_config, &vkms_config->planes, link) {
> + unsigned long idx = 0;
> +
> + xa_for_each(&plane_config->possible_crtcs, idx, crtc_config) {
> + if (crtc_config == vkms_config_crtc)
> + xa_erase(&plane_config->possible_crtcs, idx);
> + }
> + }
> +
> + list_for_each_entry(encoder_config, &vkms_config->encoders, link) {
> + unsigned long idx = 0;
> +
> + xa_for_each(&encoder_config->possible_crtcs, idx, crtc_config) {
> + if (crtc_config == vkms_config_crtc)
> + xa_erase(&encoder_config->possible_crtcs, idx);
> + }
> + }
> +
> + kfree(vkms_config_crtc);
> +}
> +
> +void vkms_config_delete_encoder(struct vkms_config_encoder *vkms_config_encoder,
> + struct vkms_config *vkms_config)
> +{
> + if (!vkms_config_encoder)
> return;
> - list_del(&vkms_config_overlay->link);
> - kfree(vkms_config_overlay->name);
> - kfree(vkms_config_overlay);
> + list_del(&vkms_config_encoder->link);
> + xa_destroy(&vkms_config_encoder->possible_crtcs);
> +
> + struct vkms_config_crtc *crtc_config;
> + struct vkms_config_encoder *encoder;
> +
> + list_for_each_entry(crtc_config, &vkms_config->crtcs, link) {
> + unsigned long idx = 0;
> +
> + xa_for_each(&crtc_config->possible_encoders, idx, encoder) {
> + if (encoder == vkms_config_encoder)
> + xa_erase(&crtc_config->possible_encoders, idx);
> + }
> + }
> +
> + kfree(vkms_config_encoder);
> }
>
> void vkms_config_destroy(struct vkms_config *config)
> {
> struct vkms_config_plane *vkms_config_plane, *tmp_plane;
> + struct vkms_config_encoder *vkms_config_encoder, *tmp_encoder;
> + struct vkms_config_crtc *vkms_config_crtc, *tmp_crtc;
> +
>
> list_for_each_entry_safe(vkms_config_plane, tmp_plane, &config->planes, link) {
> - vkms_config_delete_plane(vkms_config_plane);
> + vkms_config_delete_plane(vkms_config_plane, config);
> + }
> + list_for_each_entry_safe(vkms_config_encoder, tmp_encoder, &config->encoders, link) {
> + vkms_config_delete_encoder(vkms_config_encoder, config);
> + }
> + list_for_each_entry_safe(vkms_config_crtc, tmp_crtc, &config->crtcs, link) {
> + vkms_config_delete_crtc(vkms_config_crtc, config);
> }
>
> kfree(config);
> }
>
> +int __must_check vkms_config_plane_attach_crtc(struct vkms_config_plane *vkms_config_plane,
> + struct vkms_config_crtc *vkms_config_crtc)
> +{
> + u32 crtc_idx, encoder_idx;
> + int ret;
> +
> + ret = xa_alloc(&vkms_config_plane->possible_crtcs, &crtc_idx, vkms_config_crtc,
> + xa_limit_32b, GFP_KERNEL);
> + if (ret)
> + return ret;
> +
> + ret = xa_alloc(&vkms_config_crtc->possible_planes, &encoder_idx, vkms_config_plane,
> + xa_limit_32b, GFP_KERNEL);
> + if (ret) {
> + xa_erase(&vkms_config_plane->possible_crtcs, crtc_idx);
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *vkms_config_encoder,
> + struct vkms_config_crtc *vkms_config_crtc)
> +{
> + u32 crtc_idx, encoder_idx;
> + int ret;
> +
> + ret = xa_alloc(&vkms_config_encoder->possible_crtcs, &crtc_idx, vkms_config_crtc,
> + xa_limit_32b, GFP_KERNEL);
> + if (ret)
> + return ret;
> +
> + ret = xa_alloc(&vkms_config_crtc->possible_encoders, &encoder_idx, vkms_config_encoder,
> + xa_limit_32b, GFP_KERNEL);
> + if (ret) {
> + xa_erase(&vkms_config_encoder->possible_crtcs, crtc_idx);
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> bool vkms_config_is_valid(struct vkms_config *config)
I still need to look into the xa_* code in more detail, but I think
that it is a great solution for the problem I had handling possible_crtcs
and possible_encoders.
About the validation, If I undertood the docs correctly:
"All drivers should provide one primary plane per CRTC"
https://www.kernel.org/doc/html/v4.17/gpu/drm-kms.html#plane-abstraction
We'd need to check that the number of primary planes is equal to the number
of CRTCs and that a configuration to assing one primary plane to each CRTC
is available.
To illustrate it with an example, this is a valid config:
CRTC 0
CRTC 1
Primary Plane 0: possible_crtcs -> CRTC 0 and CRTC 1
Primary Plane 1: possible_crtcs -> CRTC 0
But this is not a valid config because there are no compatible planes with
CRTC 1:
CRTC 0
CRTC 1
Primary Plane 0: possible_crtcs -> CRTC 0
Primary Plane 1: possible_crtcs -> CRTC 0
I think that your code would fail in the first example because of:
// Multiple primary planes for only one CRTC
if (has_primary)
return false;
Also, we'd need to check that the number of CRTCs and encoders is <= 32.
> {
> struct vkms_config_plane *config_plane;
> -
> - bool has_cursor = false;
> - bool has_primary = false;
> + struct vkms_config_crtc *config_crtc;
> + struct vkms_config_encoder *config_encoder;
>
> list_for_each_entry(config_plane, &config->planes, link) {
> // Default rotation not in supported rotations
> @@ -141,22 +322,47 @@ bool vkms_config_is_valid(struct vkms_config *config)
> BIT(config_plane->default_color_range))
> return false;
>
> - if (config_plane->type == DRM_PLANE_TYPE_PRIMARY) {
> - // Multiple primary planes for only one CRTC
> - if (has_primary)
> - return false;
> - has_primary = true;
> - }
> - if (config_plane->type == DRM_PLANE_TYPE_CURSOR) {
> - // Multiple cursor planes for only one CRTC
> - if (has_cursor)
> - return false;
> - has_cursor = true;
> - }
> + // No CRTC linked to this plane
> + if (xa_empty(&config_plane->possible_crtcs))
> + return false;
> + }
> +
> + list_for_each_entry(config_encoder, &config->encoders, link) {
> + // No CRTC linked to this encoder
> + if (xa_empty(&config_encoder->possible_crtcs))
> + return false;
> }
>
> - if (!has_primary)
> - return false;
> + list_for_each_entry(config_crtc, &config->crtcs, link) {
> + bool has_primary = false;
> + bool has_cursor = false;
> + unsigned long idx = 0;
> +
> + // No encoder attached to this CRTC
> + if (xa_empty(&config_crtc->possible_encoders))
> + return false;
> +
> + xa_for_each(&config_crtc->possible_planes, idx, config_plane) {
> + if (config_plane->type == DRM_PLANE_TYPE_PRIMARY) {
> + // Multiple primary planes for only one CRTC
> + if (has_primary)
> + return false;
> +
> + has_primary = true;
> + }
> + if (config_plane->type == DRM_PLANE_TYPE_CURSOR) {
> + // Multiple cursor planes for only one CRTC
> + if (has_cursor)
> + return false;
> +
> + has_cursor = true;
> + }
> + }
> +
> + // No primary plane for this CRTC
> + if (!has_primary)
> + return false;
> + }
>
> return true;
> }
> @@ -167,8 +373,9 @@ static int vkms_config_show(struct seq_file *m, void *data)
> struct drm_device *dev = entry->dev;
> struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
> struct vkms_config_plane *config_plane;
> + struct vkms_config_crtc *config_crtc;
> + struct vkms_config_encoder *config_encoder;
>
> - seq_printf(m, "writeback=%d\n", vkmsdev->config->writeback);
> list_for_each_entry(config_plane, &vkmsdev->config->planes, link) {
> seq_puts(m, "plane:\n");
> seq_printf(m, "\tname: %s\n", config_plane->name);
> @@ -185,6 +392,15 @@ static int vkms_config_show(struct seq_file *m, void *data)
> config_plane->default_color_range);
> }
>
> + list_for_each_entry(config_encoder, &vkmsdev->config->encoders, link) {
> + seq_puts(m, "encoder:\n");
> + }
> +
> + list_for_each_entry(config_crtc, &vkmsdev->config->crtcs, link) {
> + seq_puts(m, "crtc:\n");
> + seq_printf(m, "\twriteback: %d\n", config_crtc->writeback);
> + }
> +
> return 0;
> }
>
> diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
> index d668aeab9d26..8f247fc09373 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.h
> +++ b/drivers/gpu/drm/vkms/vkms_config.h
> @@ -9,16 +9,59 @@
> /**
> * struct vkms_config - General configuration for VKMS driver
> *
> - * @writeback: If true, a writeback buffer can be attached to the CRTC
> * @planes: List of planes configured for this device. They are created by the function
> * vkms_config_create_plane().
> + * @crtcs: List of crtcs configured for this device. They are created by the function
> + * vkms_config_create_crtc().
> + * @encoders: List of encoders configured for this device. They are created by the function
> + * vkms_config_create_encoder().
> * @dev: Used to store the current vkms device. Only set when the device is instancied.
> */
> struct vkms_config {
> - bool writeback;
> struct vkms_device *dev;
>
> struct list_head planes;
> + struct list_head crtcs;
> + struct list_head encoders;
> +};
> +
> +/**
> + * struct vkms_config_crtc
> + *
> + * @link: Link to the others CRTCs
> + * @possible_planes: List of planes that can be used with this CRTC
> + * @possible_encoders: List of encoders that can be used with this CRTC
> + * @crtc: Internal usage. This pointer should never be considered as valid. It can be used to
> + * store a temporary reference to a vkms crtc during device creation. This pointer is
> + * not managed by the configuration and must be managed by other means.
> + */
> +struct vkms_config_crtc {
> + struct list_head link;
> +
> + bool writeback;
> + struct xarray possible_planes;
> + struct xarray possible_encoders;
> +
> + /* Internal usage */
> + struct vkms_crtc *crtc;
> +};
> +
> +/**
> + * struct vkms_config_encoder
> + *
> + * @link: Link to the others encoders
> + * @possible_crtcs: List of CRTC that can be used with this encoder
> + * @encoder: Internal usage. This pointer should never be considered as valid. It can be used to
> + * store a temporary reference to a vkms encoder during device creation. This pointer is
> + * not managed by the configuration and must be managed by other means.
> + */
> +struct vkms_config_encoder {
> + struct list_head link;
> +
> + struct xarray possible_crtcs;
> +
> + /* Internal usage */
> + struct drm_encoder *encoder;
> };
>
> /**
> @@ -34,6 +77,7 @@ struct vkms_config {
> * @supported_color_encoding: Color encoding that this plane will support
> * @default_color_range: Default color range that should be used by this plane
> * @supported_color_range: Color range that this plane will support
> + * @possible_crtcs: List of CRTC that can be used with this plane.
> * @plane: Internal usage. This pointer should never be considered as valid. It can be used to
> * store a temporary reference to a vkms plane during device creation. This pointer is
> * not managed by the configuration and must be managed by other means.
> @@ -50,6 +94,7 @@ struct vkms_config_plane {
> enum drm_color_range default_color_range;
> unsigned int supported_color_range;
>
> + struct xarray possible_crtcs;
> /* Internal usage */
> struct vkms_plane *plane;
> };
> @@ -84,14 +129,63 @@ bool vkms_config_is_valid(struct vkms_config *vkms_config);
> */
> struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *vkms_config);
>
> +/**
> + * vkms_config_create_crtc() - Create a crtc configuration
> + *
> + * This will allocate and add a new crtc configuration to @vkms_config.
> + * @vkms_config: Configuration where to insert new crtc configuration
> + */
> +struct vkms_config_crtc *vkms_config_create_crtc(struct vkms_config *vkms_config);
> +
> +/**
> + * vkms_config_create_encoder() - Create an encoder configuration
> + *
> + * This will allocate and add a new encoder configuration to @vkms_config.
> + * @vkms_config: Configuration where to insert new encoder configuration
> + */
> +struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *vkms_config);
> +
> +int __must_check vkms_config_plane_attach_crtc(struct vkms_config_plane *vkms_config_plane,
> + struct vkms_config_crtc *vkms_config_crtc);
> +int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *vkms_config_encoder,
> + struct vkms_config_crtc *vkms_config_crtc);
> +
> /**
> * vkms_config_delete_plane() - Remove a plane configuration and frees its memory
> *
> * This will delete a plane configuration from the parent configuration. This will NOT
> - * cleanup and frees the vkms_plane that can be stored in @vkms_config_plane.
> + * cleanup and frees the vkms_plane that can be stored in @vkms_config_plane. It will also remove
> + * any reference to this plane in @vkms_config.
> + *
> * @vkms_config_plane: Plane configuration to cleanup
> + * @vkms_config: Parent configuration
> + */
> +void vkms_config_delete_plane(struct vkms_config_plane *vkms_config_plane,
> + struct vkms_config *vkms_config);
> +/**
> + * vkms_config_delete_crtc() - Remove a CRTC configuration and frees its memory
> + *
> + * This will delete a CRTC configuration from the parent configuration. This will NOT
> + * cleanup and frees the vkms_crtc that can be stored in @vkms_config_crtc. It will also remove
> + * any reference to this CRTC in @vkms_config.
> + *
> + * @vkms_config_crtc: Plane configuration to cleanup
> + * @vkms_config: Parent configuration
> + */
> +void vkms_config_delete_crtc(struct vkms_config_crtc *vkms_config_crtc,
> + struct vkms_config *vkms_config);
> +/**
> + * vkms_config_delete_encoder() - Remove an encoder configuration and frees its memory
> + *
> + * This will delete an encoder configuration from the parent configuration. This will NOT
> + * cleanup and frees the vkms_encoder that can be stored in @vkms_config_encoder. It will also
> + * remove any reference to this CRTC in @vkms_config.
> + *
> + * @vkms_config_encoder: Plane configuration to cleanup
> + * @vkms_config: Parent configuration
> */
> -void vkms_config_delete_plane(struct vkms_config_plane *vkms_config_plane);
> +void vkms_config_delete_encoder(struct vkms_config_encoder *vkms_config_encoder,
> + struct vkms_config *vkms_config);
>
> /**
> * vkms_config_alloc_default() - Allocate the configuration for the default device
> diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c
> index 6f6d3118b2f2..654238dbba7f 100644
> --- a/drivers/gpu/drm/vkms/vkms_crtc.c
> +++ b/drivers/gpu/drm/vkms/vkms_crtc.c
> @@ -281,9 +281,17 @@ static void vkms_crtc_destroy_workqueue(struct drm_device *dev, void *raw_vkms_c
> destroy_workqueue(vkms_crtc->composer_workq);
> }
>
> +static void vkms_crtc_cleanup_writeback_connector(struct drm_device *dev, void *data)
> +{
> + struct vkms_crtc *vkms_crtc = data;
> +
> + drm_writeback_connector_cleanup(&vkms_crtc->wb_connector);
> +}
> +
> struct vkms_crtc *vkms_crtc_init(struct vkms_device *vkmsdev,
> struct drm_plane *primary,
> - struct drm_plane *cursor)
> + struct drm_plane *cursor,
> + struct vkms_config_crtc *config)
> {
> struct drm_device *dev = &vkmsdev->drm;
> struct vkms_crtc *vkms_crtc;
> @@ -317,5 +325,15 @@ struct vkms_crtc *vkms_crtc_init(struct vkms_device *vkmsdev,
> if (ret)
> return ERR_PTR(ret);
>
> + if (config->writeback) {
> + ret = vkms_enable_writeback_connector(vkmsdev, vkms_crtc);
> + if (ret)
> + return ERR_PTR(ret);
> + ret = drmm_add_action_or_reset(&vkmsdev->drm, vkms_crtc_cleanup_writeback_connector,
> + vkms_crtc);
> + if (ret)
> + return ERR_PTR(ret);
> + }
> +
> return vkms_crtc;
> }
> diff --git a/drivers/gpu/drm/vkms/vkms_crtc.h b/drivers/gpu/drm/vkms/vkms_crtc.h
> index dde28884a0a5..a271e95f1cfe 100644
> --- a/drivers/gpu/drm/vkms/vkms_crtc.h
> +++ b/drivers/gpu/drm/vkms/vkms_crtc.h
> @@ -86,5 +86,6 @@ struct vkms_crtc {
> */
> struct vkms_crtc *vkms_crtc_init(struct vkms_device *vkmsdev,
> struct drm_plane *primary,
> - struct drm_plane *cursor);
> + struct drm_plane *cursor,
> + struct vkms_config_crtc *config);
> #endif //_VKMS_CRTC_H
> diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
> index 403006a0bb61..6deff5099322 100644
> --- a/drivers/gpu/drm/vkms/vkms_drv.c
> +++ b/drivers/gpu/drm/vkms/vkms_drv.c
> @@ -153,13 +153,12 @@ static const struct drm_connector_helper_funcs vkms_conn_helper_funcs = {
>
> static int vkms_output_init(struct vkms_device *vkmsdev)
> {
> - struct vkms_config_plane *config_plane;
> + struct vkms_config_encoder *config_encoder;
> struct drm_device *dev = &vkmsdev->drm;
> + struct vkms_config_plane *config_plane;
> + struct vkms_config_crtc *config_crtc;
> struct drm_connector *connector;
> - struct drm_encoder *encoder;
> - struct vkms_crtc *crtc;
> - struct drm_plane *plane;
> - struct vkms_plane *primary, *cursor = NULL;
> + unsigned long idx;
> int ret;
> int writeback;
>
> @@ -169,22 +168,30 @@ static int vkms_output_init(struct vkms_device *vkmsdev)
> ret = PTR_ERR(config_plane->plane);
> return ret;
> }
> -
> - if (config_plane->type == DRM_PLANE_TYPE_PRIMARY)
> - primary = config_plane->plane;
> - else if (config_plane->type == DRM_PLANE_TYPE_CURSOR)
> - cursor = config_plane->plane;
> }
>
> - /* [1]: Initialize the crtc component */
> - crtc = vkms_crtc_init(vkmsdev, &primary->base,
> - cursor ? &cursor->base : NULL);
> - if (IS_ERR(crtc))
> - return PTR_ERR(crtc);
> + list_for_each_entry(config_crtc, &vkmsdev->config->crtcs, link) {
> + struct drm_plane *primary = NULL, *cursor = NULL;
> +
> + xa_for_each(&config_crtc->possible_planes, idx, config_plane) {
> + if (config_plane->type == DRM_PLANE_TYPE_PRIMARY)
> + primary = &config_plane->plane->base;
> + else if (config_plane->type == DRM_PLANE_TYPE_CURSOR)
> + cursor = &config_plane->plane->base;
> + }
> +
> + config_crtc->crtc = vkms_crtc_init(vkmsdev, primary, cursor, config_crtc);
>
> - /* Enable the output's CRTC for all the planes */
> - drm_for_each_plane(plane, &vkmsdev->drm) {
> - plane->possible_crtcs |= drm_crtc_mask(&crtc->base);
> + if (IS_ERR(config_crtc->crtc)) {
> + ret = PTR_ERR(config_crtc->crtc);
> + return ret;
> + }
> + }
> +
> + list_for_each_entry(config_crtc, &vkmsdev->config->crtcs, link) {
> + xa_for_each(&config_crtc->possible_planes, idx, config_plane) {
> + config_plane->plane->base.possible_crtcs |= drm_crtc_mask(&config_crtc->crtc->base);
> + }
> }
>
> /* Initialize the connector component */
> @@ -201,32 +208,28 @@ static int vkms_output_init(struct vkms_device *vkmsdev)
>
> drm_connector_helper_add(connector, &vkms_conn_helper_funcs);
>
> - /* Initialize the encoder component */
> - encoder = drmm_kzalloc(&vkmsdev->drm, sizeof(*encoder), GFP_KERNEL);
> - if (!encoder)
> - return -ENOMEM;
> -
> - ret = drmm_encoder_init(dev, encoder, &vkms_encoder_funcs,
> - DRM_MODE_ENCODER_VIRTUAL, NULL);
> - if (ret) {
> - DRM_ERROR("Failed to init encoder\n");
> - return ret;
> - }
> -
> - encoder->possible_crtcs = drm_crtc_mask(&crtc->base);
>
> - /* Attach the encoder and the connector */
> - ret = drm_connector_attach_encoder(connector, encoder);
> - if (ret) {
> - DRM_ERROR("Failed to attach connector to encoder\n");
> - return ret;
> - }
> + list_for_each_entry(config_encoder, &vkmsdev->config->encoders, link) {
> + config_encoder->encoder = drmm_kzalloc(&vkmsdev->drm,
> + sizeof(*config_encoder->encoder),
> + GFP_KERNEL);
> + if (!config_encoder->encoder)
> + return -ENOMEM;
> + ret = drmm_encoder_init(dev, config_encoder->encoder, &vkms_encoder_funcs,
> + DRM_MODE_ENCODER_VIRTUAL, NULL);
> + if (ret) {
> + DRM_ERROR("Failed to init encoder\n");
> + return ret;
> + }
>
> - /* Initialize the writeback component */
> - if (vkmsdev->config->writeback) {
> - writeback = vkms_enable_writeback_connector(vkmsdev, crtc);
> - if (writeback)
> - DRM_ERROR("Failed to init writeback connector\n");
> + xa_for_each(&config_encoder->possible_crtcs, idx, config_crtc) {
> + config_encoder->encoder->possible_crtcs |= drm_crtc_mask(&config_crtc->crtc->base);
> + }
> + if (IS_ERR(config_encoder->encoder))
> + return PTR_ERR(config_encoder->encoder);
> + ret = drm_connector_attach_encoder(connector, config_encoder->encoder);
> + if (ret)
> + return ret;
> }
>
> drm_mode_config_reset(dev);
>
© 2016 - 2025 Red Hat, Inc.