Hi, See below some minor comments: On Fri, Jun 23, 2023 at 06:23:47PM -0400, Jim Shargo wrote: > VKMS now supports creating and using virtual devices! > > In addition to the enabling logic, this commit also prevents users from > adding new objects once a card is registered. > > Signed-off-by: Jim Shargo <jshargo@xxxxxxxxxxxx> > --- > drivers/gpu/drm/vkms/vkms_configfs.c | 37 +++-- > drivers/gpu/drm/vkms/vkms_crtc.c | 4 +- > drivers/gpu/drm/vkms/vkms_drv.c | 3 +- > drivers/gpu/drm/vkms/vkms_drv.h | 2 +- > drivers/gpu/drm/vkms/vkms_output.c | 201 +++++++++++++++++++++++---- > 5 files changed, 202 insertions(+), 45 deletions(-) > > diff --git a/drivers/gpu/drm/vkms/vkms_configfs.c b/drivers/gpu/drm/vkms/vkms_configfs.c > index 544024735d19..f5eed6d23dcd 100644 > --- a/drivers/gpu/drm/vkms/vkms_configfs.c > +++ b/drivers/gpu/drm/vkms/vkms_configfs.c > @@ -504,29 +504,40 @@ static ssize_t device_enabled_store(struct config_item *item, const char *buf, > { > struct vkms_configfs *configfs = item_to_configfs(item); > struct vkms_device *device; > - int value, ret; > + int enabled, ret; > > - ret = kstrtoint(buf, 0, &value); > + ret = kstrtoint(buf, 0, &enabled); > if (ret) > return ret; > > - if (value != 1) > - return -EINVAL; > - > - mutex_lock(&configfs->lock); > - > - if (configfs->vkms_device) { > + if (enabled == 0) { > + mutex_lock(&configfs->lock); > + if (configfs->vkms_device) { > + vkms_remove_device(configfs->vkms_device); > + configfs->vkms_device = NULL; > + } > mutex_unlock(&configfs->lock); > + > return len; > } > > - device = vkms_add_device(configfs); > - mutex_unlock(&configfs->lock); > + if (enabled == 1) { > + mutex_lock(&configfs->lock); > + if (!configfs->vkms_device) { > + device = vkms_add_device(configfs); > + if (IS_ERR(device)) { > + mutex_unlock(&configfs->lock); > + return -PTR_ERR(device); > + } > + > + configfs->vkms_device = device; > + } > + mutex_unlock(&configfs->lock); > > - if (IS_ERR(device)) > - return -PTR_ERR(device); > + return len; > + } > > - return len; > + return -EINVAL; > } > > CONFIGFS_ATTR(device_, enabled); > diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c > index d91e49c53adc..5ebb5264f6ef 100644 > --- a/drivers/gpu/drm/vkms/vkms_crtc.c > +++ b/drivers/gpu/drm/vkms/vkms_crtc.c > @@ -278,7 +278,7 @@ static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = { > > struct vkms_crtc *vkms_crtc_init(struct vkms_device *vkmsdev, > struct drm_plane *primary, > - struct drm_plane *cursor) > + struct drm_plane *cursor, const char *name) > { > struct drm_device *dev = &vkmsdev->drm; > struct vkms_crtc *vkms_crtc; > @@ -290,7 +290,7 @@ struct vkms_crtc *vkms_crtc_init(struct vkms_device *vkmsdev, > vkms_crtc = &vkmsdev->output.crtcs[vkmsdev->output.num_crtcs++]; > > ret = drmm_crtc_init_with_planes(dev, &vkms_crtc->base, primary, cursor, > - &vkms_crtc_funcs, NULL); > + &vkms_crtc_funcs, name); > if (ret) { > DRM_ERROR("Failed to init CRTC\n"); > goto out_error; > diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c > index 1b5b7143792f..314a04659c5f 100644 > --- a/drivers/gpu/drm/vkms/vkms_drv.c > +++ b/drivers/gpu/drm/vkms/vkms_drv.c > @@ -210,7 +210,7 @@ static int vkms_platform_probe(struct platform_device *pdev) > ret = drm_dev_register(&vkms_device->drm, 0); > if (ret) { > DRM_ERROR("Unable to register device with id %d\n", pdev->id); > - return ret; > + goto out_release_group; > } > > drm_fbdev_generic_setup(&vkms_device->drm, 0); > @@ -256,6 +256,7 @@ struct vkms_device *vkms_add_device(struct vkms_configfs *configfs) > dev, &vkms_platform_driver.driver))) { > pdev = to_platform_device(dev); > max_id = max(max_id, pdev->id); > + put_device(dev); > } > > pdev = platform_device_register_data(NULL, DRIVER_NAME, max_id + 1, > diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h > index 3634eeeb4548..3d592d085f49 100644 > --- a/drivers/gpu/drm/vkms/vkms_drv.h > +++ b/drivers/gpu/drm/vkms/vkms_drv.h > @@ -239,7 +239,7 @@ void vkms_remove_device(struct vkms_device *vkms_device); > /* CRTC */ > struct vkms_crtc *vkms_crtc_init(struct vkms_device *vkmsdev, > struct drm_plane *primary, > - struct drm_plane *cursor); > + struct drm_plane *cursor, const char *name); > > int vkms_output_init(struct vkms_device *vkmsdev); > int vkms_output_init_default(struct vkms_device *vkmsdev); > diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c > index c9e6c653de19..806ff01954ad 100644 > --- a/drivers/gpu/drm/vkms/vkms_output.c > +++ b/drivers/gpu/drm/vkms/vkms_output.c > @@ -2,8 +2,10 @@ > > #include <drm/drm_atomic_helper.h> > #include <drm/drm_connector.h> > +#include <drm/drm_crtc.h> > #include <drm/drm_edid.h> > #include <drm/drm_encoder.h> > +#include <drm/drm_plane.h> > #include <drm/drm_probe_helper.h> > #include <drm/drm_simple_kms_helper.h> > > @@ -82,7 +84,6 @@ static struct drm_encoder *vkms_encoder_init(struct vkms_device *vkms_device) > > int vkms_output_init_default(struct vkms_device *vkmsdev) > { > - struct vkms_output *output = &vkmsdev->output; > struct drm_device *dev = &vkmsdev->drm; > struct drm_connector *connector; > struct drm_encoder *encoder; > @@ -101,8 +102,7 @@ int vkms_output_init_default(struct vkms_device *vkmsdev) > struct vkms_plane *overlay = vkms_plane_init( > vkmsdev, DRM_PLANE_TYPE_OVERLAY); > if (IS_ERR(overlay)) { > - ret = PTR_ERR(overlay); > - goto err_planes; > + return PTR_ERR(overlay); > } > } > } > @@ -110,17 +110,16 @@ int vkms_output_init_default(struct vkms_device *vkmsdev) > if (vkmsdev->config.cursor) { > cursor = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_CURSOR); > if (IS_ERR(cursor)) { > - ret = PTR_ERR(cursor); > - goto err_planes; > + return PTR_ERR(cursor); > } > } > > vkms_crtc = vkms_crtc_init(vkmsdev, &primary->base, > - cursor ? &cursor->base : NULL); > + cursor ? &cursor->base : NULL, > + "crtc-default"); > if (IS_ERR(vkms_crtc)) { > DRM_ERROR("Failed to init crtc\n"); > - ret = PTR_ERR(vkms_crtc); > - goto err_planes; > + return PTR_ERR(vkms_crtc); > } > > for (int i = 0; i < vkmsdev->output.num_planes; i++) { > @@ -131,22 +130,20 @@ int vkms_output_init_default(struct vkms_device *vkmsdev) > connector = vkms_connector_init(vkmsdev); > if (IS_ERR(connector)) { > DRM_ERROR("Failed to init connector\n"); > - ret = PTR_ERR(connector); > - goto err_connector; > + return PTR_ERR(connector); > } > > encoder = vkms_encoder_init(vkmsdev); > if (IS_ERR(encoder)) { > DRM_ERROR("Failed to init encoder\n"); > - ret = PTR_ERR(encoder); > - goto err_encoder; > + return PTR_ERR(encoder); > } > encoder->possible_crtcs |= drm_crtc_mask(&vkms_crtc->base); > > ret = drm_connector_attach_encoder(connector, encoder); > if (ret) { > DRM_ERROR("Failed to attach connector to encoder\n"); > - goto err_attach; > + return ret; > } > > if (vkmsdev->config.writeback) { > @@ -158,27 +155,175 @@ int vkms_output_init_default(struct vkms_device *vkmsdev) > drm_mode_config_reset(dev); > > return 0; > +} > > -err_attach: > - drm_encoder_cleanup(encoder); > +static bool is_object_linked(struct vkms_config_links *links, unsigned long idx) > +{ > + return links->linked_object_bitmap & (1 << idx); > +} > > -err_encoder: > - drm_connector_cleanup(connector); > +int vkms_output_init(struct vkms_device *vkmsdev) > +{ > + struct drm_device *dev = &vkmsdev->drm; > + struct vkms_configfs *configfs = vkmsdev->configfs; > + struct vkms_output *output = &vkmsdev->output; > + struct plane_map { > + struct vkms_config_plane *config_plane; > + struct vkms_plane *plane; > + } plane_map[VKMS_MAX_PLANES] = { 0 }; > + struct encoder_map { > + struct vkms_config_encoder *config_encoder; > + struct drm_encoder *encoder; > + } encoder_map[VKMS_MAX_OUTPUT_OBJECTS] = { 0 }; > + struct config_item *item; > + int map_idx = 0; > + > + list_for_each_entry(item, &configfs->planes_group.cg_children, > + ci_entry) { > + struct vkms_config_plane *config_plane = > + item_to_config_plane(item); > + struct vkms_plane *plane = > + vkms_plane_init(vkmsdev, config_plane->type); > + > + if (IS_ERR(plane)) { > + DRM_ERROR("Unable to init plane from config: %s", > + item->ci_name); > + return PTR_ERR(plane); > + } > > -err_connector: > - drm_crtc_cleanup(&vkms_crtc->base); > + plane_map[map_idx].config_plane = config_plane; > + plane_map[map_idx].plane = plane; > + map_idx += 1; > + } > > -err_planes: > - for (int i = 0; i < output->num_planes; i++) { > - drm_plane_cleanup(&output->planes[i].base); > + map_idx = 0; > + list_for_each_entry(item, &configfs->encoders_group.cg_children, > + ci_entry) { > + struct vkms_config_encoder *config_encoder = > + item_to_config_encoder(item); > + struct drm_encoder *encoder = vkms_encoder_init(vkmsdev); > + > + if (IS_ERR(encoder)) { > + DRM_ERROR("Failed to init config encoder: %s", > + item->ci_name); > + return PTR_ERR(encoder); > + } > + encoder_map[map_idx].config_encoder = config_encoder; A two connector configuration without an encoder would have the potential of blowing up if we don't create a second_encoder. > + encoder_map[map_idx].encoder = encoder; > + map_idx += 1; > } > > - memset(output, 0, sizeof(*output)); > + list_for_each_entry(item, &configfs->connectors_group.cg_children, > + ci_entry) { > + struct vkms_config_connector *config_connector = > + item_to_config_connector(item); > + struct drm_connector *connector = vkms_connector_init(vkmsdev); > > - return ret; > -} > + if (IS_ERR(connector)) { > + DRM_ERROR("Failed to init connector from config: %s", > + item->ci_name); > + return PTR_ERR(connector); > + } > > -int vkms_output_init(struct vkms_device *vkmsdev) > -{ > - return -ENOTSUPP; > + for (int j = 0; j < output->num_connectors; j++) { > + struct encoder_map *encoder = &encoder_map[j]; > + > + if (is_object_linked( > + &config_connector->possible_encoders, > + encoder->config_encoder > + ->encoder_config_idx)) { Here encoder->config_encoder for two connectors but a single encoder will give back a empty encoder. > + drm_connector_attach_encoder(connector, > + encoder->encoder); > + } > + } > + } > + > + list_for_each_entry(item, &configfs->crtcs_group.cg_children, > + ci_entry) { > + struct vkms_config_crtc *config_crtc = > + item_to_config_crtc(item); > + struct vkms_crtc *vkms_crtc; > + struct drm_plane *primary = NULL, *cursor = NULL; > + > + for (int j = 0; j < output->num_planes; j++) { > + struct plane_map *plane_entry = &plane_map[j]; > + struct drm_plane *plane = &plane_entry->plane->base; > + > + if (!is_object_linked( > + &plane_entry->config_plane->possible_crtcs, > + config_crtc->crtc_config_idx)) { > + continue; > + } > + > + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { > + if (primary) { > + DRM_WARN( > + "Too many primary planes found for crtc %s.", > + item->ci_name); > + return EINVAL; > + } > + primary = plane; > + } else if (plane->type == DRM_PLANE_TYPE_CURSOR) { > + if (cursor) { > + DRM_WARN( > + "Too many cursor planes found for crtc %s.", > + item->ci_name); > + return EINVAL; > + } > + cursor = plane; > + } > + } > + > + if (!primary) { > + DRM_WARN("No primary plane configured for crtc %s", > + item->ci_name); > + return EINVAL; > + } > + > + vkms_crtc = > + vkms_crtc_init(vkmsdev, primary, cursor, item->ci_name); > + if (IS_ERR(vkms_crtc)) { > + DRM_WARN("Unable to init crtc from config: %s", > + item->ci_name); > + return PTR_ERR(vkms_crtc); > + } > + > + for (int j = 0; j < output->num_planes; j++) { > + struct plane_map *plane_entry = &plane_map[j]; > + > + if (!plane_entry->plane) > + break; > + > + if (is_object_linked( > + &plane_entry->config_plane->possible_crtcs, > + config_crtc->crtc_config_idx)) { > + plane_entry->plane->base.possible_crtcs |= > + drm_crtc_mask(&vkms_crtc->base); > + } > + } > + > + for (int j = 0; j < output->num_encoders; j++) { > + struct encoder_map *encoder_entry = &encoder_map[j]; > + > + if (is_object_linked(&encoder_entry->config_encoder > + ->possible_crtcs, > + config_crtc->crtc_config_idx)) { > + encoder_entry->encoder->possible_crtcs |= > + drm_crtc_mask(&vkms_crtc->base); > + } > + } > + > + if (vkmsdev->config.writeback) { > + int ret = vkms_enable_writeback_connector(vkmsdev, > + vkms_crtc); > + if (ret) > + DRM_WARN( > + "Failed to init writeback connector for config crtc: %s. Error code %d", > + item->ci_name, ret); > + } > + } I think we might be needing here a test for missing symlinks - invalid configurations, like unused crtcs, encoders, connectors. I suppose anything that's not matched with is_object_linked(), such we avoid invalid configuration found by drm_mode_config_validate() and inform users about missing them. An example here would be to create a second encoder, connector, crtc and not symlink them to their possible_encoders,possible_crtcs mask. An i-g-t test for this very thing would be needed to stress different kind of combinations. > + > + drm_mode_config_reset(dev); > + > + return 0; > } > -- > 2.41.0.162.gfafddb0af9-goog >
Attachment:
signature.asc
Description: PGP signature