From 27d247c678a09fcac08d6865db73e41b23d3d5f3 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 09:59:26 -0700 Subject: [PATCH 01/22] drm/vmwgfx: Removed unused snooper.crtc field This field is not being used anymore Signed-off-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 1 - drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 2 -- drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 1 - 3 files changed, 4 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 59ff4197173a..bc6ee5a847a7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -153,7 +153,6 @@ enum vmw_cmdbuf_res_type { struct vmw_cmdbuf_res_manager; struct vmw_cursor_snooper { - struct drm_crtc *crtc; size_t age; uint32_t *image; }; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index d492d57d5309..6bcba56ef158 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -188,7 +188,6 @@ int vmw_du_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv, /* takedown old cursor */ if (du->cursor_surface) { - du->cursor_surface->snooper.crtc = NULL; vmw_surface_unreference(&du->cursor_surface); } if (du->cursor_dmabuf) @@ -200,7 +199,6 @@ int vmw_du_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv, /* vmw_user_surface_lookup takes one reference */ du->cursor_surface = surface; - du->cursor_surface->snooper.crtc = crtc; du->cursor_age = du->cursor_surface->snooper.age; ret = vmw_cursor_update_image(dev_priv, surface->snooper.image, 64, 64, hotspot_x, hotspot_y); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index b445ce9b9757..6abcf82d8a39 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -814,7 +814,6 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, } else { srf->snooper.image = NULL; } - srf->snooper.crtc = NULL; user_srf->prime.base.shareable = false; user_srf->prime.base.tfile = NULL; From 36cc79bc9077319c04bd3b132edcacaa9a0d9f2b Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 11:28:11 -0700 Subject: [PATCH 02/22] drm/vmwgfx: Add universal plane support Universal support is prerequisite for atomic mode set. Explicitly create planes for the cursor and the primary FB. With a functional cursor plane, the DRM will no longer use the legacy cursor_set2 and cursor_move entry points. Signed-off-by: Sinclair Yeh Signed-off-by: Thomas Hellstrom Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 271 +++++++++++++-------------- drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 39 ++-- drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 108 ++++++++--- drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 87 ++++++++- drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 89 ++++++++- 7 files changed, 398 insertions(+), 198 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index d08f26973d0b..83ff0f033d5f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -650,6 +650,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) spin_lock_init(&dev_priv->waiter_lock); spin_lock_init(&dev_priv->cap_lock); spin_lock_init(&dev_priv->svga_lock); + spin_lock_init(&dev_priv->cursor_lock); for (i = vmw_res_context; i < vmw_res_max; ++i) { idr_init(&dev_priv->res_idr[i]); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index bc6ee5a847a7..130d51c5ec6a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -414,6 +414,7 @@ struct vmw_private { unsigned num_implicit; struct vmw_framebuffer *implicit_fb; struct mutex global_kms_state_mutex; + spinlock_t cursor_lock; /* * Context and surface management. diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 6bcba56ef158..c9f5ddabfb72 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -33,10 +33,9 @@ void vmw_du_cleanup(struct vmw_display_unit *du) { - if (du->cursor_surface) - vmw_surface_unreference(&du->cursor_surface); - if (du->cursor_dmabuf) - vmw_dmabuf_unreference(&du->cursor_dmabuf); + drm_plane_cleanup(&du->primary); + drm_plane_cleanup(&du->cursor); + drm_connector_unregister(&du->connector); drm_crtc_cleanup(&du->crtc); drm_encoder_cleanup(&du->encoder); @@ -47,9 +46,9 @@ void vmw_du_cleanup(struct vmw_display_unit *du) * Display Unit Cursor functions */ -int vmw_cursor_update_image(struct vmw_private *dev_priv, - u32 *image, u32 width, u32 height, - u32 hotspotX, u32 hotspotY) +static int vmw_cursor_update_image(struct vmw_private *dev_priv, + u32 *image, u32 width, u32 height, + u32 hotspotX, u32 hotspotY) { struct { u32 cmd; @@ -83,10 +82,10 @@ int vmw_cursor_update_image(struct vmw_private *dev_priv, return 0; } -int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, - struct vmw_dma_buffer *dmabuf, - u32 width, u32 height, - u32 hotspotX, u32 hotspotY) +static int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, + struct vmw_dma_buffer *dmabuf, + u32 width, u32 height, + u32 hotspotX, u32 hotspotY) { struct ttm_bo_kmap_obj map; unsigned long kmap_offset; @@ -120,145 +119,22 @@ int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, } -void vmw_cursor_update_position(struct vmw_private *dev_priv, - bool show, int x, int y) +static void vmw_cursor_update_position(struct vmw_private *dev_priv, + bool show, int x, int y) { u32 *fifo_mem = dev_priv->mmio_virt; uint32_t count; + spin_lock(&dev_priv->cursor_lock); vmw_mmio_write(show ? 1 : 0, fifo_mem + SVGA_FIFO_CURSOR_ON); vmw_mmio_write(x, fifo_mem + SVGA_FIFO_CURSOR_X); vmw_mmio_write(y, fifo_mem + SVGA_FIFO_CURSOR_Y); count = vmw_mmio_read(fifo_mem + SVGA_FIFO_CURSOR_COUNT); vmw_mmio_write(++count, fifo_mem + SVGA_FIFO_CURSOR_COUNT); + spin_unlock(&dev_priv->cursor_lock); } -/* - * vmw_du_crtc_cursor_set2 - Driver cursor_set2 callback. - */ -int vmw_du_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv, - uint32_t handle, uint32_t width, uint32_t height, - int32_t hot_x, int32_t hot_y) -{ - struct vmw_private *dev_priv = vmw_priv(crtc->dev); - struct vmw_display_unit *du = vmw_crtc_to_du(crtc); - struct vmw_surface *surface = NULL; - struct vmw_dma_buffer *dmabuf = NULL; - s32 hotspot_x, hotspot_y; - int ret; - - /* - * FIXME: Unclear whether there's any global state touched by the - * cursor_set function, especially vmw_cursor_update_position looks - * suspicious. For now take the easy route and reacquire all locks. We - * can do this since the caller in the drm core doesn't check anything - * which is protected by any looks. - */ - drm_modeset_unlock_crtc(crtc); - drm_modeset_lock_all(dev_priv->dev); - hotspot_x = hot_x + du->hotspot_x; - hotspot_y = hot_y + du->hotspot_y; - - /* A lot of the code assumes this */ - if (handle && (width != 64 || height != 64)) { - ret = -EINVAL; - goto out; - } - - if (handle) { - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - - ret = vmw_user_lookup_handle(dev_priv, tfile, - handle, &surface, &dmabuf); - if (ret) { - DRM_ERROR("failed to find surface or dmabuf: %i\n", ret); - ret = -EINVAL; - goto out; - } - } - - /* need to do this before taking down old image */ - if (surface && !surface->snooper.image) { - DRM_ERROR("surface not suitable for cursor\n"); - vmw_surface_unreference(&surface); - ret = -EINVAL; - goto out; - } - - /* takedown old cursor */ - if (du->cursor_surface) { - vmw_surface_unreference(&du->cursor_surface); - } - if (du->cursor_dmabuf) - vmw_dmabuf_unreference(&du->cursor_dmabuf); - - /* setup new image */ - ret = 0; - if (surface) { - /* vmw_user_surface_lookup takes one reference */ - du->cursor_surface = surface; - - du->cursor_age = du->cursor_surface->snooper.age; - ret = vmw_cursor_update_image(dev_priv, surface->snooper.image, - 64, 64, hotspot_x, hotspot_y); - } else if (dmabuf) { - /* vmw_user_surface_lookup takes one reference */ - du->cursor_dmabuf = dmabuf; - - ret = vmw_cursor_update_dmabuf(dev_priv, dmabuf, width, height, - hotspot_x, hotspot_y); - } else { - vmw_cursor_update_position(dev_priv, false, 0, 0); - goto out; - } - - if (!ret) { - vmw_cursor_update_position(dev_priv, true, - du->cursor_x + hotspot_x, - du->cursor_y + hotspot_y); - du->core_hotspot_x = hot_x; - du->core_hotspot_y = hot_y; - } - -out: - drm_modeset_unlock_all(dev_priv->dev); - drm_modeset_lock_crtc(crtc, crtc->cursor); - - return ret; -} - -int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) -{ - struct vmw_private *dev_priv = vmw_priv(crtc->dev); - struct vmw_display_unit *du = vmw_crtc_to_du(crtc); - bool shown = du->cursor_surface || du->cursor_dmabuf ? true : false; - - du->cursor_x = x + du->set_gui_x; - du->cursor_y = y + du->set_gui_y; - - /* - * FIXME: Unclear whether there's any global state touched by the - * cursor_set function, especially vmw_cursor_update_position looks - * suspicious. For now take the easy route and reacquire all locks. We - * can do this since the caller in the drm core doesn't check anything - * which is protected by any looks. - */ - drm_modeset_unlock_crtc(crtc); - drm_modeset_lock_all(dev_priv->dev); - - vmw_cursor_update_position(dev_priv, shown, - du->cursor_x + du->hotspot_x + - du->core_hotspot_x, - du->cursor_y + du->hotspot_y + - du->core_hotspot_y); - - drm_modeset_unlock_all(dev_priv->dev); - drm_modeset_lock_crtc(crtc, crtc->cursor); - - return 0; -} - void vmw_kms_cursor_snoop(struct vmw_surface *srf, struct ttm_object_file *tfile, struct ttm_buffer_object *bo, @@ -393,6 +269,125 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) mutex_unlock(&dev->mode_config.mutex); } + + +/** + * vmw_du_cursor_plane_update() - Update cursor image and location + * + * @plane: plane object to update + * @crtc: owning CRTC of @plane + * @fb: framebuffer to flip onto plane + * @crtc_x: x offset of plane on crtc + * @crtc_y: y offset of plane on crtc + * @crtc_w: width of plane rectangle on crtc + * @crtc_h: height of plane rectangle on crtc + * @src_x: Not used + * @src_y: Not used + * @src_w: Not used + * @src_h: Not used + * + * + * RETURNS: + * Zero on success, error code on failure + */ +int vmw_du_cursor_plane_update(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, + unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct vmw_private *dev_priv = vmw_priv(crtc->dev); + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct vmw_surface *surface = NULL; + struct vmw_dma_buffer *dmabuf = NULL; + s32 hotspot_x, hotspot_y; + int ret; + + hotspot_x = du->hotspot_x + fb->hot_x; + hotspot_y = du->hotspot_y + fb->hot_y; + + /* A lot of the code assumes this */ + if (crtc_w != 64 || crtc_h != 64) { + ret = -EINVAL; + goto out; + } + + if (vmw_framebuffer_to_vfb(fb)->dmabuf) + dmabuf = vmw_framebuffer_to_vfbd(fb)->buffer; + else + surface = vmw_framebuffer_to_vfbs(fb)->surface; + + if (surface && !surface->snooper.image) { + DRM_ERROR("surface not suitable for cursor\n"); + ret = -EINVAL; + goto out; + } + + /* setup new image */ + ret = 0; + if (surface) { + /* vmw_user_surface_lookup takes one reference */ + du->cursor_surface = surface; + + du->cursor_age = du->cursor_surface->snooper.age; + + ret = vmw_cursor_update_image(dev_priv, surface->snooper.image, + 64, 64, hotspot_x, hotspot_y); + } else if (dmabuf) { + /* vmw_user_surface_lookup takes one reference */ + du->cursor_dmabuf = dmabuf; + + ret = vmw_cursor_update_dmabuf(dev_priv, dmabuf, crtc_w, crtc_h, + hotspot_x, hotspot_y); + } else { + vmw_cursor_update_position(dev_priv, false, 0, 0); + goto out; + } + + if (!ret) { + du->cursor_x = crtc_x + du->set_gui_x; + du->cursor_y = crtc_y + du->set_gui_y; + + vmw_cursor_update_position(dev_priv, true, + du->cursor_x + hotspot_x, + du->cursor_y + hotspot_y); + } + +out: + return ret; +} + + +int vmw_du_cursor_plane_disable(struct drm_plane *plane) +{ + if (plane->fb) { + drm_framebuffer_unreference(plane->fb); + plane->fb = NULL; + } + + return -EINVAL; +} + + +void vmw_du_cursor_plane_destroy(struct drm_plane *plane) +{ + vmw_cursor_update_position(plane->dev->dev_private, false, 0, 0); + + drm_plane_cleanup(plane); +} + + +void vmw_du_primary_plane_destroy(struct drm_plane *plane) +{ + drm_plane_cleanup(plane); + + /* Planes are static in our case so we don't free it */ +} + + /* * Generic framebuffer code */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index cb36e1d70133..e400bfb26167 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -33,6 +33,8 @@ #include #include "vmwgfx_drv.h" + + /** * struct vmw_kms_dirty - closure structure for the vmw_kms_helper_dirty * function. @@ -125,19 +127,17 @@ struct vmw_framebuffer_dmabuf { }; -/* - * Basic cursor manipulation - */ -int vmw_cursor_update_image(struct vmw_private *dev_priv, - u32 *image, u32 width, u32 height, - u32 hotspotX, u32 hotspotY); -int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv, - struct vmw_dma_buffer *dmabuf, - u32 width, u32 height, - u32 hotspotX, u32 hotspotY); -void vmw_cursor_update_position(struct vmw_private *dev_priv, - bool show, int x, int y); +static const uint32_t vmw_primary_plane_formats[] = { + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGB888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, +}; +static const uint32_t vmw_cursor_plane_formats[] = { + DRM_FORMAT_ARGB8888, +}; /** * Base class display unit. @@ -150,6 +150,8 @@ struct vmw_display_unit { struct drm_crtc crtc; struct drm_encoder encoder; struct drm_connector connector; + struct drm_plane primary; + struct drm_plane cursor; struct vmw_surface *cursor_surface; struct vmw_dma_buffer *cursor_dmabuf; @@ -270,6 +272,19 @@ void vmw_kms_update_implicit_fb(struct vmw_private *dev_priv, void vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv, bool immutable); +/* Universal Plane Helpers */ +void vmw_du_primary_plane_destroy(struct drm_plane *plane); +void vmw_du_cursor_plane_destroy(struct drm_plane *plane); +int vmw_du_cursor_plane_disable(struct drm_plane *plane); +int vmw_du_cursor_plane_update(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, + unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h); + /* * Legacy display unit functions - vmwgfx_ldu.c diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 3806148e1bdb..3efcbe514472 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -75,10 +75,9 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) { struct vmw_legacy_display *lds = dev_priv->ldu_priv; struct vmw_legacy_display_unit *entry; - struct vmw_display_unit *du = NULL; struct drm_framebuffer *fb = NULL; struct drm_crtc *crtc = NULL; - int i = 0, ret; + int i = 0; /* If there is no display topology the host just assumes * that the guest will set the same layout as the host. @@ -132,25 +131,6 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) lds->last_num_active = lds->num_active; - - /* Find the first du with a cursor. */ - list_for_each_entry(entry, &lds->active, active) { - du = &entry->base; - - if (!du->cursor_dmabuf) - continue; - - ret = vmw_cursor_update_dmabuf(dev_priv, - du->cursor_dmabuf, - 64, 64, - du->hotspot_x, - du->hotspot_y); - if (ret == 0) - break; - - DRM_ERROR("Could not update cursor image\n"); - } - return 0; } @@ -298,8 +278,6 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) } static const struct drm_crtc_funcs vmw_legacy_crtc_funcs = { - .cursor_set2 = vmw_du_crtc_cursor_set2, - .cursor_move = vmw_du_crtc_cursor_move, .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_ldu_crtc_destroy, .set_config = vmw_ldu_crtc_set_config, @@ -336,6 +314,23 @@ static const struct drm_connector_funcs vmw_legacy_connector_funcs = { .destroy = vmw_ldu_connector_destroy, }; +/* + * Legacy Display Plane Functions + */ + +static const struct drm_plane_funcs vmw_ldu_plane_funcs = { + .update_plane = drm_primary_helper_update, + .disable_plane = drm_primary_helper_disable, + .destroy = vmw_du_primary_plane_destroy, +}; + +static const struct drm_plane_funcs vmw_ldu_cursor_funcs = { + .update_plane = vmw_du_cursor_plane_update, + .disable_plane = vmw_du_cursor_plane_disable, + .destroy = vmw_du_cursor_plane_destroy, +}; + + static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) { struct vmw_legacy_display_unit *ldu; @@ -343,6 +338,7 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) struct drm_connector *connector; struct drm_encoder *encoder; struct drm_crtc *crtc; + int ret; ldu = kzalloc(sizeof(*ldu), GFP_KERNEL); if (!ldu) @@ -361,19 +357,61 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) ldu->base.pref_mode = NULL; ldu->base.is_implicit = true; - drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, - DRM_MODE_CONNECTOR_VIRTUAL); + /* Initialize primary plane */ + ret = drm_universal_plane_init(dev, &ldu->base.primary, + 0, &vmw_ldu_plane_funcs, + vmw_primary_plane_formats, + ARRAY_SIZE(vmw_primary_plane_formats), + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + DRM_ERROR("Failed to initialize primary plane"); + goto err_free; + } + + /* Initialize cursor plane */ + ret = drm_universal_plane_init(dev, &ldu->base.cursor, + 0, &vmw_ldu_cursor_funcs, + vmw_cursor_plane_formats, + ARRAY_SIZE(vmw_cursor_plane_formats), + DRM_PLANE_TYPE_CURSOR, NULL); + if (ret) { + DRM_ERROR("Failed to initialize cursor plane"); + drm_plane_cleanup(&ldu->base.primary); + goto err_free; + } + + ret = drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) { + DRM_ERROR("Failed to initialize connector\n"); + goto err_free; + } connector->status = vmw_du_connector_detect(connector, true); - drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, - DRM_MODE_ENCODER_VIRTUAL, NULL); - drm_mode_connector_attach_encoder(connector, encoder); + ret = drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL, NULL); + if (ret) { + DRM_ERROR("Failed to initialize encoder\n"); + goto err_free_connector; + } + + (void) drm_mode_connector_attach_encoder(connector, encoder); encoder->possible_crtcs = (1 << unit); encoder->possible_clones = 0; - (void) drm_connector_register(connector); + ret = drm_connector_register(connector); + if (ret) { + DRM_ERROR("Failed to register connector\n"); + goto err_free_encoder; + } - drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs); + ret = drm_crtc_init_with_planes(dev, crtc, &ldu->base.primary, + &ldu->base.cursor, + &vmw_legacy_crtc_funcs, NULL); + if (ret) { + DRM_ERROR("Failed to initialize CRTC\n"); + goto err_free_unregister; + } drm_mode_crtc_set_gamma_size(crtc, 256); @@ -390,6 +428,16 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) 1); return 0; + +err_free_unregister: + drm_connector_unregister(connector); +err_free_encoder: + drm_encoder_cleanup(encoder); +err_free_connector: + drm_connector_cleanup(connector); +err_free: + kfree(ldu); + return ret; } int vmw_kms_ldu_init_display(struct vmw_private *dev_priv) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index d4268efc37d2..8ffccb87cf3a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -459,8 +459,6 @@ static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, } static const struct drm_crtc_funcs vmw_screen_object_crtc_funcs = { - .cursor_set2 = vmw_du_crtc_cursor_set2, - .cursor_move = vmw_du_crtc_cursor_move, .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_sou_crtc_destroy, .set_config = vmw_sou_crtc_set_config, @@ -497,6 +495,23 @@ static const struct drm_connector_funcs vmw_sou_connector_funcs = { .destroy = vmw_sou_connector_destroy, }; +/* + * Screen Object Display Plane Functions + */ + +static const struct drm_plane_funcs vmw_sou_plane_funcs = { + .update_plane = drm_primary_helper_update, + .disable_plane = drm_primary_helper_disable, + .destroy = vmw_du_primary_plane_destroy, +}; + +static const struct drm_plane_funcs vmw_sou_cursor_funcs = { + .update_plane = vmw_du_cursor_plane_update, + .disable_plane = vmw_du_cursor_plane_disable, + .destroy = vmw_du_cursor_plane_destroy, +}; + + static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) { struct vmw_screen_object_unit *sou; @@ -504,6 +519,7 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) struct drm_connector *connector; struct drm_encoder *encoder; struct drm_crtc *crtc; + int ret; sou = kzalloc(sizeof(*sou), GFP_KERNEL); if (!sou) @@ -521,19 +537,62 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) sou->base.pref_mode = NULL; sou->base.is_implicit = false; - drm_connector_init(dev, connector, &vmw_sou_connector_funcs, - DRM_MODE_CONNECTOR_VIRTUAL); + /* Initialize primary plane */ + ret = drm_universal_plane_init(dev, &sou->base.primary, + 0, &vmw_sou_plane_funcs, + vmw_primary_plane_formats, + ARRAY_SIZE(vmw_primary_plane_formats), + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + DRM_ERROR("Failed to initialize primary plane"); + goto err_free; + } + + /* Initialize cursor plane */ + ret = drm_universal_plane_init(dev, &sou->base.cursor, + 0, &vmw_sou_cursor_funcs, + vmw_cursor_plane_formats, + ARRAY_SIZE(vmw_cursor_plane_formats), + DRM_PLANE_TYPE_CURSOR, NULL); + if (ret) { + DRM_ERROR("Failed to initialize cursor plane"); + drm_plane_cleanup(&sou->base.primary); + goto err_free; + } + + ret = drm_connector_init(dev, connector, &vmw_sou_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) { + DRM_ERROR("Failed to initialize connector\n"); + goto err_free; + } + connector->status = vmw_du_connector_detect(connector, true); - drm_encoder_init(dev, encoder, &vmw_screen_object_encoder_funcs, - DRM_MODE_ENCODER_VIRTUAL, NULL); - drm_mode_connector_attach_encoder(connector, encoder); + ret = drm_encoder_init(dev, encoder, &vmw_screen_object_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL, NULL); + if (ret) { + DRM_ERROR("Failed to initialize encoder\n"); + goto err_free_connector; + } + + (void) drm_mode_connector_attach_encoder(connector, encoder); encoder->possible_crtcs = (1 << unit); encoder->possible_clones = 0; - (void) drm_connector_register(connector); + ret = drm_connector_register(connector); + if (ret) { + DRM_ERROR("Failed to register connector\n"); + goto err_free_encoder; + } - drm_crtc_init(dev, crtc, &vmw_screen_object_crtc_funcs); + ret = drm_crtc_init_with_planes(dev, crtc, &sou->base.primary, + &sou->base.cursor, + &vmw_screen_object_crtc_funcs, NULL); + if (ret) { + DRM_ERROR("Failed to initialize CRTC\n"); + goto err_free_unregister; + } drm_mode_crtc_set_gamma_size(crtc, 256); @@ -550,6 +609,16 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) sou->base.is_implicit); return 0; + +err_free_unregister: + drm_connector_unregister(connector); +err_free_encoder: + drm_encoder_cleanup(encoder); +err_free_connector: + drm_connector_cleanup(connector); +err_free: + kfree(sou); + return ret; } int vmw_kms_sou_init_display(struct vmw_private *dev_priv) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index b27cd18ee66a..4d9dd1b67b93 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -1015,8 +1015,6 @@ int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv, * Screen Target CRTC dispatch table */ static const struct drm_crtc_funcs vmw_stdu_crtc_funcs = { - .cursor_set2 = vmw_du_crtc_cursor_set2, - .cursor_move = vmw_du_crtc_cursor_move, .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_stdu_crtc_destroy, .set_config = vmw_stdu_crtc_set_config, @@ -1081,6 +1079,23 @@ static const struct drm_connector_funcs vmw_stdu_connector_funcs = { +/****************************************************************************** + * Screen Target Display Plane Functions + *****************************************************************************/ + +static const struct drm_plane_funcs vmw_stdu_plane_funcs = { + .update_plane = drm_primary_helper_update, + .disable_plane = drm_primary_helper_disable, + .destroy = vmw_du_primary_plane_destroy, +}; + +static const struct drm_plane_funcs vmw_stdu_cursor_funcs = { + .update_plane = vmw_du_cursor_plane_update, + .disable_plane = vmw_du_cursor_plane_disable, + .destroy = vmw_du_cursor_plane_destroy, +}; + + /** * vmw_stdu_init - Sets up a Screen Target Display Unit * @@ -1097,7 +1112,9 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) struct drm_device *dev = dev_priv->dev; struct drm_connector *connector; struct drm_encoder *encoder; + struct drm_plane *primary, *cursor; struct drm_crtc *crtc; + int ret; stdu = kzalloc(sizeof(*stdu), GFP_KERNEL); @@ -1108,25 +1125,69 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) crtc = &stdu->base.crtc; encoder = &stdu->base.encoder; connector = &stdu->base.connector; + primary = &stdu->base.primary; + cursor = &stdu->base.cursor; stdu->base.pref_active = (unit == 0); stdu->base.pref_width = dev_priv->initial_width; stdu->base.pref_height = dev_priv->initial_height; stdu->base.is_implicit = false; - drm_connector_init(dev, connector, &vmw_stdu_connector_funcs, - DRM_MODE_CONNECTOR_VIRTUAL); + /* Initialize primary plane */ + ret = drm_universal_plane_init(dev, primary, + 0, &vmw_stdu_plane_funcs, + vmw_primary_plane_formats, + ARRAY_SIZE(vmw_primary_plane_formats), + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + DRM_ERROR("Failed to initialize primary plane"); + goto err_free; + } + + /* Initialize cursor plane */ + ret = drm_universal_plane_init(dev, cursor, + 0, &vmw_stdu_cursor_funcs, + vmw_cursor_plane_formats, + ARRAY_SIZE(vmw_cursor_plane_formats), + DRM_PLANE_TYPE_CURSOR, NULL); + if (ret) { + DRM_ERROR("Failed to initialize cursor plane"); + drm_plane_cleanup(&stdu->base.primary); + goto err_free; + } + + ret = drm_connector_init(dev, connector, &vmw_stdu_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) { + DRM_ERROR("Failed to initialize connector\n"); + goto err_free; + } connector->status = vmw_du_connector_detect(connector, false); - drm_encoder_init(dev, encoder, &vmw_stdu_encoder_funcs, - DRM_MODE_ENCODER_VIRTUAL, NULL); - drm_mode_connector_attach_encoder(connector, encoder); + ret = drm_encoder_init(dev, encoder, &vmw_stdu_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL, NULL); + if (ret) { + DRM_ERROR("Failed to initialize encoder\n"); + goto err_free_connector; + } + + (void) drm_mode_connector_attach_encoder(connector, encoder); encoder->possible_crtcs = (1 << unit); encoder->possible_clones = 0; - (void) drm_connector_register(connector); + ret = drm_connector_register(connector); + if (ret) { + DRM_ERROR("Failed to register connector\n"); + goto err_free_encoder; + } - drm_crtc_init(dev, crtc, &vmw_stdu_crtc_funcs); + ret = drm_crtc_init_with_planes(dev, crtc, &stdu->base.primary, + &stdu->base.cursor, + &vmw_stdu_crtc_funcs, NULL); + if (ret) { + DRM_ERROR("Failed to initialize CRTC\n"); + goto err_free_unregister; + } drm_mode_crtc_set_gamma_size(crtc, 256); @@ -1142,6 +1203,16 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) dev_priv->implicit_placement_property, stdu->base.is_implicit); return 0; + +err_free_unregister: + drm_connector_unregister(connector); +err_free_encoder: + drm_encoder_cleanup(encoder); +err_free_connector: + drm_connector_cleanup(connector); +err_free: + kfree(stdu); + return ret; } From 9c2542a41f559452d570b96239a81038c49becfc Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 11:33:39 -0700 Subject: [PATCH 03/22] drm/vmwgfx: CRTC atomic state Create and Add CRTC state. We currently do not track any properties or custom states so we can technically use the DRM helpers. Creating this code just to make potential future additions easier. Most of the new code will be compiled but not enabled until plane/connector state handling code is also in place. This is the first of a series to enable atomic mode set for vmwgfx. The atomic enabling effort was done in collaboration with Thomas Hellstrom and the VMware Graphics Team. Signed-off-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 81 ++++++++++++++++++++++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 23 ++++++-- drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 10 ++++ drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 10 ++++ drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 10 ++++ 5 files changed, 131 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index c9f5ddabfb72..18bd8dc46507 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -26,6 +26,8 @@ **************************************************************************/ #include "vmwgfx_kms.h" +#include +#include /* Might need a hrtimer here? */ @@ -388,6 +390,84 @@ void vmw_du_primary_plane_destroy(struct drm_plane *plane) } +/** + * vmw_du_crtc_duplicate_state - duplicate crtc state + * @crtc: DRM crtc + * + * Allocates and returns a copy of the crtc state (both common and + * vmw-specific) for the specified crtc. + * + * Returns: The newly allocated crtc state, or NULL on failure. + */ +struct drm_crtc_state * +vmw_du_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct drm_crtc_state *state; + struct vmw_crtc_state *vcs; + + if (WARN_ON(!crtc->state)) + return NULL; + + vcs = kmemdup(crtc->state, sizeof(*vcs), GFP_KERNEL); + + if (!vcs) + return NULL; + + state = &vcs->base; + + __drm_atomic_helper_crtc_duplicate_state(crtc, state); + + return state; +} + + +/** + * vmw_du_crtc_reset - creates a blank vmw crtc state + * @crtc: DRM crtc + * + * Resets the atomic state for @crtc by freeing the state pointer (which + * might be NULL, e.g. at driver load time) and allocating a new empty state + * object. + */ +void vmw_du_crtc_reset(struct drm_crtc *crtc) +{ + struct vmw_crtc_state *vcs; + + + if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); + + kfree(vmw_crtc_state_to_vcs(crtc->state)); + } + + vcs = kzalloc(sizeof(*vcs), GFP_KERNEL); + + if (!vcs) { + DRM_ERROR("Cannot allocate vmw_crtc_state\n"); + return; + } + + crtc->state = &vcs->base; + crtc->state->crtc = crtc; +} + + +/** + * vmw_du_crtc_destroy_state - destroy crtc state + * @crtc: DRM crtc + * @state: state object to destroy + * + * Destroys the crtc state (both common and vmw-specific) for the + * specified plane. + */ +void +vmw_du_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + drm_atomic_helper_crtc_destroy_state(crtc, state); +} + + /* * Generic framebuffer code */ @@ -1600,6 +1680,7 @@ int vmw_du_connector_set_property(struct drm_connector *connector, } + int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index e400bfb26167..370f75c95f56 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -139,6 +139,19 @@ static const uint32_t vmw_cursor_plane_formats[] = { DRM_FORMAT_ARGB8888, }; + +#define vmw_crtc_state_to_vcs(x) container_of(x, struct vmw_crtc_state, base) + + +/** + * Derived class for crtc state object + * + * @base DRM crtc object + */ +struct vmw_crtc_state { + struct drm_crtc_state base; +}; + /** * Base class display unit. * @@ -205,6 +218,9 @@ int vmw_du_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv, uint32_t handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y); int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y); +int vmw_du_connector_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val); int vmw_du_connector_dpms(struct drm_connector *connector, int mode); void vmw_du_connector_save(struct drm_connector *connector); void vmw_du_connector_restore(struct drm_connector *connector); @@ -212,9 +228,6 @@ enum drm_connector_status vmw_du_connector_detect(struct drm_connector *connector, bool force); int vmw_du_connector_fill_modes(struct drm_connector *connector, uint32_t max_width, uint32_t max_height); -int vmw_du_connector_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t val); int vmw_kms_helper_dirty(struct vmw_private *dev_priv, struct vmw_framebuffer *framebuffer, const struct drm_clip_rect *clips, @@ -285,6 +298,10 @@ int vmw_du_cursor_plane_update(struct drm_plane *plane, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h); +void vmw_du_crtc_reset(struct drm_crtc *crtc); +struct drm_crtc_state *vmw_du_crtc_duplicate_state(struct drm_crtc *crtc); +void vmw_du_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); /* * Legacy display unit functions - vmwgfx_ldu.c diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 3efcbe514472..3ee33f0234e4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -280,6 +280,9 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) static const struct drm_crtc_funcs vmw_legacy_crtc_funcs = { .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_ldu_crtc_destroy, + .reset = vmw_du_crtc_reset, + .atomic_duplicate_state = vmw_du_crtc_duplicate_state, + .atomic_destroy_state = vmw_du_crtc_destroy_state, .set_config = vmw_ldu_crtc_set_config, }; @@ -355,6 +358,11 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) ldu->base.pref_width = dev_priv->initial_width; ldu->base.pref_height = dev_priv->initial_height; ldu->base.pref_mode = NULL; + + /* + * Remove this after enabling atomic because property values can + * only exist in a state object + */ ldu->base.is_implicit = true; /* Initialize primary plane */ @@ -405,6 +413,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_encoder; } + /* FIXME: Turn on after plane/connector states are implemented. */ + /* vmw_du_crtc_reset(crtc); */ ret = drm_crtc_init_with_planes(dev, crtc, &ldu->base.primary, &ldu->base.cursor, &vmw_legacy_crtc_funcs, NULL); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index 8ffccb87cf3a..033e17b966b1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -461,6 +461,9 @@ static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, static const struct drm_crtc_funcs vmw_screen_object_crtc_funcs = { .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_sou_crtc_destroy, + .reset = vmw_du_crtc_reset, + .atomic_duplicate_state = vmw_du_crtc_duplicate_state, + .atomic_destroy_state = vmw_du_crtc_destroy_state, .set_config = vmw_sou_crtc_set_config, .page_flip = vmw_sou_crtc_page_flip, }; @@ -535,6 +538,11 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) sou->base.pref_width = dev_priv->initial_width; sou->base.pref_height = dev_priv->initial_height; sou->base.pref_mode = NULL; + + /* + * Remove this after enabling atomic because property values can + * only exist in a state object + */ sou->base.is_implicit = false; /* Initialize primary plane */ @@ -586,6 +594,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_encoder; } + /* FIXME: Turn on after plane/connector states are implemented. */ + /* vmw_du_crtc_reset(crtc); */ ret = drm_crtc_init_with_planes(dev, crtc, &sou->base.primary, &sou->base.cursor, &vmw_screen_object_crtc_funcs, NULL); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 4d9dd1b67b93..3b8fafe1586e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -1017,6 +1017,9 @@ int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv, static const struct drm_crtc_funcs vmw_stdu_crtc_funcs = { .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_stdu_crtc_destroy, + .reset = vmw_du_crtc_reset, + .atomic_duplicate_state = vmw_du_crtc_duplicate_state, + .atomic_destroy_state = vmw_du_crtc_destroy_state, .set_config = vmw_stdu_crtc_set_config, .page_flip = vmw_stdu_crtc_page_flip, }; @@ -1131,6 +1134,11 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) stdu->base.pref_active = (unit == 0); stdu->base.pref_width = dev_priv->initial_width; stdu->base.pref_height = dev_priv->initial_height; + + /* + * Remove this after enabling atomic because property values can + * only exist in a state object + */ stdu->base.is_implicit = false; /* Initialize primary plane */ @@ -1181,6 +1189,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_encoder; } + /* FIXME: Turn on after plane/connector states are implemented. */ + /* vmw_du_crtc_reset(crtc); */ ret = drm_crtc_init_with_planes(dev, crtc, &stdu->base.primary, &stdu->base.cursor, &vmw_stdu_crtc_funcs, NULL); From cc5ec459de323fe32514d5f47a4d00460ee30f7b Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 11:36:05 -0700 Subject: [PATCH 04/22] drm/vmwgfx: Plane atomic state Add plane state handling functions. We have to keep track of a few plane states so we cannot use the DRM helper for this. Created vmw_plane_state along with functions to reset, duplicate, and destroty it. v2 * Removed cursor clean up special case Signed-off-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom Acked-by: Daniel Vetter --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 90 ++++++++++++++++++++++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 24 ++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 13 ++++ drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 13 ++++ drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 10 ++++ 5 files changed, 150 insertions(+) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 18bd8dc46507..ec0506794dbd 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -468,6 +468,96 @@ vmw_du_crtc_destroy_state(struct drm_crtc *crtc, } +/** + * vmw_du_plane_duplicate_state - duplicate plane state + * @plane: drm plane + * + * Allocates and returns a copy of the plane state (both common and + * vmw-specific) for the specified plane. + * + * Returns: The newly allocated plane state, or NULL on failure. + */ +struct drm_plane_state * +vmw_du_plane_duplicate_state(struct drm_plane *plane) +{ + struct drm_plane_state *state; + struct vmw_plane_state *vps; + + vps = kmemdup(plane->state, sizeof(*vps), GFP_KERNEL); + + if (!vps) + return NULL; + + vps->pinned = 0; + + /* Each ref counted resource needs to be acquired again */ + if (vps->surf) + (void) vmw_surface_reference(vps->surf); + + if (vps->dmabuf) + (void) vmw_dmabuf_reference(vps->dmabuf); + + state = &vps->base; + + __drm_atomic_helper_plane_duplicate_state(plane, state); + + return state; +} + + +/** + * vmw_du_plane_reset - creates a blank vmw plane state + * @plane: drm plane + * + * Resets the atomic state for @plane by freeing the state pointer (which might + * be NULL, e.g. at driver load time) and allocating a new empty state object. + */ +void vmw_du_plane_reset(struct drm_plane *plane) +{ + struct vmw_plane_state *vps; + + + if (plane->state) + vmw_du_plane_destroy_state(plane, plane->state); + + vps = kzalloc(sizeof(*vps), GFP_KERNEL); + + if (!vps) { + DRM_ERROR("Cannot allocate vmw_plane_state\n"); + return; + } + + plane->state = &vps->base; + plane->state->plane = plane; + plane->state->rotation = DRM_ROTATE_0; +} + + +/** + * vmw_du_plane_destroy_state - destroy plane state + * @plane: DRM plane + * @state: state object to destroy + * + * Destroys the plane state (both common and vmw-specific) for the + * specified plane. + */ +void +vmw_du_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct vmw_plane_state *vps = vmw_plane_state_to_vps(state); + + + if (vps->surf) + vmw_surface_unreference(&vps->surf); + + if (vps->dmabuf) + vmw_dmabuf_unreference(&vps->dmabuf); + + drm_atomic_helper_plane_destroy_state(plane, state); +} + + /* * Generic framebuffer code */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index 370f75c95f56..5602c2430719 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -141,6 +141,7 @@ static const uint32_t vmw_cursor_plane_formats[] = { #define vmw_crtc_state_to_vcs(x) container_of(x, struct vmw_crtc_state, base) +#define vmw_plane_state_to_vps(x) container_of(x, struct vmw_plane_state, base) /** @@ -152,6 +153,25 @@ struct vmw_crtc_state { struct drm_crtc_state base; }; +/** + * Derived class for plane state object + * + * @base DRM plane object + * @surf Display surface for STDU + * @dmabuf display dmabuf for SOU + * @content_fb_type Used by STDU. + * @pinned pin count for STDU display surface + */ +struct vmw_plane_state { + struct drm_plane_state base; + struct vmw_surface *surf; + struct vmw_dma_buffer *dmabuf; + + int content_fb_type; + + int pinned; +}; + /** * Base class display unit. * @@ -298,6 +318,10 @@ int vmw_du_cursor_plane_update(struct drm_plane *plane, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h); +void vmw_du_plane_reset(struct drm_plane *plane); +struct drm_plane_state *vmw_du_plane_duplicate_state(struct drm_plane *plane); +void vmw_du_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state); void vmw_du_crtc_reset(struct drm_crtc *crtc); struct drm_crtc_state *vmw_du_crtc_duplicate_state(struct drm_crtc *crtc); void vmw_du_crtc_destroy_state(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 3ee33f0234e4..36cd1fe5fa33 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -325,12 +325,18 @@ static const struct drm_plane_funcs vmw_ldu_plane_funcs = { .update_plane = drm_primary_helper_update, .disable_plane = drm_primary_helper_disable, .destroy = vmw_du_primary_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, }; static const struct drm_plane_funcs vmw_ldu_cursor_funcs = { .update_plane = vmw_du_cursor_plane_update, .disable_plane = vmw_du_cursor_plane_disable, .destroy = vmw_du_cursor_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, }; @@ -340,6 +346,7 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) struct drm_device *dev = dev_priv->dev; struct drm_connector *connector; struct drm_encoder *encoder; + struct drm_plane *primary, *cursor; struct drm_crtc *crtc; int ret; @@ -351,6 +358,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) crtc = &ldu->base.crtc; encoder = &ldu->base.encoder; connector = &ldu->base.connector; + primary = &ldu->base.primary; + cursor = &ldu->base.cursor; INIT_LIST_HEAD(&ldu->active); @@ -366,6 +375,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) ldu->base.is_implicit = true; /* Initialize primary plane */ + vmw_du_plane_reset(primary); + ret = drm_universal_plane_init(dev, &ldu->base.primary, 0, &vmw_ldu_plane_funcs, vmw_primary_plane_formats, @@ -377,6 +388,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) } /* Initialize cursor plane */ + vmw_du_plane_reset(cursor); + ret = drm_universal_plane_init(dev, &ldu->base.cursor, 0, &vmw_ldu_cursor_funcs, vmw_cursor_plane_formats, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index 033e17b966b1..cfba59a8e155 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -506,12 +506,18 @@ static const struct drm_plane_funcs vmw_sou_plane_funcs = { .update_plane = drm_primary_helper_update, .disable_plane = drm_primary_helper_disable, .destroy = vmw_du_primary_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, }; static const struct drm_plane_funcs vmw_sou_cursor_funcs = { .update_plane = vmw_du_cursor_plane_update, .disable_plane = vmw_du_cursor_plane_disable, .destroy = vmw_du_cursor_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, }; @@ -521,6 +527,7 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) struct drm_device *dev = dev_priv->dev; struct drm_connector *connector; struct drm_encoder *encoder; + struct drm_plane *primary, *cursor; struct drm_crtc *crtc; int ret; @@ -532,6 +539,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) crtc = &sou->base.crtc; encoder = &sou->base.encoder; connector = &sou->base.connector; + primary = &sou->base.primary; + cursor = &sou->base.cursor; sou->base.active_implicit = false; sou->base.pref_active = (unit == 0); @@ -546,6 +555,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) sou->base.is_implicit = false; /* Initialize primary plane */ + vmw_du_plane_reset(primary); + ret = drm_universal_plane_init(dev, &sou->base.primary, 0, &vmw_sou_plane_funcs, vmw_primary_plane_formats, @@ -557,6 +568,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) } /* Initialize cursor plane */ + vmw_du_plane_reset(cursor); + ret = drm_universal_plane_init(dev, &sou->base.cursor, 0, &vmw_sou_cursor_funcs, vmw_cursor_plane_formats, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 3b8fafe1586e..2a0f550235c0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -1090,12 +1090,18 @@ static const struct drm_plane_funcs vmw_stdu_plane_funcs = { .update_plane = drm_primary_helper_update, .disable_plane = drm_primary_helper_disable, .destroy = vmw_du_primary_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, }; static const struct drm_plane_funcs vmw_stdu_cursor_funcs = { .update_plane = vmw_du_cursor_plane_update, .disable_plane = vmw_du_cursor_plane_disable, .destroy = vmw_du_cursor_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, }; @@ -1142,6 +1148,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) stdu->base.is_implicit = false; /* Initialize primary plane */ + vmw_du_plane_reset(primary); + ret = drm_universal_plane_init(dev, primary, 0, &vmw_stdu_plane_funcs, vmw_primary_plane_formats, @@ -1153,6 +1161,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) } /* Initialize cursor plane */ + vmw_du_plane_reset(cursor); + ret = drm_universal_plane_init(dev, cursor, 0, &vmw_stdu_cursor_funcs, vmw_cursor_plane_formats, From d7721ca71126b222fb5d66af444d33950a0e3ba3 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 11:48:44 -0700 Subject: [PATCH 05/22] drm/vmwgfx: Connector atomic state Add connector handling functions. Start tracking is_implicity in the connector state. Eventually, this field should be tracked exclusively in a connector state. Now that plane and connector states have been created, we can also activate the code that use CRTC state. Signed-off-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 139 +++++++++++++++++++++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 32 +++++- drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 13 ++- drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 13 ++- drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 14 ++- 5 files changed, 204 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index ec0506794dbd..0c1241d09d14 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -558,6 +558,81 @@ vmw_du_plane_destroy_state(struct drm_plane *plane, } +/** + * vmw_du_connector_duplicate_state - duplicate connector state + * @connector: DRM connector + * + * Allocates and returns a copy of the connector state (both common and + * vmw-specific) for the specified connector. + * + * Returns: The newly allocated connector state, or NULL on failure. + */ +struct drm_connector_state * +vmw_du_connector_duplicate_state(struct drm_connector *connector) +{ + struct drm_connector_state *state; + struct vmw_connector_state *vcs; + + if (WARN_ON(!connector->state)) + return NULL; + + vcs = kmemdup(connector->state, sizeof(*vcs), GFP_KERNEL); + + if (!vcs) + return NULL; + + state = &vcs->base; + + __drm_atomic_helper_connector_duplicate_state(connector, state); + + return state; +} + + +/** + * vmw_du_connector_reset - creates a blank vmw connector state + * @connector: DRM connector + * + * Resets the atomic state for @connector by freeing the state pointer (which + * might be NULL, e.g. at driver load time) and allocating a new empty state + * object. + */ +void vmw_du_connector_reset(struct drm_connector *connector) +{ + struct vmw_connector_state *vcs; + + + if (connector->state) { + __drm_atomic_helper_connector_destroy_state(connector->state); + + kfree(vmw_connector_state_to_vcs(connector->state)); + } + + vcs = kzalloc(sizeof(*vcs), GFP_KERNEL); + + if (!vcs) { + DRM_ERROR("Cannot allocate vmw_connector_state\n"); + return; + } + + __drm_atomic_helper_connector_reset(connector, &vcs->base); +} + + +/** + * vmw_du_connector_destroy_state - destroy connector state + * @connector: DRM connector + * @state: state object to destroy + * + * Destroys the connector state (both common and vmw-specific) for the + * specified plane. + */ +void +vmw_du_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state) +{ + drm_atomic_helper_connector_destroy_state(connector, state); +} /* * Generic framebuffer code */ @@ -1771,6 +1846,70 @@ int vmw_du_connector_set_property(struct drm_connector *connector, +/** + * vmw_du_connector_atomic_set_property - Atomic version of get property + * + * @crtc - crtc the property is associated with + * + * Returns: + * Zero on success, negative errno on failure. + */ +int +vmw_du_connector_atomic_set_property(struct drm_connector *connector, + struct drm_connector_state *state, + struct drm_property *property, + uint64_t val) +{ + struct vmw_private *dev_priv = vmw_priv(connector->dev); + struct vmw_connector_state *vcs = vmw_connector_state_to_vcs(state); + struct vmw_display_unit *du = vmw_connector_to_du(connector); + + + if (property == dev_priv->implicit_placement_property) { + vcs->is_implicit = val; + + /* + * We should really be doing a drm_atomic_commit() to + * commit the new state, but since this doesn't cause + * an immedate state change, this is probably ok + */ + du->is_implicit = vcs->is_implicit; + } else { + return -EINVAL; + } + + return 0; +} + + +/** + * vmw_du_connector_atomic_get_property - Atomic version of get property + * + * @connector - connector the property is associated with + * + * Returns: + * Zero on success, negative errno on failure. + */ +int +vmw_du_connector_atomic_get_property(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct vmw_private *dev_priv = vmw_priv(connector->dev); + struct vmw_connector_state *vcs = vmw_connector_state_to_vcs(state); + + if (property == dev_priv->implicit_placement_property) + *val = vcs->is_implicit; + else { + DRM_ERROR("Invalid Property %s\n", property->name); + return -EINVAL; + } + + return 0; +} + + int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index 5602c2430719..cc50bf35aeca 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -142,7 +142,8 @@ static const uint32_t vmw_cursor_plane_formats[] = { #define vmw_crtc_state_to_vcs(x) container_of(x, struct vmw_crtc_state, base) #define vmw_plane_state_to_vps(x) container_of(x, struct vmw_plane_state, base) - +#define vmw_connector_state_to_vcs(x) \ + container_of(x, struct vmw_connector_state, base) /** * Derived class for crtc state object @@ -172,6 +173,20 @@ struct vmw_plane_state { int pinned; }; + +/** + * Derived class for connector state object + * + * @base DRM connector object + * @is_implicit connector property + * + */ +struct vmw_connector_state { + struct drm_connector_state base; + + bool is_implicit; +}; + /** * Base class display unit. * @@ -241,6 +256,15 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y); int vmw_du_connector_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t val); +int vmw_du_connector_atomic_set_property(struct drm_connector *connector, + struct drm_connector_state *state, + struct drm_property *property, + uint64_t val); +int +vmw_du_connector_atomic_get_property(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, + uint64_t *val); int vmw_du_connector_dpms(struct drm_connector *connector, int mode); void vmw_du_connector_save(struct drm_connector *connector); void vmw_du_connector_restore(struct drm_connector *connector); @@ -326,6 +350,12 @@ void vmw_du_crtc_reset(struct drm_crtc *crtc); struct drm_crtc_state *vmw_du_crtc_duplicate_state(struct drm_crtc *crtc); void vmw_du_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state); +void vmw_du_connector_reset(struct drm_connector *connector); +struct drm_connector_state * +vmw_du_connector_duplicate_state(struct drm_connector *connector); + +void vmw_du_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state); /* * Legacy display unit functions - vmwgfx_ldu.c diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 36cd1fe5fa33..276c7440b8b1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -27,6 +27,8 @@ #include "vmwgfx_kms.h" #include +#include +#include #define vmw_crtc_to_ldu(x) \ @@ -315,6 +317,11 @@ static const struct drm_connector_funcs vmw_legacy_connector_funcs = { .fill_modes = vmw_du_connector_fill_modes, .set_property = vmw_du_connector_set_property, .destroy = vmw_ldu_connector_destroy, + .reset = vmw_du_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .atomic_set_property = vmw_du_connector_atomic_set_property, + .atomic_get_property = vmw_du_connector_atomic_get_property, }; /* @@ -408,6 +415,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free; } connector->status = vmw_du_connector_detect(connector, true); + vmw_connector_state_to_vcs(connector->state)->is_implicit = true; + ret = drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, DRM_MODE_ENCODER_VIRTUAL, NULL); @@ -426,8 +435,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_encoder; } - /* FIXME: Turn on after plane/connector states are implemented. */ - /* vmw_du_crtc_reset(crtc); */ + + vmw_du_crtc_reset(crtc); ret = drm_crtc_init_with_planes(dev, crtc, &ldu->base.primary, &ldu->base.cursor, &vmw_legacy_crtc_funcs, NULL); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index cfba59a8e155..36d42f5656b4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -27,6 +27,8 @@ #include "vmwgfx_kms.h" #include +#include +#include #define vmw_crtc_to_sou(x) \ @@ -496,6 +498,11 @@ static const struct drm_connector_funcs vmw_sou_connector_funcs = { .fill_modes = vmw_du_connector_fill_modes, .set_property = vmw_du_connector_set_property, .destroy = vmw_sou_connector_destroy, + .reset = vmw_du_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .atomic_set_property = vmw_du_connector_atomic_set_property, + .atomic_get_property = vmw_du_connector_atomic_get_property, }; /* @@ -589,6 +596,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) } connector->status = vmw_du_connector_detect(connector, true); + vmw_connector_state_to_vcs(connector->state)->is_implicit = false; + ret = drm_encoder_init(dev, encoder, &vmw_screen_object_encoder_funcs, DRM_MODE_ENCODER_VIRTUAL, NULL); @@ -607,8 +616,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_encoder; } - /* FIXME: Turn on after plane/connector states are implemented. */ - /* vmw_du_crtc_reset(crtc); */ + + vmw_du_crtc_reset(crtc); ret = drm_crtc_init_with_planes(dev, crtc, &sou->base.primary, &sou->base.cursor, &vmw_screen_object_crtc_funcs, NULL); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 2a0f550235c0..b1fae4975247 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -28,6 +28,9 @@ #include "vmwgfx_kms.h" #include "device_include/svga3d_surfacedefs.h" #include +#include +#include + #define vmw_crtc_to_stdu(x) \ container_of(x, struct vmw_screen_target_display_unit, base.crtc) @@ -1078,6 +1081,11 @@ static const struct drm_connector_funcs vmw_stdu_connector_funcs = { .fill_modes = vmw_du_connector_fill_modes, .set_property = vmw_du_connector_set_property, .destroy = vmw_stdu_connector_destroy, + .reset = vmw_du_connector_reset, + .atomic_duplicate_state = vmw_du_connector_duplicate_state, + .atomic_destroy_state = vmw_du_connector_destroy_state, + .atomic_set_property = vmw_du_connector_atomic_set_property, + .atomic_get_property = vmw_du_connector_atomic_get_property, }; @@ -1174,6 +1182,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free; } + vmw_du_connector_reset(connector); + ret = drm_connector_init(dev, connector, &vmw_stdu_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); if (ret) { @@ -1181,6 +1191,7 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free; } connector->status = vmw_du_connector_detect(connector, false); + vmw_connector_state_to_vcs(connector->state)->is_implicit = false; ret = drm_encoder_init(dev, encoder, &vmw_stdu_encoder_funcs, DRM_MODE_ENCODER_VIRTUAL, NULL); @@ -1199,8 +1210,7 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_encoder; } - /* FIXME: Turn on after plane/connector states are implemented. */ - /* vmw_du_crtc_reset(crtc); */ + vmw_du_crtc_reset(crtc); ret = drm_crtc_init_with_planes(dev, crtc, &stdu->base.primary, &stdu->base.cursor, &vmw_stdu_crtc_funcs, NULL); From 06ec41909e31be3347f8679e9667d12ac6f7ee6e Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 13:14:54 -0700 Subject: [PATCH 06/22] drm/vmwgfx: Add and connect CRTC helper functions Atomic mode set requires us to refactor existing vmw_stdu_crtc_set_config code into sections that check the validity of the new mode, and sections that actually program the hardware state. vmw_du_crtc_atomic_check() takes CRTC-related checking code. In a later patch, vmw_du_primary_plane_atomic_check() will take framebuffer-related checking code. These helpers won't be called until we flip on the atomic support flag or set drm_crtc_funcs->set_config to using the atomic helper. v2: * The state->num_connector is actually the total number of potential connectors, not just the one associated with the display unit. The proper one to check is ->connector_mask. * Add the check to only allow plane state to be the same as crtc state (Thanks to mlankhorst) * Make sure to turn on SVGA mode before using VRAM. SVGA mode is disabled in master_drop if dbdev is not running. v3: * Moved dot clock override to crtc_atomic_check Signed-off-by: Sinclair Yeh Signed-off-by: Thomas Hellstrom Reviewed-by: Thomas Hellstrom Acked-by: Daniel Vetter --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 55 +++++++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 9 ++ drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 79 ++++++++++++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 119 +++++++++++++++++++++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 117 ++++++++++++++++++++++++++ 5 files changed, 379 insertions(+) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 0c1241d09d14..40224e39af13 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -390,6 +390,61 @@ void vmw_du_primary_plane_destroy(struct drm_plane *plane) } +int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *new_state) +{ + struct vmw_display_unit *du = vmw_crtc_to_du(new_state->crtc); + int connector_mask = 1 << drm_connector_index(&du->connector); + bool has_primary = new_state->plane_mask & + BIT(drm_plane_index(crtc->primary)); + + /* We always want to have an active plane with an active CRTC */ + if (has_primary != new_state->enable) + return -EINVAL; + + + if (new_state->connector_mask != connector_mask && + new_state->connector_mask != 0) { + DRM_ERROR("Invalid connectors configuration\n"); + return -EINVAL; + } + + /* + * Our virtual device does not have a dot clock, so use the logical + * clock value as the dot clock. + */ + if (new_state->mode.crtc_clock == 0) + new_state->adjusted_mode.crtc_clock = new_state->mode.clock; + + return 0; +} + + +void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ +} + + +void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct drm_pending_vblank_event *event = crtc->state->event; + + if (event) { + crtc->state->event = NULL; + + spin_lock_irq(&crtc->dev->event_lock); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } + +} + + /** * vmw_du_crtc_duplicate_state - duplicate crtc state * @crtc: DRM crtc diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index cc50bf35aeca..f711b5da73e5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -161,6 +161,7 @@ struct vmw_crtc_state { * @surf Display surface for STDU * @dmabuf display dmabuf for SOU * @content_fb_type Used by STDU. + * @dmabuf_size Size of the dmabuf, used by Screen Object Display Unit * @pinned pin count for STDU display surface */ struct vmw_plane_state { @@ -169,6 +170,7 @@ struct vmw_plane_state { struct vmw_dma_buffer *dmabuf; int content_fb_type; + unsigned long dmabuf_size; int pinned; }; @@ -342,10 +344,17 @@ int vmw_du_cursor_plane_update(struct drm_plane *plane, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h); +/* Atomic Helpers */ void vmw_du_plane_reset(struct drm_plane *plane); struct drm_plane_state *vmw_du_plane_duplicate_state(struct drm_plane *plane); void vmw_du_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state); +int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state); +void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state); +void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state); void vmw_du_crtc_reset(struct drm_crtc *crtc); struct drm_crtc_state *vmw_du_crtc_duplicate_state(struct drm_crtc *crtc); void vmw_du_crtc_destroy_state(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 276c7440b8b1..d547e8061f6a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -167,6 +167,7 @@ static int vmw_ldu_add_active(struct vmw_private *vmw_priv, if (vfb != ld->fb) { if (ld->fb && ld->fb->unpin) ld->fb->unpin(ld->fb); + vmw_svga_enable(vmw_priv); if (vfb->pin) vfb->pin(vfb); ld->fb = vfb; @@ -190,6 +191,68 @@ static int vmw_ldu_add_active(struct vmw_private *vmw_priv, return 0; } +/** + * vmw_ldu_crtc_mode_set_nofb - Enable svga + * + * @crtc: CRTC associated with the new screen + * + * For LDU, just enable the svga + */ +static void vmw_ldu_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ +} + +/** + * vmw_ldu_crtc_helper_prepare - Noop + * + * @crtc: CRTC associated with the new screen + * + * Prepares the CRTC for a mode set, but we don't need to do anything here. + * + */ +static void vmw_ldu_crtc_helper_prepare(struct drm_crtc *crtc) +{ +} + +/** + * vmw_ldu_crtc_helper_commit - Noop + * + * @crtc: CRTC associated with the new screen + * + * This is called after a mode set has been completed. Here's + * usually a good place to call vmw_ldu_add_active/vmw_ldu_del_active + * but since for LDU the display plane is closely tied to the + * CRTC, it makes more sense to do those at plane update time. + */ +static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_legacy_display_unit *ldu; + struct vmw_framebuffer *vfb; + struct drm_framebuffer *fb; + + + ldu = vmw_crtc_to_ldu(crtc); + dev_priv = vmw_priv(crtc->dev); + fb = crtc->primary->fb; + + vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; + + if (vfb) + vmw_ldu_add_active(dev_priv, ldu, vfb); + else + vmw_ldu_del_active(dev_priv, ldu); +} + +/** + * vmw_ldu_crtc_helper_disable - Turns off CRTC + * + * @crtc: CRTC to be turned off + */ +static void vmw_ldu_crtc_helper_disable(struct drm_crtc *crtc) +{ +} + static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) { struct vmw_private *dev_priv; @@ -346,6 +409,20 @@ static const struct drm_plane_funcs vmw_ldu_cursor_funcs = { .atomic_destroy_state = vmw_du_plane_destroy_state, }; +/* + * Atomic Helpers + */ +static const struct drm_crtc_helper_funcs vmw_ldu_crtc_helper_funcs = { + .prepare = vmw_ldu_crtc_helper_prepare, + .commit = vmw_ldu_crtc_helper_commit, + .disable = vmw_ldu_crtc_helper_disable, + .mode_set = drm_helper_crtc_mode_set, + .mode_set_nofb = vmw_ldu_crtc_mode_set_nofb, + .atomic_check = vmw_du_crtc_atomic_check, + .atomic_begin = vmw_du_crtc_atomic_begin, + .atomic_flush = vmw_du_crtc_atomic_flush, +}; + static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) { @@ -445,6 +522,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_unregister; } + drm_crtc_helper_add(crtc, &vmw_ldu_crtc_helper_funcs); + drm_mode_crtc_set_gamma_size(crtc, 256); drm_object_attach_property(&connector->base, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index 36d42f5656b4..662024c9f351 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -250,6 +250,109 @@ static int vmw_sou_backing_alloc(struct vmw_private *dev_priv, return ret; } +/** + * vmw_sou_crtc_mode_set_nofb - Create new screen + * + * @crtc: CRTC associated with the new screen + * + * This function creates/destroys a screen. This function cannot fail, so if + * somehow we run into a failure, just do the best we can to get out. + */ +static void vmw_sou_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_object_unit *sou; + struct vmw_framebuffer *vfb; + struct drm_framebuffer *fb; + struct drm_plane_state *ps; + struct vmw_plane_state *vps; + int ret; + + + sou = vmw_crtc_to_sou(crtc); + dev_priv = vmw_priv(crtc->dev); + ps = crtc->primary->state; + fb = ps->fb; + vps = vmw_plane_state_to_vps(ps); + + vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; + + if (sou->defined) { + ret = vmw_sou_fifo_destroy(dev_priv, sou); + if (ret) { + DRM_ERROR("Failed to destroy Screen Object\n"); + return; + } + } + + if (vfb) { + sou->buffer = vps->dmabuf; + sou->buffer_size = vps->dmabuf_size; + + ret = vmw_sou_fifo_create(dev_priv, sou, crtc->x, crtc->y, + &crtc->mode); + if (ret) + DRM_ERROR("Failed to define Screen Object %dx%d\n", + crtc->x, crtc->y); + + vmw_kms_add_active(dev_priv, &sou->base, vfb); + } else { + sou->buffer = NULL; + sou->buffer_size = 0; + + vmw_kms_del_active(dev_priv, &sou->base); + } +} + +/** + * vmw_sou_crtc_helper_prepare - Noop + * + * @crtc: CRTC associated with the new screen + * + * Prepares the CRTC for a mode set, but we don't need to do anything here. + */ +static void vmw_sou_crtc_helper_prepare(struct drm_crtc *crtc) +{ +} + +/** + * vmw_sou_crtc_helper_commit - Noop + * + * @crtc: CRTC associated with the new screen + * + * This is called after a mode set has been completed. + */ +static void vmw_sou_crtc_helper_commit(struct drm_crtc *crtc) +{ +} + +/** + * vmw_sou_crtc_helper_disable - Turns off CRTC + * + * @crtc: CRTC to be turned off + */ +static void vmw_sou_crtc_helper_disable(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_object_unit *sou; + int ret; + + + if (!crtc) { + DRM_ERROR("CRTC is NULL\n"); + return; + } + + sou = vmw_crtc_to_sou(crtc); + dev_priv = vmw_priv(crtc->dev); + + if (sou->defined) { + ret = vmw_sou_fifo_destroy(dev_priv, sou); + if (ret) + DRM_ERROR("Failed to destroy Screen Object\n"); + } +} + static int vmw_sou_crtc_set_config(struct drm_mode_set *set) { struct vmw_private *dev_priv; @@ -527,6 +630,20 @@ static const struct drm_plane_funcs vmw_sou_cursor_funcs = { .atomic_destroy_state = vmw_du_plane_destroy_state, }; +/* + * Atomic Helpers + */ +static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = { + .prepare = vmw_sou_crtc_helper_prepare, + .commit = vmw_sou_crtc_helper_commit, + .disable = vmw_sou_crtc_helper_disable, + .mode_set = drm_helper_crtc_mode_set, + .mode_set_nofb = vmw_sou_crtc_mode_set_nofb, + .atomic_check = vmw_du_crtc_atomic_check, + .atomic_begin = vmw_du_crtc_atomic_begin, + .atomic_flush = vmw_du_crtc_atomic_flush, +}; + static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) { @@ -626,6 +743,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_unregister; } + drm_crtc_helper_add(crtc, &vmw_sou_crtc_helper_funcs); + drm_mode_crtc_set_gamma_size(crtc, 256); drm_object_attach_property(&connector->base, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index b1fae4975247..6e3cfad336b0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -500,6 +500,106 @@ static int vmw_stdu_bind_fb(struct vmw_private *dev_priv, return ret; } + +/** + * vmw_stdu_crtc_mode_set_nofb - Updates screen target size + * + * @crtc: CRTC associated with the screen target + * + * This function defines/destroys a screen target + * + */ +static void vmw_stdu_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_target_display_unit *stdu; + int ret; + + + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + + if (stdu->defined) { + ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); + if (ret) + DRM_ERROR("Failed to blank CRTC\n"); + + (void) vmw_stdu_update_st(dev_priv, stdu); + + ret = vmw_stdu_destroy_st(dev_priv, stdu); + if (ret) + DRM_ERROR("Failed to destroy Screen Target\n"); + + stdu->content_fb_type = SAME_AS_DISPLAY; + } + + if (!crtc->state->enable) + return; + + vmw_svga_enable(dev_priv); + ret = vmw_stdu_define_st(dev_priv, stdu, &crtc->mode, crtc->x, crtc->y); + + if (ret) + DRM_ERROR("Failed to define Screen Target of size %dx%d\n", + crtc->x, crtc->y); +} + + +static void vmw_stdu_crtc_helper_prepare(struct drm_crtc *crtc) +{ +} + + +static void vmw_stdu_crtc_helper_commit(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_target_display_unit *stdu; + struct vmw_framebuffer *vfb; + struct drm_framebuffer *fb; + + + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + fb = crtc->primary->fb; + + vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; + + if (vfb) + vmw_kms_add_active(dev_priv, &stdu->base, vfb); + else + vmw_kms_del_active(dev_priv, &stdu->base); +} + +static void vmw_stdu_crtc_helper_disable(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_target_display_unit *stdu; + int ret; + + + if (!crtc) { + DRM_ERROR("CRTC is NULL\n"); + return; + } + + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + + if (stdu->defined) { + ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); + if (ret) + DRM_ERROR("Failed to blank CRTC\n"); + + (void) vmw_stdu_update_st(dev_priv, stdu); + + ret = vmw_stdu_destroy_st(dev_priv, stdu); + if (ret) + DRM_ERROR("Failed to destroy Screen Target\n"); + + stdu->content_fb_type = SAME_AS_DISPLAY; + } +} + /** * vmw_stdu_crtc_set_config - Sets a mode * @@ -1113,6 +1213,21 @@ static const struct drm_plane_funcs vmw_stdu_cursor_funcs = { }; +/* + * Atomic Helpers + */ +static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = { + .prepare = vmw_stdu_crtc_helper_prepare, + .commit = vmw_stdu_crtc_helper_commit, + .disable = vmw_stdu_crtc_helper_disable, + .mode_set = drm_helper_crtc_mode_set, + .mode_set_nofb = vmw_stdu_crtc_mode_set_nofb, + .atomic_check = vmw_du_crtc_atomic_check, + .atomic_begin = vmw_du_crtc_atomic_begin, + .atomic_flush = vmw_du_crtc_atomic_flush, +}; + + /** * vmw_stdu_init - Sets up a Screen Target Display Unit * @@ -1219,6 +1334,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_unregister; } + drm_crtc_helper_add(crtc, &vmw_stdu_crtc_helper_funcs); + drm_mode_crtc_set_gamma_size(crtc, 256); drm_object_attach_property(&connector->base, From 060e2ad57041b42ccecd0047ef4d893f200692c2 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 14:18:32 -0700 Subject: [PATCH 07/22] drm/vmwgfx: Add and connect plane helper functions Refactor previous FB and cursor plane update code into their atomic counterparts: check, update, prepare, cleanup, and disable. These helpers won't be called until we flip on the atomic support flag or set drm_crtc_funcs->set_config to using the atomic helper. v2: * Removed unnecessary pinning of cursor surface * Added a few function headers v3: * Set clip region equal to the destination region * Fixed surface pinning policy * Enable SVGA mode in vmw_sou_primary_plane_prepare_fb Signed-off-by: Sinclair Yeh Signed-off-by: Thomas Hellstrom Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 256 +++++++++++++++++++++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 15 ++ drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 66 ++++++- drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 115 ++++++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 244 +++++++++++++++++++++++++ 5 files changed, 695 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 40224e39af13..0644b716f1dc 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -26,8 +26,10 @@ **************************************************************************/ #include "vmwgfx_kms.h" +#include #include #include +#include /* Might need a hrtimer here? */ @@ -390,6 +392,260 @@ void vmw_du_primary_plane_destroy(struct drm_plane *plane) } +/** + * vmw_du_vps_unpin_surf - unpins resource associated with a framebuffer surface + * + * @vps: plane state associated with the display surface + * @unreference: true if we also want to unreference the display. + */ +void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps, + bool unreference) +{ + if (vps->surf) { + if (vps->pinned) { + vmw_resource_unpin(&vps->surf->res); + vps->pinned--; + } + + if (unreference) { + if (vps->pinned) + DRM_ERROR("Surface still pinned\n"); + vmw_surface_unreference(&vps->surf); + } + } +} + + +/** + * vmw_du_plane_cleanup_fb - Unpins the cursor + * + * @plane: display plane + * @old_state: Contains the FB to clean up + * + * Unpins the framebuffer surface + * + * Returns 0 on success + */ +void +vmw_du_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); + + vmw_du_plane_unpin_surf(vps, false); +} + + +/** + * vmw_du_cursor_plane_prepare_fb - Readies the cursor by referencing it + * + * @plane: display plane + * @new_state: info on the new plane state, including the FB + * + * Returns 0 on success + */ +int +vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_framebuffer *fb = new_state->fb; + struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + + + if (vps->surf) + vmw_surface_unreference(&vps->surf); + + if (vps->dmabuf) + vmw_dmabuf_unreference(&vps->dmabuf); + + if (fb) { + if (vmw_framebuffer_to_vfb(fb)->dmabuf) { + vps->dmabuf = vmw_framebuffer_to_vfbd(fb)->buffer; + vmw_dmabuf_reference(vps->dmabuf); + } else { + vps->surf = vmw_framebuffer_to_vfbs(fb)->surface; + vmw_surface_reference(vps->surf); + } + } + + return 0; +} + + +void +vmw_du_cursor_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc; + struct vmw_private *dev_priv = vmw_priv(crtc->dev); + + drm_atomic_set_fb_for_plane(plane->state, NULL); + vmw_cursor_update_position(dev_priv, false, 0, 0); +} + + +void +vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc; + struct vmw_private *dev_priv = vmw_priv(crtc->dev); + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct vmw_plane_state *vps = vmw_plane_state_to_vps(plane->state); + s32 hotspot_x, hotspot_y; + int ret = 0; + + + hotspot_x = du->hotspot_x; + hotspot_y = du->hotspot_y; + du->cursor_surface = vps->surf; + du->cursor_dmabuf = vps->dmabuf; + + /* setup new image */ + if (vps->surf) { + du->cursor_age = du->cursor_surface->snooper.age; + + ret = vmw_cursor_update_image(dev_priv, + vps->surf->snooper.image, + 64, 64, hotspot_x, hotspot_y); + } else if (vps->dmabuf) { + ret = vmw_cursor_update_dmabuf(dev_priv, vps->dmabuf, + plane->state->crtc_w, + plane->state->crtc_h, + hotspot_x, hotspot_y); + } else { + vmw_cursor_update_position(dev_priv, false, 0, 0); + return; + } + + if (!ret) { + du->cursor_x = plane->state->crtc_x + du->set_gui_x; + du->cursor_y = plane->state->crtc_y + du->set_gui_y; + + vmw_cursor_update_position(dev_priv, true, + du->cursor_x + hotspot_x, + du->cursor_y + hotspot_y); + } else { + DRM_ERROR("Failed to update cursor image\n"); + } +} + + +/** + * vmw_du_primary_plane_atomic_check - check if the new state is okay + * + * @plane: display plane + * @state: info on the new plane state, including the FB + * + * Check if the new state is settable given the current state. Other + * than what the atomic helper checks, we care about crtc fitting + * the FB and maintaining one active framebuffer. + * + * Returns 0 on success + */ +int vmw_du_primary_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_framebuffer *new_fb = state->fb; + bool visible; + + struct drm_rect src = { + .x1 = state->src_x, + .y1 = state->src_y, + .x2 = state->src_x + state->src_w, + .y2 = state->src_y + state->src_h, + }; + struct drm_rect dest = { + .x1 = state->crtc_x, + .y1 = state->crtc_y, + .x2 = state->crtc_x + state->crtc_w, + .y2 = state->crtc_y + state->crtc_h, + }; + struct drm_rect clip = dest; + int ret; + + ret = drm_plane_helper_check_update(plane, state->crtc, new_fb, + &src, &dest, &clip, + DRM_ROTATE_0, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true, &visible); + + + if (!ret && new_fb) { + struct drm_crtc *crtc = state->crtc; + struct vmw_connector_state *vcs; + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct vmw_private *dev_priv = vmw_priv(crtc->dev); + struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); + + vcs = vmw_connector_state_to_vcs(du->connector.state); + + if ((dest.x2 > new_fb->width || + dest.y2 > new_fb->height)) { + DRM_ERROR("CRTC area outside of framebuffer\n"); + return -EINVAL; + } + + /* Only one active implicit framebuffer at a time. */ + mutex_lock(&dev_priv->global_kms_state_mutex); + if (vcs->is_implicit && dev_priv->implicit_fb && + !(dev_priv->num_implicit == 1 && du->active_implicit) + && dev_priv->implicit_fb != vfb) { + DRM_ERROR("Multiple implicit framebuffers " + "not supported.\n"); + ret = -EINVAL; + } + mutex_unlock(&dev_priv->global_kms_state_mutex); + } + + + return ret; +} + + +/** + * vmw_du_cursor_plane_atomic_check - check if the new state is okay + * + * @plane: cursor plane + * @state: info on the new plane state + * + * This is a chance to fail if the new cursor state does not fit + * our requirements. + * + * Returns 0 on success + */ +int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + int ret = 0; + struct vmw_surface *surface = NULL; + struct drm_framebuffer *fb = new_state->fb; + + + /* Turning off */ + if (!fb) + return ret; + + /* A lot of the code assumes this */ + if (new_state->crtc_w != 64 || new_state->crtc_h != 64) { + DRM_ERROR("Invalid cursor dimensions (%d, %d)\n", + new_state->crtc_w, new_state->crtc_h); + ret = -EINVAL; + } + + if (!vmw_framebuffer_to_vfb(fb)->dmabuf) + surface = vmw_framebuffer_to_vfbs(fb)->surface; + + if (surface && !surface->snooper.image) { + DRM_ERROR("surface not suitable for cursor\n"); + ret = -EINVAL; + } + + return ret; +} + + int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *new_state) { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index f711b5da73e5..de6a0b64bb4b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -345,10 +345,25 @@ int vmw_du_cursor_plane_update(struct drm_plane *plane, uint32_t src_w, uint32_t src_h); /* Atomic Helpers */ +int vmw_du_primary_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state); +int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state); +void vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state); +void vmw_du_cursor_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state); +int vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state); +void vmw_du_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state); void vmw_du_plane_reset(struct drm_plane *plane); struct drm_plane_state *vmw_du_plane_duplicate_state(struct drm_plane *plane); void vmw_du_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state); +void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps, + bool unreference); + int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state); void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index d547e8061f6a..1d734de1e6e1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -234,7 +234,7 @@ static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc) ldu = vmw_crtc_to_ldu(crtc); dev_priv = vmw_priv(crtc->dev); - fb = crtc->primary->fb; + fb = crtc->primary->state->fb; vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; @@ -242,6 +242,8 @@ static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc) vmw_ldu_add_active(dev_priv, ldu, vfb); else vmw_ldu_del_active(dev_priv, ldu); + + vmw_ldu_commit_list(dev_priv); } /** @@ -391,6 +393,46 @@ static const struct drm_connector_funcs vmw_legacy_connector_funcs = { * Legacy Display Plane Functions */ +/** + * vmw_ldu_primary_plane_cleanup_fb - Unpin fb + * + * @plane: display plane + * @old_state: Contains the FB to clean up + * + * Unpins the display surface + * + * Returns 0 on success + */ +static void +vmw_ldu_primary_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ +} + + +/** + * vmw_ldu_primary_plane_prepare_fb - + * + * @plane: display plane + * @new_state: info on the new plane state, including the FB + * + * Returns 0 on success + */ +static int +vmw_ldu_primary_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + return 0; +} + + +static void +vmw_ldu_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ +} + + static const struct drm_plane_funcs vmw_ldu_plane_funcs = { .update_plane = drm_primary_helper_update, .disable_plane = drm_primary_helper_disable, @@ -412,6 +454,22 @@ static const struct drm_plane_funcs vmw_ldu_cursor_funcs = { /* * Atomic Helpers */ +static const struct +drm_plane_helper_funcs vmw_ldu_cursor_plane_helper_funcs = { + .atomic_check = vmw_du_cursor_plane_atomic_check, + .atomic_update = vmw_du_cursor_plane_atomic_update, + .prepare_fb = vmw_du_cursor_plane_prepare_fb, + .cleanup_fb = vmw_du_plane_cleanup_fb, +}; + +static const struct +drm_plane_helper_funcs vmw_ldu_primary_plane_helper_funcs = { + .atomic_check = vmw_du_primary_plane_atomic_check, + .atomic_update = vmw_ldu_primary_plane_atomic_update, + .prepare_fb = vmw_ldu_primary_plane_prepare_fb, + .cleanup_fb = vmw_ldu_primary_plane_cleanup_fb, +}; + static const struct drm_crtc_helper_funcs vmw_ldu_crtc_helper_funcs = { .prepare = vmw_ldu_crtc_helper_prepare, .commit = vmw_ldu_crtc_helper_commit, @@ -471,6 +529,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free; } + drm_plane_helper_add(primary, &vmw_ldu_primary_plane_helper_funcs); + /* Initialize cursor plane */ vmw_du_plane_reset(cursor); @@ -485,6 +545,10 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free; } + drm_plane_helper_add(cursor, &vmw_ldu_cursor_plane_helper_funcs); + + + vmw_du_connector_reset(connector); ret = drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); if (ret) { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index 662024c9f351..eca055ec27c8 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -612,6 +612,100 @@ static const struct drm_connector_funcs vmw_sou_connector_funcs = { * Screen Object Display Plane Functions */ +/** + * vmw_sou_primary_plane_cleanup_fb - Frees sou backing buffer + * + * @plane: display plane + * @old_state: Contains the FB to clean up + * + * Unpins the display surface + * + * Returns 0 on success + */ +static void +vmw_sou_primary_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); + + vmw_dmabuf_unreference(&vps->dmabuf); + vps->dmabuf_size = 0; + + vmw_du_plane_cleanup_fb(plane, old_state); +} + + +/** + * vmw_sou_primary_plane_prepare_fb - allocate backing buffer + * + * @plane: display plane + * @new_state: info on the new plane state, including the FB + * + * The SOU backing buffer is our equivalent of the display plane. + * + * Returns 0 on success + */ +static int +vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_framebuffer *new_fb = new_state->fb; + struct drm_crtc *crtc = plane->state->crtc ?: new_state->crtc; + struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + struct vmw_private *dev_priv; + size_t size; + int ret; + + + if (!new_fb) { + vmw_dmabuf_unreference(&vps->dmabuf); + vps->dmabuf_size = 0; + + return 0; + } + + size = new_state->crtc_w * new_state->crtc_h * 4; + + if (vps->dmabuf) { + if (vps->dmabuf_size == size) + return 0; + + vmw_dmabuf_unreference(&vps->dmabuf); + vps->dmabuf_size = 0; + } + + vps->dmabuf = kzalloc(sizeof(*vps->dmabuf), GFP_KERNEL); + if (!vps->dmabuf) + return -ENOMEM; + + dev_priv = vmw_priv(crtc->dev); + vmw_svga_enable(dev_priv); + + /* After we have alloced the backing store might not be able to + * resume the overlays, this is preferred to failing to alloc. + */ + vmw_overlay_pause_all(dev_priv); + ret = vmw_dmabuf_init(dev_priv, vps->dmabuf, size, + &vmw_vram_ne_placement, + false, &vmw_dmabuf_bo_free); + vmw_overlay_resume_all(dev_priv); + + if (ret != 0) + vps->dmabuf = NULL; /* vmw_dmabuf_init frees on error */ + else + vps->dmabuf_size = size; + + return ret; +} + + +static void +vmw_sou_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ +} + + static const struct drm_plane_funcs vmw_sou_plane_funcs = { .update_plane = drm_primary_helper_update, .disable_plane = drm_primary_helper_disable, @@ -633,6 +727,22 @@ static const struct drm_plane_funcs vmw_sou_cursor_funcs = { /* * Atomic Helpers */ +static const struct +drm_plane_helper_funcs vmw_sou_cursor_plane_helper_funcs = { + .atomic_check = vmw_du_cursor_plane_atomic_check, + .atomic_update = vmw_du_cursor_plane_atomic_update, + .prepare_fb = vmw_du_cursor_plane_prepare_fb, + .cleanup_fb = vmw_du_plane_cleanup_fb, +}; + +static const struct +drm_plane_helper_funcs vmw_sou_primary_plane_helper_funcs = { + .atomic_check = vmw_du_primary_plane_atomic_check, + .atomic_update = vmw_sou_primary_plane_atomic_update, + .prepare_fb = vmw_sou_primary_plane_prepare_fb, + .cleanup_fb = vmw_sou_primary_plane_cleanup_fb, +}; + static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = { .prepare = vmw_sou_crtc_helper_prepare, .commit = vmw_sou_crtc_helper_commit, @@ -691,6 +801,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) goto err_free; } + drm_plane_helper_add(primary, &vmw_sou_primary_plane_helper_funcs); + /* Initialize cursor plane */ vmw_du_plane_reset(cursor); @@ -705,6 +817,9 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) goto err_free; } + drm_plane_helper_add(cursor, &vmw_sou_cursor_plane_helper_funcs); + + vmw_du_connector_reset(connector); ret = drm_connector_init(dev, connector, &vmw_sou_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); if (ret) { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 6e3cfad336b0..cce5e5b02e20 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -1194,6 +1194,230 @@ static const struct drm_connector_funcs vmw_stdu_connector_funcs = { * Screen Target Display Plane Functions *****************************************************************************/ + + +/** + * vmw_stdu_primary_plane_cleanup_fb - Unpins the display surface + * + * @plane: display plane + * @old_state: Contains the FB to clean up + * + * Unpins the display surface + * + * Returns 0 on success + */ +static void +vmw_stdu_primary_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); + + if (vps->surf) + WARN_ON(!vps->pinned); + + vmw_du_plane_cleanup_fb(plane, old_state); + + vps->content_fb_type = SAME_AS_DISPLAY; +} + + + +/** + * vmw_stdu_primary_plane_prepare_fb - Readies the display surface + * + * @plane: display plane + * @new_state: info on the new plane state, including the FB + * + * This function allocates a new display surface if the content is + * backed by a DMA. The display surface is pinned here, and it'll + * be unpinned in .cleanup_fb() + * + * Returns 0 on success + */ +static int +vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_framebuffer *new_fb = new_state->fb; + struct vmw_framebuffer *vfb; + struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + enum stdu_content_type new_content_type; + struct vmw_framebuffer_surface *new_vfbs; + struct drm_crtc *crtc = new_state->crtc; + uint32_t hdisplay = new_state->crtc_w, vdisplay = new_state->crtc_h; + int ret; + + /* No FB to prepare */ + if (!new_fb) { + if (vps->surf) { + WARN_ON(vps->pinned != 0); + vmw_surface_unreference(&vps->surf); + } + + return 0; + } + + vfb = vmw_framebuffer_to_vfb(new_fb); + new_vfbs = (vfb->dmabuf) ? NULL : vmw_framebuffer_to_vfbs(new_fb); + + if (new_vfbs && new_vfbs->surface->base_size.width == hdisplay && + new_vfbs->surface->base_size.height == vdisplay) + new_content_type = SAME_AS_DISPLAY; + else if (vfb->dmabuf) + new_content_type = SEPARATE_DMA; + else + new_content_type = SEPARATE_SURFACE; + + if (new_content_type != SAME_AS_DISPLAY) { + struct vmw_surface content_srf; + struct drm_vmw_size display_base_size = {0}; + + display_base_size.width = hdisplay; + display_base_size.height = vdisplay; + display_base_size.depth = 1; + + /* + * If content buffer is a DMA buf, then we have to construct + * surface info + */ + if (new_content_type == SEPARATE_DMA) { + + switch (new_fb->format->cpp[0]*8) { + case 32: + content_srf.format = SVGA3D_X8R8G8B8; + break; + + case 16: + content_srf.format = SVGA3D_R5G6B5; + break; + + case 8: + content_srf.format = SVGA3D_P8; + break; + + default: + DRM_ERROR("Invalid format\n"); + return -EINVAL; + } + + content_srf.flags = 0; + content_srf.mip_levels[0] = 1; + content_srf.multisample_count = 0; + } else { + content_srf = *new_vfbs->surface; + } + + if (vps->surf) { + struct drm_vmw_size cur_base_size = vps->surf->base_size; + + if (cur_base_size.width != display_base_size.width || + cur_base_size.height != display_base_size.height || + vps->surf->format != content_srf.format) { + WARN_ON(vps->pinned != 0); + vmw_surface_unreference(&vps->surf); + } + + } + + if (!vps->surf) { + ret = vmw_surface_gb_priv_define + (crtc->dev, + /* Kernel visible only */ + 0, + content_srf.flags, + content_srf.format, + true, /* a scanout buffer */ + content_srf.mip_levels[0], + content_srf.multisample_count, + 0, + display_base_size, + &vps->surf); + if (ret != 0) { + DRM_ERROR("Couldn't allocate STDU surface.\n"); + return ret; + } + } + } else { + /* + * prepare_fb and clean_fb should only take care of pinning + * and unpinning. References are tracked by state objects. + * The only time we add a reference in prepare_fb is if the + * state object doesn't have a reference to begin with + */ + if (vps->surf) { + WARN_ON(vps->pinned != 0); + vmw_surface_unreference(&vps->surf); + } + + vps->surf = vmw_surface_reference(new_vfbs->surface); + } + + if (vps->surf) { + + /* Pin new surface before flipping */ + ret = vmw_resource_pin(&vps->surf->res, false); + if (ret) + goto out_srf_unref; + + vps->pinned++; + } + + vps->content_fb_type = new_content_type; + return 0; + +out_srf_unref: + vmw_surface_unreference(&vps->surf); + return ret; +} + + + +/** + * vmw_stdu_primary_plane_atomic_update - formally switches STDU to new plane + * + * @plane: display plane + * @old_state: Only used to get crtc info + * + * Formally update stdu->display_srf to the new plane, and bind the new + * plane STDU. This function is called during the commit phase when + * all the preparation have been done and all the configurations have + * been checked. + */ +static void +vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_private *dev_priv; + struct vmw_screen_target_display_unit *stdu; + struct vmw_plane_state *vps = vmw_plane_state_to_vps(plane->state); + struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc; + int ret; + + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + + stdu->display_srf = vps->surf; + stdu->content_fb_type = vps->content_fb_type; + + if (!stdu->defined) + return; + + if (plane->state->fb) + ret = vmw_stdu_bind_st(dev_priv, stdu, &stdu->display_srf->res); + else + ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); + + /* + * We cannot really fail this function, so if we do, then output an + * error and quit + */ + if (ret) + DRM_ERROR("Failed to bind surface to STDU.\n"); + else + crtc->primary->fb = plane->state->fb; +} + + static const struct drm_plane_funcs vmw_stdu_plane_funcs = { .update_plane = drm_primary_helper_update, .disable_plane = drm_primary_helper_disable, @@ -1216,6 +1440,22 @@ static const struct drm_plane_funcs vmw_stdu_cursor_funcs = { /* * Atomic Helpers */ +static const struct +drm_plane_helper_funcs vmw_stdu_cursor_plane_helper_funcs = { + .atomic_check = vmw_du_cursor_plane_atomic_check, + .atomic_update = vmw_du_cursor_plane_atomic_update, + .prepare_fb = vmw_du_cursor_plane_prepare_fb, + .cleanup_fb = vmw_du_plane_cleanup_fb, +}; + +static const struct +drm_plane_helper_funcs vmw_stdu_primary_plane_helper_funcs = { + .atomic_check = vmw_du_primary_plane_atomic_check, + .atomic_update = vmw_stdu_primary_plane_atomic_update, + .prepare_fb = vmw_stdu_primary_plane_prepare_fb, + .cleanup_fb = vmw_stdu_primary_plane_cleanup_fb, +}; + static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = { .prepare = vmw_stdu_crtc_helper_prepare, .commit = vmw_stdu_crtc_helper_commit, @@ -1283,6 +1523,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free; } + drm_plane_helper_add(primary, &vmw_stdu_primary_plane_helper_funcs); + /* Initialize cursor plane */ vmw_du_plane_reset(cursor); @@ -1297,6 +1539,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free; } + drm_plane_helper_add(cursor, &vmw_stdu_cursor_plane_helper_funcs); + vmw_du_connector_reset(connector); ret = drm_connector_init(dev, connector, &vmw_stdu_connector_funcs, From d947d1b71deb1efa0e7b2600f09e7fe7ea1e622b Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 14:23:20 -0700 Subject: [PATCH 08/22] drm/vmwgfx: Add and connect connector helper function These helpers won't be called until we flip on the atomic support flag or set drm_crtc_funcs->set_config to using the atomic helper. v2 Use drm_atomic_helper_best_encoder() rather than a vmwgfx-specific one Signed-off-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom Acked-by: Daniel Vetter --- drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 7 +++++++ drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 9 +++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 1d734de1e6e1..1b9e08335cce 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -389,6 +389,11 @@ static const struct drm_connector_funcs vmw_legacy_connector_funcs = { .atomic_get_property = vmw_du_connector_atomic_get_property, }; +static const struct +drm_connector_helper_funcs vmw_ldu_connector_helper_funcs = { + .best_encoder = drm_atomic_helper_best_encoder, +}; + /* * Legacy Display Plane Functions */ @@ -555,6 +560,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) DRM_ERROR("Failed to initialize connector\n"); goto err_free; } + + drm_connector_helper_add(connector, &vmw_ldu_connector_helper_funcs); connector->status = vmw_du_connector_detect(connector, true); vmw_connector_state_to_vcs(connector->state)->is_implicit = true; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index eca055ec27c8..536f4b55492e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -608,6 +608,14 @@ static const struct drm_connector_funcs vmw_sou_connector_funcs = { .atomic_get_property = vmw_du_connector_atomic_get_property, }; + +static const struct +drm_connector_helper_funcs vmw_sou_connector_helper_funcs = { + .best_encoder = drm_atomic_helper_best_encoder, +}; + + + /* * Screen Object Display Plane Functions */ @@ -827,6 +835,7 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) goto err_free; } + drm_connector_helper_add(connector, &vmw_sou_connector_helper_funcs); connector->status = vmw_du_connector_detect(connector, true); vmw_connector_state_to_vcs(connector->state)->is_implicit = false; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index cce5e5b02e20..76ca5fa1e3bf 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -1189,6 +1189,12 @@ static const struct drm_connector_funcs vmw_stdu_connector_funcs = { }; +static const struct +drm_connector_helper_funcs vmw_stdu_connector_helper_funcs = { + .best_encoder = drm_atomic_helper_best_encoder, +}; + + /****************************************************************************** * Screen Target Display Plane Functions @@ -1549,6 +1555,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) DRM_ERROR("Failed to initialize connector\n"); goto err_free; } + + drm_connector_helper_add(connector, &vmw_stdu_connector_helper_funcs); connector->status = vmw_du_connector_detect(connector, false); vmw_connector_state_to_vcs(connector->state)->is_implicit = false; From c46a30644a1e7093bbd839e9089f10d64e465302 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 14:24:53 -0700 Subject: [PATCH 09/22] drm/vmwgfx: Add and connect atomic state object check/commit This connects the main state object check and commit function. v2 * Use drm_atomic_helper_commit() rather than a vmwgfx-specific one Signed-off-by: Sinclair Yeh Signed-off-by: Thomas Hellstrom Reviewed-by: Thomas Hellstrom Acked-by: Daniel Vetter --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 48 +++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 0644b716f1dc..1071e1075da8 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1579,8 +1579,56 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, return &vfb->base; } + + +/** + * vmw_kms_atomic_check_modeset- validate state object for modeset changes + * + * @dev: DRM device + * @state: the driver state object + * + * This is a simple wrapper around drm_atomic_helper_check_modeset() for + * us to assign a value to mode->crtc_clock so that + * drm_calc_timestamping_constants() won't throw an error message + * + * RETURNS + * Zero for success or -errno + */ +int +vmw_kms_atomic_check_modeset(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + struct vmw_private *dev_priv = vmw_priv(dev); + int i; + + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + unsigned long requested_bb_mem = 0; + + if (dev_priv->active_display_unit == vmw_du_screen_target) { + if (crtc->primary->fb) { + int cpp = crtc->primary->fb->pitches[0] / + crtc->primary->fb->width; + + requested_bb_mem += crtc->mode.hdisplay * cpp * + crtc->mode.vdisplay; + } + + if (requested_bb_mem > dev_priv->prim_bb_mem) + return -EINVAL; + } + } + + return drm_atomic_helper_check(dev, state); +} + + static const struct drm_mode_config_funcs vmw_kms_funcs = { .fb_create = vmw_kms_fb_create, + .atomic_check = vmw_kms_atomic_check_modeset, + .atomic_commit = drm_atomic_helper_commit, }; static int vmw_kms_generic_present(struct vmw_private *dev_priv, From aa74f0687cfe998e59b20d6454f45e8aa4403c45 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 14:28:21 -0700 Subject: [PATCH 10/22] drm/vmwgfx: Fixes to vmwgfx_fb 1. When unsetting a mode, num_connector should be set to zero 2. The pixel_format field needs to be initialized as newer DRM internal functions checks this field 3. Take the drm_modeset_lock_all() because vmw_fb_kms_detach() can change current mode Signed-off-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index e9005b9a5e8c..7d3d5e3bcd32 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -434,7 +434,7 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par, set.y = 0; set.mode = NULL; set.fb = NULL; - set.num_connectors = 1; + set.num_connectors = 0; set.connectors = &par->con; ret = drm_mode_set_config_internal(&set); if (ret) { @@ -822,7 +822,9 @@ int vmw_fb_off(struct vmw_private *vmw_priv) flush_delayed_work(&par->local_work); mutex_lock(&par->bo_mutex); + drm_modeset_lock_all(vmw_priv->dev); (void) vmw_fb_kms_detach(par, true, false); + drm_modeset_unlock_all(vmw_priv->dev); mutex_unlock(&par->bo_mutex); return 0; From 904bb5e5817f5c5b42e6e3775699c728fd420284 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 14:29:22 -0700 Subject: [PATCH 11/22] drm/vmwgfx: Switch over to internal atomic API for STDU Switch over to using internal atomic API for mode set. This removes the legacy set_config API, replacing it with drm_atomic_helper_set_config(). The DRM helper will use various vmwgfx-specific atomic functions to set a mode. DRIVER_ATOMIC capability flag is not yet set, so the user mode will still use the legacy mode set IOCTL. v2: * Avoid a clash between page-flip pinning and setcrtc pinning, modify the page-flip code to use the page-flip helper and the atomic callbacks. To enable this, we will need to add a wrapper around atomic_commit. * Add vmw_kms_set_config() to work around vmwgfx xorg driver bug Signed-off-by: Sinclair Yeh Signed-off-by: Thomas Hellstrom Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 20 ++ drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 325 +++------------------------ 3 files changed, 51 insertions(+), 295 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 1071e1075da8..fe226e723287 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -2885,3 +2885,23 @@ vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv, "implicit_placement", 0, 1); } + + +/** + * vmw_kms_set_config - Wrapper around drm_atomic_helper_set_config + * + * @set: The configuration to set. + * + * The vmwgfx Xorg driver doesn't assign the mode::type member, which + * when drm_mode_set_crtcinfo is called as part of the configuration setting + * causes it to return incorrect crtc dimensions causing severe problems in + * the vmwgfx modesetting. So explicitly clear that member before calling + * into drm_atomic_helper_set_config. + */ +int vmw_kms_set_config(struct drm_mode_set *set) +{ + if (set && set->mode) + set->mode->type = 0; + + return drm_atomic_helper_set_config(set); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index de6a0b64bb4b..7689f477b726 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -449,5 +449,6 @@ int vmw_kms_stdu_dma(struct vmw_private *dev_priv, bool to_surface, bool interruptible); +int vmw_kms_set_config(struct drm_mode_set *set); #endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 76ca5fa1e3bf..b7999eb4f5fc 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -104,8 +104,7 @@ struct vmw_stdu_surface_copy { */ struct vmw_screen_target_display_unit { struct vmw_display_unit base; - - struct vmw_surface *display_srf; + const struct vmw_surface *display_srf; enum stdu_content_type content_fb_type; bool defined; @@ -117,32 +116,6 @@ static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu); -/****************************************************************************** - * Screen Target Display Unit helper Functions - *****************************************************************************/ - -/** - * vmw_stdu_unpin_display - unpins the resource associated with display surface - * - * @stdu: contains the display surface - * - * If the display surface was privatedly allocated by - * vmw_surface_gb_priv_define() and not registered as a framebuffer, then it - * won't be automatically cleaned up when all the framebuffers are freed. As - * such, we have to explicitly call vmw_resource_unreference() to get it freed. - */ -static void vmw_stdu_unpin_display(struct vmw_screen_target_display_unit *stdu) -{ - if (stdu->display_srf) { - struct vmw_resource *res = &stdu->display_srf->res; - - vmw_resource_unpin(res); - vmw_surface_unreference(&stdu->display_srf); - } -} - - - /****************************************************************************** * Screen Target Display Unit CRTC Functions *****************************************************************************/ @@ -228,7 +201,7 @@ static int vmw_stdu_define_st(struct vmw_private *dev_priv, */ static int vmw_stdu_bind_st(struct vmw_private *dev_priv, struct vmw_screen_target_display_unit *stdu, - struct vmw_resource *res) + const struct vmw_resource *res) { SVGA3dSurfaceImageId image; @@ -377,129 +350,6 @@ static int vmw_stdu_destroy_st(struct vmw_private *dev_priv, return ret; } -/** - * vmw_stdu_bind_fb - Bind an fb to a defined screen target - * - * @dev_priv: Pointer to a device private struct. - * @crtc: The crtc holding the screen target. - * @mode: The mode currently used by the screen target. Must be non-NULL. - * @new_fb: The new framebuffer to bind. Must be non-NULL. - * - * RETURNS: - * 0 on success, error code on failure. - */ -static int vmw_stdu_bind_fb(struct vmw_private *dev_priv, - struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_framebuffer *new_fb) -{ - struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc); - struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); - struct vmw_surface *new_display_srf = NULL; - enum stdu_content_type new_content_type; - struct vmw_framebuffer_surface *new_vfbs; - int ret; - - WARN_ON_ONCE(!stdu->defined); - - new_vfbs = (vfb->dmabuf) ? NULL : vmw_framebuffer_to_vfbs(new_fb); - - if (new_vfbs && new_vfbs->surface->base_size.width == mode->hdisplay && - new_vfbs->surface->base_size.height == mode->vdisplay) - new_content_type = SAME_AS_DISPLAY; - else if (vfb->dmabuf) - new_content_type = SEPARATE_DMA; - else - new_content_type = SEPARATE_SURFACE; - - if (new_content_type != SAME_AS_DISPLAY && - !stdu->display_srf) { - struct vmw_surface content_srf; - struct drm_vmw_size display_base_size = {0}; - - display_base_size.width = mode->hdisplay; - display_base_size.height = mode->vdisplay; - display_base_size.depth = 1; - - /* - * If content buffer is a DMA buf, then we have to construct - * surface info - */ - if (new_content_type == SEPARATE_DMA) { - - switch (new_fb->format->cpp[0] * 8) { - case 32: - content_srf.format = SVGA3D_X8R8G8B8; - break; - - case 16: - content_srf.format = SVGA3D_R5G6B5; - break; - - case 8: - content_srf.format = SVGA3D_P8; - break; - - default: - DRM_ERROR("Invalid format\n"); - return -EINVAL; - } - - content_srf.flags = 0; - content_srf.mip_levels[0] = 1; - content_srf.multisample_count = 0; - } else { - content_srf = *new_vfbs->surface; - } - - - ret = vmw_surface_gb_priv_define(crtc->dev, - 0, /* because kernel visible only */ - content_srf.flags, - content_srf.format, - true, /* a scanout buffer */ - content_srf.mip_levels[0], - content_srf.multisample_count, - 0, - display_base_size, - &new_display_srf); - if (unlikely(ret != 0)) { - DRM_ERROR("Could not allocate screen target surface.\n"); - return ret; - } - } else if (new_content_type == SAME_AS_DISPLAY) { - new_display_srf = vmw_surface_reference(new_vfbs->surface); - } - - if (new_display_srf) { - /* Pin new surface before flipping */ - ret = vmw_resource_pin(&new_display_srf->res, false); - if (ret) - goto out_srf_unref; - - ret = vmw_stdu_bind_st(dev_priv, stdu, &new_display_srf->res); - if (ret) - goto out_srf_unpin; - - /* Unpin and unreference old surface */ - vmw_stdu_unpin_display(stdu); - - /* Transfer the reference */ - stdu->display_srf = new_display_srf; - new_display_srf = NULL; - } - - crtc->primary->fb = new_fb; - stdu->content_fb_type = new_content_type; - return 0; - -out_srf_unpin: - vmw_resource_unpin(&new_display_srf->res); -out_srf_unref: - vmw_surface_unreference(&new_display_srf); - return ret; -} - /** * vmw_stdu_crtc_mode_set_nofb - Updates screen target size @@ -600,136 +450,6 @@ static void vmw_stdu_crtc_helper_disable(struct drm_crtc *crtc) } } -/** - * vmw_stdu_crtc_set_config - Sets a mode - * - * @set: mode parameters - * - * This function is the device-specific portion of the DRM CRTC mode set. - * For the SVGA device, we do this by defining a Screen Target, binding a - * GB Surface to that target, and finally update the screen target. - * - * RETURNS: - * 0 on success, error code otherwise - */ -static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) -{ - struct vmw_private *dev_priv; - struct vmw_framebuffer *vfb; - struct vmw_screen_target_display_unit *stdu; - struct drm_display_mode *mode; - struct drm_framebuffer *new_fb; - struct drm_crtc *crtc; - struct drm_encoder *encoder; - struct drm_connector *connector; - bool turning_off; - int ret; - - - if (!set || !set->crtc) - return -EINVAL; - - crtc = set->crtc; - stdu = vmw_crtc_to_stdu(crtc); - mode = set->mode; - new_fb = set->fb; - dev_priv = vmw_priv(crtc->dev); - turning_off = set->num_connectors == 0 || !mode || !new_fb; - vfb = (new_fb) ? vmw_framebuffer_to_vfb(new_fb) : NULL; - - if (set->num_connectors > 1) { - DRM_ERROR("Too many connectors\n"); - return -EINVAL; - } - - if (set->num_connectors == 1 && - set->connectors[0] != &stdu->base.connector) { - DRM_ERROR("Connectors don't match %p %p\n", - set->connectors[0], &stdu->base.connector); - return -EINVAL; - } - - if (!turning_off && (set->x + mode->hdisplay > new_fb->width || - set->y + mode->vdisplay > new_fb->height)) { - DRM_ERROR("Set outside of framebuffer\n"); - return -EINVAL; - } - - /* Only one active implicit frame-buffer at a time. */ - mutex_lock(&dev_priv->global_kms_state_mutex); - if (!turning_off && stdu->base.is_implicit && dev_priv->implicit_fb && - !(dev_priv->num_implicit == 1 && stdu->base.active_implicit) - && dev_priv->implicit_fb != vfb) { - mutex_unlock(&dev_priv->global_kms_state_mutex); - DRM_ERROR("Multiple implicit framebuffers not supported.\n"); - return -EINVAL; - } - mutex_unlock(&dev_priv->global_kms_state_mutex); - - /* Since they always map one to one these are safe */ - connector = &stdu->base.connector; - encoder = &stdu->base.encoder; - - if (stdu->defined) { - ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); - if (ret) - return ret; - - vmw_stdu_unpin_display(stdu); - (void) vmw_stdu_update_st(dev_priv, stdu); - vmw_kms_del_active(dev_priv, &stdu->base); - - ret = vmw_stdu_destroy_st(dev_priv, stdu); - if (ret) - return ret; - - crtc->primary->fb = NULL; - crtc->enabled = false; - encoder->crtc = NULL; - connector->encoder = NULL; - stdu->content_fb_type = SAME_AS_DISPLAY; - crtc->x = set->x; - crtc->y = set->y; - } - - if (turning_off) - return 0; - - /* - * Steps to displaying a surface, assume surface is already - * bound: - * 1. define a screen target - * 2. bind a fb to the screen target - * 3. update that screen target (this is done later by - * vmw_kms_stdu_do_surface_dirty_or_present) - */ - /* - * Note on error handling: We can't really restore the crtc to - * it's original state on error, but we at least update the - * current state to what's submitted to hardware to enable - * future recovery. - */ - vmw_svga_enable(dev_priv); - ret = vmw_stdu_define_st(dev_priv, stdu, mode, set->x, set->y); - if (ret) - return ret; - - crtc->x = set->x; - crtc->y = set->y; - crtc->mode = *mode; - - ret = vmw_stdu_bind_fb(dev_priv, crtc, mode, new_fb); - if (ret) - return ret; - - vmw_kms_add_active(dev_priv, &stdu->base, vfb); - crtc->enabled = true; - connector->encoder = encoder; - encoder->crtc = crtc; - - return 0; -} - /** * vmw_stdu_crtc_page_flip - Binds a buffer to a screen target * @@ -756,9 +476,9 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc, { struct vmw_private *dev_priv = vmw_priv(crtc->dev); - struct vmw_screen_target_display_unit *stdu; - struct drm_vmw_rect vclips; + struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc); struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); + struct drm_vmw_rect vclips; int ret; dev_priv = vmw_priv(crtc->dev); @@ -767,25 +487,42 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc, if (!stdu->defined || !vmw_kms_crtc_flippable(dev_priv, crtc)) return -EINVAL; - ret = vmw_stdu_bind_fb(dev_priv, crtc, &crtc->mode, new_fb); - if (ret) + /* + * We're always async, but the helper doesn't know how to set async + * so lie to the helper. Also, the helper expects someone + * to pick the event up from the crtc state, and if nobody does, + * it will free it. Since we handle the event in this function, + * don't hand it to the helper. + */ + flags &= ~DRM_MODE_PAGE_FLIP_ASYNC; + ret = drm_atomic_helper_page_flip(crtc, new_fb, NULL, flags); + if (ret) { + DRM_ERROR("Page flip error %d.\n", ret); return ret; + } if (stdu->base.is_implicit) vmw_kms_update_implicit_fb(dev_priv, crtc); + /* + * Now that we've bound a new surface to the screen target, + * update the contents. + */ vclips.x = crtc->x; vclips.y = crtc->y; vclips.w = crtc->mode.hdisplay; vclips.h = crtc->mode.vdisplay; + if (vfb->dmabuf) ret = vmw_kms_stdu_dma(dev_priv, NULL, vfb, NULL, NULL, &vclips, 1, 1, true, false); else ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, &vclips, NULL, 0, 0, 1, 1, NULL); - if (ret) + if (ret) { + DRM_ERROR("Page flip update error %d.\n", ret); return ret; + } if (event) { struct vmw_fence_obj *fence = NULL; @@ -802,7 +539,7 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc, true); vmw_fence_obj_unreference(&fence); } else { - vmw_fifo_flush(dev_priv, false); + (void) vmw_fifo_flush(dev_priv, false); } return 0; @@ -1123,7 +860,7 @@ static const struct drm_crtc_funcs vmw_stdu_crtc_funcs = { .reset = vmw_du_crtc_reset, .atomic_duplicate_state = vmw_du_crtc_duplicate_state, .atomic_destroy_state = vmw_du_crtc_destroy_state, - .set_config = vmw_stdu_crtc_set_config, + .set_config = vmw_kms_set_config, .page_flip = vmw_stdu_crtc_page_flip, }; @@ -1425,8 +1162,8 @@ vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane, static const struct drm_plane_funcs vmw_stdu_plane_funcs = { - .update_plane = drm_primary_helper_update, - .disable_plane = drm_primary_helper_disable, + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, .destroy = vmw_du_primary_plane_destroy, .reset = vmw_du_plane_reset, .atomic_duplicate_state = vmw_du_plane_duplicate_state, @@ -1434,8 +1171,8 @@ static const struct drm_plane_funcs vmw_stdu_plane_funcs = { }; static const struct drm_plane_funcs vmw_stdu_cursor_funcs = { - .update_plane = vmw_du_cursor_plane_update, - .disable_plane = vmw_du_cursor_plane_disable, + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, .destroy = vmw_du_cursor_plane_destroy, .reset = vmw_du_plane_reset, .atomic_duplicate_state = vmw_du_plane_duplicate_state, @@ -1625,8 +1362,6 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) */ static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu) { - vmw_stdu_unpin_display(stdu); - vmw_du_cleanup(&stdu->base); kfree(stdu); } From b0119cb9229d0db0d8c3ec8b302d549775e03a0c Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 14:38:18 -0700 Subject: [PATCH 12/22] drm/vmwgfx: Switch over to internal atomic API for SOU and LDU Switch over to internal atomic API. This completes the atomic internal atomic switch for all the Display Units. Signed-off-by: Sinclair Yeh Signed-off-by: Thomas Hellstrom Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 103 +------------ drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 217 +++------------------------ 2 files changed, 25 insertions(+), 295 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 1b9e08335cce..5e2f639b8d04 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -95,7 +95,7 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) if (crtc == NULL) return 0; - fb = entry->base.crtc.primary->fb; + fb = entry->base.crtc.primary->state->fb; return vmw_kms_write_svga(dev_priv, w, h, fb->pitches[0], fb->format->cpp[0] * 8, @@ -104,7 +104,7 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) if (!list_empty(&lds->active)) { entry = list_entry(lds->active.next, typeof(*entry), active); - fb = entry->base.crtc.primary->fb; + fb = entry->base.crtc.primary->state->fb; vmw_kms_write_svga(dev_priv, fb->width, fb->height, fb->pitches[0], fb->format->cpp[0] * 8, fb->format->depth); @@ -255,102 +255,13 @@ static void vmw_ldu_crtc_helper_disable(struct drm_crtc *crtc) { } -static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) -{ - struct vmw_private *dev_priv; - struct vmw_legacy_display_unit *ldu; - struct drm_connector *connector; - struct drm_display_mode *mode; - struct drm_encoder *encoder; - struct vmw_framebuffer *vfb; - struct drm_framebuffer *fb; - struct drm_crtc *crtc; - - if (!set) - return -EINVAL; - - if (!set->crtc) - return -EINVAL; - - /* get the ldu */ - crtc = set->crtc; - ldu = vmw_crtc_to_ldu(crtc); - vfb = set->fb ? vmw_framebuffer_to_vfb(set->fb) : NULL; - dev_priv = vmw_priv(crtc->dev); - - if (set->num_connectors > 1) { - DRM_ERROR("to many connectors\n"); - return -EINVAL; - } - - if (set->num_connectors == 1 && - set->connectors[0] != &ldu->base.connector) { - DRM_ERROR("connector doesn't match %p %p\n", - set->connectors[0], &ldu->base.connector); - return -EINVAL; - } - - /* ldu only supports one fb active at the time */ - if (dev_priv->ldu_priv->fb && vfb && - !(dev_priv->ldu_priv->num_active == 1 && - !list_empty(&ldu->active)) && - dev_priv->ldu_priv->fb != vfb) { - DRM_ERROR("Multiple framebuffers not supported\n"); - return -EINVAL; - } - - /* since they always map one to one these are safe */ - connector = &ldu->base.connector; - encoder = &ldu->base.encoder; - - /* should we turn the crtc off? */ - if (set->num_connectors == 0 || !set->mode || !set->fb) { - - connector->encoder = NULL; - encoder->crtc = NULL; - crtc->primary->fb = NULL; - crtc->enabled = false; - - vmw_ldu_del_active(dev_priv, ldu); - - return vmw_ldu_commit_list(dev_priv); - } - - - /* we now know we want to set a mode */ - mode = set->mode; - fb = set->fb; - - if (set->x + mode->hdisplay > fb->width || - set->y + mode->vdisplay > fb->height) { - DRM_ERROR("set outside of framebuffer\n"); - return -EINVAL; - } - - vmw_svga_enable(dev_priv); - - crtc->primary->fb = fb; - encoder->crtc = crtc; - connector->encoder = encoder; - crtc->x = set->x; - crtc->y = set->y; - crtc->mode = *mode; - crtc->enabled = true; - ldu->base.set_gui_x = set->x; - ldu->base.set_gui_y = set->y; - - vmw_ldu_add_active(dev_priv, ldu, vfb); - - return vmw_ldu_commit_list(dev_priv); -} - static const struct drm_crtc_funcs vmw_legacy_crtc_funcs = { .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_ldu_crtc_destroy, .reset = vmw_du_crtc_reset, .atomic_duplicate_state = vmw_du_crtc_duplicate_state, .atomic_destroy_state = vmw_du_crtc_destroy_state, - .set_config = vmw_ldu_crtc_set_config, + .set_config = vmw_kms_set_config, }; @@ -439,8 +350,8 @@ vmw_ldu_primary_plane_atomic_update(struct drm_plane *plane, static const struct drm_plane_funcs vmw_ldu_plane_funcs = { - .update_plane = drm_primary_helper_update, - .disable_plane = drm_primary_helper_disable, + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, .destroy = vmw_du_primary_plane_destroy, .reset = vmw_du_plane_reset, .atomic_duplicate_state = vmw_du_plane_duplicate_state, @@ -448,8 +359,8 @@ static const struct drm_plane_funcs vmw_ldu_plane_funcs = { }; static const struct drm_plane_funcs vmw_ldu_cursor_funcs = { - .update_plane = vmw_du_cursor_plane_update, - .disable_plane = vmw_du_cursor_plane_disable, + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, .destroy = vmw_du_cursor_plane_destroy, .reset = vmw_du_plane_reset, .atomic_duplicate_state = vmw_du_plane_duplicate_state, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index 536f4b55492e..106a943a613c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -204,52 +204,6 @@ static int vmw_sou_fifo_destroy(struct vmw_private *dev_priv, return ret; } -/** - * Free the backing store. - */ -static void vmw_sou_backing_free(struct vmw_private *dev_priv, - struct vmw_screen_object_unit *sou) -{ - vmw_dmabuf_unreference(&sou->buffer); - sou->buffer_size = 0; -} - -/** - * Allocate the backing store for the buffer. - */ -static int vmw_sou_backing_alloc(struct vmw_private *dev_priv, - struct vmw_screen_object_unit *sou, - unsigned long size) -{ - int ret; - - if (sou->buffer_size == size) - return 0; - - if (sou->buffer) - vmw_sou_backing_free(dev_priv, sou); - - sou->buffer = kzalloc(sizeof(*sou->buffer), GFP_KERNEL); - if (unlikely(sou->buffer == NULL)) - return -ENOMEM; - - /* After we have alloced the backing store might not be able to - * resume the overlays, this is preferred to failing to alloc. - */ - vmw_overlay_pause_all(dev_priv); - ret = vmw_dmabuf_init(dev_priv, sou->buffer, size, - &vmw_vram_ne_placement, - false, &vmw_dmabuf_bo_free); - vmw_overlay_resume_all(dev_priv); - - if (unlikely(ret != 0)) - sou->buffer = NULL; /* vmw_dmabuf_init frees on error */ - else - sou->buffer_size = size; - - return ret; -} - /** * vmw_sou_crtc_mode_set_nofb - Create new screen * @@ -353,158 +307,14 @@ static void vmw_sou_crtc_helper_disable(struct drm_crtc *crtc) } } -static int vmw_sou_crtc_set_config(struct drm_mode_set *set) -{ - struct vmw_private *dev_priv; - struct vmw_screen_object_unit *sou; - struct drm_connector *connector; - struct drm_display_mode *mode; - struct drm_encoder *encoder; - struct vmw_framebuffer *vfb; - struct drm_framebuffer *fb; - struct drm_crtc *crtc; - int ret = 0; - - if (!set) - return -EINVAL; - - if (!set->crtc) - return -EINVAL; - - /* get the sou */ - crtc = set->crtc; - sou = vmw_crtc_to_sou(crtc); - vfb = set->fb ? vmw_framebuffer_to_vfb(set->fb) : NULL; - dev_priv = vmw_priv(crtc->dev); - - if (set->num_connectors > 1) { - DRM_ERROR("Too many connectors\n"); - return -EINVAL; - } - - if (set->num_connectors == 1 && - set->connectors[0] != &sou->base.connector) { - DRM_ERROR("Connector doesn't match %p %p\n", - set->connectors[0], &sou->base.connector); - return -EINVAL; - } - - /* Only one active implicit frame-buffer at a time. */ - mutex_lock(&dev_priv->global_kms_state_mutex); - if (sou->base.is_implicit && - dev_priv->implicit_fb && vfb && - !(dev_priv->num_implicit == 1 && - sou->base.active_implicit) && - dev_priv->implicit_fb != vfb) { - mutex_unlock(&dev_priv->global_kms_state_mutex); - DRM_ERROR("Multiple implicit framebuffers not supported.\n"); - return -EINVAL; - } - mutex_unlock(&dev_priv->global_kms_state_mutex); - - /* since they always map one to one these are safe */ - connector = &sou->base.connector; - encoder = &sou->base.encoder; - - /* should we turn the crtc off */ - if (set->num_connectors == 0 || !set->mode || !set->fb) { - ret = vmw_sou_fifo_destroy(dev_priv, sou); - /* the hardware has hung don't do anything more */ - if (unlikely(ret != 0)) - return ret; - - connector->encoder = NULL; - encoder->crtc = NULL; - crtc->primary->fb = NULL; - crtc->x = 0; - crtc->y = 0; - crtc->enabled = false; - - vmw_kms_del_active(dev_priv, &sou->base); - - vmw_sou_backing_free(dev_priv, sou); - - return 0; - } - - - /* we now know we want to set a mode */ - mode = set->mode; - fb = set->fb; - - if (set->x + mode->hdisplay > fb->width || - set->y + mode->vdisplay > fb->height) { - DRM_ERROR("set outside of framebuffer\n"); - return -EINVAL; - } - - vmw_svga_enable(dev_priv); - - if (mode->hdisplay != crtc->mode.hdisplay || - mode->vdisplay != crtc->mode.vdisplay) { - /* no need to check if depth is different, because backing - * store depth is forced to 4 by the device. - */ - - ret = vmw_sou_fifo_destroy(dev_priv, sou); - /* the hardware has hung don't do anything more */ - if (unlikely(ret != 0)) - return ret; - - vmw_sou_backing_free(dev_priv, sou); - } - - if (!sou->buffer) { - /* forced to depth 4 by the device */ - size_t size = mode->hdisplay * mode->vdisplay * 4; - ret = vmw_sou_backing_alloc(dev_priv, sou, size); - if (unlikely(ret != 0)) - return ret; - } - - ret = vmw_sou_fifo_create(dev_priv, sou, set->x, set->y, mode); - if (unlikely(ret != 0)) { - /* - * We are in a bit of a situation here, the hardware has - * hung and we may or may not have a buffer hanging of - * the screen object, best thing to do is not do anything - * if we where defined, if not just turn the crtc of. - * Not what userspace wants but it needs to htfu. - */ - if (sou->defined) - return ret; - - connector->encoder = NULL; - encoder->crtc = NULL; - crtc->primary->fb = NULL; - crtc->x = 0; - crtc->y = 0; - crtc->enabled = false; - - return ret; - } - - vmw_kms_add_active(dev_priv, &sou->base, vfb); - - connector->encoder = encoder; - encoder->crtc = crtc; - crtc->mode = *mode; - crtc->primary->fb = fb; - crtc->x = set->x; - crtc->y = set->y; - crtc->enabled = true; - - return 0; -} - static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, + struct drm_framebuffer *new_fb, struct drm_pending_vblank_event *event, uint32_t flags) { struct vmw_private *dev_priv = vmw_priv(crtc->dev); struct drm_framebuffer *old_fb = crtc->primary->fb; - struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(fb); + struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); struct vmw_fence_obj *fence = NULL; struct drm_vmw_rect vclips; int ret; @@ -512,7 +322,12 @@ static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, if (!vmw_kms_crtc_flippable(dev_priv, crtc)) return -EINVAL; - crtc->primary->fb = fb; + flags &= ~DRM_MODE_PAGE_FLIP_ASYNC; + ret = drm_atomic_helper_page_flip(crtc, new_fb, NULL, flags); + if (ret) { + DRM_ERROR("Page flip error %d.\n", ret); + return ret; + } /* do a full screen dirty update */ vclips.x = crtc->x; @@ -559,7 +374,7 @@ static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, return ret; out_no_fence: - crtc->primary->fb = old_fb; + drm_atomic_set_fb_for_plane(crtc->primary->state, old_fb); return ret; } @@ -569,7 +384,7 @@ static const struct drm_crtc_funcs vmw_screen_object_crtc_funcs = { .reset = vmw_du_crtc_reset, .atomic_duplicate_state = vmw_du_crtc_duplicate_state, .atomic_destroy_state = vmw_du_crtc_destroy_state, - .set_config = vmw_sou_crtc_set_config, + .set_config = vmw_kms_set_config, .page_flip = vmw_sou_crtc_page_flip, }; @@ -711,12 +526,16 @@ static void vmw_sou_primary_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { + struct drm_crtc *crtc = plane->state->crtc; + + if (crtc) + crtc->primary->fb = plane->state->fb; } static const struct drm_plane_funcs vmw_sou_plane_funcs = { - .update_plane = drm_primary_helper_update, - .disable_plane = drm_primary_helper_disable, + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, .destroy = vmw_du_primary_plane_destroy, .reset = vmw_du_plane_reset, .atomic_duplicate_state = vmw_du_plane_duplicate_state, @@ -724,8 +543,8 @@ static const struct drm_plane_funcs vmw_sou_plane_funcs = { }; static const struct drm_plane_funcs vmw_sou_cursor_funcs = { - .update_plane = vmw_du_cursor_plane_update, - .disable_plane = vmw_du_cursor_plane_disable, + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, .destroy = vmw_du_cursor_plane_destroy, .reset = vmw_du_plane_reset, .atomic_duplicate_state = vmw_du_plane_duplicate_state, From f7c478be0abb5f6db5dfa13bb8118628817b9afe Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Fri, 31 Mar 2017 10:16:22 -0700 Subject: [PATCH 13/22] drm/vmwgfx: Turn on DRIVER_ATOMIC flag Now that the legacy path has been tested, turn on the DRIVER_ATOMIC flag so user mode driver can start going through the Atomic path. Signed-off-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom Acked-by: Daniel Vetter --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 4 +++- drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 1 - drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 1 - drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 83ff0f033d5f..fcabb31d0463 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -898,6 +898,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) goto out_no_fifo; DRM_INFO("DX: %s\n", dev_priv->has_dx ? "yes." : "no."); + DRM_INFO("Atomic: %s\n", + (dev->driver->driver_features & DRIVER_ATOMIC) ? "yes" : "no"); snprintf(host_log, sizeof(host_log), "vmwgfx: %s-%s", VMWGFX_REPO, VMWGFX_GIT_VERSION); @@ -1510,7 +1512,7 @@ static const struct file_operations vmwgfx_driver_fops = { static struct drm_driver driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | - DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER, + DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER | DRIVER_ATOMIC, .load = vmw_driver_load, .unload = vmw_driver_unload, .lastclose = vmw_lastclose, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 5e2f639b8d04..0a3e6eaa5f97 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -390,7 +390,6 @@ static const struct drm_crtc_helper_funcs vmw_ldu_crtc_helper_funcs = { .prepare = vmw_ldu_crtc_helper_prepare, .commit = vmw_ldu_crtc_helper_commit, .disable = vmw_ldu_crtc_helper_disable, - .mode_set = drm_helper_crtc_mode_set, .mode_set_nofb = vmw_ldu_crtc_mode_set_nofb, .atomic_check = vmw_du_crtc_atomic_check, .atomic_begin = vmw_du_crtc_atomic_begin, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index 106a943a613c..2664e4c16750 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -574,7 +574,6 @@ static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = { .prepare = vmw_sou_crtc_helper_prepare, .commit = vmw_sou_crtc_helper_commit, .disable = vmw_sou_crtc_helper_disable, - .mode_set = drm_helper_crtc_mode_set, .mode_set_nofb = vmw_sou_crtc_mode_set_nofb, .atomic_check = vmw_du_crtc_atomic_check, .atomic_begin = vmw_du_crtc_atomic_begin, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index b7999eb4f5fc..e5a38f4ddb62 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -1203,7 +1203,6 @@ static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = { .prepare = vmw_stdu_crtc_helper_prepare, .commit = vmw_stdu_crtc_helper_commit, .disable = vmw_stdu_crtc_helper_disable, - .mode_set = drm_helper_crtc_mode_set, .mode_set_nofb = vmw_stdu_crtc_mode_set_nofb, .atomic_check = vmw_du_crtc_atomic_check, .atomic_begin = vmw_du_crtc_atomic_begin, From 9aa8dcab7e44288c6fd620c5b831d2ca535866c2 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 14:40:04 -0700 Subject: [PATCH 14/22] drm/vmwgfx: Explicityly track screen target width and height We can no longer make the assumption that vmw_stdu_update_st() will be called when there's a valid display surface attached. So instead of using display_srf for width and height, make a record of these paremeters when the screen target is first defined. Signed-off-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index e5a38f4ddb62..2757cda391bb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -106,6 +106,7 @@ struct vmw_screen_target_display_unit { struct vmw_display_unit base; const struct vmw_surface *display_srf; enum stdu_content_type content_fb_type; + s32 display_width, display_height; bool defined; }; @@ -184,6 +185,8 @@ static int vmw_stdu_define_st(struct vmw_private *dev_priv, vmw_fifo_commit(dev_priv, sizeof(*cmd)); stdu->defined = true; + stdu->display_width = mode->hdisplay; + stdu->display_height = mode->vdisplay; return 0; } @@ -281,7 +284,6 @@ static int vmw_stdu_update_st(struct vmw_private *dev_priv, struct vmw_screen_target_display_unit *stdu) { struct vmw_stdu_update *cmd; - struct drm_crtc *crtc = &stdu->base.crtc; if (!stdu->defined) { DRM_ERROR("No screen target defined"); @@ -295,8 +297,9 @@ static int vmw_stdu_update_st(struct vmw_private *dev_priv, return -ENOMEM; } - vmw_stdu_populate_update(cmd, stdu->base.unit, 0, crtc->mode.hdisplay, - 0, crtc->mode.vdisplay); + vmw_stdu_populate_update(cmd, stdu->base.unit, + 0, stdu->display_width, + 0, stdu->display_height); vmw_fifo_commit(dev_priv, sizeof(*cmd)); @@ -346,6 +349,8 @@ static int vmw_stdu_destroy_st(struct vmw_private *dev_priv, DRM_ERROR("Failed to sync with HW"); stdu->defined = false; + stdu->display_width = 0; + stdu->display_height = 0; return ret; } From 5c6089a1dfa6e699a9c8ca95df02e8c39b4e8e15 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 14:41:21 -0700 Subject: [PATCH 15/22] drm/vmwgfx: Skipping fbdev fb pinning for ldu Pinning fbdev's FB at the start of VRAM prevents X from pinning its FB. Since for ldu, the fb would be pinned anyway during a mode set, just skip pinning it in fbdev. This is not the best solution, but since ldu is not used much anymore, it seems like a reasonable workaround. Signed-off-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 7d3d5e3bcd32..09e120d50e65 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -451,13 +451,15 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par, } if (par->vmw_bo && detach_bo) { + struct vmw_private *vmw_priv = par->vmw_priv; + if (par->bo_ptr) { ttm_bo_kunmap(&par->map); par->bo_ptr = NULL; } if (unref_bo) vmw_dmabuf_unreference(&par->vmw_bo); - else + else if (vmw_priv->active_display_unit != vmw_du_legacy) vmw_dmabuf_unpin(par->vmw_priv, par->vmw_bo, false); } @@ -585,18 +587,25 @@ static int vmw_fb_set_par(struct fb_info *info) /* * Pin before mapping. Since we don't know in what placement - * to pin, call into KMS to do it for us. + * to pin, call into KMS to do it for us. LDU doesn't require + * additional pinning because set_config() would've pinned + * it already */ - ret = vfb->pin(vfb); - if (ret) { - DRM_ERROR("Could not pin the fbdev framebuffer.\n"); - goto out_unlock; + if (vmw_priv->active_display_unit != vmw_du_legacy) { + ret = vfb->pin(vfb); + if (ret) { + DRM_ERROR("Could not pin the fbdev " + "framebuffer.\n"); + goto out_unlock; + } } ret = ttm_bo_kmap(&par->vmw_bo->base, 0, par->vmw_bo->base.num_pages, &par->map); if (ret) { - vfb->unpin(vfb); + if (vmw_priv->active_display_unit != vmw_du_legacy) + vfb->unpin(vfb); + DRM_ERROR("Could not map the fbdev framebuffer.\n"); goto out_unlock; } From e9cebe837be720af5972959d2ea295ece3be3721 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 14:42:36 -0700 Subject: [PATCH 16/22] drm/vmwgfx: Fix LDU X blank screen until mode change issue vmw_ldu_crtc_helper_commit() is not called if drm_atomic_crtc_needs_modeset() decides nothing related to CRTC timing has changed. So a better place for this code is in vmw_ldu_primary_plane_atomic_update() since we will need to update ld->fb every time the FB is updated. Signed-off-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 41 +++++++++++++++-------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 0a3e6eaa5f97..d3987bcf53f8 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -226,24 +226,6 @@ static void vmw_ldu_crtc_helper_prepare(struct drm_crtc *crtc) */ static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc) { - struct vmw_private *dev_priv; - struct vmw_legacy_display_unit *ldu; - struct vmw_framebuffer *vfb; - struct drm_framebuffer *fb; - - - ldu = vmw_crtc_to_ldu(crtc); - dev_priv = vmw_priv(crtc->dev); - fb = crtc->primary->state->fb; - - vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; - - if (vfb) - vmw_ldu_add_active(dev_priv, ldu, vfb); - else - vmw_ldu_del_active(dev_priv, ldu); - - vmw_ldu_commit_list(dev_priv); } /** @@ -310,7 +292,7 @@ drm_connector_helper_funcs vmw_ldu_connector_helper_funcs = { */ /** - * vmw_ldu_primary_plane_cleanup_fb - Unpin fb + * vmw_ldu_primary_plane_cleanup_fb - Noop * * @plane: display plane * @old_state: Contains the FB to clean up @@ -327,7 +309,7 @@ vmw_ldu_primary_plane_cleanup_fb(struct drm_plane *plane, /** - * vmw_ldu_primary_plane_prepare_fb - + * vmw_ldu_primary_plane_prepare_fb - Noop * * @plane: display plane * @new_state: info on the new plane state, including the FB @@ -346,6 +328,25 @@ static void vmw_ldu_primary_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { + struct vmw_private *dev_priv; + struct vmw_legacy_display_unit *ldu; + struct vmw_framebuffer *vfb; + struct drm_framebuffer *fb; + struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc; + + + ldu = vmw_crtc_to_ldu(crtc); + dev_priv = vmw_priv(plane->dev); + fb = plane->state->fb; + + vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; + + if (vfb) + vmw_ldu_add_active(dev_priv, ldu, vfb); + else + vmw_ldu_del_active(dev_priv, ldu); + + vmw_ldu_commit_list(dev_priv); } From 50f837371dd9aea5470c06d5d10bc9ca3e8155b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20A=2E=20Holm?= Date: Thu, 23 Mar 2017 14:54:48 -0700 Subject: [PATCH 17/22] drm/vmwgfx: Revert "drm/vmwgfx: Replace numeric parameter like 0444 with macro" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2d8e60e8b074 ("drm/vmwgfx: Replace numeric parameter like 0444 with macro") The commit belongs to the series of 1285 patches sent to LKML on 2016-08-02, it changes the representation of file permissions from the octal value "0600" to "S_IRUSR | S_IWUSR". The general consensus was that the changes does not increase readability, quite the opposite; 0600 is easier to parse mentally than S_IRUSR | S_IWUSR. It also causes argument inconsistency, due to commit 04319d89fbec ("drm/vmwgfx: Add an option to change assumed FB bpp") that added another call to module_param_named() where the permissions are written as 0600. Signed-off-by: Øyvind A. Holm Reviewed-by: Sinclair Yeh --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index fcabb31d0463..f6e936d90517 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -246,13 +246,13 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, void *ptr); MODULE_PARM_DESC(enable_fbdev, "Enable vmwgfx fbdev"); -module_param_named(enable_fbdev, enable_fbdev, int, S_IRUSR | S_IWUSR); +module_param_named(enable_fbdev, enable_fbdev, int, 0600); MODULE_PARM_DESC(force_dma_api, "Force using the DMA API for TTM pages"); -module_param_named(force_dma_api, vmw_force_iommu, int, S_IRUSR | S_IWUSR); +module_param_named(force_dma_api, vmw_force_iommu, int, 0600); MODULE_PARM_DESC(restrict_iommu, "Try to limit IOMMU usage for TTM pages"); -module_param_named(restrict_iommu, vmw_restrict_iommu, int, S_IRUSR | S_IWUSR); +module_param_named(restrict_iommu, vmw_restrict_iommu, int, 0600); MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages"); -module_param_named(force_coherent, vmw_force_coherent, int, S_IRUSR | S_IWUSR); +module_param_named(force_coherent, vmw_force_coherent, int, 0600); MODULE_PARM_DESC(restrict_dma_mask, "Restrict DMA mask to 44 bits with IOMMU"); module_param_named(restrict_dma_mask, vmw_restrict_dma_mask, int, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(assume_16bpp, "Assume 16-bpp when filtering modes"); From d4d21902222d44acc76a520f11f78040b1e34b5a Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Thu, 23 Mar 2017 15:09:42 -0700 Subject: [PATCH 18/22] drm/vmwgfx: Introduce a simple resource type The callbacks we need to provide to many resources are very similar, so provide a simple resource type with a number of helpers for these callbacks. Signed-off-by: Thomas Hellstrom Reviewed-by: Sinclair Yeh --- drivers/gpu/drm/vmwgfx/Makefile | 3 +- drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h | 40 +++ .../gpu/drm/vmwgfx/vmwgfx_simple_resource.c | 256 ++++++++++++++++++ 3 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/vmwgfx/vmwgfx_simple_resource.c diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile index 473d00451b0f..2258908b1436 100644 --- a/drivers/gpu/drm/vmwgfx/Makefile +++ b/drivers/gpu/drm/vmwgfx/Makefile @@ -8,6 +8,7 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ vmwgfx_fence.o vmwgfx_dmabuf.o vmwgfx_scrn.o vmwgfx_context.o \ vmwgfx_surface.o vmwgfx_prime.o vmwgfx_mob.o vmwgfx_shader.o \ vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \ - vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o + vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o \ + vmwgfx_simple_resource.o obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h index 5994ef6265e0..ac05968a832b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h @@ -30,6 +30,8 @@ #include "vmwgfx_drv.h" +#define VMW_IDA_ACC_SIZE 128 + enum vmw_cmdbuf_res_state { VMW_CMDBUF_RES_COMMITTED, VMW_CMDBUF_RES_ADD, @@ -83,6 +85,35 @@ struct vmw_res_func { enum vmw_cmdbuf_res_state state); }; +/** + * struct vmw_simple_resource_func - members and functions common for the + * simple resource helpers. + * @res_func: struct vmw_res_func as described above. + * @ttm_res_type: TTM resource type used for handle recognition. + * @size: Size of the simple resource information struct. + * @init: Initialize the simple resource information. + * @hw_destroy: A resource hw_destroy function. + * @set_arg_handle: Set the handle output argument of the ioctl create struct. + */ +struct vmw_simple_resource_func { + const struct vmw_res_func res_func; + int ttm_res_type; + size_t size; + int (*init)(struct vmw_resource *res, void *data); + void (*hw_destroy)(struct vmw_resource *res); + void (*set_arg_handle)(void *data, u32 handle); +}; + +/** + * struct vmw_simple_resource - Kernel only side simple resource + * @res: The resource we derive from. + * @func: The method and member virtual table. + */ +struct vmw_simple_resource { + struct vmw_resource res; + const struct vmw_simple_resource_func *func; +}; + int vmw_resource_alloc_id(struct vmw_resource *res); void vmw_resource_release_id(struct vmw_resource *res); int vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res, @@ -91,4 +122,13 @@ int vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res, const struct vmw_res_func *func); void vmw_resource_activate(struct vmw_resource *res, void (*hw_destroy) (struct vmw_resource *)); +int +vmw_simple_resource_create_ioctl(struct drm_device *dev, + void *data, + struct drm_file *file_priv, + const struct vmw_simple_resource_func *func); +struct vmw_resource * +vmw_simple_resource_lookup(struct ttm_object_file *tfile, + uint32_t handle, + const struct vmw_simple_resource_func *func); #endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_simple_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_simple_resource.c new file mode 100644 index 000000000000..051d3b39b0ea --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_simple_resource.c @@ -0,0 +1,256 @@ +/************************************************************************** + * + * Copyright © 2016 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" + +/** + * struct vmw_user_simple_resource - User-space simple resource struct + * + * @base: The TTM base object implementing user-space visibility. + * @account_size: How much memory was accounted for this object. + * @simple: The embedded struct vmw_simple_resource. + */ +struct vmw_user_simple_resource { + struct ttm_base_object base; + size_t account_size; + struct vmw_simple_resource simple; +/* + * Nothing to be placed after @simple, since size of @simple is + * unknown. + */ +}; + + +/** + * vmw_simple_resource_init - Initialize a simple resource object. + * + * @dev_priv: Pointer to a struct device private. + * @simple: The struct vmw_simple_resource to initialize. + * @data: Data passed to the information initialization function. + * @res_free: Function pointer to destroy the simple resource. + * + * Returns: + * 0 if succeeded. + * Negative error value if error, in which case the resource will have been + * freed. + */ +static int vmw_simple_resource_init(struct vmw_private *dev_priv, + struct vmw_simple_resource *simple, + void *data, + void (*res_free)(struct vmw_resource *res)) +{ + struct vmw_resource *res = &simple->res; + int ret; + + ret = vmw_resource_init(dev_priv, res, false, res_free, + &simple->func->res_func); + + if (ret) { + res_free(res); + return ret; + } + + ret = simple->func->init(res, data); + if (ret) { + vmw_resource_unreference(&res); + return ret; + } + + vmw_resource_activate(&simple->res, simple->func->hw_destroy); + + return 0; +} + +/** + * vmw_simple_resource_free - Free a simple resource object. + * + * @res: The struct vmw_resource member of the simple resource object. + * + * Frees memory and memory accounting for the object. + */ +static void vmw_simple_resource_free(struct vmw_resource *res) +{ + struct vmw_user_simple_resource *usimple = + container_of(res, struct vmw_user_simple_resource, + simple.res); + struct vmw_private *dev_priv = res->dev_priv; + size_t size = usimple->account_size; + + ttm_base_object_kfree(usimple, base); + ttm_mem_global_free(vmw_mem_glob(dev_priv), size); +} + +/** + * vmw_simple_resource_base_release - TTM object release callback + * + * @p_base: The struct ttm_base_object member of the simple resource object. + * + * Called when the last reference to the embedded struct ttm_base_object is + * gone. Typically results in an object free, unless there are other + * references to the embedded struct vmw_resource. + */ +static void vmw_simple_resource_base_release(struct ttm_base_object **p_base) +{ + struct ttm_base_object *base = *p_base; + struct vmw_user_simple_resource *usimple = + container_of(base, struct vmw_user_simple_resource, base); + struct vmw_resource *res = &usimple->simple.res; + + *p_base = NULL; + vmw_resource_unreference(&res); +} + +/** + * vmw_simple_resource_create_ioctl - Helper to set up an ioctl function to + * create a struct vmw_simple_resource. + * + * @dev: Pointer to a struct drm device. + * @data: Ioctl argument. + * @file_priv: Pointer to a struct drm_file identifying the caller. + * @func: Pointer to a struct vmw_simple_resource_func identifying the + * simple resource type. + * + * Returns: + * 0 if success, + * Negative error value on error. + */ +int +vmw_simple_resource_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv, + const struct vmw_simple_resource_func *func) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_user_simple_resource *usimple; + struct vmw_resource *res; + struct vmw_resource *tmp; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + size_t alloc_size; + size_t account_size; + int ret; + + alloc_size = offsetof(struct vmw_user_simple_resource, simple) + + func->size; + account_size = ttm_round_pot(alloc_size) + VMW_IDA_ACC_SIZE; + + ret = ttm_read_lock(&dev_priv->reservation_sem, true); + if (ret) + return ret; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), account_size, + false, true); + ttm_read_unlock(&dev_priv->reservation_sem); + if (ret) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for %s" + " creation.\n", func->res_func.type_name); + + goto out_ret; + } + + usimple = kzalloc(alloc_size, GFP_KERNEL); + if (!usimple) { + ttm_mem_global_free(vmw_mem_glob(dev_priv), + account_size); + ret = -ENOMEM; + goto out_ret; + } + + usimple->simple.func = func; + usimple->account_size = account_size; + res = &usimple->simple.res; + usimple->base.shareable = false; + usimple->base.tfile = NULL; + + /* + * From here on, the destructor takes over resource freeing. + */ + ret = vmw_simple_resource_init(dev_priv, &usimple->simple, + data, vmw_simple_resource_free); + if (ret) + goto out_ret; + + tmp = vmw_resource_reference(res); + ret = ttm_base_object_init(tfile, &usimple->base, false, + func->ttm_res_type, + &vmw_simple_resource_base_release, NULL); + + if (ret) { + vmw_resource_unreference(&tmp); + goto out_err; + } + + func->set_arg_handle(data, usimple->base.hash.key); +out_err: + vmw_resource_unreference(&res); +out_ret: + return ret; +} + +/** + * vmw_simple_resource_lookup - Look up a simple resource from its user-space + * handle. + * + * @tfile: struct ttm_object_file identifying the caller. + * @handle: The user-space handle. + * @func: The struct vmw_simple_resource_func identifying the simple resource + * type. + * + * Returns: Refcounted pointer to the embedded struct vmw_resource if + * successfule. Error pointer otherwise. + */ +struct vmw_resource * +vmw_simple_resource_lookup(struct ttm_object_file *tfile, + uint32_t handle, + const struct vmw_simple_resource_func *func) +{ + struct vmw_user_simple_resource *usimple; + struct ttm_base_object *base; + struct vmw_resource *res; + + base = ttm_base_object_lookup(tfile, handle); + if (!base) { + DRM_ERROR("Invalid %s handle 0x%08lx.\n", + func->res_func.type_name, + (unsigned long) handle); + return ERR_PTR(-ESRCH); + } + + if (ttm_base_object_type(base) != func->ttm_res_type) { + ttm_base_object_unref(&base); + DRM_ERROR("Invalid type of %s handle 0x%08lx.\n", + func->res_func.type_name, + (unsigned long) handle); + return ERR_PTR(-EINVAL); + } + + usimple = container_of(base, typeof(*usimple), base); + res = vmw_resource_reference(&usimple->simple.res); + ttm_base_object_unref(&base); + + return res; +} From 9f7d148022b12b0f706c9be99be94e934fbdb50d Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Thu, 23 Mar 2017 15:19:45 -0700 Subject: [PATCH 19/22] drm/vmwgfx: Re-implement the stream resource as a simple resource. Provide and document a reference implementation. Signed-off-by: Thomas Hellstrom Reviewed-by: Sinclair Yeh --- drivers/gpu/drm/vmwgfx/Makefile | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 254 ----------------------- drivers/gpu/drm/vmwgfx/vmwgfx_va.c | 168 +++++++++++++++ 3 files changed, 169 insertions(+), 255 deletions(-) create mode 100644 drivers/gpu/drm/vmwgfx/vmwgfx_va.c diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile index 2258908b1436..aac17a640cce 100644 --- a/drivers/gpu/drm/vmwgfx/Makefile +++ b/drivers/gpu/drm/vmwgfx/Makefile @@ -9,6 +9,6 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ vmwgfx_surface.o vmwgfx_prime.o vmwgfx_mob.o vmwgfx_shader.o \ vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \ vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o \ - vmwgfx_simple_resource.o + vmwgfx_simple_resource.o vmwgfx_va.o obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 27033d944b08..fa1037ec8e5f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -45,31 +45,6 @@ struct vmw_bo_user_rep { uint64_t map_handle; }; -struct vmw_stream { - struct vmw_resource res; - uint32_t stream_id; -}; - -struct vmw_user_stream { - struct ttm_base_object base; - struct vmw_stream stream; -}; - - -static uint64_t vmw_user_stream_size; - -static const struct vmw_res_func vmw_stream_func = { - .res_type = vmw_res_stream, - .needs_backup = false, - .may_evict = false, - .type_name = "video streams", - .backup_placement = NULL, - .create = NULL, - .destroy = NULL, - .bind = NULL, - .unbind = NULL -}; - static inline struct vmw_dma_buffer * vmw_dma_buffer(struct ttm_buffer_object *bo) { @@ -259,24 +234,6 @@ void vmw_resource_activate(struct vmw_resource *res, write_unlock(&dev_priv->resource_lock); } -static struct vmw_resource *vmw_resource_lookup(struct vmw_private *dev_priv, - struct idr *idr, int id) -{ - struct vmw_resource *res; - - read_lock(&dev_priv->resource_lock); - res = idr_find(idr, id); - if (!res || !res->avail || !kref_get_unless_zero(&res->kref)) - res = NULL; - - read_unlock(&dev_priv->resource_lock); - - if (unlikely(res == NULL)) - return NULL; - - return res; -} - /** * vmw_user_resource_lookup_handle - lookup a struct resource from a * TTM user-space handle and perform basic type checks @@ -776,217 +733,6 @@ int vmw_user_dmabuf_reference(struct ttm_object_file *tfile, TTM_REF_USAGE, NULL); } -/* - * Stream management - */ - -static void vmw_stream_destroy(struct vmw_resource *res) -{ - struct vmw_private *dev_priv = res->dev_priv; - struct vmw_stream *stream; - int ret; - - DRM_INFO("%s: unref\n", __func__); - stream = container_of(res, struct vmw_stream, res); - - ret = vmw_overlay_unref(dev_priv, stream->stream_id); - WARN_ON(ret != 0); -} - -static int vmw_stream_init(struct vmw_private *dev_priv, - struct vmw_stream *stream, - void (*res_free) (struct vmw_resource *res)) -{ - struct vmw_resource *res = &stream->res; - int ret; - - ret = vmw_resource_init(dev_priv, res, false, res_free, - &vmw_stream_func); - - if (unlikely(ret != 0)) { - if (res_free == NULL) - kfree(stream); - else - res_free(&stream->res); - return ret; - } - - ret = vmw_overlay_claim(dev_priv, &stream->stream_id); - if (ret) { - vmw_resource_unreference(&res); - return ret; - } - - DRM_INFO("%s: claimed\n", __func__); - - vmw_resource_activate(&stream->res, vmw_stream_destroy); - return 0; -} - -static void vmw_user_stream_free(struct vmw_resource *res) -{ - struct vmw_user_stream *stream = - container_of(res, struct vmw_user_stream, stream.res); - struct vmw_private *dev_priv = res->dev_priv; - - ttm_base_object_kfree(stream, base); - ttm_mem_global_free(vmw_mem_glob(dev_priv), - vmw_user_stream_size); -} - -/** - * This function is called when user space has no more references on the - * base object. It releases the base-object's reference on the resource object. - */ - -static void vmw_user_stream_base_release(struct ttm_base_object **p_base) -{ - struct ttm_base_object *base = *p_base; - struct vmw_user_stream *stream = - container_of(base, struct vmw_user_stream, base); - struct vmw_resource *res = &stream->stream.res; - - *p_base = NULL; - vmw_resource_unreference(&res); -} - -int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_resource *res; - struct vmw_user_stream *stream; - struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - struct idr *idr = &dev_priv->res_idr[vmw_res_stream]; - int ret = 0; - - - res = vmw_resource_lookup(dev_priv, idr, arg->stream_id); - if (unlikely(res == NULL)) - return -EINVAL; - - if (res->res_free != &vmw_user_stream_free) { - ret = -EINVAL; - goto out; - } - - stream = container_of(res, struct vmw_user_stream, stream.res); - if (stream->base.tfile != tfile) { - ret = -EINVAL; - goto out; - } - - ttm_ref_object_base_unref(tfile, stream->base.hash.key, TTM_REF_USAGE); -out: - vmw_resource_unreference(&res); - return ret; -} - -int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_user_stream *stream; - struct vmw_resource *res; - struct vmw_resource *tmp; - struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - int ret; - - /* - * Approximate idr memory usage with 128 bytes. It will be limited - * by maximum number_of streams anyway? - */ - - if (unlikely(vmw_user_stream_size == 0)) - vmw_user_stream_size = ttm_round_pot(sizeof(*stream)) + 128; - - ret = ttm_read_lock(&dev_priv->reservation_sem, true); - if (unlikely(ret != 0)) - return ret; - - ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), - vmw_user_stream_size, - false, true); - ttm_read_unlock(&dev_priv->reservation_sem); - if (unlikely(ret != 0)) { - if (ret != -ERESTARTSYS) - DRM_ERROR("Out of graphics memory for stream" - " creation.\n"); - - goto out_ret; - } - - stream = kmalloc(sizeof(*stream), GFP_KERNEL); - if (unlikely(stream == NULL)) { - ttm_mem_global_free(vmw_mem_glob(dev_priv), - vmw_user_stream_size); - ret = -ENOMEM; - goto out_ret; - } - - res = &stream->stream.res; - stream->base.shareable = false; - stream->base.tfile = NULL; - - /* - * From here on, the destructor takes over resource freeing. - */ - - ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free); - if (unlikely(ret != 0)) - goto out_ret; - - tmp = vmw_resource_reference(res); - ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM, - &vmw_user_stream_base_release, NULL); - - if (unlikely(ret != 0)) { - vmw_resource_unreference(&tmp); - goto out_err; - } - - arg->stream_id = res->id; -out_err: - vmw_resource_unreference(&res); -out_ret: - return ret; -} - -int vmw_user_stream_lookup(struct vmw_private *dev_priv, - struct ttm_object_file *tfile, - uint32_t *inout_id, struct vmw_resource **out) -{ - struct vmw_user_stream *stream; - struct vmw_resource *res; - int ret; - - res = vmw_resource_lookup(dev_priv, &dev_priv->res_idr[vmw_res_stream], - *inout_id); - if (unlikely(res == NULL)) - return -EINVAL; - - if (res->res_free != &vmw_user_stream_free) { - ret = -EINVAL; - goto err_ref; - } - - stream = container_of(res, struct vmw_user_stream, stream.res); - if (stream->base.tfile != tfile) { - ret = -EPERM; - goto err_ref; - } - - *inout_id = stream->stream.stream_id; - *out = res; - return 0; -err_ref: - vmw_resource_unreference(&res); - return ret; -} - - /** * vmw_dumb_create - Create a dumb kms buffer * diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_va.c b/drivers/gpu/drm/vmwgfx/vmwgfx_va.c new file mode 100644 index 000000000000..b4162fd78600 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_va.c @@ -0,0 +1,168 @@ +/************************************************************************** + * + * Copyright © 2012-2016 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" + +/** + * struct vmw_stream - Overlay stream simple resource. + * @sres: The simple resource we derive from. + * @stream_id: The overlay stream id. + */ +struct vmw_stream { + struct vmw_simple_resource sres; + u32 stream_id; +}; + +/** + * vmw_stream - Typecast a struct vmw_resource to a struct vmw_stream. + * @res: Pointer to the struct vmw_resource. + * + * Returns: Returns a pointer to the struct vmw_stream. + */ +static struct vmw_stream * +vmw_stream(struct vmw_resource *res) +{ + return container_of(res, struct vmw_stream, sres.res); +} + +/*************************************************************************** + * Simple resource callbacks for struct vmw_stream + **************************************************************************/ +static void vmw_stream_hw_destroy(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_stream *stream = vmw_stream(res); + int ret; + + ret = vmw_overlay_unref(dev_priv, stream->stream_id); + WARN_ON_ONCE(ret != 0); +} + +static int vmw_stream_init(struct vmw_resource *res, void *data) +{ + struct vmw_stream *stream = vmw_stream(res); + + return vmw_overlay_claim(res->dev_priv, &stream->stream_id); +} + +static void vmw_stream_set_arg_handle(void *data, u32 handle) +{ + struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; + + arg->stream_id = handle; +} + +static const struct vmw_simple_resource_func va_stream_func = { + .res_func = { + .res_type = vmw_res_stream, + .needs_backup = false, + .may_evict = false, + .type_name = "overlay stream", + .backup_placement = NULL, + .create = NULL, + .destroy = NULL, + .bind = NULL, + .unbind = NULL + }, + .ttm_res_type = VMW_RES_STREAM, + .size = sizeof(struct vmw_stream), + .init = vmw_stream_init, + .hw_destroy = vmw_stream_hw_destroy, + .set_arg_handle = vmw_stream_set_arg_handle, +}; + +/*************************************************************************** + * End simple resource callbacks for struct vmw_stream + **************************************************************************/ + +/** + * vmw_stream_unref_ioctl - Ioctl to unreference a user-space handle to + * a struct vmw_stream. + * @dev: Pointer to the drm device. + * @data: The ioctl argument + * @file_priv: Pointer to a struct drm_file identifying the caller. + * + * Return: + * 0 if successful. + * Negative error value on failure. + */ +int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; + + return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, + arg->stream_id, TTM_REF_USAGE); +} + +/** + * vmw_stream_claim_ioctl - Ioctl to claim a struct vmw_stream overlay. + * @dev: Pointer to the drm device. + * @data: The ioctl argument + * @file_priv: Pointer to a struct drm_file identifying the caller. + * + * Return: + * 0 if successful. + * Negative error value on failure. + */ +int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return vmw_simple_resource_create_ioctl(dev, data, file_priv, + &va_stream_func); +} + +/** + * vmw_user_stream_lookup - Look up a struct vmw_user_stream from a handle. + * @dev_priv: Pointer to a struct vmw_private. + * @tfile: struct ttm_object_file identifying the caller. + * @inout_id: In: The user-space handle. Out: The stream id. + * @out: On output contains a refcounted pointer to the embedded + * struct vmw_resource. + * + * Return: + * 0 if successful. + * Negative error value on failure. + */ +int vmw_user_stream_lookup(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t *inout_id, struct vmw_resource **out) +{ + struct vmw_stream *stream; + struct vmw_resource *res = + vmw_simple_resource_lookup(tfile, *inout_id, &va_stream_func); + + if (IS_ERR(res)) + return PTR_ERR(res); + + stream = vmw_stream(res); + *inout_id = stream->stream_id; + *out = res; + + return 0; +} From 2f9545cec6ffd490b08c5675c94c249f169a7e87 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Thu, 23 Mar 2017 15:26:16 -0700 Subject: [PATCH 20/22] drm/vmwgfx: Define an overlaid handle_close ioctl. Instead of providing an ioctl for each handle type, provide a single handle_close ioctl, and reuse the UNREF_DMABUF ioctl. Signed-off-by: Thomas Hellstrom Reviewed-by: Sinclair Yeh --- include/uapi/drm/vmwgfx_drm.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/include/uapi/drm/vmwgfx_drm.h b/include/uapi/drm/vmwgfx_drm.h index d325a4107916..d9dfde9aa757 100644 --- a/include/uapi/drm/vmwgfx_drm.h +++ b/include/uapi/drm/vmwgfx_drm.h @@ -41,6 +41,7 @@ extern "C" { #define DRM_VMW_GET_PARAM 0 #define DRM_VMW_ALLOC_DMABUF 1 #define DRM_VMW_UNREF_DMABUF 2 +#define DRM_VMW_HANDLE_CLOSE 2 #define DRM_VMW_CURSOR_BYPASS 3 /* guarded by DRM_VMW_PARAM_NUM_STREAMS != 0*/ #define DRM_VMW_CONTROL_STREAM 4 @@ -1092,6 +1093,29 @@ union drm_vmw_extended_context_arg { struct drm_vmw_context_arg rep; }; +/*************************************************************************/ +/* + * DRM_VMW_HANDLE_CLOSE - Close a user-space handle and release its + * underlying resource. + * + * Note that this ioctl is overlaid on the DRM_VMW_UNREF_DMABUF Ioctl. + * The ioctl arguments therefore need to be identical in layout. + * + */ + +/** + * struct drm_vmw_handle_close_arg + * + * @handle: Handle to close. + * + * Argument to the DRM_VMW_HANDLE_CLOSE Ioctl. + */ +struct drm_vmw_handle_close_arg { + __u32 handle; + __u32 pad64; +}; + + #if defined(__cplusplus) } #endif From 810b3e1683d00e51c8392bcee7dc7a1c65354777 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 23 Mar 2017 15:39:16 -0700 Subject: [PATCH 21/22] drm/vmwgfx: Support topology greater than texture size Most of the display servers today use a single surface to represent the entire desktop even if it's stretched across multiple screens. For vmwgfx with STDU, the maximum surface size is limited to the maximum texture size on the host. On a 2D VM, this limits our ability to support configurations with more than one 4K monitor. To get past this limitation, we will now allow using a large DMA buf as the framebuffer, and take care of blitting contents from this DMA buf to the display buffer. Signed-off-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 48 ++++++- drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 4 + drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 202 +++++++++++++++++++++++++++ 3 files changed, 253 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index fe226e723287..0832381242b1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -801,6 +801,11 @@ vmw_du_plane_duplicate_state(struct drm_plane *plane) vps->pinned = 0; + /* Mapping is managed by prepare_fb/cleanup_fb */ + memset(&vps->guest_map, 0, sizeof(vps->guest_map)); + memset(&vps->host_map, 0, sizeof(vps->host_map)); + vps->cpp = 0; + /* Each ref counted resource needs to be acquired again */ if (vps->surf) (void) vmw_surface_reference(vps->surf); @@ -859,6 +864,17 @@ vmw_du_plane_destroy_state(struct drm_plane *plane, struct vmw_plane_state *vps = vmw_plane_state_to_vps(state); + /* Should have been freed by cleanup_fb */ + if (vps->guest_map.virtual) { + DRM_ERROR("Guest mapping not freed\n"); + ttm_bo_kunmap(&vps->guest_map); + } + + if (vps->host_map.virtual) { + DRM_ERROR("Host mapping not freed\n"); + ttm_bo_kunmap(&vps->host_map); + } + if (vps->surf) vmw_surface_unreference(&vps->surf); @@ -1433,6 +1449,25 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, return ret; } + +/** + * vmw_kms_srf_ok - check if a surface can be created + * + * @width: requested width + * @height: requested height + * + * Surfaces need to be less than texture size + */ +static bool +vmw_kms_srf_ok(struct vmw_private *dev_priv, uint32_t width, uint32_t height) +{ + if (width > dev_priv->texture_max_width || + height > dev_priv->texture_max_height) + return false; + + return true; +} + /** * vmw_kms_new_framebuffer - Create a new framebuffer. * @@ -1461,7 +1496,8 @@ vmw_kms_new_framebuffer(struct vmw_private *dev_priv, * therefore, wrap the DMA buf in a surface so we can use the * SurfaceCopy command. */ - if (dmabuf && only_2d && + if (vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height) && + dmabuf && only_2d && dev_priv->active_display_unit == vmw_du_screen_target) { ret = vmw_create_dmabuf_proxy(dev_priv->dev, mode_cmd, dmabuf, &surface); @@ -1554,6 +1590,16 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, if (ret) goto err_out; + + if (!bo && + !vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height)) { + DRM_ERROR("Surface size cannot exceed %dx%d", + dev_priv->texture_max_width, + dev_priv->texture_max_height); + goto err_out; + } + + vfb = vmw_kms_new_framebuffer(dev_priv, bo, surface, !(dev_priv->capabilities & SVGA_CAP_3D), mode_cmd); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index 7689f477b726..9c161d29aaeb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -173,6 +173,10 @@ struct vmw_plane_state { unsigned long dmabuf_size; int pinned; + + /* For CPU Blit */ + struct ttm_bo_kmap_obj host_map, guest_map; + unsigned int cpp; }; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 2757cda391bb..4fc46b23ba8b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -109,6 +109,10 @@ struct vmw_screen_target_display_unit { s32 display_width, display_height; bool defined; + + /* For CPU Blit */ + struct ttm_bo_kmap_obj host_map, guest_map; + unsigned int cpp; }; @@ -636,6 +640,129 @@ static void vmw_stdu_dmabuf_fifo_commit(struct vmw_kms_dirty *dirty) ddirty->right = ddirty->bottom = S32_MIN; } + +/** + * vmw_stdu_dmabuf_cpu_clip - Callback to encode a CPU blit + * + * @dirty: The closure structure. + * + * This function calculates the bounding box for all the incoming clips + */ +static void vmw_stdu_dmabuf_cpu_clip(struct vmw_kms_dirty *dirty) +{ + struct vmw_stdu_dirty *ddirty = + container_of(dirty, struct vmw_stdu_dirty, base); + + dirty->num_hits = 1; + + /* Calculate bounding box */ + ddirty->left = min_t(s32, ddirty->left, dirty->unit_x1); + ddirty->top = min_t(s32, ddirty->top, dirty->unit_y1); + ddirty->right = max_t(s32, ddirty->right, dirty->unit_x2); + ddirty->bottom = max_t(s32, ddirty->bottom, dirty->unit_y2); +} + + +/** + * vmw_stdu_dmabuf_cpu_commit - Callback to do a CPU blit from DMAbuf + * + * @dirty: The closure structure. + * + * For the special case when we cannot create a proxy surface in a + * 2D VM, we have to do a CPU blit ourselves. + */ +static void vmw_stdu_dmabuf_cpu_commit(struct vmw_kms_dirty *dirty) +{ + struct vmw_stdu_dirty *ddirty = + container_of(dirty, struct vmw_stdu_dirty, base); + struct vmw_screen_target_display_unit *stdu = + container_of(dirty->unit, typeof(*stdu), base); + s32 width, height; + s32 src_pitch, dst_pitch; + u8 *src, *dst; + bool not_used; + + + if (!dirty->num_hits) + return; + + width = ddirty->right - ddirty->left; + height = ddirty->bottom - ddirty->top; + + if (width == 0 || height == 0) + return; + + + /* Assume we are blitting from Host (display_srf) to Guest (dmabuf) */ + src_pitch = stdu->display_srf->base_size.width * stdu->cpp; + src = ttm_kmap_obj_virtual(&stdu->host_map, ¬_used); + src += dirty->unit_y1 * src_pitch + dirty->unit_x1 * stdu->cpp; + + dst_pitch = ddirty->pitch; + dst = ttm_kmap_obj_virtual(&stdu->guest_map, ¬_used); + dst += dirty->fb_y * dst_pitch + dirty->fb_x * stdu->cpp; + + + /* Figure out the real direction */ + if (ddirty->transfer == SVGA3D_WRITE_HOST_VRAM) { + u8 *tmp; + s32 tmp_pitch; + + tmp = src; + tmp_pitch = src_pitch; + + src = dst; + src_pitch = dst_pitch; + + dst = tmp; + dst_pitch = tmp_pitch; + } + + /* CPU Blit */ + while (height-- > 0) { + memcpy(dst, src, width * stdu->cpp); + dst += dst_pitch; + src += src_pitch; + } + + if (ddirty->transfer == SVGA3D_WRITE_HOST_VRAM) { + struct vmw_private *dev_priv; + struct vmw_stdu_update *cmd; + struct drm_clip_rect region; + int ret; + + /* We are updating the actual surface, not a proxy */ + region.x1 = ddirty->left; + region.x2 = ddirty->right; + region.y1 = ddirty->top; + region.y2 = ddirty->bottom; + ret = vmw_kms_update_proxy( + (struct vmw_resource *) &stdu->display_srf->res, + (const struct drm_clip_rect *) ®ion, 1, 1); + if (ret) + goto out_cleanup; + + + dev_priv = vmw_priv(stdu->base.crtc.dev); + cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); + + if (!cmd) { + DRM_ERROR("Cannot reserve FIFO space to update STDU"); + goto out_cleanup; + } + + vmw_stdu_populate_update(cmd, stdu->base.unit, + ddirty->left, ddirty->right, + ddirty->top, ddirty->bottom); + + vmw_fifo_commit(dev_priv, sizeof(*cmd)); + } + +out_cleanup: + ddirty->left = ddirty->top = S32_MAX; + ddirty->right = ddirty->bottom = S32_MIN; +} + /** * vmw_kms_stdu_dma - Perform a DMA transfer between a dma-buffer backed * framebuffer and the screen target system. @@ -694,6 +821,13 @@ int vmw_kms_stdu_dma(struct vmw_private *dev_priv, if (to_surface) ddirty.base.fifo_reserve_size += sizeof(struct vmw_stdu_update); + /* 2D VMs cannot use SVGA_3D_CMD_SURFACE_DMA so do CPU blit instead */ + if (!(dev_priv->capabilities & SVGA_CAP_3D)) { + ddirty.base.fifo_commit = vmw_stdu_dmabuf_cpu_commit; + ddirty.base.clip = vmw_stdu_dmabuf_cpu_clip; + ddirty.base.fifo_reserve_size = 0; + } + ret = vmw_kms_helper_dirty(dev_priv, vfb, clips, vclips, 0, 0, num_clips, increment, &ddirty.base); vmw_kms_helper_buffer_finish(dev_priv, file_priv, buf, NULL, @@ -960,12 +1094,19 @@ vmw_stdu_primary_plane_cleanup_fb(struct drm_plane *plane, { struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); + if (vps->guest_map.virtual) + ttm_bo_kunmap(&vps->guest_map); + + if (vps->host_map.virtual) + ttm_bo_kunmap(&vps->host_map); + if (vps->surf) WARN_ON(!vps->pinned); vmw_du_plane_cleanup_fb(plane, old_state); vps->content_fb_type = SAME_AS_DISPLAY; + vps->cpp = 0; } @@ -986,6 +1127,7 @@ static int vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state) { + struct vmw_private *dev_priv = vmw_priv(plane->dev); struct drm_framebuffer *new_fb = new_state->fb; struct vmw_framebuffer *vfb; struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); @@ -1111,8 +1253,54 @@ vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane, } vps->content_fb_type = new_content_type; + + /* + * This should only happen if the DMA buf is too large to create a + * proxy surface for. + * If we are a 2D VM with a DMA buffer then we have to use CPU blit + * so cache these mappings + */ + if (vps->content_fb_type == SEPARATE_DMA && + !(dev_priv->capabilities & SVGA_CAP_3D)) { + + struct vmw_framebuffer_dmabuf *new_vfbd; + + new_vfbd = vmw_framebuffer_to_vfbd(new_fb); + + ret = ttm_bo_reserve(&new_vfbd->buffer->base, false, false, + NULL); + if (ret) + goto out_srf_unpin; + + ret = ttm_bo_kmap(&new_vfbd->buffer->base, 0, + new_vfbd->buffer->base.num_pages, + &vps->guest_map); + + ttm_bo_unreserve(&new_vfbd->buffer->base); + + if (ret) { + DRM_ERROR("Failed to map content buffer to CPU\n"); + goto out_srf_unpin; + } + + ret = ttm_bo_kmap(&vps->surf->res.backup->base, 0, + vps->surf->res.backup->base.num_pages, + &vps->host_map); + if (ret) { + DRM_ERROR("Failed to map display buffer to CPU\n"); + ttm_bo_kunmap(&vps->guest_map); + goto out_srf_unpin; + } + + vps->cpp = new_fb->pitches[0] / new_fb->width; + } + return 0; +out_srf_unpin: + vmw_resource_unpin(&vps->surf->res); + vps->pinned--; + out_srf_unref: vmw_surface_unreference(&vps->surf); return ret; @@ -1146,6 +1334,9 @@ vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane, stdu->display_srf = vps->surf; stdu->content_fb_type = vps->content_fb_type; + stdu->cpp = vps->cpp; + memcpy(&stdu->guest_map, &vps->guest_map, sizeof(vps->guest_map)); + memcpy(&stdu->host_map, &vps->host_map, sizeof(vps->host_map)); if (!stdu->defined) return; @@ -1410,6 +1601,17 @@ int vmw_kms_stdu_init_display(struct vmw_private *dev_priv) dev_priv->active_display_unit = vmw_du_screen_target; + if (!(dev_priv->capabilities & SVGA_CAP_3D)) { + /* + * Given various display aspect ratios, there's no way to + * estimate these using prim_bb_mem. So just set these to + * something arbitrarily large and we will reject any layout + * that doesn't fit prim_bb_mem later + */ + dev->mode_config.max_width = 16384; + dev->mode_config.max_height = 16384; + } + vmw_kms_create_implicit_placement_property(dev_priv, false); for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) { From 28c954299cd2b5cb5411816c9eeaf3f51ed4638e Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Mon, 27 Mar 2017 11:12:27 -0700 Subject: [PATCH 22/22] drm/vmwgfx: Properly check display/scanout surface size The scanout surface size is the smaller of max texture size and max STDU size. Signed-off-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 3 +++ drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 16 +++++++++++++++- drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 14 ++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 0832381242b1..c18c81f63ac3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -2182,7 +2182,10 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector, if (dev_priv->active_display_unit == vmw_du_screen_target) { max_width = min(max_width, dev_priv->stdu_max_width); + max_width = min(max_width, dev_priv->texture_max_width); + max_height = min(max_height, dev_priv->stdu_max_height); + max_height = min(max_height, dev_priv->texture_max_height); } /* Add preferred mode */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 4fc46b23ba8b..e872ffe5f0db 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -1601,7 +1601,21 @@ int vmw_kms_stdu_init_display(struct vmw_private *dev_priv) dev_priv->active_display_unit = vmw_du_screen_target; - if (!(dev_priv->capabilities & SVGA_CAP_3D)) { + if (dev_priv->capabilities & SVGA_CAP_3D) { + /* + * For 3D VMs, display (scanout) buffer size is the smaller of + * max texture and max STDU + */ + uint32_t max_width, max_height; + + max_width = min(dev_priv->texture_max_width, + dev_priv->stdu_max_width); + max_height = min(dev_priv->texture_max_height, + dev_priv->stdu_max_height); + + dev->mode_config.max_width = max_width; + dev->mode_config.max_height = max_height; + } else { /* * Given various display aspect ratios, there's no way to * estimate these using prim_bb_mem. So just set these to diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index 6abcf82d8a39..41b9d20d6ae7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -1479,10 +1479,24 @@ int vmw_surface_gb_priv_define(struct drm_device *dev, *srf_out = NULL; if (for_scanout) { + uint32_t max_width, max_height; + if (!svga3dsurface_is_screen_target_format(format)) { DRM_ERROR("Invalid Screen Target surface format."); return -EINVAL; } + + max_width = min(dev_priv->texture_max_width, + dev_priv->stdu_max_width); + max_height = min(dev_priv->texture_max_height, + dev_priv->stdu_max_height); + + if (size.width > max_width || size.height > max_height) { + DRM_ERROR("%ux%u\n, exeeds max surface size %ux%u", + size.width, size.height, + max_width, max_height); + return -EINVAL; + } } else { const struct svga3d_surface_desc *desc;