> 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@xxxxxxxxxxx> > --- > 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); >