Re: [PATCH v2 13/15] drm/vkms: Allow to attach encoders and CRTCs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 11/02/25 - 12:09, José Expósito wrote:
> Add a list of possible CRTCs to the encoder configuration and helpers to
> attach and detach them.
> 
> Now that the default configuration has its encoder and CRTC correctly
> attached, configure the output following the configuration.
> 
> Co-developed-by: Louis Chauvet <louis.chauvet@xxxxxxxxxxx>
> Signed-off-by: Louis Chauvet <louis.chauvet@xxxxxxxxxxx>
> Signed-off-by: José Expósito <jose.exposito89@xxxxxxxxx>

Build is broken when compiling VKMS as a module:

diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index fdb535be912a..a5e77c752da3 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -497,6 +497,7 @@ int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *enc
        return xa_alloc(&encoder_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
                        xa_limit_32b, GFP_KERNEL);
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_encoder_attach_crtc);

 void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
                                     struct vkms_config_crtc *crtc_cfg)
@@ -509,3 +510,4 @@ void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
                        xa_erase(&encoder_cfg->possible_crtcs, idx);
        }
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_encoder_detach_crtc);


> ---
>  .clang-format                                 |   1 +
>  drivers/gpu/drm/vkms/tests/vkms_config_test.c | 115 ++++++++++++++++++
>  drivers/gpu/drm/vkms/vkms_config.c            |  77 ++++++++++++
>  drivers/gpu/drm/vkms/vkms_config.h            |  29 +++++
>  drivers/gpu/drm/vkms/vkms_output.c            |  49 +++++---
>  5 files changed, 251 insertions(+), 20 deletions(-)
> 
> diff --git a/.clang-format b/.clang-format
> index c355a2f58eed..5d21c0e4edbd 100644
> --- a/.clang-format
> +++ b/.clang-format
> @@ -693,6 +693,7 @@ ForEachMacros:
>    - 'vkms_config_for_each_crtc'
>    - 'vkms_config_for_each_encoder'
>    - 'vkms_config_for_each_plane'
> +  - 'vkms_config_encoder_for_each_possible_crtc'
>    - 'vkms_config_plane_for_each_possible_crtc'
>    - 'while_for_each_ftrace_op'
>    - 'xa_for_each'
> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> index 0bb76a1e6c79..7458d175acb6 100644
> --- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> +++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> @@ -260,6 +260,7 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
>  	struct vkms_config *config;
>  	struct vkms_config_plane *plane_cfg;
>  	struct vkms_config_crtc *crtc_cfg;
> +	struct vkms_config_encoder *encoder_cfg;
>  	int err;
>  
>  	config = vkms_config_default_create(false, false, false);
> @@ -313,6 +314,9 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
>  
>  	/* Invalid: Second CRTC without primary plane */
>  	crtc_cfg = vkms_config_create_crtc(config);
> +	encoder_cfg = vkms_config_create_encoder(config);
> +	err = vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg);
> +	KUNIT_EXPECT_EQ(test, err, 0);
>  	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
>  
>  	/* Valid: Second CRTC with a primary plane */
> @@ -390,6 +394,51 @@ static void vkms_config_test_invalid_encoder_number(struct kunit *test)
>  	vkms_config_destroy(config);
>  }
>  
> +static void vkms_config_test_valid_encoder_possible_crtcs(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_plane *plane_cfg;
> +	struct vkms_config_crtc *crtc_cfg1, *crtc_cfg2;
> +	struct vkms_config_encoder *encoder_cfg;
> +	int err;
> +
> +	config = vkms_config_default_create(false, false, false);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	crtc_cfg1 = list_first_entry(&config->crtcs, typeof(*crtc_cfg1), link);
> +
> +	/* Invalid: Encoder without a possible CRTC */
> +	encoder_cfg = vkms_config_create_encoder(config);
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	/* Valid: Second CRTC with shared encoder */
> +	crtc_cfg2 = vkms_config_create_crtc(config);
> +
> +	plane_cfg = vkms_config_create_plane(config);
> +	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
> +	err = vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg2);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +
> +	err = vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg1);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +
> +	err = vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg2);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +
> +	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
> +
> +	/* Invalid: Second CRTC without encoders */
> +	vkms_config_encoder_detach_crtc(encoder_cfg, crtc_cfg2);
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	/* Valid: First CRTC with 2 possible encoder */
> +	vkms_config_destroy_plane(plane_cfg);
> +	vkms_config_destroy_crtc(config, crtc_cfg2);
> +	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
> +
> +	vkms_config_destroy(config);
> +}
> +
>  static void vkms_config_test_plane_attach_crtc(struct kunit *test)
>  {
>  	struct vkms_config *config;
> @@ -515,6 +564,70 @@ static void vkms_config_test_plane_get_possible_crtcs(struct kunit *test)
>  	vkms_config_destroy(config);
>  }
>  
> +static void vkms_config_test_encoder_get_possible_crtcs(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_encoder *encoder_cfg1, *encoder_cfg2;
> +	struct vkms_config_crtc *crtc_cfg1, *crtc_cfg2;
> +	struct vkms_config_crtc *possible_crtc;
> +	unsigned long idx = 0;
> +	int n_crtcs = 0;
> +	int err;
> +
> +	config = vkms_config_create("test");
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	encoder_cfg1 = vkms_config_create_encoder(config);
> +	encoder_cfg2 = vkms_config_create_encoder(config);
> +	crtc_cfg1 = vkms_config_create_crtc(config);
> +	crtc_cfg2 = vkms_config_create_crtc(config);
> +
> +	/* No possible CRTCs */
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg1, idx, possible_crtc)
> +		KUNIT_FAIL(test, "Unexpected possible CRTC");
> +
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg2, idx, possible_crtc)
> +		KUNIT_FAIL(test, "Unexpected possible CRTC");
> +
> +	/* Encoder 1 attached to CRTC 1 and 2 */
> +	err = vkms_config_encoder_attach_crtc(encoder_cfg1, crtc_cfg1);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +	err = vkms_config_encoder_attach_crtc(encoder_cfg1, crtc_cfg2);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg1, idx, possible_crtc) {
> +		n_crtcs++;
> +		if (possible_crtc != crtc_cfg1 && possible_crtc != crtc_cfg2)
> +			KUNIT_FAIL(test, "Unexpected possible CRTC");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_crtcs, 2);
> +	n_crtcs = 0;
> +
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg2, idx, possible_crtc)
> +		KUNIT_FAIL(test, "Unexpected possible CRTC");
> +
> +	/* Encoder 1 attached to CRTC 1 and encoder 2 to CRTC 2 */
> +	vkms_config_encoder_detach_crtc(encoder_cfg1, crtc_cfg2);
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg1, idx, possible_crtc) {
> +		n_crtcs++;
> +		if (possible_crtc != crtc_cfg1)
> +			KUNIT_FAIL(test, "Unexpected possible CRTC");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_crtcs, 1);
> +	n_crtcs = 0;
> +
> +	err = vkms_config_encoder_attach_crtc(encoder_cfg2, crtc_cfg2);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg2, idx, possible_crtc) {
> +		n_crtcs++;
> +		if (possible_crtc != crtc_cfg2)
> +			KUNIT_FAIL(test, "Unexpected possible CRTC");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_crtcs, 1);
> +
> +	vkms_config_destroy(config);
> +}
> +
>  static struct kunit_case vkms_config_test_cases[] = {
>  	KUNIT_CASE(vkms_config_test_empty_config),
>  	KUNIT_CASE_PARAM(vkms_config_test_default_config,
> @@ -527,8 +640,10 @@ static struct kunit_case vkms_config_test_cases[] = {
>  	KUNIT_CASE(vkms_config_test_valid_plane_possible_crtcs),
>  	KUNIT_CASE(vkms_config_test_invalid_crtc_number),
>  	KUNIT_CASE(vkms_config_test_invalid_encoder_number),
> +	KUNIT_CASE(vkms_config_test_valid_encoder_possible_crtcs),
>  	KUNIT_CASE(vkms_config_test_plane_attach_crtc),
>  	KUNIT_CASE(vkms_config_test_plane_get_possible_crtcs),
> +	KUNIT_CASE(vkms_config_test_encoder_get_possible_crtcs),
>  	{}
>  };
>  
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
> index 0cf6105fe743..f727c0009489 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.c
> +++ b/drivers/gpu/drm/vkms/vkms_config.c
> @@ -84,6 +84,9 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
>  	if (IS_ERR(encoder_cfg))
>  		goto err_alloc;
>  
> +	if (vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg))
> +		goto err_alloc;
> +
>  	return config;
>  
>  err_alloc:
> @@ -212,6 +215,42 @@ static bool valid_encoder_number(struct vkms_config *config)
>  	return true;
>  }
>  
> +static bool valid_encoder_possible_crtcs(struct vkms_config *config)
> +{
> +	struct drm_device *dev = &config->dev->drm;

Ditto

> +	struct vkms_config_crtc *crtc_cfg;
> +	struct vkms_config_encoder *encoder_cfg;
> +
> +	vkms_config_for_each_encoder(config, encoder_cfg) {
> +		if (xa_empty(&encoder_cfg->possible_crtcs)) {
> +			drm_info(dev, "All encoders must have at least one possible CRTC\n");
> +			return false;
> +		}
> +	}
> +
> +	vkms_config_for_each_crtc(config, crtc_cfg) {
> +		bool crtc_has_encoder = false;
> +
> +		vkms_config_for_each_encoder(config, encoder_cfg) {
> +			struct vkms_config_crtc *possible_crtc;
> +			unsigned long idx = 0;
> +
> +			vkms_config_encoder_for_each_possible_crtc(encoder_cfg,
> +								   idx, possible_crtc) {
> +				if (possible_crtc == crtc_cfg)
> +					crtc_has_encoder = true;
> +			}
> +		}
> +
> +		if (!crtc_has_encoder) {
> +			drm_info(dev, "All CRTCs must have at least one possible encoder\n");
> +			return false;
> +		}
> +	}
> +
> +	return true;
> +}
> +
>  bool vkms_config_is_valid(struct vkms_config *config)
>  {
>  	struct vkms_config_crtc *crtc_cfg;
> @@ -233,6 +272,9 @@ bool vkms_config_is_valid(struct vkms_config *config)
>  			return false;
>  	}
>  
> +	if (!valid_encoder_possible_crtcs(config))
> +		return false;
> +
>  	return true;
>  }
>  
> @@ -347,10 +389,14 @@ void vkms_config_destroy_crtc(struct vkms_config *config,
>  			      struct vkms_config_crtc *crtc_cfg)
>  {
>  	struct vkms_config_plane *plane_cfg;
> +	struct vkms_config_encoder *encoder_cfg;
>  
>  	vkms_config_for_each_plane(config, plane_cfg)
>  		vkms_config_plane_detach_crtc(plane_cfg, crtc_cfg);
>  
> +	vkms_config_for_each_encoder(config, encoder_cfg)
> +		vkms_config_encoder_detach_crtc(encoder_cfg, crtc_cfg);
> +
>  	list_del(&crtc_cfg->link);
>  	kfree(crtc_cfg);
>  }
> @@ -406,6 +452,8 @@ struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *confi
>  	if (!encoder_cfg)
>  		return ERR_PTR(-ENOMEM);
>  
> +	xa_init_flags(&encoder_cfg->possible_crtcs, XA_FLAGS_ALLOC);
> +
>  	list_add_tail(&encoder_cfg->link, &config->encoders);
>  
>  	return encoder_cfg;
> @@ -414,6 +462,35 @@ struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *confi
>  void vkms_config_destroy_encoder(struct vkms_config *config,
>  				 struct vkms_config_encoder *encoder_cfg)
>  {
> +	xa_destroy(&encoder_cfg->possible_crtcs);
>  	list_del(&encoder_cfg->link);
>  	kfree(encoder_cfg);
>  }
> +
> +int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *encoder_cfg,
> +						 struct vkms_config_crtc *crtc_cfg)
> +{
> +	struct vkms_config_crtc *possible_crtc;
> +	unsigned long idx = 0;
> +	u32 crtc_idx = 0;
> +
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) {
> +		if (possible_crtc == crtc_cfg)
> +			return -EINVAL;
> +	}
> +
> +	return xa_alloc(&encoder_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
> +			xa_limit_32b, GFP_KERNEL);
> +}
> +
> +void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
> +				     struct vkms_config_crtc *crtc_cfg)
> +{
> +	struct vkms_config_crtc *possible_crtc;
> +	unsigned long idx = 0;
> +
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) {
> +		if (possible_crtc == crtc_cfg)
> +			xa_erase(&encoder_cfg->possible_crtcs, idx);
> +	}
> +}
> diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
> index 2ba80c4c9ce5..28c24afebe1e 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.h
> +++ b/drivers/gpu/drm/vkms/vkms_config.h
> @@ -71,6 +71,7 @@ struct vkms_config_crtc {
>   * struct vkms_config_encoder
>   *
>   * @link: Link to the others encoders in vkms_config
> + * @possible_crtcs: Array of CRTCs 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
> @@ -79,6 +80,8 @@ struct vkms_config_crtc {
>  struct vkms_config_encoder {
>  	struct list_head link;
>  
> +	struct xarray possible_crtcs;
> +
>  	/* Internal usage */
>  	struct drm_encoder *encoder;
>  };
> @@ -117,6 +120,16 @@ struct vkms_config_encoder {
>  #define vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) \
>  	xa_for_each(&(plane_cfg)->possible_crtcs, idx, (possible_crtc))
>  
> +/**
> + * vkms_config_encoder_for_each_possible_crtc - Iterate over the
> + * vkms_config_encoder possible CRTCs
> + * @encoder_cfg: &struct vkms_config_encoder pointer
> + * @idx: Index of the cursor
> + * @possible_crtc: &struct vkms_config_crtc pointer used as cursor
> + */
> +#define vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) \
> +	xa_for_each(&(encoder_cfg)->possible_crtcs, idx, (possible_crtc))
> +
>  /**
>   * vkms_config_create() - Create a new VKMS configuration
>   * @dev_name: Name of the device
> @@ -326,4 +339,20 @@ struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *confi
>  void vkms_config_destroy_encoder(struct vkms_config *config,
>  				 struct vkms_config_encoder *encoder_cfg);
>  
> +/**
> + * vkms_config_encoder_attach_crtc - Attach a encoder to a CRTC
> + * @encoder_cfg: Encoder to attach
> + * @crtc_cfg: CRTC to attach @encoder_cfg to
> + */
> +int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *encoder_cfg,
> +						 struct vkms_config_crtc *crtc_cfg);
> +
> +/**
> + * vkms_config_encoder_detach_crtc - Detach a encoder from a CRTC
> + * @encoder_cfg: Encoder to detach
> + * @crtc_cfg: CRTC to detach @encoder_cfg from
> + */
> +void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
> +				     struct vkms_config_crtc *crtc_cfg);
> +
>  #endif /* _VKMS_CONFIG_H_ */
> diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
> index f63bc8e3014b..8920d6b5d105 100644
> --- a/drivers/gpu/drm/vkms/vkms_output.c
> +++ b/drivers/gpu/drm/vkms/vkms_output.c
> @@ -9,9 +9,9 @@ int vkms_output_init(struct vkms_device *vkmsdev)
>  {
>  	struct drm_device *dev = &vkmsdev->drm;
>  	struct vkms_connector *connector;
> -	struct drm_encoder *encoder;
>  	struct vkms_config_plane *plane_cfg;
>  	struct vkms_config_crtc *crtc_cfg;
> +	struct vkms_config_encoder *encoder_cfg;
>  	int ret;
>  	int writeback;
>  
> @@ -61,32 +61,41 @@ int vkms_output_init(struct vkms_device *vkmsdev)
>  		}
>  	}
>  
> +	vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg) {
> +		struct vkms_config_crtc *possible_crtc;
> +		unsigned long idx = 0;
> +
> +		encoder_cfg->encoder = drmm_kzalloc(dev, sizeof(*encoder_cfg->encoder), GFP_KERNEL);
> +		if (!encoder_cfg->encoder) {
> +			DRM_ERROR("Failed to allocate encoder\n");
> +			return -ENOMEM;
> +		}
> +		ret = drmm_encoder_init(dev, encoder_cfg->encoder, NULL,
> +					DRM_MODE_ENCODER_VIRTUAL, NULL);
> +		if (ret) {
> +			DRM_ERROR("Failed to init encoder\n");
> +			return ret;
> +		}
> +
> +		vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) {
> +			encoder_cfg->encoder->possible_crtcs |=
> +				drm_crtc_mask(&possible_crtc->crtc->crtc);
> +		}
> +	}
> +
>  	connector = vkms_connector_init(vkmsdev);
>  	if (IS_ERR(connector)) {
>  		DRM_ERROR("Failed to init connector\n");
>  		return PTR_ERR(connector);
>  	}
>  
> -	encoder = drmm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL);
> -	if (!encoder) {
> -		DRM_ERROR("Failed to allocate encoder\n");
> -		return -ENOMEM;
> -	}
> -	ret = drmm_encoder_init(dev, encoder, NULL,
> -				DRM_MODE_ENCODER_VIRTUAL, NULL);
> -	if (ret) {
> -		DRM_ERROR("Failed to init encoder\n");
> -		return ret;
> -	}
> -
> -	vkms_config_for_each_crtc(vkmsdev->config, crtc_cfg)
> -		encoder->possible_crtcs = drm_crtc_mask(&crtc_cfg->crtc->crtc);
> -
>  	/* Attach the encoder and the connector */
> -	ret = drm_connector_attach_encoder(&connector->base, encoder);
> -	if (ret) {
> -		DRM_ERROR("Failed to attach connector to encoder\n");
> -		return ret;
> +	vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg) {
> +		ret = drm_connector_attach_encoder(&connector->base, encoder_cfg->encoder);
> +		if (ret) {
> +			DRM_ERROR("Failed to attach connector to encoder\n");
> +			return ret;
> +		}
>  	}
>  
>  	drm_mode_config_reset(dev);
> -- 
> 2.48.1
> 



[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux