// SPDX-License-Identifier: GPL-2.0 OR MIT /****************************************************************************** * * Copyright (c) 2014-2024 Broadcom. All Rights Reserved. The term * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * 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. *
******************************************************************************/
/* * Some renderers such as llvmpipe will align the width and height of their * buffers to match their tile size. We need to keep this in mind when exposing * modes to userspace so that this possible over-allocation will not exceed * graphics memory. 64x64 pixels seems to be a reasonable upper bound for the * tile size of current renderers.
*/ #define GPU_TILE_SIZE 64
/** * struct vmw_stdu_dirty - closure structure for the update functions * * @base: The base type we derive from. Used by vmw_kms_helper_dirty(). * @left: Left side of bounding box. * @right: Right side of bounding box. * @top: Top side of bounding box. * @bottom: Bottom side of bounding box. * @fb_left: Left side of the framebuffer/content bounding box * @fb_top: Top of the framebuffer/content bounding box * @pitch: framebuffer pitch (stride) * @buf: buffer object when DMA-ing between buffer and screen targets. * @sid: Surface ID when copying between surface and screen targets.
*/ struct vmw_stdu_dirty { struct vmw_kms_dirty base;
s32 left, right, top, bottom;
s32 fb_left, fb_top;
u32 pitch; union { struct vmw_bo *buf;
u32 sid;
};
};
/* * SVGA commands that are used by this code. Please see the device headers * for explanation.
*/ struct vmw_stdu_update {
SVGA3dCmdHeader header;
SVGA3dCmdUpdateGBScreenTarget body;
};
/** * struct vmw_screen_target_display_unit - conglomerated STDU structure * * @base: VMW specific DU structure * @display_srf: surface to be displayed. The dimension of this will always * match the display mode. If the display mode matches * content_vfbs dimensions, then this is a pointer into the * corresponding field in content_vfbs. If not, then this * is a separate buffer to which content_vfbs will blit to. * @content_fb_type: content_fb type * @display_width: display width * @display_height: display height * @defined: true if the current display unit has been initialized * @cpp: Bytes per pixel
*/ struct vmw_screen_target_display_unit { struct vmw_display_unit base; struct vmw_surface *display_srf; enum stdu_content_type content_fb_type;
s32 display_width, display_height;
/****************************************************************************** * Screen Target Display Unit CRTC Functions
*****************************************************************************/
/** * vmw_stdu_crtc_destroy - cleans up the STDU * * @crtc: used to get a reference to the containing STDU
*/ staticvoid vmw_stdu_crtc_destroy(struct drm_crtc *crtc)
{
vmw_stdu_destroy(vmw_crtc_to_stdu(crtc));
}
/** * vmw_stdu_define_st - Defines a Screen Target * * @dev_priv: VMW DRM device * @stdu: display unit to create a Screen Target for * @mode: The mode to set. * @crtc_x: X coordinate of screen target relative to framebuffer origin. * @crtc_y: Y coordinate of screen target relative to framebuffer origin. * * Creates a STDU that we can used later. This function is called whenever the * framebuffer size changes. * * RETURNs: * 0 on success, error code on failure
*/ staticint vmw_stdu_define_st(struct vmw_private *dev_priv, struct vmw_screen_target_display_unit *stdu, struct drm_display_mode *mode, int crtc_x, int crtc_y)
{ struct {
SVGA3dCmdHeader header;
SVGA3dCmdDefineGBScreenTarget body;
} *cmd;
cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); if (unlikely(cmd == NULL)) return -ENOMEM;
/** * vmw_stdu_bind_st - Binds a surface to a Screen Target * * @dev_priv: VMW DRM device * @stdu: display unit affected * @res: Buffer to bind to the screen target. Set to NULL to blank screen. * * Binding a surface to a Screen Target the same as flipping * * Returns: %0 on success or -errno code on failure
*/ staticint vmw_stdu_bind_st(struct vmw_private *dev_priv, struct vmw_screen_target_display_unit *stdu, conststruct vmw_resource *res)
{
SVGA3dSurfaceImageId image;
/** * vmw_stdu_populate_update - populate an UPDATE_GB_SCREENTARGET command with a * bounding box. * * @cmd: Pointer to command stream. * @unit: Screen target unit. * @left: Left side of bounding box. * @right: Right side of bounding box. * @top: Top side of bounding box. * @bottom: Bottom side of bounding box.
*/ staticvoid vmw_stdu_populate_update(void *cmd, int unit,
s32 left, s32 right, s32 top, s32 bottom)
{ struct vmw_stdu_update *update = cmd;
/** * vmw_stdu_update_st - Full update of a Screen Target * * @dev_priv: VMW DRM device * @stdu: display unit affected * * This function needs to be called whenever the content of a screen * target has changed completely. Typically as a result of a backing * surface change. * * RETURNS: * 0 on success, error code on failure
*/ staticint vmw_stdu_update_st(struct vmw_private *dev_priv, struct vmw_screen_target_display_unit *stdu)
{ struct vmw_stdu_update *cmd;
if (!stdu->defined) {
DRM_ERROR("No screen target defined"); return -EINVAL;
}
cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); if (unlikely(cmd == NULL)) return -ENOMEM;
if (dev_priv->vkms_enabled)
drm_crtc_vblank_off(crtc);
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);
/* Don't destroy the Screen Target if we are only setting the * display as inactive
*/ if (new_crtc_state->enable &&
!new_crtc_state->active &&
!new_crtc_state->mode_changed) return;
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_bo_cpu_clip - Callback to encode a CPU blit * * @dirty: The closure structure. * * This function calculates the bounding box for all the incoming clips.
*/ staticvoid vmw_stdu_bo_cpu_clip(struct vmw_kms_dirty *dirty)
{ struct vmw_stdu_dirty *ddirty =
container_of(dirty, struct vmw_stdu_dirty, base);
/* * Calculate content bounding box. We only need the top-left * coordinate because width and height will be the same as the * destination bounding box above
*/
ddirty->fb_left = min_t(s32, ddirty->fb_left, dirty->fb_x);
ddirty->fb_top = min_t(s32, ddirty->fb_top, dirty->fb_y);
}
/** * vmw_stdu_bo_cpu_commit - Callback to do a CPU blit from buffer object * * @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.
*/ staticvoid vmw_stdu_bo_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; struct vmw_bo *src_bo, *dst_bo;
u32 src_offset, dst_offset; struct vmw_diff_cpy diff = VMW_CPU_BLIT_DIFF_INITIALIZER(stdu->cpp);
/** * vmw_kms_stdu_readback - Perform a readback from a buffer-object backed * framebuffer and the screen target system. * * @dev_priv: Pointer to the device private structure. * @file_priv: Pointer to a struct drm-file identifying the caller. May be * set to NULL, but then @user_fence_rep must also be set to NULL. * @vfb: Pointer to the buffer-object backed framebuffer. * @user_fence_rep: User-space provided structure for fence information. * @clips: Array of clip rects. Either @clips or @vclips must be NULL. * @vclips: Alternate array of clip rects. Either @clips or @vclips must * be NULL. * @num_clips: Number of clip rects in @clips or @vclips. * @increment: Increment to use when looping over @clips or @vclips. * @crtc: If crtc is passed, perform stdu dma on that crtc only. * * If DMA-ing till the screen target system, the function will also notify * the screen target system that a bounding box of the cliprects has been * updated. * * Returns: %0 on success, negative error code on failure. -ERESTARTSYS if * interrupted.
*/ int vmw_kms_stdu_readback(struct vmw_private *dev_priv, struct drm_file *file_priv, struct vmw_framebuffer *vfb, struct drm_vmw_fence_rep __user *user_fence_rep, struct drm_clip_rect *clips, struct drm_vmw_rect *vclips,
uint32_t num_clips, int increment, struct drm_crtc *crtc)
{ struct vmw_bo *buf =
container_of(vfb, struct vmw_framebuffer_bo, base)->buffer; struct vmw_stdu_dirty ddirty; int ret;
DECLARE_VAL_CONTEXT(val_ctx, NULL, 0);
/* * The GMR domain might seem confusing because it might seem like it should * never happen with screen targets but e.g. the xorg vmware driver issues * CMD_SURFACE_DMA for various pixmap updates which might transition our bo to * a GMR. Instead of forcing another transition we can optimize the readback * by reading directly from the GMR.
*/
vmw_bo_placement_set(buf,
VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_GMR,
VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_GMR);
ret = vmw_validation_add_bo(&val_ctx, buf); if (ret) return ret;
ret = vmw_validation_prepare(&val_ctx, NULL, true); if (ret) goto out_unref;
/** * vmw_kms_stdu_surface_dirty - Dirty part of a surface backed framebuffer * * @dev_priv: Pointer to the device private structure. * @framebuffer: Pointer to the surface-buffer backed framebuffer. * @clips: Array of clip rects. Either @clips or @vclips must be NULL. * @vclips: Alternate array of clip rects. Either @clips or @vclips must * be NULL. * @srf: Pointer to surface to blit from. If NULL, the surface attached * to @framebuffer will be used. * @dest_x: X coordinate offset to align @srf with framebuffer coordinates. * @dest_y: Y coordinate offset to align @srf with framebuffer coordinates. * @num_clips: Number of clip rects in @clips. * @inc: Increment to use when looping over @clips. * @out_fence: If non-NULL, will return a ref-counted pointer to a * struct vmw_fence_obj. The returned fence pointer may be NULL in which * case the device has already synchronized. * @crtc: If crtc is passed, perform surface dirty on that crtc only. * * Returns: %0 on success, negative error code on failure. -ERESTARTSYS if * interrupted.
*/ int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv, struct vmw_framebuffer *framebuffer, struct drm_clip_rect *clips, struct drm_vmw_rect *vclips, struct vmw_resource *srf,
s32 dest_x,
s32 dest_y, unsigned num_clips, int inc, struct vmw_fence_obj **out_fence, struct drm_crtc *crtc)
{ struct vmw_framebuffer_surface *vfbs =
container_of(framebuffer, typeof(*vfbs), base); struct vmw_stdu_dirty sdirty;
DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); int ret;
if (!srf)
srf = &vmw_user_object_surface(&vfbs->uo)->res;
ret = vmw_validation_add_resource(&val_ctx, srf, 0, VMW_RES_DIRTY_NONE,
NULL, NULL); if (ret) return ret;
ret = vmw_validation_prepare(&val_ctx, &dev_priv->cmdbuf_mutex, true); if (ret) goto out_unref;
/****************************************************************************** * Screen Target Display Unit Encoder Functions
*****************************************************************************/
/** * vmw_stdu_encoder_destroy - cleans up the STDU * * @encoder: used the get the containing STDU * * vmwgfx cleans up crtc/encoder/connector all at the same time so technically * this can be a no-op. Nevertheless, it doesn't hurt of have this in case * the common KMS code changes and somehow vmw_stdu_crtc_destroy() doesn't * get called.
*/ staticvoid vmw_stdu_encoder_destroy(struct drm_encoder *encoder)
{
vmw_stdu_destroy(vmw_encoder_to_stdu(encoder));
}
/****************************************************************************** * Screen Target Display Unit Connector Functions
*****************************************************************************/
/** * vmw_stdu_connector_destroy - cleans up the STDU * * @connector: used to get the containing STDU * * vmwgfx cleans up crtc/encoder/connector all at the same time so technically * this can be a no-op. Nevertheless, it doesn't hurt of have this in case * the common KMS code changes and somehow vmw_stdu_crtc_destroy() doesn't * get called.
*/ staticvoid vmw_stdu_connector_destroy(struct drm_connector *connector)
{
vmw_stdu_destroy(vmw_connector_to_stdu(connector));
}
ret = drm_mode_validate_size(mode, dev_priv->stdu_max_width,
dev_priv->stdu_max_height); if (ret != MODE_OK) return ret;
ret = drm_mode_validate_size(mode, dev_priv->texture_max_width,
dev_priv->texture_max_height); if (ret != MODE_OK) return ret;
if (required_mem > dev_priv->max_primary_mem) return MODE_MEM;
if (required_mem > dev_priv->max_mob_pages * PAGE_SIZE) return MODE_MEM;
if (required_mem > dev_priv->max_mob_size) return MODE_MEM;
return MODE_OK;
}
/* * Trigger a modeset if the X,Y position of the Screen Target changes. * This is needed when multi-mon is cycled. The original Screen Target will have * the same mode but its relative X,Y position in the topology will change.
*/ staticint vmw_stdu_connector_atomic_check(struct drm_connector *conn, struct drm_atomic_state *state)
{ struct drm_connector_state *conn_state; struct vmw_screen_target_display_unit *du; struct drm_crtc_state *new_crtc_state;
/** * 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 buffer object. The display surface is pinned here, and it'll * be unpinned in .cleanup_fb() * * Returns: %0 on success
*/ staticint
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); enum stdu_content_type new_content_type; struct vmw_framebuffer_surface *new_vfbs;
uint32_t hdisplay = new_state->crtc_w, vdisplay = new_state->crtc_h; struct drm_plane_state *old_state = plane->state; struct drm_rect rect; int ret;
/* No FB to prepare */ if (!new_fb) { if (vmw_user_object_surface(&vps->uo)) {
WARN_ON(vps->pinned != 0);
vmw_user_object_unref(&vps->uo);
}
if (!vmw_user_object_surface(&vps->uo)) {
ret = vmw_gb_surface_define(dev_priv, &metadata,
&vps->uo.surface); 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 (vmw_user_object_surface(&vps->uo)) {
WARN_ON(vps->pinned != 0);
vmw_user_object_unref(&vps->uo);
}
/* Pin new surface before flipping */
ret = vmw_resource_pin(&vmw_user_object_surface(&vps->uo)->res, false); if (ret) goto out_srf_unref;
vps->pinned++;
}
vps->content_fb_type = new_content_type;
/* * The drm fb code will do blit's via the vmap interface, which doesn't * trigger vmw_bo page dirty tracking due to being kernel side (and thus * doesn't require mmap'ing) so we have to update the surface's dirty * regions by hand but we want to be careful to not overwrite the * resource if it has been written to by the gpu (res_dirty).
*/ if (vps->uo.buffer && vps->uo.buffer->is_dumb) { struct vmw_surface *surf = vmw_user_object_surface(&vps->uo); struct vmw_resource *res = &surf->res;
if (!res->res_dirty && drm_atomic_helper_damage_merged(old_state,
new_state,
&rect)) { /* * At some point it might be useful to actually translate * (rect.x1, rect.y1) => start, and (rect.x2, rect.y2) => end, * but currently the fb code will just report the entire fb * dirty so in practice it doesn't matter.
*/
pgoff_t start = res->guest_memory_offset >> PAGE_SHIFT;
pgoff_t end = __KERNEL_DIV_ROUND_UP(res->guest_memory_offset +
res->guest_memory_size,
PAGE_SIZE);
vmw_resource_dirty_update(res, start, end);
}
}
/* * This should only happen if the buffer object is too large to create a * proxy surface for.
*/ if (vps->content_fb_type == SEPARATE_BO)
vps->cpp = new_fb->pitches[0] / new_fb->width;
/** * vmw_stdu_plane_update_bo - Update display unit for bo backed fb. * @dev_priv: device private. * @plane: plane state. * @old_state: old plane state. * @vfb: framebuffer which is blitted to display unit. * @out_fence: If non-NULL, will return a ref-counted pointer to vmw_fence_obj. * The returned fence pointer may be NULL in which case the device * has already synchronized. * * Return: 0 on success or a negative error code on failure.
*/ staticint vmw_stdu_plane_update_bo(struct vmw_private *dev_priv, struct drm_plane *plane, struct drm_plane_state *old_state, struct vmw_framebuffer *vfb, struct vmw_fence_obj **out_fence)
{ struct vmw_du_update_plane_buffer bo_update;
/** * vmw_stdu_plane_update_surface - Update display unit for surface backed fb * @dev_priv: Device private * @plane: Plane state * @old_state: Old plane state * @vfb: Framebuffer which is blitted to display unit * @out_fence: If non-NULL, will return a ref-counted pointer to vmw_fence_obj. * The returned fence pointer may be NULL in which case the device * has already synchronized. * * Return: 0 on success or a negative error code on failure.
*/ staticint vmw_stdu_plane_update_surface(struct vmw_private *dev_priv, struct drm_plane *plane, struct drm_plane_state *old_state, struct vmw_framebuffer *vfb, struct vmw_fence_obj **out_fence)
{ struct vmw_du_update_plane srf_update; struct vmw_screen_target_display_unit *stdu; struct vmw_framebuffer_surface *vfbs;
/** * vmw_stdu_primary_plane_atomic_update - formally switches STDU to new plane * @plane: display plane * @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.
*/ staticvoid
vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state)
{ struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); struct drm_crtc *crtc = new_state->crtc; struct vmw_screen_target_display_unit *stdu; struct vmw_fence_obj *fence = NULL; struct vmw_private *dev_priv; int ret;
/* If case of device error, maintain consistent atomic state */ if (crtc && new_state->fb) { struct vmw_framebuffer *vfb =
vmw_framebuffer_to_vfb(new_state->fb);
stdu = vmw_crtc_to_stdu(crtc);
dev_priv = vmw_priv(crtc->dev);
/** * vmw_stdu_init - Sets up a Screen Target Display Unit * * @dev_priv: VMW DRM device * @unit: unit number range from 0 to VMWGFX_NUM_DISPLAY_UNITS * * This function is called once per CRTC, and allocates one Screen Target * display unit to represent that CRTC. Since the SVGA device does not separate * out encoder and connector, they are represented as part of the STDU as well. * * Returns: %0 on success or -errno code on failure
*/ staticint vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
{ struct vmw_screen_target_display_unit *stdu; struct drm_device *dev = &dev_priv->drm; struct drm_connector *connector; struct drm_encoder *encoder; struct drm_plane *primary; struct vmw_cursor_plane *cursor; struct drm_crtc *crtc; int ret;
stdu = kzalloc(sizeof(*stdu), GFP_KERNEL); if (!stdu) return -ENOMEM;
/** * vmw_stdu_destroy - Cleans up a vmw_screen_target_display_unit * * @stdu: Screen Target Display Unit to be destroyed * * Clean up after vmw_stdu_init
*/ staticvoid vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu)
{
vmw_du_cleanup(&stdu->base);
kfree(stdu);
}
/****************************************************************************** * Screen Target Display KMS Functions * * These functions are called by the common KMS code in vmwgfx_kms.c
*****************************************************************************/
/** * vmw_kms_stdu_init_display - Initializes a Screen Target based display * * @dev_priv: VMW DRM device * * This function initialize a Screen Target based display device. It checks * the capability bits to make sure the underlying hardware can support * screen targets, and then creates the maximum number of CRTCs, a.k.a Display * Units, as supported by the display hardware. * * RETURNS: * 0 on success, error code otherwise
*/ int vmw_kms_stdu_init_display(struct vmw_private *dev_priv)
{ struct drm_device *dev = &dev_priv->drm; int i, ret;
/* Do nothing if there's no support for MOBs */ if (!dev_priv->has_mob) return -ENOSYS;
if (!(dev_priv->capabilities & SVGA_CAP_GBOBJECTS)) return -ENOSYS;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.