> 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.chau...@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);
> 

Reply via email to