@@ -688,6 +688,67 @@ static void vkms_config_test_encoder_get_possible_crtcs(struct kunit *test)
vkms_config_destroy(config);
}
+static void vkms_config_test_connector_get_possible_encoders(struct kunit *test)
+{
+ struct vkms_config *config;
+ struct vkms_config_connector *connector_cfg1, *connector_cfg2;
+ struct vkms_config_encoder *encoder_cfg1, *encoder_cfg2;
+ struct vkms_config_encoder **array;
+ size_t length;
+ int err;
+
+ config = vkms_config_create("test");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+ connector_cfg1 = vkms_config_add_connector(config);
+ connector_cfg2 = vkms_config_add_connector(config);
+ encoder_cfg1 = vkms_config_add_encoder(config);
+ encoder_cfg2 = vkms_config_add_encoder(config);
+
+ /* No possible encoders */
+ array = vkms_config_connector_get_possible_encoders(connector_cfg1, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ array = vkms_config_connector_get_possible_encoders(connector_cfg2, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ /* Connector 1 attached to encoders 1 and 2 */
+ err = vkms_config_connector_attach_encoder(connector_cfg1, encoder_cfg1);
+ KUNIT_EXPECT_EQ(test, err, 0);
+ err = vkms_config_connector_attach_encoder(connector_cfg1, encoder_cfg2);
+ KUNIT_EXPECT_EQ(test, err, 0);
+
+ array = vkms_config_connector_get_possible_encoders(connector_cfg1, &length);
+ KUNIT_ASSERT_EQ(test, length, 2);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], encoder_cfg1);
+ KUNIT_ASSERT_PTR_EQ(test, array[1], encoder_cfg2);
+ kfree(array);
+
+ array = vkms_config_connector_get_possible_encoders(connector_cfg2, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ /* Connector 1 attached to encoder 1 and connector 2 to encoder 2 */
+ vkms_config_connector_detach_encoder(connector_cfg1, encoder_cfg2);
+
+ array = vkms_config_connector_get_possible_encoders(connector_cfg1, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], encoder_cfg1);
+ kfree(array);
+
+ err = vkms_config_connector_attach_encoder(connector_cfg2, encoder_cfg2);
+ KUNIT_EXPECT_EQ(test, err, 0);
+
+ array = vkms_config_connector_get_possible_encoders(connector_cfg2, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], encoder_cfg2);
+ kfree(array);
+
+ 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,
@@ -706,6 +767,7 @@ static struct kunit_case vkms_config_test_cases[] = {
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),
+ KUNIT_CASE(vkms_config_test_connector_get_possible_encoders),
{}
};
@@ -94,6 +94,9 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
goto err_alloc;
vkms_config_connector_set_enabled(connector_cfg, true);
+ if (vkms_config_connector_attach_encoder(connector_cfg, encoder_cfg))
+ goto err_alloc;
+
return config;
err_alloc:
@@ -629,6 +632,11 @@ struct vkms_config_encoder *vkms_config_add_encoder(struct vkms_config *config)
void vkms_config_destroy_encoder(struct vkms_config *config,
struct vkms_config_encoder *encoder_cfg)
{
+ struct vkms_config_connector *connector_cfg;
+
+ list_for_each_entry(connector_cfg, &config->connectors, link)
+ vkms_config_connector_detach_encoder(connector_cfg, encoder_cfg);
+
xa_destroy(&encoder_cfg->possible_crtcs);
list_del(&encoder_cfg->link);
kfree(encoder_cfg);
@@ -702,6 +710,7 @@ struct vkms_config_connector *vkms_config_add_connector(struct vkms_config *conf
return ERR_PTR(-ENOMEM);
vkms_config_connector_set_enabled(connector_cfg, false);
+ xa_init_flags(&connector_cfg->possible_encoders, XA_FLAGS_ALLOC);
list_add_tail(&connector_cfg->link, &config->connectors);
@@ -710,6 +719,66 @@ struct vkms_config_connector *vkms_config_add_connector(struct vkms_config *conf
void vkms_config_destroy_connector(struct vkms_config_connector *connector_cfg)
{
+ xa_destroy(&connector_cfg->possible_encoders);
list_del(&connector_cfg->link);
kfree(connector_cfg);
}
+
+int __must_check vkms_config_connector_attach_encoder(struct vkms_config_connector *connector_cfg,
+ struct vkms_config_encoder *encoder_cfg)
+{
+ struct vkms_config_encoder *possible_encoder;
+ unsigned long idx = 0;
+ u32 encoder_idx = 0;
+
+ xa_for_each(&connector_cfg->possible_encoders, idx, possible_encoder) {
+ if (possible_encoder == encoder_cfg)
+ return -EINVAL;
+ }
+
+ return xa_alloc(&connector_cfg->possible_encoders, &encoder_idx,
+ encoder_cfg, xa_limit_32b, GFP_KERNEL);
+}
+
+void vkms_config_connector_detach_encoder(struct vkms_config_connector *connector_cfg,
+ struct vkms_config_encoder *encoder_cfg)
+{
+ struct vkms_config_encoder *possible_encoder;
+ unsigned long idx = 0;
+
+ xa_for_each(&connector_cfg->possible_encoders, idx, possible_encoder) {
+ if (possible_encoder == encoder_cfg)
+ xa_erase(&connector_cfg->possible_encoders, idx);
+ }
+}
+
+struct vkms_config_encoder **
+vkms_config_connector_get_possible_encoders(struct vkms_config_connector *connector_cfg,
+ size_t *out_length)
+{
+ struct vkms_config_encoder **array;
+ struct vkms_config_encoder *possible_encoder;
+ unsigned long idx;
+ size_t length = 0;
+ int n = 0;
+
+ xa_for_each(&connector_cfg->possible_encoders, idx, possible_encoder)
+ length++;
+
+ if (length == 0) {
+ *out_length = length;
+ return NULL;
+ }
+
+ array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
+ if (!array)
+ return ERR_PTR(-ENOMEM);
+
+ xa_for_each(&connector_cfg->possible_encoders, idx, possible_encoder) {
+ array[n] = possible_encoder;
+ n++;
+ }
+
+ *out_length = length;
+ return array;
+}
@@ -94,6 +94,7 @@ struct vkms_config_encoder {
* @enabled: Connector are a different from planes, CRTCs and encoders because
* they can be added and removed once the device is created.
* This flag represents if they are part of the device or not.
+ * @possible_encoders: Array of encoders that can be used with this connector
* @connector: Internal usage. This pointer should never be considered as valid.
* It can be used to store a temporary reference to a VKMS connector
* during device creation. This pointer is not managed by the
@@ -103,6 +104,7 @@ struct vkms_config_connector {
struct list_head link;
bool enabled;
+ struct xarray possible_encoders;
/* Internal usage */
struct vkms_connector *connector;
@@ -443,4 +445,35 @@ vkms_config_connector_set_enabled(struct vkms_config_connector *connector_cfg,
connector_cfg->enabled = enabled;
}
+/**
+ * vkms_config_connector_attach_encoder - Attach a connector to an encoder
+ * @connector_cfg: Connector to attach
+ * @encoder_cfg: Encoder to attach @connector_cfg to
+ */
+int __must_check vkms_config_connector_attach_encoder(struct vkms_config_connector *connector_cfg,
+ struct vkms_config_encoder *encoder_cfg);
+
+/**
+ * vkms_config_connector_detach_encoder - Detach a connector from an encoder
+ * @connector_cfg: Connector to detach
+ * @encoder_cfg: Encoder to detach @connector_cfg from
+ */
+void vkms_config_connector_detach_encoder(struct vkms_config_connector *connector_cfg,
+ struct vkms_config_encoder *encoder_cfg);
+
+/**
+ * vkms_config_connector_get_possible_encoders() - Return the array of possible
+ * encoders
+ * @connector_cfg: Connector to get the possible encoders from
+ * @out_length: Length of the returned array
+ *
+ * Returns:
+ * A list of pointers to the configurations. On success, the caller is
+ * responsible to free the returned array, but not its contents. On error,
+ * it returns an error and @out_length is invalid.
+ */
+struct vkms_config_encoder **
+vkms_config_connector_get_possible_encoders(struct vkms_config_connector *connector_cfg,
+ size_t *out_length);
+
#endif /* _VKMS_CONFIG_H_ */
@@ -8,13 +8,14 @@
int vkms_output_init(struct vkms_device *vkmsdev)
{
struct drm_device *dev = &vkmsdev->drm;
- struct vkms_connector *connector;
struct vkms_config_plane **plane_cfgs = NULL;
size_t n_planes;
struct vkms_config_crtc **crtc_cfgs = NULL;
size_t n_crtcs;
struct vkms_config_encoder **encoder_cfgs = NULL;
size_t n_encoders;
+ struct vkms_config_connector **connector_cfgs = NULL;
+ size_t n_connectors;
int ret = 0;
int writeback;
unsigned int n, i;
@@ -35,6 +36,13 @@ int vkms_output_init(struct vkms_device *vkmsdev)
goto err_free;
}
+ connector_cfgs = vkms_config_get_connectors(vkmsdev->config,
+ &n_connectors);
+ if (IS_ERR(connector_cfgs)) {
+ ret = PTR_ERR(connector_cfgs);
+ goto err_free;
+ }
+
for (n = 0; n < n_planes; n++) {
struct vkms_config_plane *plane_cfg;
enum drm_plane_type type;
@@ -136,18 +144,42 @@ int vkms_output_init(struct vkms_device *vkmsdev)
kfree(possible_crtcs);
}
- connector = vkms_connector_init(vkmsdev);
- if (IS_ERR(connector)) {
- DRM_ERROR("Failed to init connector\n");
- ret = PTR_ERR(connector);
- goto err_free;
- }
+ for (n = 0; n < n_connectors; n++) {
+ struct vkms_config_connector *connector_cfg;
+ struct vkms_config_encoder **possible_encoders;
+ size_t n_possible_encoders;
- /* Attach the encoder and the connector */
- ret = drm_connector_attach_encoder(&connector->base, encoder_cfgs[0]->encoder);
- if (ret) {
- DRM_ERROR("Failed to attach connector to encoder\n");
- goto err_free;
+ connector_cfg = connector_cfgs[n];
+
+ connector_cfg->connector = vkms_connector_init(vkmsdev);
+ if (IS_ERR(connector_cfg->connector)) {
+ DRM_ERROR("Failed to init connector\n");
+ ret = PTR_ERR(connector_cfg->connector);
+ goto err_free;
+ }
+
+ possible_encoders =
+ vkms_config_connector_get_possible_encoders(connector_cfg,
+ &n_possible_encoders);
+ if (IS_ERR(possible_encoders)) {
+ ret = PTR_ERR(possible_encoders);
+ goto err_free;
+ }
+
+ for (i = 0; i < n_possible_encoders; i++) {
+ struct vkms_config_encoder *possible_encoder;
+
+ possible_encoder = possible_encoders[i];
+ ret = drm_connector_attach_encoder(&connector_cfg->connector->base,
+ possible_encoder->encoder);
+ if (ret) {
+ DRM_ERROR("Failed to attach connector to encoder\n");
+ kfree(possible_encoders);
+ goto err_free;
+ }
+ }
+
+ kfree(possible_encoders);
}
drm_mode_config_reset(dev);
@@ -156,6 +188,7 @@ int vkms_output_init(struct vkms_device *vkmsdev)
kfree(plane_cfgs);
kfree(crtc_cfgs);
kfree(encoder_cfgs);
+ kfree(connector_cfgs);
return ret;
}