162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT 262306a36Sopenharmony_ci/************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 762306a36Sopenharmony_ci * copy of this software and associated documentation files (the 862306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 962306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 1062306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 1162306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 1262306a36Sopenharmony_ci * the following conditions: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the 1562306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 1662306a36Sopenharmony_ci * of the Software. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1962306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2062306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 2162306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 2262306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2362306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 2462306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci **************************************************************************/ 2762306a36Sopenharmony_ci#include "vmwgfx_bo.h" 2862306a36Sopenharmony_ci#include "vmwgfx_drv.h" 2962306a36Sopenharmony_ci#include "vmwgfx_devcaps.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <drm/ttm/ttm_placement.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <linux/sched/signal.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cibool vmw_supports_3d(struct vmw_private *dev_priv) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci uint32_t fifo_min, hwversion; 3862306a36Sopenharmony_ci const struct vmw_fifo_state *fifo = dev_priv->fifo; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (!(dev_priv->capabilities & SVGA_CAP_3D)) 4162306a36Sopenharmony_ci return false; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) { 4462306a36Sopenharmony_ci uint32_t result; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (!dev_priv->has_mob) 4762306a36Sopenharmony_ci return false; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci result = vmw_devcap_get(dev_priv, SVGA3D_DEVCAP_3D); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci return (result != 0); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (!(dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO)) 5562306a36Sopenharmony_ci return false; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci BUG_ON(vmw_is_svga_v3(dev_priv)); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci fifo_min = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MIN); 6062306a36Sopenharmony_ci if (fifo_min <= SVGA_FIFO_3D_HWVERSION * sizeof(unsigned int)) 6162306a36Sopenharmony_ci return false; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci hwversion = vmw_fifo_mem_read(dev_priv, 6462306a36Sopenharmony_ci ((fifo->capabilities & 6562306a36Sopenharmony_ci SVGA_FIFO_CAP_3D_HWVERSION_REVISED) ? 6662306a36Sopenharmony_ci SVGA_FIFO_3D_HWVERSION_REVISED : 6762306a36Sopenharmony_ci SVGA_FIFO_3D_HWVERSION)); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (hwversion == 0) 7062306a36Sopenharmony_ci return false; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (hwversion < SVGA3D_HWVERSION_WS8_B1) 7362306a36Sopenharmony_ci return false; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* Legacy Display Unit does not support surfaces */ 7662306a36Sopenharmony_ci if (dev_priv->active_display_unit == vmw_du_legacy) 7762306a36Sopenharmony_ci return false; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return true; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cibool vmw_fifo_have_pitchlock(struct vmw_private *dev_priv) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci uint32_t caps; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (!(dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO)) 8762306a36Sopenharmony_ci return false; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci caps = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_CAPABILITIES); 9062306a36Sopenharmony_ci if (caps & SVGA_FIFO_CAP_PITCHLOCK) 9162306a36Sopenharmony_ci return true; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return false; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistruct vmw_fifo_state *vmw_fifo_create(struct vmw_private *dev_priv) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct vmw_fifo_state *fifo; 9962306a36Sopenharmony_ci uint32_t max; 10062306a36Sopenharmony_ci uint32_t min; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (!dev_priv->fifo_mem) 10362306a36Sopenharmony_ci return NULL; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci fifo = kzalloc(sizeof(*fifo), GFP_KERNEL); 10662306a36Sopenharmony_ci if (!fifo) 10762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 10862306a36Sopenharmony_ci fifo->static_buffer_size = VMWGFX_FIFO_STATIC_SIZE; 10962306a36Sopenharmony_ci fifo->static_buffer = vmalloc(fifo->static_buffer_size); 11062306a36Sopenharmony_ci if (unlikely(fifo->static_buffer == NULL)) { 11162306a36Sopenharmony_ci kfree(fifo); 11262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci fifo->dynamic_buffer = NULL; 11662306a36Sopenharmony_ci fifo->reserved_size = 0; 11762306a36Sopenharmony_ci fifo->using_bounce_buffer = false; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci mutex_init(&fifo->fifo_mutex); 12062306a36Sopenharmony_ci init_rwsem(&fifo->rwsem); 12162306a36Sopenharmony_ci min = 4; 12262306a36Sopenharmony_ci if (dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO) 12362306a36Sopenharmony_ci min = vmw_read(dev_priv, SVGA_REG_MEM_REGS); 12462306a36Sopenharmony_ci min <<= 2; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (min < PAGE_SIZE) 12762306a36Sopenharmony_ci min = PAGE_SIZE; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci vmw_fifo_mem_write(dev_priv, SVGA_FIFO_MIN, min); 13062306a36Sopenharmony_ci vmw_fifo_mem_write(dev_priv, SVGA_FIFO_MAX, dev_priv->fifo_mem_size); 13162306a36Sopenharmony_ci wmb(); 13262306a36Sopenharmony_ci vmw_fifo_mem_write(dev_priv, SVGA_FIFO_NEXT_CMD, min); 13362306a36Sopenharmony_ci vmw_fifo_mem_write(dev_priv, SVGA_FIFO_STOP, min); 13462306a36Sopenharmony_ci vmw_fifo_mem_write(dev_priv, SVGA_FIFO_BUSY, 0); 13562306a36Sopenharmony_ci mb(); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_CONFIG_DONE, 1); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci max = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MAX); 14062306a36Sopenharmony_ci min = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MIN); 14162306a36Sopenharmony_ci fifo->capabilities = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_CAPABILITIES); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci drm_info(&dev_priv->drm, 14462306a36Sopenharmony_ci "Fifo max 0x%08x min 0x%08x cap 0x%08x\n", 14562306a36Sopenharmony_ci (unsigned int) max, 14662306a36Sopenharmony_ci (unsigned int) min, 14762306a36Sopenharmony_ci (unsigned int) fifo->capabilities); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (unlikely(min >= max)) { 15062306a36Sopenharmony_ci drm_warn(&dev_priv->drm, 15162306a36Sopenharmony_ci "FIFO memory is not usable. Driver failed to initialize."); 15262306a36Sopenharmony_ci return ERR_PTR(-ENXIO); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return fifo; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_civoid vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci u32 *fifo_mem = dev_priv->fifo_mem; 16162306a36Sopenharmony_ci if (fifo_mem && cmpxchg(fifo_mem + SVGA_FIFO_BUSY, 0, 1) == 0) 16262306a36Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_SYNC, reason); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_civoid vmw_fifo_destroy(struct vmw_private *dev_priv) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct vmw_fifo_state *fifo = dev_priv->fifo; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (!fifo) 17162306a36Sopenharmony_ci return; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (likely(fifo->static_buffer != NULL)) { 17462306a36Sopenharmony_ci vfree(fifo->static_buffer); 17562306a36Sopenharmony_ci fifo->static_buffer = NULL; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (likely(fifo->dynamic_buffer != NULL)) { 17962306a36Sopenharmony_ci vfree(fifo->dynamic_buffer); 18062306a36Sopenharmony_ci fifo->dynamic_buffer = NULL; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci kfree(fifo); 18362306a36Sopenharmony_ci dev_priv->fifo = NULL; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic bool vmw_fifo_is_full(struct vmw_private *dev_priv, uint32_t bytes) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci uint32_t max = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MAX); 18962306a36Sopenharmony_ci uint32_t next_cmd = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_NEXT_CMD); 19062306a36Sopenharmony_ci uint32_t min = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MIN); 19162306a36Sopenharmony_ci uint32_t stop = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_STOP); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return ((max - next_cmd) + (stop - min) <= bytes); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int vmw_fifo_wait_noirq(struct vmw_private *dev_priv, 19762306a36Sopenharmony_ci uint32_t bytes, bool interruptible, 19862306a36Sopenharmony_ci unsigned long timeout) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci int ret = 0; 20162306a36Sopenharmony_ci unsigned long end_jiffies = jiffies + timeout; 20262306a36Sopenharmony_ci DEFINE_WAIT(__wait); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci DRM_INFO("Fifo wait noirq.\n"); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci for (;;) { 20762306a36Sopenharmony_ci prepare_to_wait(&dev_priv->fifo_queue, &__wait, 20862306a36Sopenharmony_ci (interruptible) ? 20962306a36Sopenharmony_ci TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); 21062306a36Sopenharmony_ci if (!vmw_fifo_is_full(dev_priv, bytes)) 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci if (time_after_eq(jiffies, end_jiffies)) { 21362306a36Sopenharmony_ci ret = -EBUSY; 21462306a36Sopenharmony_ci DRM_ERROR("SVGA device lockup.\n"); 21562306a36Sopenharmony_ci break; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci schedule_timeout(1); 21862306a36Sopenharmony_ci if (interruptible && signal_pending(current)) { 21962306a36Sopenharmony_ci ret = -ERESTARTSYS; 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci finish_wait(&dev_priv->fifo_queue, &__wait); 22462306a36Sopenharmony_ci wake_up_all(&dev_priv->fifo_queue); 22562306a36Sopenharmony_ci DRM_INFO("Fifo noirq exit.\n"); 22662306a36Sopenharmony_ci return ret; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int vmw_fifo_wait(struct vmw_private *dev_priv, 23062306a36Sopenharmony_ci uint32_t bytes, bool interruptible, 23162306a36Sopenharmony_ci unsigned long timeout) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci long ret = 1L; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (likely(!vmw_fifo_is_full(dev_priv, bytes))) 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci vmw_fifo_ping_host(dev_priv, SVGA_SYNC_FIFOFULL); 23962306a36Sopenharmony_ci if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) 24062306a36Sopenharmony_ci return vmw_fifo_wait_noirq(dev_priv, bytes, 24162306a36Sopenharmony_ci interruptible, timeout); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_FIFO_PROGRESS, 24462306a36Sopenharmony_ci &dev_priv->fifo_queue_waiters); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (interruptible) 24762306a36Sopenharmony_ci ret = wait_event_interruptible_timeout 24862306a36Sopenharmony_ci (dev_priv->fifo_queue, 24962306a36Sopenharmony_ci !vmw_fifo_is_full(dev_priv, bytes), timeout); 25062306a36Sopenharmony_ci else 25162306a36Sopenharmony_ci ret = wait_event_timeout 25262306a36Sopenharmony_ci (dev_priv->fifo_queue, 25362306a36Sopenharmony_ci !vmw_fifo_is_full(dev_priv, bytes), timeout); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (unlikely(ret == 0)) 25662306a36Sopenharmony_ci ret = -EBUSY; 25762306a36Sopenharmony_ci else if (likely(ret > 0)) 25862306a36Sopenharmony_ci ret = 0; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci vmw_generic_waiter_remove(dev_priv, SVGA_IRQFLAG_FIFO_PROGRESS, 26162306a36Sopenharmony_ci &dev_priv->fifo_queue_waiters); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return ret; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* 26762306a36Sopenharmony_ci * Reserve @bytes number of bytes in the fifo. 26862306a36Sopenharmony_ci * 26962306a36Sopenharmony_ci * This function will return NULL (error) on two conditions: 27062306a36Sopenharmony_ci * If it timeouts waiting for fifo space, or if @bytes is larger than the 27162306a36Sopenharmony_ci * available fifo space. 27262306a36Sopenharmony_ci * 27362306a36Sopenharmony_ci * Returns: 27462306a36Sopenharmony_ci * Pointer to the fifo, or null on error (possible hardware hang). 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_cistatic void *vmw_local_fifo_reserve(struct vmw_private *dev_priv, 27762306a36Sopenharmony_ci uint32_t bytes) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct vmw_fifo_state *fifo_state = dev_priv->fifo; 28062306a36Sopenharmony_ci u32 *fifo_mem = dev_priv->fifo_mem; 28162306a36Sopenharmony_ci uint32_t max; 28262306a36Sopenharmony_ci uint32_t min; 28362306a36Sopenharmony_ci uint32_t next_cmd; 28462306a36Sopenharmony_ci uint32_t reserveable = fifo_state->capabilities & SVGA_FIFO_CAP_RESERVE; 28562306a36Sopenharmony_ci int ret; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci mutex_lock(&fifo_state->fifo_mutex); 28862306a36Sopenharmony_ci max = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MAX); 28962306a36Sopenharmony_ci min = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MIN); 29062306a36Sopenharmony_ci next_cmd = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_NEXT_CMD); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (unlikely(bytes >= (max - min))) 29362306a36Sopenharmony_ci goto out_err; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci BUG_ON(fifo_state->reserved_size != 0); 29662306a36Sopenharmony_ci BUG_ON(fifo_state->dynamic_buffer != NULL); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci fifo_state->reserved_size = bytes; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci while (1) { 30162306a36Sopenharmony_ci uint32_t stop = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_STOP); 30262306a36Sopenharmony_ci bool need_bounce = false; 30362306a36Sopenharmony_ci bool reserve_in_place = false; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (next_cmd >= stop) { 30662306a36Sopenharmony_ci if (likely((next_cmd + bytes < max || 30762306a36Sopenharmony_ci (next_cmd + bytes == max && stop > min)))) 30862306a36Sopenharmony_ci reserve_in_place = true; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci else if (vmw_fifo_is_full(dev_priv, bytes)) { 31162306a36Sopenharmony_ci ret = vmw_fifo_wait(dev_priv, bytes, 31262306a36Sopenharmony_ci false, 3 * HZ); 31362306a36Sopenharmony_ci if (unlikely(ret != 0)) 31462306a36Sopenharmony_ci goto out_err; 31562306a36Sopenharmony_ci } else 31662306a36Sopenharmony_ci need_bounce = true; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci } else { 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (likely((next_cmd + bytes < stop))) 32162306a36Sopenharmony_ci reserve_in_place = true; 32262306a36Sopenharmony_ci else { 32362306a36Sopenharmony_ci ret = vmw_fifo_wait(dev_priv, bytes, 32462306a36Sopenharmony_ci false, 3 * HZ); 32562306a36Sopenharmony_ci if (unlikely(ret != 0)) 32662306a36Sopenharmony_ci goto out_err; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (reserve_in_place) { 33162306a36Sopenharmony_ci if (reserveable || bytes <= sizeof(uint32_t)) { 33262306a36Sopenharmony_ci fifo_state->using_bounce_buffer = false; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (reserveable) 33562306a36Sopenharmony_ci vmw_fifo_mem_write(dev_priv, 33662306a36Sopenharmony_ci SVGA_FIFO_RESERVED, 33762306a36Sopenharmony_ci bytes); 33862306a36Sopenharmony_ci return (void __force *) (fifo_mem + 33962306a36Sopenharmony_ci (next_cmd >> 2)); 34062306a36Sopenharmony_ci } else { 34162306a36Sopenharmony_ci need_bounce = true; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (need_bounce) { 34662306a36Sopenharmony_ci fifo_state->using_bounce_buffer = true; 34762306a36Sopenharmony_ci if (bytes < fifo_state->static_buffer_size) 34862306a36Sopenharmony_ci return fifo_state->static_buffer; 34962306a36Sopenharmony_ci else { 35062306a36Sopenharmony_ci fifo_state->dynamic_buffer = vmalloc(bytes); 35162306a36Sopenharmony_ci if (!fifo_state->dynamic_buffer) 35262306a36Sopenharmony_ci goto out_err; 35362306a36Sopenharmony_ci return fifo_state->dynamic_buffer; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ciout_err: 35862306a36Sopenharmony_ci fifo_state->reserved_size = 0; 35962306a36Sopenharmony_ci mutex_unlock(&fifo_state->fifo_mutex); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return NULL; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_civoid *vmw_cmd_ctx_reserve(struct vmw_private *dev_priv, uint32_t bytes, 36562306a36Sopenharmony_ci int ctx_id) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci void *ret; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (dev_priv->cman) 37062306a36Sopenharmony_ci ret = vmw_cmdbuf_reserve(dev_priv->cman, bytes, 37162306a36Sopenharmony_ci ctx_id, false, NULL); 37262306a36Sopenharmony_ci else if (ctx_id == SVGA3D_INVALID_ID) 37362306a36Sopenharmony_ci ret = vmw_local_fifo_reserve(dev_priv, bytes); 37462306a36Sopenharmony_ci else { 37562306a36Sopenharmony_ci WARN(1, "Command buffer has not been allocated.\n"); 37662306a36Sopenharmony_ci ret = NULL; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci if (IS_ERR_OR_NULL(ret)) 37962306a36Sopenharmony_ci return NULL; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic void vmw_fifo_res_copy(struct vmw_fifo_state *fifo_state, 38562306a36Sopenharmony_ci struct vmw_private *vmw, 38662306a36Sopenharmony_ci uint32_t next_cmd, 38762306a36Sopenharmony_ci uint32_t max, uint32_t min, uint32_t bytes) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci u32 *fifo_mem = vmw->fifo_mem; 39062306a36Sopenharmony_ci uint32_t chunk_size = max - next_cmd; 39162306a36Sopenharmony_ci uint32_t rest; 39262306a36Sopenharmony_ci uint32_t *buffer = (fifo_state->dynamic_buffer != NULL) ? 39362306a36Sopenharmony_ci fifo_state->dynamic_buffer : fifo_state->static_buffer; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (bytes < chunk_size) 39662306a36Sopenharmony_ci chunk_size = bytes; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci vmw_fifo_mem_write(vmw, SVGA_FIFO_RESERVED, bytes); 39962306a36Sopenharmony_ci mb(); 40062306a36Sopenharmony_ci memcpy(fifo_mem + (next_cmd >> 2), buffer, chunk_size); 40162306a36Sopenharmony_ci rest = bytes - chunk_size; 40262306a36Sopenharmony_ci if (rest) 40362306a36Sopenharmony_ci memcpy(fifo_mem + (min >> 2), buffer + (chunk_size >> 2), rest); 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic void vmw_fifo_slow_copy(struct vmw_fifo_state *fifo_state, 40762306a36Sopenharmony_ci struct vmw_private *vmw, 40862306a36Sopenharmony_ci uint32_t next_cmd, 40962306a36Sopenharmony_ci uint32_t max, uint32_t min, uint32_t bytes) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci uint32_t *buffer = (fifo_state->dynamic_buffer != NULL) ? 41262306a36Sopenharmony_ci fifo_state->dynamic_buffer : fifo_state->static_buffer; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci while (bytes > 0) { 41562306a36Sopenharmony_ci vmw_fifo_mem_write(vmw, (next_cmd >> 2), *buffer++); 41662306a36Sopenharmony_ci next_cmd += sizeof(uint32_t); 41762306a36Sopenharmony_ci if (unlikely(next_cmd == max)) 41862306a36Sopenharmony_ci next_cmd = min; 41962306a36Sopenharmony_ci mb(); 42062306a36Sopenharmony_ci vmw_fifo_mem_write(vmw, SVGA_FIFO_NEXT_CMD, next_cmd); 42162306a36Sopenharmony_ci mb(); 42262306a36Sopenharmony_ci bytes -= sizeof(uint32_t); 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic void vmw_local_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct vmw_fifo_state *fifo_state = dev_priv->fifo; 42962306a36Sopenharmony_ci uint32_t next_cmd = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_NEXT_CMD); 43062306a36Sopenharmony_ci uint32_t max = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MAX); 43162306a36Sopenharmony_ci uint32_t min = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MIN); 43262306a36Sopenharmony_ci bool reserveable = fifo_state->capabilities & SVGA_FIFO_CAP_RESERVE; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci BUG_ON((bytes & 3) != 0); 43562306a36Sopenharmony_ci BUG_ON(bytes > fifo_state->reserved_size); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci fifo_state->reserved_size = 0; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (fifo_state->using_bounce_buffer) { 44062306a36Sopenharmony_ci if (reserveable) 44162306a36Sopenharmony_ci vmw_fifo_res_copy(fifo_state, dev_priv, 44262306a36Sopenharmony_ci next_cmd, max, min, bytes); 44362306a36Sopenharmony_ci else 44462306a36Sopenharmony_ci vmw_fifo_slow_copy(fifo_state, dev_priv, 44562306a36Sopenharmony_ci next_cmd, max, min, bytes); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (fifo_state->dynamic_buffer) { 44862306a36Sopenharmony_ci vfree(fifo_state->dynamic_buffer); 44962306a36Sopenharmony_ci fifo_state->dynamic_buffer = NULL; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci down_write(&fifo_state->rwsem); 45562306a36Sopenharmony_ci if (fifo_state->using_bounce_buffer || reserveable) { 45662306a36Sopenharmony_ci next_cmd += bytes; 45762306a36Sopenharmony_ci if (next_cmd >= max) 45862306a36Sopenharmony_ci next_cmd -= max - min; 45962306a36Sopenharmony_ci mb(); 46062306a36Sopenharmony_ci vmw_fifo_mem_write(dev_priv, SVGA_FIFO_NEXT_CMD, next_cmd); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (reserveable) 46462306a36Sopenharmony_ci vmw_fifo_mem_write(dev_priv, SVGA_FIFO_RESERVED, 0); 46562306a36Sopenharmony_ci mb(); 46662306a36Sopenharmony_ci up_write(&fifo_state->rwsem); 46762306a36Sopenharmony_ci vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); 46862306a36Sopenharmony_ci mutex_unlock(&fifo_state->fifo_mutex); 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_civoid vmw_cmd_commit(struct vmw_private *dev_priv, uint32_t bytes) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci if (dev_priv->cman) 47462306a36Sopenharmony_ci vmw_cmdbuf_commit(dev_priv->cman, bytes, NULL, false); 47562306a36Sopenharmony_ci else 47662306a36Sopenharmony_ci vmw_local_fifo_commit(dev_priv, bytes); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci/** 48162306a36Sopenharmony_ci * vmw_cmd_commit_flush - Commit fifo space and flush any buffered commands. 48262306a36Sopenharmony_ci * 48362306a36Sopenharmony_ci * @dev_priv: Pointer to device private structure. 48462306a36Sopenharmony_ci * @bytes: Number of bytes to commit. 48562306a36Sopenharmony_ci */ 48662306a36Sopenharmony_civoid vmw_cmd_commit_flush(struct vmw_private *dev_priv, uint32_t bytes) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci if (dev_priv->cman) 48962306a36Sopenharmony_ci vmw_cmdbuf_commit(dev_priv->cman, bytes, NULL, true); 49062306a36Sopenharmony_ci else 49162306a36Sopenharmony_ci vmw_local_fifo_commit(dev_priv, bytes); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/** 49562306a36Sopenharmony_ci * vmw_cmd_flush - Flush any buffered commands and make sure command processing 49662306a36Sopenharmony_ci * starts. 49762306a36Sopenharmony_ci * 49862306a36Sopenharmony_ci * @dev_priv: Pointer to device private structure. 49962306a36Sopenharmony_ci * @interruptible: Whether to wait interruptible if function needs to sleep. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_ciint vmw_cmd_flush(struct vmw_private *dev_priv, bool interruptible) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci might_sleep(); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (dev_priv->cman) 50662306a36Sopenharmony_ci return vmw_cmdbuf_cur_flush(dev_priv->cman, interruptible); 50762306a36Sopenharmony_ci else 50862306a36Sopenharmony_ci return 0; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ciint vmw_cmd_send_fence(struct vmw_private *dev_priv, uint32_t *seqno) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct svga_fifo_cmd_fence *cmd_fence; 51462306a36Sopenharmony_ci u32 *fm; 51562306a36Sopenharmony_ci int ret = 0; 51662306a36Sopenharmony_ci uint32_t bytes = sizeof(u32) + sizeof(*cmd_fence); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci fm = VMW_CMD_RESERVE(dev_priv, bytes); 51962306a36Sopenharmony_ci if (unlikely(fm == NULL)) { 52062306a36Sopenharmony_ci *seqno = atomic_read(&dev_priv->marker_seq); 52162306a36Sopenharmony_ci ret = -ENOMEM; 52262306a36Sopenharmony_ci (void)vmw_fallback_wait(dev_priv, false, true, *seqno, 52362306a36Sopenharmony_ci false, 3*HZ); 52462306a36Sopenharmony_ci goto out_err; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci do { 52862306a36Sopenharmony_ci *seqno = atomic_add_return(1, &dev_priv->marker_seq); 52962306a36Sopenharmony_ci } while (*seqno == 0); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (!vmw_has_fences(dev_priv)) { 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* 53462306a36Sopenharmony_ci * Don't request hardware to send a fence. The 53562306a36Sopenharmony_ci * waiting code in vmwgfx_irq.c will emulate this. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci vmw_cmd_commit(dev_priv, 0); 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci *fm++ = SVGA_CMD_FENCE; 54362306a36Sopenharmony_ci cmd_fence = (struct svga_fifo_cmd_fence *) fm; 54462306a36Sopenharmony_ci cmd_fence->fence = *seqno; 54562306a36Sopenharmony_ci vmw_cmd_commit_flush(dev_priv, bytes); 54662306a36Sopenharmony_ci vmw_update_seqno(dev_priv); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ciout_err: 54962306a36Sopenharmony_ci return ret; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci/** 55362306a36Sopenharmony_ci * vmw_cmd_emit_dummy_legacy_query - emits a dummy query to the fifo using 55462306a36Sopenharmony_ci * legacy query commands. 55562306a36Sopenharmony_ci * 55662306a36Sopenharmony_ci * @dev_priv: The device private structure. 55762306a36Sopenharmony_ci * @cid: The hardware context id used for the query. 55862306a36Sopenharmony_ci * 55962306a36Sopenharmony_ci * See the vmw_cmd_emit_dummy_query documentation. 56062306a36Sopenharmony_ci */ 56162306a36Sopenharmony_cistatic int vmw_cmd_emit_dummy_legacy_query(struct vmw_private *dev_priv, 56262306a36Sopenharmony_ci uint32_t cid) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci /* 56562306a36Sopenharmony_ci * A query wait without a preceding query end will 56662306a36Sopenharmony_ci * actually finish all queries for this cid 56762306a36Sopenharmony_ci * without writing to the query result structure. 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci struct ttm_buffer_object *bo = &dev_priv->dummy_query_bo->tbo; 57162306a36Sopenharmony_ci struct { 57262306a36Sopenharmony_ci SVGA3dCmdHeader header; 57362306a36Sopenharmony_ci SVGA3dCmdWaitForQuery body; 57462306a36Sopenharmony_ci } *cmd; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); 57762306a36Sopenharmony_ci if (unlikely(cmd == NULL)) 57862306a36Sopenharmony_ci return -ENOMEM; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci cmd->header.id = SVGA_3D_CMD_WAIT_FOR_QUERY; 58162306a36Sopenharmony_ci cmd->header.size = sizeof(cmd->body); 58262306a36Sopenharmony_ci cmd->body.cid = cid; 58362306a36Sopenharmony_ci cmd->body.type = SVGA3D_QUERYTYPE_OCCLUSION; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (bo->resource->mem_type == TTM_PL_VRAM) { 58662306a36Sopenharmony_ci cmd->body.guestResult.gmrId = SVGA_GMR_FRAMEBUFFER; 58762306a36Sopenharmony_ci cmd->body.guestResult.offset = bo->resource->start << PAGE_SHIFT; 58862306a36Sopenharmony_ci } else { 58962306a36Sopenharmony_ci cmd->body.guestResult.gmrId = bo->resource->start; 59062306a36Sopenharmony_ci cmd->body.guestResult.offset = 0; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci vmw_cmd_commit(dev_priv, sizeof(*cmd)); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci/** 59962306a36Sopenharmony_ci * vmw_cmd_emit_dummy_gb_query - emits a dummy query to the fifo using 60062306a36Sopenharmony_ci * guest-backed resource query commands. 60162306a36Sopenharmony_ci * 60262306a36Sopenharmony_ci * @dev_priv: The device private structure. 60362306a36Sopenharmony_ci * @cid: The hardware context id used for the query. 60462306a36Sopenharmony_ci * 60562306a36Sopenharmony_ci * See the vmw_cmd_emit_dummy_query documentation. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_cistatic int vmw_cmd_emit_dummy_gb_query(struct vmw_private *dev_priv, 60862306a36Sopenharmony_ci uint32_t cid) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci /* 61162306a36Sopenharmony_ci * A query wait without a preceding query end will 61262306a36Sopenharmony_ci * actually finish all queries for this cid 61362306a36Sopenharmony_ci * without writing to the query result structure. 61462306a36Sopenharmony_ci */ 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci struct ttm_buffer_object *bo = &dev_priv->dummy_query_bo->tbo; 61762306a36Sopenharmony_ci struct { 61862306a36Sopenharmony_ci SVGA3dCmdHeader header; 61962306a36Sopenharmony_ci SVGA3dCmdWaitForGBQuery body; 62062306a36Sopenharmony_ci } *cmd; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); 62362306a36Sopenharmony_ci if (unlikely(cmd == NULL)) 62462306a36Sopenharmony_ci return -ENOMEM; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci cmd->header.id = SVGA_3D_CMD_WAIT_FOR_GB_QUERY; 62762306a36Sopenharmony_ci cmd->header.size = sizeof(cmd->body); 62862306a36Sopenharmony_ci cmd->body.cid = cid; 62962306a36Sopenharmony_ci cmd->body.type = SVGA3D_QUERYTYPE_OCCLUSION; 63062306a36Sopenharmony_ci BUG_ON(bo->resource->mem_type != VMW_PL_MOB); 63162306a36Sopenharmony_ci cmd->body.mobid = bo->resource->start; 63262306a36Sopenharmony_ci cmd->body.offset = 0; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci vmw_cmd_commit(dev_priv, sizeof(*cmd)); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci return 0; 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci/** 64162306a36Sopenharmony_ci * vmw_cmd_emit_dummy_query - emits a dummy query to the fifo using 64262306a36Sopenharmony_ci * appropriate resource query commands. 64362306a36Sopenharmony_ci * 64462306a36Sopenharmony_ci * @dev_priv: The device private structure. 64562306a36Sopenharmony_ci * @cid: The hardware context id used for the query. 64662306a36Sopenharmony_ci * 64762306a36Sopenharmony_ci * This function is used to emit a dummy occlusion query with 64862306a36Sopenharmony_ci * no primitives rendered between query begin and query end. 64962306a36Sopenharmony_ci * It's used to provide a query barrier, in order to know that when 65062306a36Sopenharmony_ci * this query is finished, all preceding queries are also finished. 65162306a36Sopenharmony_ci * 65262306a36Sopenharmony_ci * A Query results structure should have been initialized at the start 65362306a36Sopenharmony_ci * of the dev_priv->dummy_query_bo buffer object. And that buffer object 65462306a36Sopenharmony_ci * must also be either reserved or pinned when this function is called. 65562306a36Sopenharmony_ci * 65662306a36Sopenharmony_ci * Returns -ENOMEM on failure to reserve fifo space. 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ciint vmw_cmd_emit_dummy_query(struct vmw_private *dev_priv, 65962306a36Sopenharmony_ci uint32_t cid) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci if (dev_priv->has_mob) 66262306a36Sopenharmony_ci return vmw_cmd_emit_dummy_gb_query(dev_priv, cid); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci return vmw_cmd_emit_dummy_legacy_query(dev_priv, cid); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci/** 66962306a36Sopenharmony_ci * vmw_cmd_supported - returns true if the given device supports 67062306a36Sopenharmony_ci * command queues. 67162306a36Sopenharmony_ci * 67262306a36Sopenharmony_ci * @vmw: The device private structure. 67362306a36Sopenharmony_ci * 67462306a36Sopenharmony_ci * Returns true if we can issue commands. 67562306a36Sopenharmony_ci */ 67662306a36Sopenharmony_cibool vmw_cmd_supported(struct vmw_private *vmw) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci bool has_cmdbufs = 67962306a36Sopenharmony_ci (vmw->capabilities & (SVGA_CAP_COMMAND_BUFFERS | 68062306a36Sopenharmony_ci SVGA_CAP_CMD_BUFFERS_2)) != 0; 68162306a36Sopenharmony_ci if (vmw_is_svga_v3(vmw)) 68262306a36Sopenharmony_ci return (has_cmdbufs && 68362306a36Sopenharmony_ci (vmw->capabilities & SVGA_CAP_GBOBJECTS) != 0); 68462306a36Sopenharmony_ci /* 68562306a36Sopenharmony_ci * We have FIFO cmd's 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_ci return has_cmdbufs || vmw->fifo_mem != NULL; 68862306a36Sopenharmony_ci} 689