// SPDX-License-Identifier: GPL-2.0 OR MIT /************************************************************************** * * Copyright (c) 2009-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_kms.h" #include "vmwgfx_bo.h" #include "vmwgfx_vkms.h" #include "vmw_surface_cache.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_rect.h> #include <drm/drm_sysfs.h> #include <drm/drm_edid.h> void vmw_du_init(struct vmw_display_unit *du) { … } void vmw_du_cleanup(struct vmw_display_unit *du) { … } /* * Display Unit Cursor functions */ static int vmw_du_cursor_plane_unmap_cm(struct vmw_plane_state *vps); static void vmw_cursor_update_mob(struct vmw_private *dev_priv, struct vmw_plane_state *vps, u32 *image, u32 width, u32 height, u32 hotspotX, u32 hotspotY); struct vmw_svga_fifo_cmd_define_cursor { … }; /** * vmw_send_define_cursor_cmd - queue a define cursor command * @dev_priv: the private driver struct * @image: buffer which holds the cursor image * @width: width of the mouse cursor image * @height: height of the mouse cursor image * @hotspotX: the horizontal position of mouse hotspot * @hotspotY: the vertical position of mouse hotspot */ static void vmw_send_define_cursor_cmd(struct vmw_private *dev_priv, u32 *image, u32 width, u32 height, u32 hotspotX, u32 hotspotY) { … } /** * vmw_cursor_update_image - update the cursor image on the provided plane * @dev_priv: the private driver struct * @vps: the plane state of the cursor plane * @image: buffer which holds the cursor image * @width: width of the mouse cursor image * @height: height of the mouse cursor image * @hotspotX: the horizontal position of mouse hotspot * @hotspotY: the vertical position of mouse hotspot */ static void vmw_cursor_update_image(struct vmw_private *dev_priv, struct vmw_plane_state *vps, u32 *image, u32 width, u32 height, u32 hotspotX, u32 hotspotY) { … } /** * vmw_cursor_update_mob - Update cursor vis CursorMob mechanism * * Called from inside vmw_du_cursor_plane_atomic_update to actually * make the cursor-image live. * * @dev_priv: device to work with * @vps: the plane state of the cursor plane * @image: cursor source data to fill the MOB with * @width: source data width * @height: source data height * @hotspotX: cursor hotspot x * @hotspotY: cursor hotspot Y */ static void vmw_cursor_update_mob(struct vmw_private *dev_priv, struct vmw_plane_state *vps, u32 *image, u32 width, u32 height, u32 hotspotX, u32 hotspotY) { … } static u32 vmw_du_cursor_mob_size(u32 w, u32 h) { … } /** * vmw_du_cursor_plane_acquire_image -- Acquire the image data * @vps: cursor plane state */ static u32 *vmw_du_cursor_plane_acquire_image(struct vmw_plane_state *vps) { … } static bool vmw_du_cursor_plane_has_changed(struct vmw_plane_state *old_vps, struct vmw_plane_state *new_vps) { … } static void vmw_du_destroy_cursor_mob(struct vmw_bo **vbo) { … } static void vmw_du_put_cursor_mob(struct vmw_cursor_plane *vcp, struct vmw_plane_state *vps) { … } static int vmw_du_get_cursor_mob(struct vmw_cursor_plane *vcp, struct vmw_plane_state *vps) { … } static void vmw_cursor_update_position(struct vmw_private *dev_priv, bool show, int x, int y) { … } void vmw_kms_cursor_snoop(struct vmw_surface *srf, struct ttm_object_file *tfile, struct ttm_buffer_object *bo, SVGA3dCmdHeader *header) { … } /** * vmw_kms_legacy_hotspot_clear - Clear legacy hotspots * * @dev_priv: Pointer to the device private struct. * * Clears all legacy hotspots. */ void vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_priv) { … } void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) { … } void vmw_du_cursor_plane_destroy(struct drm_plane *plane) { … } void vmw_du_primary_plane_destroy(struct drm_plane *plane) { … } /** * vmw_du_plane_unpin_surf - unpins resource associated with a framebuffer surface * * @vps: plane state associated with the display surface */ void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps) { … } /** * vmw_du_plane_cleanup_fb - Unpins the plane surface * * @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) { … } /** * vmw_du_cursor_plane_map_cm - Maps the cursor mobs. * * @vps: plane_state * * Returns 0 on success */ static int vmw_du_cursor_plane_map_cm(struct vmw_plane_state *vps) { … } /** * vmw_du_cursor_plane_unmap_cm - Unmaps the cursor mobs. * * @vps: state of the cursor plane * * Returns 0 on success */ static int vmw_du_cursor_plane_unmap_cm(struct vmw_plane_state *vps) { … } /** * vmw_du_cursor_plane_cleanup_fb - Unpins the plane surface * * @plane: cursor plane * @old_state: contains the state to clean up * * Unmaps all cursor bo mappings and unpins the cursor surface * * Returns 0 on success */ void vmw_du_cursor_plane_cleanup_fb(struct drm_plane *plane, struct drm_plane_state *old_state) { … } /** * 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) { … } void vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) { … } /** * 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_atomic_state *state) { … } /** * 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_atomic_state *state) { … } int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) { … } void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_state *state) { … } /** * 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) { … } /** * 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) { … } /** * 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) { … } /** * 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) { … } /** * 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) { … } /** * 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) { … } /** * 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) { … } /** * 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) { … } /** * 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) { … } /* * Generic framebuffer code */ /* * Surface framebuffer code */ static void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer) { … } /** * vmw_kms_readback - Perform a readback from the screen system to * a buffer-object backed framebuffer. * * @dev_priv: Pointer to the device private structure. * @file_priv: Pointer to a struct drm_file identifying the caller. * Must be set to NULL if @user_fence_rep is NULL. * @vfb: Pointer to the buffer-object backed framebuffer. * @user_fence_rep: User-space provided structure for fence information. * Must be set to non-NULL if @file_priv is non-NULL. * @vclips: Array of clip rects. * @num_clips: Number of clip rects in @vclips. * * Returns 0 on success, negative error code on failure. -ERESTARTSYS if * interrupted. */ int vmw_kms_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_vmw_rect *vclips, uint32_t num_clips) { … } static int vmw_framebuffer_surface_create_handle(struct drm_framebuffer *fb, struct drm_file *file_priv, unsigned int *handle) { … } static const struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = …; static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, struct vmw_user_object *uo, struct vmw_framebuffer **out, const struct drm_mode_fb_cmd2 *mode_cmd) { … } /* * Buffer-object framebuffer code */ static int vmw_framebuffer_bo_create_handle(struct drm_framebuffer *fb, struct drm_file *file_priv, unsigned int *handle) { … } static void vmw_framebuffer_bo_destroy(struct drm_framebuffer *framebuffer) { … } static const struct drm_framebuffer_funcs vmw_framebuffer_bo_funcs = …; static int vmw_kms_new_framebuffer_bo(struct vmw_private *dev_priv, struct vmw_bo *bo, struct vmw_framebuffer **out, const struct drm_mode_fb_cmd2 *mode_cmd) { … } /** * vmw_kms_srf_ok - check if a surface can be created * * @dev_priv: Pointer to device private struct. * @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) { … } /** * vmw_kms_new_framebuffer - Create a new framebuffer. * * @dev_priv: Pointer to device private struct. * @uo: Pointer to user object to wrap the kms framebuffer around. * Either the buffer or surface inside the user object must be NULL. * @mode_cmd: Frame-buffer metadata. */ struct vmw_framebuffer * vmw_kms_new_framebuffer(struct vmw_private *dev_priv, struct vmw_user_object *uo, const struct drm_mode_fb_cmd2 *mode_cmd) { … } /* * Generic Kernel modesetting functions */ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) { … } /** * vmw_kms_check_display_memory - Validates display memory required for a * topology * @dev: DRM device * @num_rects: number of drm_rect in rects * @rects: array of drm_rect representing the topology to validate indexed by * crtc index. * * Returns: * 0 on success otherwise negative error code */ static int vmw_kms_check_display_memory(struct drm_device *dev, uint32_t num_rects, struct drm_rect *rects) { … } /** * vmw_crtc_state_and_lock - Return new or current crtc state with locked * crtc mutex * @state: The atomic state pointer containing the new atomic state * @crtc: The crtc * * This function returns the new crtc state if it's part of the state update. * Otherwise returns the current crtc state. It also makes sure that the * crtc mutex is locked. * * Returns: A valid crtc state pointer or NULL. It may also return a * pointer error, in particular -EDEADLK if locking needs to be rerun. */ static struct drm_crtc_state * vmw_crtc_state_and_lock(struct drm_atomic_state *state, struct drm_crtc *crtc) { … } /** * vmw_kms_check_implicit - Verify that all implicit display units scan out * from the same fb after the new state is committed. * @dev: The drm_device. * @state: The new state to be checked. * * Returns: * Zero on success, * -EINVAL on invalid state, * -EDEADLK if modeset locking needs to be rerun. */ static int vmw_kms_check_implicit(struct drm_device *dev, struct drm_atomic_state *state) { … } /** * vmw_kms_check_topology - Validates topology in drm_atomic_state * @dev: DRM device * @state: the driver state object * * Returns: * 0 on success otherwise negative error code */ static int vmw_kms_check_topology(struct drm_device *dev, struct drm_atomic_state *state) { … } /** * 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 */ static int vmw_kms_atomic_check_modeset(struct drm_device *dev, struct drm_atomic_state *state) { … } static const struct drm_mode_config_funcs vmw_kms_funcs = …; static int vmw_kms_generic_present(struct vmw_private *dev_priv, struct drm_file *file_priv, struct vmw_framebuffer *vfb, struct vmw_surface *surface, uint32_t sid, int32_t destX, int32_t destY, struct drm_vmw_rect *clips, uint32_t num_clips) { … } int vmw_kms_present(struct vmw_private *dev_priv, struct drm_file *file_priv, struct vmw_framebuffer *vfb, struct vmw_surface *surface, uint32_t sid, int32_t destX, int32_t destY, struct drm_vmw_rect *clips, uint32_t num_clips) { … } static void vmw_kms_create_hotplug_mode_update_property(struct vmw_private *dev_priv) { … } static void vmw_atomic_commit_tail(struct drm_atomic_state *old_state) { … } static const struct drm_mode_config_helper_funcs vmw_mode_config_helpers = …; int vmw_kms_init(struct vmw_private *dev_priv) { … } int vmw_kms_close(struct vmw_private *dev_priv) { … } int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { … } int vmw_kms_write_svga(struct vmw_private *vmw_priv, unsigned width, unsigned height, unsigned pitch, unsigned bpp, unsigned depth) { … } static bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, u64 pitch, u64 height) { … } /** * vmw_du_update_layout - Update the display unit with topology from resolution * plugin and generate DRM uevent * @dev_priv: device private * @num_rects: number of drm_rect in rects * @rects: toplogy to update */ static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned int num_rects, struct drm_rect *rects) { … } int vmw_du_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t size, struct drm_modeset_acquire_ctx *ctx) { … } int vmw_du_connector_dpms(struct drm_connector *connector, int mode) { … } enum drm_connector_status vmw_du_connector_detect(struct drm_connector *connector, bool force) { … } /** * vmw_guess_mode_timing - Provide fake timings for a * 60Hz vrefresh mode. * * @mode: Pointer to a struct drm_display_mode with hdisplay and vdisplay * members filled in. */ void vmw_guess_mode_timing(struct drm_display_mode *mode) { … } /** * vmw_kms_update_layout_ioctl - Handler for DRM_VMW_UPDATE_LAYOUT ioctl * @dev: drm device for the ioctl * @data: data pointer for the ioctl * @file_priv: drm file for the ioctl call * * Update preferred topology of display unit as per ioctl request. The topology * is expressed as array of drm_vmw_rect. * e.g. * [0 0 640 480] [640 0 800 600] [0 480 640 480] * * NOTE: * The x and y offset (upper left) in drm_vmw_rect cannot be less than 0. Beside * device limit on topology, x + w and y + h (lower right) cannot be greater * than INT_MAX. So topology beyond these limits will return with error. * * Returns: * Zero on success, negative errno on failure. */ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { … } /** * vmw_kms_helper_dirty - Helper to build commands and perform actions based * on a set of cliprects and a set of display units. * * @dev_priv: Pointer to a device private structure. * @framebuffer: Pointer to the framebuffer on which to perform the actions. * @clips: A set of struct drm_clip_rect. Either this os @vclips must be NULL. * Cliprects are given in framebuffer coordinates. * @vclips: A set of struct drm_vmw_rect cliprects. Either this or @clips must * be NULL. Cliprects are given in source coordinates. * @dest_x: X coordinate offset for the crtc / destination clip rects. * @dest_y: Y coordinate offset for the crtc / destination clip rects. * @num_clips: Number of cliprects in the @clips or @vclips array. * @increment: Integer with which to increment the clip counter when looping. * Used to skip a predetermined number of clip rects. * @dirty: Closure structure. See the description of struct vmw_kms_dirty. */ int vmw_kms_helper_dirty(struct vmw_private *dev_priv, struct vmw_framebuffer *framebuffer, const struct drm_clip_rect *clips, const struct drm_vmw_rect *vclips, s32 dest_x, s32 dest_y, int num_clips, int increment, struct vmw_kms_dirty *dirty) { … } /** * vmw_kms_helper_validation_finish - Helper for post KMS command submission * cleanup and fencing * @dev_priv: Pointer to the device-private struct * @file_priv: Pointer identifying the client when user-space fencing is used * @ctx: Pointer to the validation context * @out_fence: If non-NULL, returned refcounted fence-pointer * @user_fence_rep: If non-NULL, pointer to user-space address area * in which to copy user-space fence info */ void vmw_kms_helper_validation_finish(struct vmw_private *dev_priv, struct drm_file *file_priv, struct vmw_validation_context *ctx, struct vmw_fence_obj **out_fence, struct drm_vmw_fence_rep __user * user_fence_rep) { … } /** * vmw_kms_create_implicit_placement_property - Set up the implicit placement * property. * * @dev_priv: Pointer to a device private struct. * * Sets up the implicit placement property unless it's already set up. */ void vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv) { … } /** * vmw_kms_suspend - Save modesetting state and turn modesetting off. * * @dev: Pointer to the drm device * Return: 0 on success. Negative error code on failure. */ int vmw_kms_suspend(struct drm_device *dev) { … } /** * vmw_kms_resume - Re-enable modesetting and restore state * * @dev: Pointer to the drm device * Return: 0 on success. Negative error code on failure. * * State is resumed from a previous vmw_kms_suspend(). It's illegal * to call this function without a previous vmw_kms_suspend(). */ int vmw_kms_resume(struct drm_device *dev) { … } /** * vmw_kms_lost_device - Notify kms that modesetting capabilities will be lost * * @dev: Pointer to the drm device */ void vmw_kms_lost_device(struct drm_device *dev) { … } /** * vmw_du_helper_plane_update - Helper to do plane update on a display unit. * @update: The closure structure. * * Call this helper after setting callbacks in &vmw_du_update_plane to do plane * update on display unit. * * Return: 0 on success or a negative error code on failure. */ int vmw_du_helper_plane_update(struct vmw_du_update_plane *update) { … } /** * vmw_connector_mode_valid - implements drm_connector_helper_funcs.mode_valid callback * * @connector: the drm connector, part of a DU container * @mode: drm mode to check * * Returns MODE_OK on success, or a drm_mode_status error code. */ enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { … } /** * vmw_connector_get_modes - implements drm_connector_helper_funcs.get_modes callback * * @connector: the drm connector, part of a DU container * * Returns the number of added modes. */ int vmw_connector_get_modes(struct drm_connector *connector) { … } struct vmw_user_object *vmw_user_object_ref(struct vmw_user_object *uo) { … } void vmw_user_object_unref(struct vmw_user_object *uo) { … } struct vmw_bo * vmw_user_object_buffer(struct vmw_user_object *uo) { … } struct vmw_surface * vmw_user_object_surface(struct vmw_user_object *uo) { … } void *vmw_user_object_map(struct vmw_user_object *uo) { … } void *vmw_user_object_map_size(struct vmw_user_object *uo, size_t size) { … } void vmw_user_object_unmap(struct vmw_user_object *uo) { … } bool vmw_user_object_is_mapped(struct vmw_user_object *uo) { … } bool vmw_user_object_is_null(struct vmw_user_object *uo) { … }