// 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. * ******************************************************************************/ #include "vmwgfx_bo.h" #include "vmwgfx_kms.h" #include "vmwgfx_vkms.h" #include "vmw_surface_cache.h" #include <linux/fsnotify.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_damage_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_vblank.h> #define vmw_crtc_to_stdu(x) … #define vmw_encoder_to_stdu(x) … #define vmw_connector_to_stdu(x) … /* * 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 … enum stdu_content_type { … }; /** * 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 { … }; /* * SVGA commands that are used by this code. Please see the device headers * for explanation. */ struct vmw_stdu_update { … }; struct vmw_stdu_surface_copy { … }; struct vmw_stdu_update_gb_image { … }; /** * 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 { … }; static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu); /****************************************************************************** * Screen Target Display Unit CRTC Functions *****************************************************************************/ /** * vmw_stdu_crtc_destroy - cleans up the STDU * * @crtc: used to get a reference to the containing STDU */ static void vmw_stdu_crtc_destroy(struct drm_crtc *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 */ static int 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) { … } /** * 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 */ static int vmw_stdu_bind_st(struct vmw_private *dev_priv, struct vmw_screen_target_display_unit *stdu, const struct vmw_resource *res) { … } /** * 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. */ static void vmw_stdu_populate_update(void *cmd, int unit, s32 left, s32 right, s32 top, s32 bottom) { … } /** * 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 */ static int vmw_stdu_update_st(struct vmw_private *dev_priv, struct vmw_screen_target_display_unit *stdu) { … } /** * vmw_stdu_destroy_st - Destroy a Screen Target * * @dev_priv: VMW DRM device * @stdu: display unit to destroy * * Returns: %0 on success, negative error code on failure. -ERESTARTSYS if * interrupted. */ static int vmw_stdu_destroy_st(struct vmw_private *dev_priv, struct vmw_screen_target_display_unit *stdu) { … } /** * 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) { … } static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state) { … } /** * 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. */ static void vmw_stdu_bo_cpu_clip(struct vmw_kms_dirty *dirty) { … } /** * 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. */ static void vmw_stdu_bo_cpu_commit(struct vmw_kms_dirty *dirty) { … } /** * 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) { … } /** * vmw_kms_stdu_surface_clip - Callback to encode a surface copy command cliprect * * @dirty: The closure structure. * * Encodes a surface copy command cliprect and updates the bounding box * for the copy. */ static void vmw_kms_stdu_surface_clip(struct vmw_kms_dirty *dirty) { … } /** * vmw_kms_stdu_surface_fifo_commit - Callback to fill in and submit a surface * copy command. * * @dirty: The closure structure. * * Fills in the missing fields in a surface copy command, and encodes a screen * target update command. */ static void vmw_kms_stdu_surface_fifo_commit(struct vmw_kms_dirty *dirty) { … } /** * 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) { … } /* * Screen Target CRTC dispatch table */ static const struct drm_crtc_funcs vmw_stdu_crtc_funcs = …; /****************************************************************************** * 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. */ static void vmw_stdu_encoder_destroy(struct drm_encoder *encoder) { … } static const struct drm_encoder_funcs vmw_stdu_encoder_funcs = …; /****************************************************************************** * 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. */ static void vmw_stdu_connector_destroy(struct drm_connector *connector) { … } static enum drm_mode_status vmw_stdu_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { … } /* * 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. */ static int vmw_stdu_connector_atomic_check(struct drm_connector *conn, struct drm_atomic_state *state) { … } static const struct drm_connector_funcs vmw_stdu_connector_funcs = …; static const struct drm_connector_helper_funcs vmw_stdu_connector_helper_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) { … } /** * 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 */ static int vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state) { … } static uint32_t vmw_stdu_bo_fifo_size_cpu(struct vmw_du_update_plane *update, uint32_t num_hits) { … } static uint32_t vmw_stdu_bo_pre_clip_cpu(struct vmw_du_update_plane *update, void *cmd, uint32_t num_hits) { … } static uint32_t vmw_stdu_bo_clip_cpu(struct vmw_du_update_plane *update, void *cmd, struct drm_rect *clip, uint32_t fb_x, uint32_t fb_y) { … } static uint32_t vmw_stdu_bo_populate_update_cpu(struct vmw_du_update_plane *update, void *cmd, struct drm_rect *bb) { … } /** * 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. */ static int 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) { … } static uint32_t vmw_stdu_surface_fifo_size_same_display(struct vmw_du_update_plane *update, uint32_t num_hits) { … } static uint32_t vmw_stdu_surface_fifo_size(struct vmw_du_update_plane *update, uint32_t num_hits) { … } static uint32_t vmw_stdu_surface_populate_copy(struct vmw_du_update_plane *update, void *cmd, uint32_t num_hits) { … } static uint32_t vmw_stdu_surface_populate_clip(struct vmw_du_update_plane *update, void *cmd, struct drm_rect *clip, uint32_t fb_x, uint32_t fb_y) { … } static uint32_t vmw_stdu_surface_populate_update(struct vmw_du_update_plane *update, void *cmd, struct drm_rect *bb) { … } /** * 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. */ static int 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) { … } /** * 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. */ static void vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) { … } static void vmw_stdu_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) { … } static const struct drm_plane_funcs vmw_stdu_plane_funcs = …; 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 = …; static const struct drm_plane_helper_funcs vmw_stdu_primary_plane_helper_funcs = …; static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = …; /** * 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 */ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) { … } /** * 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 */ static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *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) { … }