Message ID | 1490652064-44817-7-git-send-email-syeh@vmware.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Mon, Mar 27, 2017 at 03:00:59PM -0700, Sinclair Yeh wrote: > 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 <syeh@vmware.com> > Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com> > Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com> > --- > 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 6f0f160..d00ff21 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 <drm/drm_plane_helper.h> > #include <drm/drm_atomic.h> > #include <drm/drm_atomic_helper.h> > +#include <drm/drm_rect.h> > > > /* Might need a hrtimer here? */ > @@ -399,6 +401,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); There's a new atomic version of this, drm_plane_helper_check_state(), for less boilerplate. > + > + > + 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); It hasn't (yet) merged, but my recommendation for global kms state is to mirror the main atomic framework, i.e. - state struct, with duplicate/commit semantics - separate ww_mutex We should also be merging a new helper for driver private state objects rsn, to make this pattern a lot easier/direct to implement. You can make this work too, but since it's a different way of atomic commit it means more work trying to understand it. -Daniel > + 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 f711b5d..de6a0b6 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 d547e80..1d734de 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 662024c..eca055e 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 6e3cfad..cce5e5b 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, > -- > 2.7.4 >
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 6f0f160..d00ff21 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 <drm/drm_plane_helper.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_rect.h> /* Might need a hrtimer here? */ @@ -399,6 +401,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 f711b5d..de6a0b6 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 d547e80..1d734de 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 662024c..eca055e 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 6e3cfad..cce5e5b 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,