18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
28c2ecf20Sopenharmony_ci/**************************************************************************
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright 2015 VMware, Inc., Palo Alto, CA., USA
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the
88c2ecf20Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
98c2ecf20Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
108c2ecf20Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
118c2ecf20Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
128c2ecf20Sopenharmony_ci * the following conditions:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the
158c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
168c2ecf20Sopenharmony_ci * of the Software.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
198c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
208c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
218c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
228c2ecf20Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
238c2ecf20Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
248c2ecf20Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci **************************************************************************/
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <linux/dmapool.h>
298c2ecf20Sopenharmony_ci#include <linux/pci.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <drm/ttm/ttm_bo_api.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include "vmwgfx_drv.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * Size of inline command buffers. Try to make sure that a page size is a
378c2ecf20Sopenharmony_ci * multiple of the DMA pool allocation size.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_ci#define VMW_CMDBUF_INLINE_ALIGN 64
408c2ecf20Sopenharmony_ci#define VMW_CMDBUF_INLINE_SIZE \
418c2ecf20Sopenharmony_ci	(1024 - ALIGN(sizeof(SVGACBHeader), VMW_CMDBUF_INLINE_ALIGN))
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/**
448c2ecf20Sopenharmony_ci * struct vmw_cmdbuf_context - Command buffer context queues
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci * @submitted: List of command buffers that have been submitted to the
478c2ecf20Sopenharmony_ci * manager but not yet submitted to hardware.
488c2ecf20Sopenharmony_ci * @hw_submitted: List of command buffers submitted to hardware.
498c2ecf20Sopenharmony_ci * @preempted: List of preempted command buffers.
508c2ecf20Sopenharmony_ci * @num_hw_submitted: Number of buffers currently being processed by hardware
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_cistruct vmw_cmdbuf_context {
538c2ecf20Sopenharmony_ci	struct list_head submitted;
548c2ecf20Sopenharmony_ci	struct list_head hw_submitted;
558c2ecf20Sopenharmony_ci	struct list_head preempted;
568c2ecf20Sopenharmony_ci	unsigned num_hw_submitted;
578c2ecf20Sopenharmony_ci	bool block_submission;
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/**
618c2ecf20Sopenharmony_ci * struct vmw_cmdbuf_man: - Command buffer manager
628c2ecf20Sopenharmony_ci *
638c2ecf20Sopenharmony_ci * @cur_mutex: Mutex protecting the command buffer used for incremental small
648c2ecf20Sopenharmony_ci * kernel command submissions, @cur.
658c2ecf20Sopenharmony_ci * @space_mutex: Mutex to protect against starvation when we allocate
668c2ecf20Sopenharmony_ci * main pool buffer space.
678c2ecf20Sopenharmony_ci * @error_mutex: Mutex to serialize the work queue error handling.
688c2ecf20Sopenharmony_ci * Note this is not needed if the same workqueue handler
698c2ecf20Sopenharmony_ci * can't race with itself...
708c2ecf20Sopenharmony_ci * @work: A struct work_struct implementeing command buffer error handling.
718c2ecf20Sopenharmony_ci * Immutable.
728c2ecf20Sopenharmony_ci * @dev_priv: Pointer to the device private struct. Immutable.
738c2ecf20Sopenharmony_ci * @ctx: Array of command buffer context queues. The queues and the context
748c2ecf20Sopenharmony_ci * data is protected by @lock.
758c2ecf20Sopenharmony_ci * @error: List of command buffers that have caused device errors.
768c2ecf20Sopenharmony_ci * Protected by @lock.
778c2ecf20Sopenharmony_ci * @mm: Range manager for the command buffer space. Manager allocations and
788c2ecf20Sopenharmony_ci * frees are protected by @lock.
798c2ecf20Sopenharmony_ci * @cmd_space: Buffer object for the command buffer space, unless we were
808c2ecf20Sopenharmony_ci * able to make a contigous coherent DMA memory allocation, @handle. Immutable.
818c2ecf20Sopenharmony_ci * @map_obj: Mapping state for @cmd_space. Immutable.
828c2ecf20Sopenharmony_ci * @map: Pointer to command buffer space. May be a mapped buffer object or
838c2ecf20Sopenharmony_ci * a contigous coherent DMA memory allocation. Immutable.
848c2ecf20Sopenharmony_ci * @cur: Command buffer for small kernel command submissions. Protected by
858c2ecf20Sopenharmony_ci * the @cur_mutex.
868c2ecf20Sopenharmony_ci * @cur_pos: Space already used in @cur. Protected by @cur_mutex.
878c2ecf20Sopenharmony_ci * @default_size: Default size for the @cur command buffer. Immutable.
888c2ecf20Sopenharmony_ci * @max_hw_submitted: Max number of in-flight command buffers the device can
898c2ecf20Sopenharmony_ci * handle. Immutable.
908c2ecf20Sopenharmony_ci * @lock: Spinlock protecting command submission queues.
918c2ecf20Sopenharmony_ci * @header: Pool of DMA memory for device command buffer headers.
928c2ecf20Sopenharmony_ci * Internal protection.
938c2ecf20Sopenharmony_ci * @dheaders: Pool of DMA memory for device command buffer headers with trailing
948c2ecf20Sopenharmony_ci * space for inline data. Internal protection.
958c2ecf20Sopenharmony_ci * @alloc_queue: Wait queue for processes waiting to allocate command buffer
968c2ecf20Sopenharmony_ci * space.
978c2ecf20Sopenharmony_ci * @idle_queue: Wait queue for processes waiting for command buffer idle.
988c2ecf20Sopenharmony_ci * @irq_on: Whether the process function has requested irq to be turned on.
998c2ecf20Sopenharmony_ci * Protected by @lock.
1008c2ecf20Sopenharmony_ci * @using_mob: Whether the command buffer space is a MOB or a contigous DMA
1018c2ecf20Sopenharmony_ci * allocation. Immutable.
1028c2ecf20Sopenharmony_ci * @has_pool: Has a large pool of DMA memory which allows larger allocations.
1038c2ecf20Sopenharmony_ci * Typically this is false only during bootstrap.
1048c2ecf20Sopenharmony_ci * @handle: DMA address handle for the command buffer space if @using_mob is
1058c2ecf20Sopenharmony_ci * false. Immutable.
1068c2ecf20Sopenharmony_ci * @size: The size of the command buffer space. Immutable.
1078c2ecf20Sopenharmony_ci * @num_contexts: Number of contexts actually enabled.
1088c2ecf20Sopenharmony_ci */
1098c2ecf20Sopenharmony_cistruct vmw_cmdbuf_man {
1108c2ecf20Sopenharmony_ci	struct mutex cur_mutex;
1118c2ecf20Sopenharmony_ci	struct mutex space_mutex;
1128c2ecf20Sopenharmony_ci	struct mutex error_mutex;
1138c2ecf20Sopenharmony_ci	struct work_struct work;
1148c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv;
1158c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_context ctx[SVGA_CB_CONTEXT_MAX];
1168c2ecf20Sopenharmony_ci	struct list_head error;
1178c2ecf20Sopenharmony_ci	struct drm_mm mm;
1188c2ecf20Sopenharmony_ci	struct ttm_buffer_object *cmd_space;
1198c2ecf20Sopenharmony_ci	struct ttm_bo_kmap_obj map_obj;
1208c2ecf20Sopenharmony_ci	u8 *map;
1218c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_header *cur;
1228c2ecf20Sopenharmony_ci	size_t cur_pos;
1238c2ecf20Sopenharmony_ci	size_t default_size;
1248c2ecf20Sopenharmony_ci	unsigned max_hw_submitted;
1258c2ecf20Sopenharmony_ci	spinlock_t lock;
1268c2ecf20Sopenharmony_ci	struct dma_pool *headers;
1278c2ecf20Sopenharmony_ci	struct dma_pool *dheaders;
1288c2ecf20Sopenharmony_ci	wait_queue_head_t alloc_queue;
1298c2ecf20Sopenharmony_ci	wait_queue_head_t idle_queue;
1308c2ecf20Sopenharmony_ci	bool irq_on;
1318c2ecf20Sopenharmony_ci	bool using_mob;
1328c2ecf20Sopenharmony_ci	bool has_pool;
1338c2ecf20Sopenharmony_ci	dma_addr_t handle;
1348c2ecf20Sopenharmony_ci	size_t size;
1358c2ecf20Sopenharmony_ci	u32 num_contexts;
1368c2ecf20Sopenharmony_ci};
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/**
1398c2ecf20Sopenharmony_ci * struct vmw_cmdbuf_header - Command buffer metadata
1408c2ecf20Sopenharmony_ci *
1418c2ecf20Sopenharmony_ci * @man: The command buffer manager.
1428c2ecf20Sopenharmony_ci * @cb_header: Device command buffer header, allocated from a DMA pool.
1438c2ecf20Sopenharmony_ci * @cb_context: The device command buffer context.
1448c2ecf20Sopenharmony_ci * @list: List head for attaching to the manager lists.
1458c2ecf20Sopenharmony_ci * @node: The range manager node.
1468c2ecf20Sopenharmony_ci * @handle. The DMA address of @cb_header. Handed to the device on command
1478c2ecf20Sopenharmony_ci * buffer submission.
1488c2ecf20Sopenharmony_ci * @cmd: Pointer to the command buffer space of this buffer.
1498c2ecf20Sopenharmony_ci * @size: Size of the command buffer space of this buffer.
1508c2ecf20Sopenharmony_ci * @reserved: Reserved space of this buffer.
1518c2ecf20Sopenharmony_ci * @inline_space: Whether inline command buffer space is used.
1528c2ecf20Sopenharmony_ci */
1538c2ecf20Sopenharmony_cistruct vmw_cmdbuf_header {
1548c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_man *man;
1558c2ecf20Sopenharmony_ci	SVGACBHeader *cb_header;
1568c2ecf20Sopenharmony_ci	SVGACBContext cb_context;
1578c2ecf20Sopenharmony_ci	struct list_head list;
1588c2ecf20Sopenharmony_ci	struct drm_mm_node node;
1598c2ecf20Sopenharmony_ci	dma_addr_t handle;
1608c2ecf20Sopenharmony_ci	u8 *cmd;
1618c2ecf20Sopenharmony_ci	size_t size;
1628c2ecf20Sopenharmony_ci	size_t reserved;
1638c2ecf20Sopenharmony_ci	bool inline_space;
1648c2ecf20Sopenharmony_ci};
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/**
1678c2ecf20Sopenharmony_ci * struct vmw_cmdbuf_dheader - Device command buffer header with inline
1688c2ecf20Sopenharmony_ci * command buffer space.
1698c2ecf20Sopenharmony_ci *
1708c2ecf20Sopenharmony_ci * @cb_header: Device command buffer header.
1718c2ecf20Sopenharmony_ci * @cmd: Inline command buffer space.
1728c2ecf20Sopenharmony_ci */
1738c2ecf20Sopenharmony_cistruct vmw_cmdbuf_dheader {
1748c2ecf20Sopenharmony_ci	SVGACBHeader cb_header;
1758c2ecf20Sopenharmony_ci	u8 cmd[VMW_CMDBUF_INLINE_SIZE] __aligned(VMW_CMDBUF_INLINE_ALIGN);
1768c2ecf20Sopenharmony_ci};
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/**
1798c2ecf20Sopenharmony_ci * struct vmw_cmdbuf_alloc_info - Command buffer space allocation metadata
1808c2ecf20Sopenharmony_ci *
1818c2ecf20Sopenharmony_ci * @page_size: Size of requested command buffer space in pages.
1828c2ecf20Sopenharmony_ci * @node: Pointer to the range manager node.
1838c2ecf20Sopenharmony_ci * @done: True if this allocation has succeeded.
1848c2ecf20Sopenharmony_ci */
1858c2ecf20Sopenharmony_cistruct vmw_cmdbuf_alloc_info {
1868c2ecf20Sopenharmony_ci	size_t page_size;
1878c2ecf20Sopenharmony_ci	struct drm_mm_node *node;
1888c2ecf20Sopenharmony_ci	bool done;
1898c2ecf20Sopenharmony_ci};
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/* Loop over each context in the command buffer manager. */
1928c2ecf20Sopenharmony_ci#define for_each_cmdbuf_ctx(_man, _i, _ctx)				\
1938c2ecf20Sopenharmony_ci	for (_i = 0, _ctx = &(_man)->ctx[0]; (_i) < (_man)->num_contexts; \
1948c2ecf20Sopenharmony_ci	     ++(_i), ++(_ctx))
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, u32 context,
1978c2ecf20Sopenharmony_ci				bool enable);
1988c2ecf20Sopenharmony_cistatic int vmw_cmdbuf_preempt(struct vmw_cmdbuf_man *man, u32 context);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci/**
2018c2ecf20Sopenharmony_ci * vmw_cmdbuf_cur_lock - Helper to lock the cur_mutex.
2028c2ecf20Sopenharmony_ci *
2038c2ecf20Sopenharmony_ci * @man: The range manager.
2048c2ecf20Sopenharmony_ci * @interruptible: Whether to wait interruptible when locking.
2058c2ecf20Sopenharmony_ci */
2068c2ecf20Sopenharmony_cistatic int vmw_cmdbuf_cur_lock(struct vmw_cmdbuf_man *man, bool interruptible)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	if (interruptible) {
2098c2ecf20Sopenharmony_ci		if (mutex_lock_interruptible(&man->cur_mutex))
2108c2ecf20Sopenharmony_ci			return -ERESTARTSYS;
2118c2ecf20Sopenharmony_ci	} else {
2128c2ecf20Sopenharmony_ci		mutex_lock(&man->cur_mutex);
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return 0;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/**
2198c2ecf20Sopenharmony_ci * vmw_cmdbuf_cur_unlock - Helper to unlock the cur_mutex.
2208c2ecf20Sopenharmony_ci *
2218c2ecf20Sopenharmony_ci * @man: The range manager.
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_cistatic void vmw_cmdbuf_cur_unlock(struct vmw_cmdbuf_man *man)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	mutex_unlock(&man->cur_mutex);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/**
2298c2ecf20Sopenharmony_ci * vmw_cmdbuf_header_inline_free - Free a struct vmw_cmdbuf_header that has
2308c2ecf20Sopenharmony_ci * been used for the device context with inline command buffers.
2318c2ecf20Sopenharmony_ci * Need not be called locked.
2328c2ecf20Sopenharmony_ci *
2338c2ecf20Sopenharmony_ci * @header: Pointer to the header to free.
2348c2ecf20Sopenharmony_ci */
2358c2ecf20Sopenharmony_cistatic void vmw_cmdbuf_header_inline_free(struct vmw_cmdbuf_header *header)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_dheader *dheader;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!header->inline_space))
2408c2ecf20Sopenharmony_ci		return;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	dheader = container_of(header->cb_header, struct vmw_cmdbuf_dheader,
2438c2ecf20Sopenharmony_ci			       cb_header);
2448c2ecf20Sopenharmony_ci	dma_pool_free(header->man->dheaders, dheader, header->handle);
2458c2ecf20Sopenharmony_ci	kfree(header);
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci/**
2498c2ecf20Sopenharmony_ci * __vmw_cmdbuf_header_free - Free a struct vmw_cmdbuf_header  and its
2508c2ecf20Sopenharmony_ci * associated structures.
2518c2ecf20Sopenharmony_ci *
2528c2ecf20Sopenharmony_ci * header: Pointer to the header to free.
2538c2ecf20Sopenharmony_ci *
2548c2ecf20Sopenharmony_ci * For internal use. Must be called with man::lock held.
2558c2ecf20Sopenharmony_ci */
2568c2ecf20Sopenharmony_cistatic void __vmw_cmdbuf_header_free(struct vmw_cmdbuf_header *header)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_man *man = header->man;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	lockdep_assert_held_once(&man->lock);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (header->inline_space) {
2638c2ecf20Sopenharmony_ci		vmw_cmdbuf_header_inline_free(header);
2648c2ecf20Sopenharmony_ci		return;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	drm_mm_remove_node(&header->node);
2688c2ecf20Sopenharmony_ci	wake_up_all(&man->alloc_queue);
2698c2ecf20Sopenharmony_ci	if (header->cb_header)
2708c2ecf20Sopenharmony_ci		dma_pool_free(man->headers, header->cb_header,
2718c2ecf20Sopenharmony_ci			      header->handle);
2728c2ecf20Sopenharmony_ci	kfree(header);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci/**
2768c2ecf20Sopenharmony_ci * vmw_cmdbuf_header_free - Free a struct vmw_cmdbuf_header  and its
2778c2ecf20Sopenharmony_ci * associated structures.
2788c2ecf20Sopenharmony_ci *
2798c2ecf20Sopenharmony_ci * @header: Pointer to the header to free.
2808c2ecf20Sopenharmony_ci */
2818c2ecf20Sopenharmony_civoid vmw_cmdbuf_header_free(struct vmw_cmdbuf_header *header)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_man *man = header->man;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	/* Avoid locking if inline_space */
2868c2ecf20Sopenharmony_ci	if (header->inline_space) {
2878c2ecf20Sopenharmony_ci		vmw_cmdbuf_header_inline_free(header);
2888c2ecf20Sopenharmony_ci		return;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	spin_lock(&man->lock);
2918c2ecf20Sopenharmony_ci	__vmw_cmdbuf_header_free(header);
2928c2ecf20Sopenharmony_ci	spin_unlock(&man->lock);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci/**
2978c2ecf20Sopenharmony_ci * vmw_cmbuf_header_submit: Submit a command buffer to hardware.
2988c2ecf20Sopenharmony_ci *
2998c2ecf20Sopenharmony_ci * @header: The header of the buffer to submit.
3008c2ecf20Sopenharmony_ci */
3018c2ecf20Sopenharmony_cistatic int vmw_cmdbuf_header_submit(struct vmw_cmdbuf_header *header)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_man *man = header->man;
3048c2ecf20Sopenharmony_ci	u32 val;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	val = upper_32_bits(header->handle);
3078c2ecf20Sopenharmony_ci	vmw_write(man->dev_priv, SVGA_REG_COMMAND_HIGH, val);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	val = lower_32_bits(header->handle);
3108c2ecf20Sopenharmony_ci	val |= header->cb_context & SVGA_CB_CONTEXT_MASK;
3118c2ecf20Sopenharmony_ci	vmw_write(man->dev_priv, SVGA_REG_COMMAND_LOW, val);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	return header->cb_header->status;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci/**
3178c2ecf20Sopenharmony_ci * vmw_cmdbuf_ctx_init: Initialize a command buffer context.
3188c2ecf20Sopenharmony_ci *
3198c2ecf20Sopenharmony_ci * @ctx: The command buffer context to initialize
3208c2ecf20Sopenharmony_ci */
3218c2ecf20Sopenharmony_cistatic void vmw_cmdbuf_ctx_init(struct vmw_cmdbuf_context *ctx)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctx->hw_submitted);
3248c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctx->submitted);
3258c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctx->preempted);
3268c2ecf20Sopenharmony_ci	ctx->num_hw_submitted = 0;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci/**
3308c2ecf20Sopenharmony_ci * vmw_cmdbuf_ctx_submit: Submit command buffers from a command buffer
3318c2ecf20Sopenharmony_ci * context.
3328c2ecf20Sopenharmony_ci *
3338c2ecf20Sopenharmony_ci * @man: The command buffer manager.
3348c2ecf20Sopenharmony_ci * @ctx: The command buffer context.
3358c2ecf20Sopenharmony_ci *
3368c2ecf20Sopenharmony_ci * Submits command buffers to hardware until there are no more command
3378c2ecf20Sopenharmony_ci * buffers to submit or the hardware can't handle more command buffers.
3388c2ecf20Sopenharmony_ci */
3398c2ecf20Sopenharmony_cistatic void vmw_cmdbuf_ctx_submit(struct vmw_cmdbuf_man *man,
3408c2ecf20Sopenharmony_ci				  struct vmw_cmdbuf_context *ctx)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	while (ctx->num_hw_submitted < man->max_hw_submitted &&
3438c2ecf20Sopenharmony_ci	       !list_empty(&ctx->submitted) &&
3448c2ecf20Sopenharmony_ci	       !ctx->block_submission) {
3458c2ecf20Sopenharmony_ci		struct vmw_cmdbuf_header *entry;
3468c2ecf20Sopenharmony_ci		SVGACBStatus status;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci		entry = list_first_entry(&ctx->submitted,
3498c2ecf20Sopenharmony_ci					 struct vmw_cmdbuf_header,
3508c2ecf20Sopenharmony_ci					 list);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		status = vmw_cmdbuf_header_submit(entry);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		/* This should never happen */
3558c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(status == SVGA_CB_STATUS_QUEUE_FULL)) {
3568c2ecf20Sopenharmony_ci			entry->cb_header->status = SVGA_CB_STATUS_NONE;
3578c2ecf20Sopenharmony_ci			break;
3588c2ecf20Sopenharmony_ci		}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci		list_del(&entry->list);
3618c2ecf20Sopenharmony_ci		list_add_tail(&entry->list, &ctx->hw_submitted);
3628c2ecf20Sopenharmony_ci		ctx->num_hw_submitted++;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci/**
3688c2ecf20Sopenharmony_ci * vmw_cmdbuf_ctx_submit: Process a command buffer context.
3698c2ecf20Sopenharmony_ci *
3708c2ecf20Sopenharmony_ci * @man: The command buffer manager.
3718c2ecf20Sopenharmony_ci * @ctx: The command buffer context.
3728c2ecf20Sopenharmony_ci *
3738c2ecf20Sopenharmony_ci * Submit command buffers to hardware if possible, and process finished
3748c2ecf20Sopenharmony_ci * buffers. Typically freeing them, but on preemption or error take
3758c2ecf20Sopenharmony_ci * appropriate action. Wake up waiters if appropriate.
3768c2ecf20Sopenharmony_ci */
3778c2ecf20Sopenharmony_cistatic void vmw_cmdbuf_ctx_process(struct vmw_cmdbuf_man *man,
3788c2ecf20Sopenharmony_ci				   struct vmw_cmdbuf_context *ctx,
3798c2ecf20Sopenharmony_ci				   int *notempty)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_header *entry, *next;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	vmw_cmdbuf_ctx_submit(man, ctx);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	list_for_each_entry_safe(entry, next, &ctx->hw_submitted, list) {
3868c2ecf20Sopenharmony_ci		SVGACBStatus status = entry->cb_header->status;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		if (status == SVGA_CB_STATUS_NONE)
3898c2ecf20Sopenharmony_ci			break;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci		list_del(&entry->list);
3928c2ecf20Sopenharmony_ci		wake_up_all(&man->idle_queue);
3938c2ecf20Sopenharmony_ci		ctx->num_hw_submitted--;
3948c2ecf20Sopenharmony_ci		switch (status) {
3958c2ecf20Sopenharmony_ci		case SVGA_CB_STATUS_COMPLETED:
3968c2ecf20Sopenharmony_ci			__vmw_cmdbuf_header_free(entry);
3978c2ecf20Sopenharmony_ci			break;
3988c2ecf20Sopenharmony_ci		case SVGA_CB_STATUS_COMMAND_ERROR:
3998c2ecf20Sopenharmony_ci			WARN_ONCE(true, "Command buffer error.\n");
4008c2ecf20Sopenharmony_ci			entry->cb_header->status = SVGA_CB_STATUS_NONE;
4018c2ecf20Sopenharmony_ci			list_add_tail(&entry->list, &man->error);
4028c2ecf20Sopenharmony_ci			schedule_work(&man->work);
4038c2ecf20Sopenharmony_ci			break;
4048c2ecf20Sopenharmony_ci		case SVGA_CB_STATUS_PREEMPTED:
4058c2ecf20Sopenharmony_ci			entry->cb_header->status = SVGA_CB_STATUS_NONE;
4068c2ecf20Sopenharmony_ci			list_add_tail(&entry->list, &ctx->preempted);
4078c2ecf20Sopenharmony_ci			break;
4088c2ecf20Sopenharmony_ci		case SVGA_CB_STATUS_CB_HEADER_ERROR:
4098c2ecf20Sopenharmony_ci			WARN_ONCE(true, "Command buffer header error.\n");
4108c2ecf20Sopenharmony_ci			__vmw_cmdbuf_header_free(entry);
4118c2ecf20Sopenharmony_ci			break;
4128c2ecf20Sopenharmony_ci		default:
4138c2ecf20Sopenharmony_ci			WARN_ONCE(true, "Undefined command buffer status.\n");
4148c2ecf20Sopenharmony_ci			__vmw_cmdbuf_header_free(entry);
4158c2ecf20Sopenharmony_ci			break;
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	vmw_cmdbuf_ctx_submit(man, ctx);
4208c2ecf20Sopenharmony_ci	if (!list_empty(&ctx->submitted))
4218c2ecf20Sopenharmony_ci		(*notempty)++;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci/**
4258c2ecf20Sopenharmony_ci * vmw_cmdbuf_man_process - Process all command buffer contexts and
4268c2ecf20Sopenharmony_ci * switch on and off irqs as appropriate.
4278c2ecf20Sopenharmony_ci *
4288c2ecf20Sopenharmony_ci * @man: The command buffer manager.
4298c2ecf20Sopenharmony_ci *
4308c2ecf20Sopenharmony_ci * Calls vmw_cmdbuf_ctx_process() on all contexts. If any context has
4318c2ecf20Sopenharmony_ci * command buffers left that are not submitted to hardware, Make sure
4328c2ecf20Sopenharmony_ci * IRQ handling is turned on. Otherwise, make sure it's turned off.
4338c2ecf20Sopenharmony_ci */
4348c2ecf20Sopenharmony_cistatic void vmw_cmdbuf_man_process(struct vmw_cmdbuf_man *man)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	int notempty;
4378c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_context *ctx;
4388c2ecf20Sopenharmony_ci	int i;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ciretry:
4418c2ecf20Sopenharmony_ci	notempty = 0;
4428c2ecf20Sopenharmony_ci	for_each_cmdbuf_ctx(man, i, ctx)
4438c2ecf20Sopenharmony_ci		vmw_cmdbuf_ctx_process(man, ctx, &notempty);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (man->irq_on && !notempty) {
4468c2ecf20Sopenharmony_ci		vmw_generic_waiter_remove(man->dev_priv,
4478c2ecf20Sopenharmony_ci					  SVGA_IRQFLAG_COMMAND_BUFFER,
4488c2ecf20Sopenharmony_ci					  &man->dev_priv->cmdbuf_waiters);
4498c2ecf20Sopenharmony_ci		man->irq_on = false;
4508c2ecf20Sopenharmony_ci	} else if (!man->irq_on && notempty) {
4518c2ecf20Sopenharmony_ci		vmw_generic_waiter_add(man->dev_priv,
4528c2ecf20Sopenharmony_ci				       SVGA_IRQFLAG_COMMAND_BUFFER,
4538c2ecf20Sopenharmony_ci				       &man->dev_priv->cmdbuf_waiters);
4548c2ecf20Sopenharmony_ci		man->irq_on = true;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		/* Rerun in case we just missed an irq. */
4578c2ecf20Sopenharmony_ci		goto retry;
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci/**
4628c2ecf20Sopenharmony_ci * vmw_cmdbuf_ctx_add - Schedule a command buffer for submission on a
4638c2ecf20Sopenharmony_ci * command buffer context
4648c2ecf20Sopenharmony_ci *
4658c2ecf20Sopenharmony_ci * @man: The command buffer manager.
4668c2ecf20Sopenharmony_ci * @header: The header of the buffer to submit.
4678c2ecf20Sopenharmony_ci * @cb_context: The command buffer context to use.
4688c2ecf20Sopenharmony_ci *
4698c2ecf20Sopenharmony_ci * This function adds @header to the "submitted" queue of the command
4708c2ecf20Sopenharmony_ci * buffer context identified by @cb_context. It then calls the command buffer
4718c2ecf20Sopenharmony_ci * manager processing to potentially submit the buffer to hardware.
4728c2ecf20Sopenharmony_ci * @man->lock needs to be held when calling this function.
4738c2ecf20Sopenharmony_ci */
4748c2ecf20Sopenharmony_cistatic void vmw_cmdbuf_ctx_add(struct vmw_cmdbuf_man *man,
4758c2ecf20Sopenharmony_ci			       struct vmw_cmdbuf_header *header,
4768c2ecf20Sopenharmony_ci			       SVGACBContext cb_context)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	if (!(header->cb_header->flags & SVGA_CB_FLAG_DX_CONTEXT))
4798c2ecf20Sopenharmony_ci		header->cb_header->dxContext = 0;
4808c2ecf20Sopenharmony_ci	header->cb_context = cb_context;
4818c2ecf20Sopenharmony_ci	list_add_tail(&header->list, &man->ctx[cb_context].submitted);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	vmw_cmdbuf_man_process(man);
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci/**
4878c2ecf20Sopenharmony_ci * vmw_cmdbuf_irqthread - The main part of the command buffer interrupt
4888c2ecf20Sopenharmony_ci * handler implemented as a threaded irq task.
4898c2ecf20Sopenharmony_ci *
4908c2ecf20Sopenharmony_ci * @man: Pointer to the command buffer manager.
4918c2ecf20Sopenharmony_ci *
4928c2ecf20Sopenharmony_ci * The bottom half of the interrupt handler simply calls into the
4938c2ecf20Sopenharmony_ci * command buffer processor to free finished buffers and submit any
4948c2ecf20Sopenharmony_ci * queued buffers to hardware.
4958c2ecf20Sopenharmony_ci */
4968c2ecf20Sopenharmony_civoid vmw_cmdbuf_irqthread(struct vmw_cmdbuf_man *man)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	spin_lock(&man->lock);
4998c2ecf20Sopenharmony_ci	vmw_cmdbuf_man_process(man);
5008c2ecf20Sopenharmony_ci	spin_unlock(&man->lock);
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci/**
5048c2ecf20Sopenharmony_ci * vmw_cmdbuf_work_func - The deferred work function that handles
5058c2ecf20Sopenharmony_ci * command buffer errors.
5068c2ecf20Sopenharmony_ci *
5078c2ecf20Sopenharmony_ci * @work: The work func closure argument.
5088c2ecf20Sopenharmony_ci *
5098c2ecf20Sopenharmony_ci * Restarting the command buffer context after an error requires process
5108c2ecf20Sopenharmony_ci * context, so it is deferred to this work function.
5118c2ecf20Sopenharmony_ci */
5128c2ecf20Sopenharmony_cistatic void vmw_cmdbuf_work_func(struct work_struct *work)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_man *man =
5158c2ecf20Sopenharmony_ci		container_of(work, struct vmw_cmdbuf_man, work);
5168c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_header *entry, *next;
5178c2ecf20Sopenharmony_ci	uint32_t dummy;
5188c2ecf20Sopenharmony_ci	bool send_fence = false;
5198c2ecf20Sopenharmony_ci	struct list_head restart_head[SVGA_CB_CONTEXT_MAX];
5208c2ecf20Sopenharmony_ci	int i;
5218c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_context *ctx;
5228c2ecf20Sopenharmony_ci	bool global_block = false;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	for_each_cmdbuf_ctx(man, i, ctx)
5258c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&restart_head[i]);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	mutex_lock(&man->error_mutex);
5288c2ecf20Sopenharmony_ci	spin_lock(&man->lock);
5298c2ecf20Sopenharmony_ci	list_for_each_entry_safe(entry, next, &man->error, list) {
5308c2ecf20Sopenharmony_ci		SVGACBHeader *cb_hdr = entry->cb_header;
5318c2ecf20Sopenharmony_ci		SVGA3dCmdHeader *header = (SVGA3dCmdHeader *)
5328c2ecf20Sopenharmony_ci			(entry->cmd + cb_hdr->errorOffset);
5338c2ecf20Sopenharmony_ci		u32 error_cmd_size, new_start_offset;
5348c2ecf20Sopenharmony_ci		const char *cmd_name;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci		list_del_init(&entry->list);
5378c2ecf20Sopenharmony_ci		global_block = true;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		if (!vmw_cmd_describe(header, &error_cmd_size, &cmd_name)) {
5408c2ecf20Sopenharmony_ci			VMW_DEBUG_USER("Unknown command causing device error.\n");
5418c2ecf20Sopenharmony_ci			VMW_DEBUG_USER("Command buffer offset is %lu\n",
5428c2ecf20Sopenharmony_ci				       (unsigned long) cb_hdr->errorOffset);
5438c2ecf20Sopenharmony_ci			__vmw_cmdbuf_header_free(entry);
5448c2ecf20Sopenharmony_ci			send_fence = true;
5458c2ecf20Sopenharmony_ci			continue;
5468c2ecf20Sopenharmony_ci		}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci		VMW_DEBUG_USER("Command \"%s\" causing device error.\n",
5498c2ecf20Sopenharmony_ci			       cmd_name);
5508c2ecf20Sopenharmony_ci		VMW_DEBUG_USER("Command buffer offset is %lu\n",
5518c2ecf20Sopenharmony_ci			       (unsigned long) cb_hdr->errorOffset);
5528c2ecf20Sopenharmony_ci		VMW_DEBUG_USER("Command size is %lu\n",
5538c2ecf20Sopenharmony_ci			       (unsigned long) error_cmd_size);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci		new_start_offset = cb_hdr->errorOffset + error_cmd_size;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci		if (new_start_offset >= cb_hdr->length) {
5588c2ecf20Sopenharmony_ci			__vmw_cmdbuf_header_free(entry);
5598c2ecf20Sopenharmony_ci			send_fence = true;
5608c2ecf20Sopenharmony_ci			continue;
5618c2ecf20Sopenharmony_ci		}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci		if (man->using_mob)
5648c2ecf20Sopenharmony_ci			cb_hdr->ptr.mob.mobOffset += new_start_offset;
5658c2ecf20Sopenharmony_ci		else
5668c2ecf20Sopenharmony_ci			cb_hdr->ptr.pa += (u64) new_start_offset;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci		entry->cmd += new_start_offset;
5698c2ecf20Sopenharmony_ci		cb_hdr->length -= new_start_offset;
5708c2ecf20Sopenharmony_ci		cb_hdr->errorOffset = 0;
5718c2ecf20Sopenharmony_ci		cb_hdr->offset = 0;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci		list_add_tail(&entry->list, &restart_head[entry->cb_context]);
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	for_each_cmdbuf_ctx(man, i, ctx)
5778c2ecf20Sopenharmony_ci		man->ctx[i].block_submission = true;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	spin_unlock(&man->lock);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	/* Preempt all contexts */
5828c2ecf20Sopenharmony_ci	if (global_block && vmw_cmdbuf_preempt(man, 0))
5838c2ecf20Sopenharmony_ci		DRM_ERROR("Failed preempting command buffer contexts\n");
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	spin_lock(&man->lock);
5868c2ecf20Sopenharmony_ci	for_each_cmdbuf_ctx(man, i, ctx) {
5878c2ecf20Sopenharmony_ci		/* Move preempted command buffers to the preempted queue. */
5888c2ecf20Sopenharmony_ci		vmw_cmdbuf_ctx_process(man, ctx, &dummy);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci		/*
5918c2ecf20Sopenharmony_ci		 * Add the preempted queue after the command buffer
5928c2ecf20Sopenharmony_ci		 * that caused an error.
5938c2ecf20Sopenharmony_ci		 */
5948c2ecf20Sopenharmony_ci		list_splice_init(&ctx->preempted, restart_head[i].prev);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci		/*
5978c2ecf20Sopenharmony_ci		 * Finally add all command buffers first in the submitted
5988c2ecf20Sopenharmony_ci		 * queue, to rerun them.
5998c2ecf20Sopenharmony_ci		 */
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci		ctx->block_submission = false;
6028c2ecf20Sopenharmony_ci		list_splice_init(&restart_head[i], &ctx->submitted);
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	vmw_cmdbuf_man_process(man);
6068c2ecf20Sopenharmony_ci	spin_unlock(&man->lock);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	if (global_block && vmw_cmdbuf_startstop(man, 0, true))
6098c2ecf20Sopenharmony_ci		DRM_ERROR("Failed restarting command buffer contexts\n");
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	/* Send a new fence in case one was removed */
6128c2ecf20Sopenharmony_ci	if (send_fence) {
6138c2ecf20Sopenharmony_ci		vmw_fifo_send_fence(man->dev_priv, &dummy);
6148c2ecf20Sopenharmony_ci		wake_up_all(&man->idle_queue);
6158c2ecf20Sopenharmony_ci	}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	mutex_unlock(&man->error_mutex);
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci/**
6218c2ecf20Sopenharmony_ci * vmw_cmdbuf_man idle - Check whether the command buffer manager is idle.
6228c2ecf20Sopenharmony_ci *
6238c2ecf20Sopenharmony_ci * @man: The command buffer manager.
6248c2ecf20Sopenharmony_ci * @check_preempted: Check also the preempted queue for pending command buffers.
6258c2ecf20Sopenharmony_ci *
6268c2ecf20Sopenharmony_ci */
6278c2ecf20Sopenharmony_cistatic bool vmw_cmdbuf_man_idle(struct vmw_cmdbuf_man *man,
6288c2ecf20Sopenharmony_ci				bool check_preempted)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_context *ctx;
6318c2ecf20Sopenharmony_ci	bool idle = false;
6328c2ecf20Sopenharmony_ci	int i;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	spin_lock(&man->lock);
6358c2ecf20Sopenharmony_ci	vmw_cmdbuf_man_process(man);
6368c2ecf20Sopenharmony_ci	for_each_cmdbuf_ctx(man, i, ctx) {
6378c2ecf20Sopenharmony_ci		if (!list_empty(&ctx->submitted) ||
6388c2ecf20Sopenharmony_ci		    !list_empty(&ctx->hw_submitted) ||
6398c2ecf20Sopenharmony_ci		    (check_preempted && !list_empty(&ctx->preempted)))
6408c2ecf20Sopenharmony_ci			goto out_unlock;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	idle = list_empty(&man->error);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ciout_unlock:
6468c2ecf20Sopenharmony_ci	spin_unlock(&man->lock);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	return idle;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci/**
6528c2ecf20Sopenharmony_ci * __vmw_cmdbuf_cur_flush - Flush the current command buffer for small kernel
6538c2ecf20Sopenharmony_ci * command submissions
6548c2ecf20Sopenharmony_ci *
6558c2ecf20Sopenharmony_ci * @man: The command buffer manager.
6568c2ecf20Sopenharmony_ci *
6578c2ecf20Sopenharmony_ci * Flushes the current command buffer without allocating a new one. A new one
6588c2ecf20Sopenharmony_ci * is automatically allocated when needed. Call with @man->cur_mutex held.
6598c2ecf20Sopenharmony_ci */
6608c2ecf20Sopenharmony_cistatic void __vmw_cmdbuf_cur_flush(struct vmw_cmdbuf_man *man)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_header *cur = man->cur;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	lockdep_assert_held_once(&man->cur_mutex);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	if (!cur)
6678c2ecf20Sopenharmony_ci		return;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	spin_lock(&man->lock);
6708c2ecf20Sopenharmony_ci	if (man->cur_pos == 0) {
6718c2ecf20Sopenharmony_ci		__vmw_cmdbuf_header_free(cur);
6728c2ecf20Sopenharmony_ci		goto out_unlock;
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	man->cur->cb_header->length = man->cur_pos;
6768c2ecf20Sopenharmony_ci	vmw_cmdbuf_ctx_add(man, man->cur, SVGA_CB_CONTEXT_0);
6778c2ecf20Sopenharmony_ciout_unlock:
6788c2ecf20Sopenharmony_ci	spin_unlock(&man->lock);
6798c2ecf20Sopenharmony_ci	man->cur = NULL;
6808c2ecf20Sopenharmony_ci	man->cur_pos = 0;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci/**
6848c2ecf20Sopenharmony_ci * vmw_cmdbuf_cur_flush - Flush the current command buffer for small kernel
6858c2ecf20Sopenharmony_ci * command submissions
6868c2ecf20Sopenharmony_ci *
6878c2ecf20Sopenharmony_ci * @man: The command buffer manager.
6888c2ecf20Sopenharmony_ci * @interruptible: Whether to sleep interruptible when sleeping.
6898c2ecf20Sopenharmony_ci *
6908c2ecf20Sopenharmony_ci * Flushes the current command buffer without allocating a new one. A new one
6918c2ecf20Sopenharmony_ci * is automatically allocated when needed.
6928c2ecf20Sopenharmony_ci */
6938c2ecf20Sopenharmony_ciint vmw_cmdbuf_cur_flush(struct vmw_cmdbuf_man *man,
6948c2ecf20Sopenharmony_ci			 bool interruptible)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	int ret = vmw_cmdbuf_cur_lock(man, interruptible);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	if (ret)
6998c2ecf20Sopenharmony_ci		return ret;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	__vmw_cmdbuf_cur_flush(man);
7028c2ecf20Sopenharmony_ci	vmw_cmdbuf_cur_unlock(man);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	return 0;
7058c2ecf20Sopenharmony_ci}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci/**
7088c2ecf20Sopenharmony_ci * vmw_cmdbuf_idle - Wait for command buffer manager idle.
7098c2ecf20Sopenharmony_ci *
7108c2ecf20Sopenharmony_ci * @man: The command buffer manager.
7118c2ecf20Sopenharmony_ci * @interruptible: Sleep interruptible while waiting.
7128c2ecf20Sopenharmony_ci * @timeout: Time out after this many ticks.
7138c2ecf20Sopenharmony_ci *
7148c2ecf20Sopenharmony_ci * Wait until the command buffer manager has processed all command buffers,
7158c2ecf20Sopenharmony_ci * or until a timeout occurs. If a timeout occurs, the function will return
7168c2ecf20Sopenharmony_ci * -EBUSY.
7178c2ecf20Sopenharmony_ci */
7188c2ecf20Sopenharmony_ciint vmw_cmdbuf_idle(struct vmw_cmdbuf_man *man, bool interruptible,
7198c2ecf20Sopenharmony_ci		    unsigned long timeout)
7208c2ecf20Sopenharmony_ci{
7218c2ecf20Sopenharmony_ci	int ret;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	ret = vmw_cmdbuf_cur_flush(man, interruptible);
7248c2ecf20Sopenharmony_ci	vmw_generic_waiter_add(man->dev_priv,
7258c2ecf20Sopenharmony_ci			       SVGA_IRQFLAG_COMMAND_BUFFER,
7268c2ecf20Sopenharmony_ci			       &man->dev_priv->cmdbuf_waiters);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	if (interruptible) {
7298c2ecf20Sopenharmony_ci		ret = wait_event_interruptible_timeout
7308c2ecf20Sopenharmony_ci			(man->idle_queue, vmw_cmdbuf_man_idle(man, true),
7318c2ecf20Sopenharmony_ci			 timeout);
7328c2ecf20Sopenharmony_ci	} else {
7338c2ecf20Sopenharmony_ci		ret = wait_event_timeout
7348c2ecf20Sopenharmony_ci			(man->idle_queue, vmw_cmdbuf_man_idle(man, true),
7358c2ecf20Sopenharmony_ci			 timeout);
7368c2ecf20Sopenharmony_ci	}
7378c2ecf20Sopenharmony_ci	vmw_generic_waiter_remove(man->dev_priv,
7388c2ecf20Sopenharmony_ci				  SVGA_IRQFLAG_COMMAND_BUFFER,
7398c2ecf20Sopenharmony_ci				  &man->dev_priv->cmdbuf_waiters);
7408c2ecf20Sopenharmony_ci	if (ret == 0) {
7418c2ecf20Sopenharmony_ci		if (!vmw_cmdbuf_man_idle(man, true))
7428c2ecf20Sopenharmony_ci			ret = -EBUSY;
7438c2ecf20Sopenharmony_ci		else
7448c2ecf20Sopenharmony_ci			ret = 0;
7458c2ecf20Sopenharmony_ci	}
7468c2ecf20Sopenharmony_ci	if (ret > 0)
7478c2ecf20Sopenharmony_ci		ret = 0;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	return ret;
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci/**
7538c2ecf20Sopenharmony_ci * vmw_cmdbuf_try_alloc - Try to allocate buffer space from the main pool.
7548c2ecf20Sopenharmony_ci *
7558c2ecf20Sopenharmony_ci * @man: The command buffer manager.
7568c2ecf20Sopenharmony_ci * @info: Allocation info. Will hold the size on entry and allocated mm node
7578c2ecf20Sopenharmony_ci * on successful return.
7588c2ecf20Sopenharmony_ci *
7598c2ecf20Sopenharmony_ci * Try to allocate buffer space from the main pool. Returns true if succeeded.
7608c2ecf20Sopenharmony_ci * If a fatal error was hit, the error code is returned in @info->ret.
7618c2ecf20Sopenharmony_ci */
7628c2ecf20Sopenharmony_cistatic bool vmw_cmdbuf_try_alloc(struct vmw_cmdbuf_man *man,
7638c2ecf20Sopenharmony_ci				 struct vmw_cmdbuf_alloc_info *info)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	int ret;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	if (info->done)
7688c2ecf20Sopenharmony_ci		return true;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	memset(info->node, 0, sizeof(*info->node));
7718c2ecf20Sopenharmony_ci	spin_lock(&man->lock);
7728c2ecf20Sopenharmony_ci	ret = drm_mm_insert_node(&man->mm, info->node, info->page_size);
7738c2ecf20Sopenharmony_ci	if (ret) {
7748c2ecf20Sopenharmony_ci		vmw_cmdbuf_man_process(man);
7758c2ecf20Sopenharmony_ci		ret = drm_mm_insert_node(&man->mm, info->node, info->page_size);
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	spin_unlock(&man->lock);
7798c2ecf20Sopenharmony_ci	info->done = !ret;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	return info->done;
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci/**
7858c2ecf20Sopenharmony_ci * vmw_cmdbuf_alloc_space - Allocate buffer space from the main pool.
7868c2ecf20Sopenharmony_ci *
7878c2ecf20Sopenharmony_ci * @man: The command buffer manager.
7888c2ecf20Sopenharmony_ci * @node: Pointer to pre-allocated range-manager node.
7898c2ecf20Sopenharmony_ci * @size: The size of the allocation.
7908c2ecf20Sopenharmony_ci * @interruptible: Whether to sleep interruptible while waiting for space.
7918c2ecf20Sopenharmony_ci *
7928c2ecf20Sopenharmony_ci * This function allocates buffer space from the main pool, and if there is
7938c2ecf20Sopenharmony_ci * no space available ATM, it turns on IRQ handling and sleeps waiting for it to
7948c2ecf20Sopenharmony_ci * become available.
7958c2ecf20Sopenharmony_ci */
7968c2ecf20Sopenharmony_cistatic int vmw_cmdbuf_alloc_space(struct vmw_cmdbuf_man *man,
7978c2ecf20Sopenharmony_ci				  struct drm_mm_node *node,
7988c2ecf20Sopenharmony_ci				  size_t size,
7998c2ecf20Sopenharmony_ci				  bool interruptible)
8008c2ecf20Sopenharmony_ci{
8018c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_alloc_info info;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	info.page_size = PAGE_ALIGN(size) >> PAGE_SHIFT;
8048c2ecf20Sopenharmony_ci	info.node = node;
8058c2ecf20Sopenharmony_ci	info.done = false;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	/*
8088c2ecf20Sopenharmony_ci	 * To prevent starvation of large requests, only one allocating call
8098c2ecf20Sopenharmony_ci	 * at a time waiting for space.
8108c2ecf20Sopenharmony_ci	 */
8118c2ecf20Sopenharmony_ci	if (interruptible) {
8128c2ecf20Sopenharmony_ci		if (mutex_lock_interruptible(&man->space_mutex))
8138c2ecf20Sopenharmony_ci			return -ERESTARTSYS;
8148c2ecf20Sopenharmony_ci	} else {
8158c2ecf20Sopenharmony_ci		mutex_lock(&man->space_mutex);
8168c2ecf20Sopenharmony_ci	}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	/* Try to allocate space without waiting. */
8198c2ecf20Sopenharmony_ci	if (vmw_cmdbuf_try_alloc(man, &info))
8208c2ecf20Sopenharmony_ci		goto out_unlock;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	vmw_generic_waiter_add(man->dev_priv,
8238c2ecf20Sopenharmony_ci			       SVGA_IRQFLAG_COMMAND_BUFFER,
8248c2ecf20Sopenharmony_ci			       &man->dev_priv->cmdbuf_waiters);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (interruptible) {
8278c2ecf20Sopenharmony_ci		int ret;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci		ret = wait_event_interruptible
8308c2ecf20Sopenharmony_ci			(man->alloc_queue, vmw_cmdbuf_try_alloc(man, &info));
8318c2ecf20Sopenharmony_ci		if (ret) {
8328c2ecf20Sopenharmony_ci			vmw_generic_waiter_remove
8338c2ecf20Sopenharmony_ci				(man->dev_priv, SVGA_IRQFLAG_COMMAND_BUFFER,
8348c2ecf20Sopenharmony_ci				 &man->dev_priv->cmdbuf_waiters);
8358c2ecf20Sopenharmony_ci			mutex_unlock(&man->space_mutex);
8368c2ecf20Sopenharmony_ci			return ret;
8378c2ecf20Sopenharmony_ci		}
8388c2ecf20Sopenharmony_ci	} else {
8398c2ecf20Sopenharmony_ci		wait_event(man->alloc_queue, vmw_cmdbuf_try_alloc(man, &info));
8408c2ecf20Sopenharmony_ci	}
8418c2ecf20Sopenharmony_ci	vmw_generic_waiter_remove(man->dev_priv,
8428c2ecf20Sopenharmony_ci				  SVGA_IRQFLAG_COMMAND_BUFFER,
8438c2ecf20Sopenharmony_ci				  &man->dev_priv->cmdbuf_waiters);
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ciout_unlock:
8468c2ecf20Sopenharmony_ci	mutex_unlock(&man->space_mutex);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	return 0;
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci/**
8528c2ecf20Sopenharmony_ci * vmw_cmdbuf_space_pool - Set up a command buffer header with command buffer
8538c2ecf20Sopenharmony_ci * space from the main pool.
8548c2ecf20Sopenharmony_ci *
8558c2ecf20Sopenharmony_ci * @man: The command buffer manager.
8568c2ecf20Sopenharmony_ci * @header: Pointer to the header to set up.
8578c2ecf20Sopenharmony_ci * @size: The requested size of the buffer space.
8588c2ecf20Sopenharmony_ci * @interruptible: Whether to sleep interruptible while waiting for space.
8598c2ecf20Sopenharmony_ci */
8608c2ecf20Sopenharmony_cistatic int vmw_cmdbuf_space_pool(struct vmw_cmdbuf_man *man,
8618c2ecf20Sopenharmony_ci				 struct vmw_cmdbuf_header *header,
8628c2ecf20Sopenharmony_ci				 size_t size,
8638c2ecf20Sopenharmony_ci				 bool interruptible)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	SVGACBHeader *cb_hdr;
8668c2ecf20Sopenharmony_ci	size_t offset;
8678c2ecf20Sopenharmony_ci	int ret;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	if (!man->has_pool)
8708c2ecf20Sopenharmony_ci		return -ENOMEM;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	ret = vmw_cmdbuf_alloc_space(man, &header->node,  size, interruptible);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	if (ret)
8758c2ecf20Sopenharmony_ci		return ret;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	header->cb_header = dma_pool_zalloc(man->headers, GFP_KERNEL,
8788c2ecf20Sopenharmony_ci					    &header->handle);
8798c2ecf20Sopenharmony_ci	if (!header->cb_header) {
8808c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8818c2ecf20Sopenharmony_ci		goto out_no_cb_header;
8828c2ecf20Sopenharmony_ci	}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	header->size = header->node.size << PAGE_SHIFT;
8858c2ecf20Sopenharmony_ci	cb_hdr = header->cb_header;
8868c2ecf20Sopenharmony_ci	offset = header->node.start << PAGE_SHIFT;
8878c2ecf20Sopenharmony_ci	header->cmd = man->map + offset;
8888c2ecf20Sopenharmony_ci	if (man->using_mob) {
8898c2ecf20Sopenharmony_ci		cb_hdr->flags = SVGA_CB_FLAG_MOB;
8908c2ecf20Sopenharmony_ci		cb_hdr->ptr.mob.mobid = man->cmd_space->mem.start;
8918c2ecf20Sopenharmony_ci		cb_hdr->ptr.mob.mobOffset = offset;
8928c2ecf20Sopenharmony_ci	} else {
8938c2ecf20Sopenharmony_ci		cb_hdr->ptr.pa = (u64)man->handle + (u64)offset;
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	return 0;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ciout_no_cb_header:
8998c2ecf20Sopenharmony_ci	spin_lock(&man->lock);
9008c2ecf20Sopenharmony_ci	drm_mm_remove_node(&header->node);
9018c2ecf20Sopenharmony_ci	spin_unlock(&man->lock);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	return ret;
9048c2ecf20Sopenharmony_ci}
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci/**
9078c2ecf20Sopenharmony_ci * vmw_cmdbuf_space_inline - Set up a command buffer header with
9088c2ecf20Sopenharmony_ci * inline command buffer space.
9098c2ecf20Sopenharmony_ci *
9108c2ecf20Sopenharmony_ci * @man: The command buffer manager.
9118c2ecf20Sopenharmony_ci * @header: Pointer to the header to set up.
9128c2ecf20Sopenharmony_ci * @size: The requested size of the buffer space.
9138c2ecf20Sopenharmony_ci */
9148c2ecf20Sopenharmony_cistatic int vmw_cmdbuf_space_inline(struct vmw_cmdbuf_man *man,
9158c2ecf20Sopenharmony_ci				   struct vmw_cmdbuf_header *header,
9168c2ecf20Sopenharmony_ci				   int size)
9178c2ecf20Sopenharmony_ci{
9188c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_dheader *dheader;
9198c2ecf20Sopenharmony_ci	SVGACBHeader *cb_hdr;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(size > VMW_CMDBUF_INLINE_SIZE))
9228c2ecf20Sopenharmony_ci		return -ENOMEM;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	dheader = dma_pool_zalloc(man->dheaders, GFP_KERNEL,
9258c2ecf20Sopenharmony_ci				  &header->handle);
9268c2ecf20Sopenharmony_ci	if (!dheader)
9278c2ecf20Sopenharmony_ci		return -ENOMEM;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	header->inline_space = true;
9308c2ecf20Sopenharmony_ci	header->size = VMW_CMDBUF_INLINE_SIZE;
9318c2ecf20Sopenharmony_ci	cb_hdr = &dheader->cb_header;
9328c2ecf20Sopenharmony_ci	header->cb_header = cb_hdr;
9338c2ecf20Sopenharmony_ci	header->cmd = dheader->cmd;
9348c2ecf20Sopenharmony_ci	cb_hdr->status = SVGA_CB_STATUS_NONE;
9358c2ecf20Sopenharmony_ci	cb_hdr->flags = SVGA_CB_FLAG_NONE;
9368c2ecf20Sopenharmony_ci	cb_hdr->ptr.pa = (u64)header->handle +
9378c2ecf20Sopenharmony_ci		(u64)offsetof(struct vmw_cmdbuf_dheader, cmd);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	return 0;
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci/**
9438c2ecf20Sopenharmony_ci * vmw_cmdbuf_alloc - Allocate a command buffer header complete with
9448c2ecf20Sopenharmony_ci * command buffer space.
9458c2ecf20Sopenharmony_ci *
9468c2ecf20Sopenharmony_ci * @man: The command buffer manager.
9478c2ecf20Sopenharmony_ci * @size: The requested size of the buffer space.
9488c2ecf20Sopenharmony_ci * @interruptible: Whether to sleep interruptible while waiting for space.
9498c2ecf20Sopenharmony_ci * @p_header: points to a header pointer to populate on successful return.
9508c2ecf20Sopenharmony_ci *
9518c2ecf20Sopenharmony_ci * Returns a pointer to command buffer space if successful. Otherwise
9528c2ecf20Sopenharmony_ci * returns an error pointer. The header pointer returned in @p_header should
9538c2ecf20Sopenharmony_ci * be used for upcoming calls to vmw_cmdbuf_reserve() and vmw_cmdbuf_commit().
9548c2ecf20Sopenharmony_ci */
9558c2ecf20Sopenharmony_civoid *vmw_cmdbuf_alloc(struct vmw_cmdbuf_man *man,
9568c2ecf20Sopenharmony_ci		       size_t size, bool interruptible,
9578c2ecf20Sopenharmony_ci		       struct vmw_cmdbuf_header **p_header)
9588c2ecf20Sopenharmony_ci{
9598c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_header *header;
9608c2ecf20Sopenharmony_ci	int ret = 0;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	*p_header = NULL;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	header = kzalloc(sizeof(*header), GFP_KERNEL);
9658c2ecf20Sopenharmony_ci	if (!header)
9668c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	if (size <= VMW_CMDBUF_INLINE_SIZE)
9698c2ecf20Sopenharmony_ci		ret = vmw_cmdbuf_space_inline(man, header, size);
9708c2ecf20Sopenharmony_ci	else
9718c2ecf20Sopenharmony_ci		ret = vmw_cmdbuf_space_pool(man, header, size, interruptible);
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	if (ret) {
9748c2ecf20Sopenharmony_ci		kfree(header);
9758c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
9768c2ecf20Sopenharmony_ci	}
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	header->man = man;
9798c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&header->list);
9808c2ecf20Sopenharmony_ci	header->cb_header->status = SVGA_CB_STATUS_NONE;
9818c2ecf20Sopenharmony_ci	*p_header = header;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	return header->cmd;
9848c2ecf20Sopenharmony_ci}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci/**
9878c2ecf20Sopenharmony_ci * vmw_cmdbuf_reserve_cur - Reserve space for commands in the current
9888c2ecf20Sopenharmony_ci * command buffer.
9898c2ecf20Sopenharmony_ci *
9908c2ecf20Sopenharmony_ci * @man: The command buffer manager.
9918c2ecf20Sopenharmony_ci * @size: The requested size of the commands.
9928c2ecf20Sopenharmony_ci * @ctx_id: The context id if any. Otherwise set to SVGA3D_REG_INVALID.
9938c2ecf20Sopenharmony_ci * @interruptible: Whether to sleep interruptible while waiting for space.
9948c2ecf20Sopenharmony_ci *
9958c2ecf20Sopenharmony_ci * Returns a pointer to command buffer space if successful. Otherwise
9968c2ecf20Sopenharmony_ci * returns an error pointer.
9978c2ecf20Sopenharmony_ci */
9988c2ecf20Sopenharmony_cistatic void *vmw_cmdbuf_reserve_cur(struct vmw_cmdbuf_man *man,
9998c2ecf20Sopenharmony_ci				    size_t size,
10008c2ecf20Sopenharmony_ci				    int ctx_id,
10018c2ecf20Sopenharmony_ci				    bool interruptible)
10028c2ecf20Sopenharmony_ci{
10038c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_header *cur;
10048c2ecf20Sopenharmony_ci	void *ret;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	if (vmw_cmdbuf_cur_lock(man, interruptible))
10078c2ecf20Sopenharmony_ci		return ERR_PTR(-ERESTARTSYS);
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	cur = man->cur;
10108c2ecf20Sopenharmony_ci	if (cur && (size + man->cur_pos > cur->size ||
10118c2ecf20Sopenharmony_ci		    ((cur->cb_header->flags & SVGA_CB_FLAG_DX_CONTEXT) &&
10128c2ecf20Sopenharmony_ci		     ctx_id != cur->cb_header->dxContext)))
10138c2ecf20Sopenharmony_ci		__vmw_cmdbuf_cur_flush(man);
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	if (!man->cur) {
10168c2ecf20Sopenharmony_ci		ret = vmw_cmdbuf_alloc(man,
10178c2ecf20Sopenharmony_ci				       max_t(size_t, size, man->default_size),
10188c2ecf20Sopenharmony_ci				       interruptible, &man->cur);
10198c2ecf20Sopenharmony_ci		if (IS_ERR(ret)) {
10208c2ecf20Sopenharmony_ci			vmw_cmdbuf_cur_unlock(man);
10218c2ecf20Sopenharmony_ci			return ret;
10228c2ecf20Sopenharmony_ci		}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci		cur = man->cur;
10258c2ecf20Sopenharmony_ci	}
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	if (ctx_id != SVGA3D_INVALID_ID) {
10288c2ecf20Sopenharmony_ci		cur->cb_header->flags |= SVGA_CB_FLAG_DX_CONTEXT;
10298c2ecf20Sopenharmony_ci		cur->cb_header->dxContext = ctx_id;
10308c2ecf20Sopenharmony_ci	}
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	cur->reserved = size;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	return (void *) (man->cur->cmd + man->cur_pos);
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci/**
10388c2ecf20Sopenharmony_ci * vmw_cmdbuf_commit_cur - Commit commands in the current command buffer.
10398c2ecf20Sopenharmony_ci *
10408c2ecf20Sopenharmony_ci * @man: The command buffer manager.
10418c2ecf20Sopenharmony_ci * @size: The size of the commands actually written.
10428c2ecf20Sopenharmony_ci * @flush: Whether to flush the command buffer immediately.
10438c2ecf20Sopenharmony_ci */
10448c2ecf20Sopenharmony_cistatic void vmw_cmdbuf_commit_cur(struct vmw_cmdbuf_man *man,
10458c2ecf20Sopenharmony_ci				  size_t size, bool flush)
10468c2ecf20Sopenharmony_ci{
10478c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_header *cur = man->cur;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	lockdep_assert_held_once(&man->cur_mutex);
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	WARN_ON(size > cur->reserved);
10528c2ecf20Sopenharmony_ci	man->cur_pos += size;
10538c2ecf20Sopenharmony_ci	if (!size)
10548c2ecf20Sopenharmony_ci		cur->cb_header->flags &= ~SVGA_CB_FLAG_DX_CONTEXT;
10558c2ecf20Sopenharmony_ci	if (flush)
10568c2ecf20Sopenharmony_ci		__vmw_cmdbuf_cur_flush(man);
10578c2ecf20Sopenharmony_ci	vmw_cmdbuf_cur_unlock(man);
10588c2ecf20Sopenharmony_ci}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci/**
10618c2ecf20Sopenharmony_ci * vmw_cmdbuf_reserve - Reserve space for commands in a command buffer.
10628c2ecf20Sopenharmony_ci *
10638c2ecf20Sopenharmony_ci * @man: The command buffer manager.
10648c2ecf20Sopenharmony_ci * @size: The requested size of the commands.
10658c2ecf20Sopenharmony_ci * @ctx_id: The context id if any. Otherwise set to SVGA3D_REG_INVALID.
10668c2ecf20Sopenharmony_ci * @interruptible: Whether to sleep interruptible while waiting for space.
10678c2ecf20Sopenharmony_ci * @header: Header of the command buffer. NULL if the current command buffer
10688c2ecf20Sopenharmony_ci * should be used.
10698c2ecf20Sopenharmony_ci *
10708c2ecf20Sopenharmony_ci * Returns a pointer to command buffer space if successful. Otherwise
10718c2ecf20Sopenharmony_ci * returns an error pointer.
10728c2ecf20Sopenharmony_ci */
10738c2ecf20Sopenharmony_civoid *vmw_cmdbuf_reserve(struct vmw_cmdbuf_man *man, size_t size,
10748c2ecf20Sopenharmony_ci			 int ctx_id, bool interruptible,
10758c2ecf20Sopenharmony_ci			 struct vmw_cmdbuf_header *header)
10768c2ecf20Sopenharmony_ci{
10778c2ecf20Sopenharmony_ci	if (!header)
10788c2ecf20Sopenharmony_ci		return vmw_cmdbuf_reserve_cur(man, size, ctx_id, interruptible);
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	if (size > header->size)
10818c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	if (ctx_id != SVGA3D_INVALID_ID) {
10848c2ecf20Sopenharmony_ci		header->cb_header->flags |= SVGA_CB_FLAG_DX_CONTEXT;
10858c2ecf20Sopenharmony_ci		header->cb_header->dxContext = ctx_id;
10868c2ecf20Sopenharmony_ci	}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	header->reserved = size;
10898c2ecf20Sopenharmony_ci	return header->cmd;
10908c2ecf20Sopenharmony_ci}
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci/**
10938c2ecf20Sopenharmony_ci * vmw_cmdbuf_commit - Commit commands in a command buffer.
10948c2ecf20Sopenharmony_ci *
10958c2ecf20Sopenharmony_ci * @man: The command buffer manager.
10968c2ecf20Sopenharmony_ci * @size: The size of the commands actually written.
10978c2ecf20Sopenharmony_ci * @header: Header of the command buffer. NULL if the current command buffer
10988c2ecf20Sopenharmony_ci * should be used.
10998c2ecf20Sopenharmony_ci * @flush: Whether to flush the command buffer immediately.
11008c2ecf20Sopenharmony_ci */
11018c2ecf20Sopenharmony_civoid vmw_cmdbuf_commit(struct vmw_cmdbuf_man *man, size_t size,
11028c2ecf20Sopenharmony_ci		       struct vmw_cmdbuf_header *header, bool flush)
11038c2ecf20Sopenharmony_ci{
11048c2ecf20Sopenharmony_ci	if (!header) {
11058c2ecf20Sopenharmony_ci		vmw_cmdbuf_commit_cur(man, size, flush);
11068c2ecf20Sopenharmony_ci		return;
11078c2ecf20Sopenharmony_ci	}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	(void) vmw_cmdbuf_cur_lock(man, false);
11108c2ecf20Sopenharmony_ci	__vmw_cmdbuf_cur_flush(man);
11118c2ecf20Sopenharmony_ci	WARN_ON(size > header->reserved);
11128c2ecf20Sopenharmony_ci	man->cur = header;
11138c2ecf20Sopenharmony_ci	man->cur_pos = size;
11148c2ecf20Sopenharmony_ci	if (!size)
11158c2ecf20Sopenharmony_ci		header->cb_header->flags &= ~SVGA_CB_FLAG_DX_CONTEXT;
11168c2ecf20Sopenharmony_ci	if (flush)
11178c2ecf20Sopenharmony_ci		__vmw_cmdbuf_cur_flush(man);
11188c2ecf20Sopenharmony_ci	vmw_cmdbuf_cur_unlock(man);
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci/**
11238c2ecf20Sopenharmony_ci * vmw_cmdbuf_send_device_command - Send a command through the device context.
11248c2ecf20Sopenharmony_ci *
11258c2ecf20Sopenharmony_ci * @man: The command buffer manager.
11268c2ecf20Sopenharmony_ci * @command: Pointer to the command to send.
11278c2ecf20Sopenharmony_ci * @size: Size of the command.
11288c2ecf20Sopenharmony_ci *
11298c2ecf20Sopenharmony_ci * Synchronously sends a device context command.
11308c2ecf20Sopenharmony_ci */
11318c2ecf20Sopenharmony_cistatic int vmw_cmdbuf_send_device_command(struct vmw_cmdbuf_man *man,
11328c2ecf20Sopenharmony_ci					  const void *command,
11338c2ecf20Sopenharmony_ci					  size_t size)
11348c2ecf20Sopenharmony_ci{
11358c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_header *header;
11368c2ecf20Sopenharmony_ci	int status;
11378c2ecf20Sopenharmony_ci	void *cmd = vmw_cmdbuf_alloc(man, size, false, &header);
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	if (IS_ERR(cmd))
11408c2ecf20Sopenharmony_ci		return PTR_ERR(cmd);
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	memcpy(cmd, command, size);
11438c2ecf20Sopenharmony_ci	header->cb_header->length = size;
11448c2ecf20Sopenharmony_ci	header->cb_context = SVGA_CB_CONTEXT_DEVICE;
11458c2ecf20Sopenharmony_ci	spin_lock(&man->lock);
11468c2ecf20Sopenharmony_ci	status = vmw_cmdbuf_header_submit(header);
11478c2ecf20Sopenharmony_ci	spin_unlock(&man->lock);
11488c2ecf20Sopenharmony_ci	vmw_cmdbuf_header_free(header);
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	if (status != SVGA_CB_STATUS_COMPLETED) {
11518c2ecf20Sopenharmony_ci		DRM_ERROR("Device context command failed with status %d\n",
11528c2ecf20Sopenharmony_ci			  status);
11538c2ecf20Sopenharmony_ci		return -EINVAL;
11548c2ecf20Sopenharmony_ci	}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	return 0;
11578c2ecf20Sopenharmony_ci}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci/**
11608c2ecf20Sopenharmony_ci * vmw_cmdbuf_preempt - Send a preempt command through the device
11618c2ecf20Sopenharmony_ci * context.
11628c2ecf20Sopenharmony_ci *
11638c2ecf20Sopenharmony_ci * @man: The command buffer manager.
11648c2ecf20Sopenharmony_ci *
11658c2ecf20Sopenharmony_ci * Synchronously sends a preempt command.
11668c2ecf20Sopenharmony_ci */
11678c2ecf20Sopenharmony_cistatic int vmw_cmdbuf_preempt(struct vmw_cmdbuf_man *man, u32 context)
11688c2ecf20Sopenharmony_ci{
11698c2ecf20Sopenharmony_ci	struct {
11708c2ecf20Sopenharmony_ci		uint32 id;
11718c2ecf20Sopenharmony_ci		SVGADCCmdPreempt body;
11728c2ecf20Sopenharmony_ci	} __packed cmd;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	cmd.id = SVGA_DC_CMD_PREEMPT;
11758c2ecf20Sopenharmony_ci	cmd.body.context = SVGA_CB_CONTEXT_0 + context;
11768c2ecf20Sopenharmony_ci	cmd.body.ignoreIDZero = 0;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	return vmw_cmdbuf_send_device_command(man, &cmd, sizeof(cmd));
11798c2ecf20Sopenharmony_ci}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci/**
11838c2ecf20Sopenharmony_ci * vmw_cmdbuf_startstop - Send a start / stop command through the device
11848c2ecf20Sopenharmony_ci * context.
11858c2ecf20Sopenharmony_ci *
11868c2ecf20Sopenharmony_ci * @man: The command buffer manager.
11878c2ecf20Sopenharmony_ci * @enable: Whether to enable or disable the context.
11888c2ecf20Sopenharmony_ci *
11898c2ecf20Sopenharmony_ci * Synchronously sends a device start / stop context command.
11908c2ecf20Sopenharmony_ci */
11918c2ecf20Sopenharmony_cistatic int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, u32 context,
11928c2ecf20Sopenharmony_ci				bool enable)
11938c2ecf20Sopenharmony_ci{
11948c2ecf20Sopenharmony_ci	struct {
11958c2ecf20Sopenharmony_ci		uint32 id;
11968c2ecf20Sopenharmony_ci		SVGADCCmdStartStop body;
11978c2ecf20Sopenharmony_ci	} __packed cmd;
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	cmd.id = SVGA_DC_CMD_START_STOP_CONTEXT;
12008c2ecf20Sopenharmony_ci	cmd.body.enable = (enable) ? 1 : 0;
12018c2ecf20Sopenharmony_ci	cmd.body.context = SVGA_CB_CONTEXT_0 + context;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	return vmw_cmdbuf_send_device_command(man, &cmd, sizeof(cmd));
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci/**
12078c2ecf20Sopenharmony_ci * vmw_cmdbuf_set_pool_size - Set command buffer manager sizes
12088c2ecf20Sopenharmony_ci *
12098c2ecf20Sopenharmony_ci * @man: The command buffer manager.
12108c2ecf20Sopenharmony_ci * @size: The size of the main space pool.
12118c2ecf20Sopenharmony_ci * @default_size: The default size of the command buffer for small kernel
12128c2ecf20Sopenharmony_ci * submissions.
12138c2ecf20Sopenharmony_ci *
12148c2ecf20Sopenharmony_ci * Set the size and allocate the main command buffer space pool,
12158c2ecf20Sopenharmony_ci * as well as the default size of the command buffer for
12168c2ecf20Sopenharmony_ci * small kernel submissions. If successful, this enables large command
12178c2ecf20Sopenharmony_ci * submissions. Note that this function requires that rudimentary command
12188c2ecf20Sopenharmony_ci * submission is already available and that the MOB memory manager is alive.
12198c2ecf20Sopenharmony_ci * Returns 0 on success. Negative error code on failure.
12208c2ecf20Sopenharmony_ci */
12218c2ecf20Sopenharmony_ciint vmw_cmdbuf_set_pool_size(struct vmw_cmdbuf_man *man,
12228c2ecf20Sopenharmony_ci			     size_t size, size_t default_size)
12238c2ecf20Sopenharmony_ci{
12248c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv = man->dev_priv;
12258c2ecf20Sopenharmony_ci	bool dummy;
12268c2ecf20Sopenharmony_ci	int ret;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	if (man->has_pool)
12298c2ecf20Sopenharmony_ci		return -EINVAL;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	/* First, try to allocate a huge chunk of DMA memory */
12328c2ecf20Sopenharmony_ci	size = PAGE_ALIGN(size);
12338c2ecf20Sopenharmony_ci	man->map = dma_alloc_coherent(&dev_priv->dev->pdev->dev, size,
12348c2ecf20Sopenharmony_ci				      &man->handle, GFP_KERNEL);
12358c2ecf20Sopenharmony_ci	if (man->map) {
12368c2ecf20Sopenharmony_ci		man->using_mob = false;
12378c2ecf20Sopenharmony_ci	} else {
12388c2ecf20Sopenharmony_ci		/*
12398c2ecf20Sopenharmony_ci		 * DMA memory failed. If we can have command buffers in a
12408c2ecf20Sopenharmony_ci		 * MOB, try to use that instead. Note that this will
12418c2ecf20Sopenharmony_ci		 * actually call into the already enabled manager, when
12428c2ecf20Sopenharmony_ci		 * binding the MOB.
12438c2ecf20Sopenharmony_ci		 */
12448c2ecf20Sopenharmony_ci		if (!(dev_priv->capabilities & SVGA_CAP_DX) ||
12458c2ecf20Sopenharmony_ci		    !dev_priv->has_mob)
12468c2ecf20Sopenharmony_ci			return -ENOMEM;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci		ret = ttm_bo_create(&dev_priv->bdev, size, ttm_bo_type_device,
12498c2ecf20Sopenharmony_ci				    &vmw_mob_ne_placement, 0, false,
12508c2ecf20Sopenharmony_ci				    &man->cmd_space);
12518c2ecf20Sopenharmony_ci		if (ret)
12528c2ecf20Sopenharmony_ci			return ret;
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci		man->using_mob = true;
12558c2ecf20Sopenharmony_ci		ret = ttm_bo_kmap(man->cmd_space, 0, size >> PAGE_SHIFT,
12568c2ecf20Sopenharmony_ci				  &man->map_obj);
12578c2ecf20Sopenharmony_ci		if (ret)
12588c2ecf20Sopenharmony_ci			goto out_no_map;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci		man->map = ttm_kmap_obj_virtual(&man->map_obj, &dummy);
12618c2ecf20Sopenharmony_ci	}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	man->size = size;
12648c2ecf20Sopenharmony_ci	drm_mm_init(&man->mm, 0, size >> PAGE_SHIFT);
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	man->has_pool = true;
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	/*
12698c2ecf20Sopenharmony_ci	 * For now, set the default size to VMW_CMDBUF_INLINE_SIZE to
12708c2ecf20Sopenharmony_ci	 * prevent deadlocks from happening when vmw_cmdbuf_space_pool()
12718c2ecf20Sopenharmony_ci	 * needs to wait for space and we block on further command
12728c2ecf20Sopenharmony_ci	 * submissions to be able to free up space.
12738c2ecf20Sopenharmony_ci	 */
12748c2ecf20Sopenharmony_ci	man->default_size = VMW_CMDBUF_INLINE_SIZE;
12758c2ecf20Sopenharmony_ci	DRM_INFO("Using command buffers with %s pool.\n",
12768c2ecf20Sopenharmony_ci		 (man->using_mob) ? "MOB" : "DMA");
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	return 0;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ciout_no_map:
12818c2ecf20Sopenharmony_ci	if (man->using_mob) {
12828c2ecf20Sopenharmony_ci		ttm_bo_put(man->cmd_space);
12838c2ecf20Sopenharmony_ci		man->cmd_space = NULL;
12848c2ecf20Sopenharmony_ci	}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	return ret;
12878c2ecf20Sopenharmony_ci}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci/**
12908c2ecf20Sopenharmony_ci * vmw_cmdbuf_man_create: Create a command buffer manager and enable it for
12918c2ecf20Sopenharmony_ci * inline command buffer submissions only.
12928c2ecf20Sopenharmony_ci *
12938c2ecf20Sopenharmony_ci * @dev_priv: Pointer to device private structure.
12948c2ecf20Sopenharmony_ci *
12958c2ecf20Sopenharmony_ci * Returns a pointer to a cummand buffer manager to success or error pointer
12968c2ecf20Sopenharmony_ci * on failure. The command buffer manager will be enabled for submissions of
12978c2ecf20Sopenharmony_ci * size VMW_CMDBUF_INLINE_SIZE only.
12988c2ecf20Sopenharmony_ci */
12998c2ecf20Sopenharmony_cistruct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv)
13008c2ecf20Sopenharmony_ci{
13018c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_man *man;
13028c2ecf20Sopenharmony_ci	struct vmw_cmdbuf_context *ctx;
13038c2ecf20Sopenharmony_ci	unsigned int i;
13048c2ecf20Sopenharmony_ci	int ret;
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	if (!(dev_priv->capabilities & SVGA_CAP_COMMAND_BUFFERS))
13078c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOSYS);
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	man = kzalloc(sizeof(*man), GFP_KERNEL);
13108c2ecf20Sopenharmony_ci	if (!man)
13118c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	man->num_contexts = (dev_priv->capabilities & SVGA_CAP_HP_CMD_QUEUE) ?
13148c2ecf20Sopenharmony_ci		2 : 1;
13158c2ecf20Sopenharmony_ci	man->headers = dma_pool_create("vmwgfx cmdbuf",
13168c2ecf20Sopenharmony_ci				       &dev_priv->dev->pdev->dev,
13178c2ecf20Sopenharmony_ci				       sizeof(SVGACBHeader),
13188c2ecf20Sopenharmony_ci				       64, PAGE_SIZE);
13198c2ecf20Sopenharmony_ci	if (!man->headers) {
13208c2ecf20Sopenharmony_ci		ret = -ENOMEM;
13218c2ecf20Sopenharmony_ci		goto out_no_pool;
13228c2ecf20Sopenharmony_ci	}
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	man->dheaders = dma_pool_create("vmwgfx inline cmdbuf",
13258c2ecf20Sopenharmony_ci					&dev_priv->dev->pdev->dev,
13268c2ecf20Sopenharmony_ci					sizeof(struct vmw_cmdbuf_dheader),
13278c2ecf20Sopenharmony_ci					64, PAGE_SIZE);
13288c2ecf20Sopenharmony_ci	if (!man->dheaders) {
13298c2ecf20Sopenharmony_ci		ret = -ENOMEM;
13308c2ecf20Sopenharmony_ci		goto out_no_dpool;
13318c2ecf20Sopenharmony_ci	}
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	for_each_cmdbuf_ctx(man, i, ctx)
13348c2ecf20Sopenharmony_ci		vmw_cmdbuf_ctx_init(ctx);
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&man->error);
13378c2ecf20Sopenharmony_ci	spin_lock_init(&man->lock);
13388c2ecf20Sopenharmony_ci	mutex_init(&man->cur_mutex);
13398c2ecf20Sopenharmony_ci	mutex_init(&man->space_mutex);
13408c2ecf20Sopenharmony_ci	mutex_init(&man->error_mutex);
13418c2ecf20Sopenharmony_ci	man->default_size = VMW_CMDBUF_INLINE_SIZE;
13428c2ecf20Sopenharmony_ci	init_waitqueue_head(&man->alloc_queue);
13438c2ecf20Sopenharmony_ci	init_waitqueue_head(&man->idle_queue);
13448c2ecf20Sopenharmony_ci	man->dev_priv = dev_priv;
13458c2ecf20Sopenharmony_ci	man->max_hw_submitted = SVGA_CB_MAX_QUEUED_PER_CONTEXT - 1;
13468c2ecf20Sopenharmony_ci	INIT_WORK(&man->work, &vmw_cmdbuf_work_func);
13478c2ecf20Sopenharmony_ci	vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_ERROR,
13488c2ecf20Sopenharmony_ci			       &dev_priv->error_waiters);
13498c2ecf20Sopenharmony_ci	ret = vmw_cmdbuf_startstop(man, 0, true);
13508c2ecf20Sopenharmony_ci	if (ret) {
13518c2ecf20Sopenharmony_ci		DRM_ERROR("Failed starting command buffer contexts\n");
13528c2ecf20Sopenharmony_ci		vmw_cmdbuf_man_destroy(man);
13538c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
13548c2ecf20Sopenharmony_ci	}
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	return man;
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ciout_no_dpool:
13598c2ecf20Sopenharmony_ci	dma_pool_destroy(man->headers);
13608c2ecf20Sopenharmony_ciout_no_pool:
13618c2ecf20Sopenharmony_ci	kfree(man);
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
13648c2ecf20Sopenharmony_ci}
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci/**
13678c2ecf20Sopenharmony_ci * vmw_cmdbuf_remove_pool - Take down the main buffer space pool.
13688c2ecf20Sopenharmony_ci *
13698c2ecf20Sopenharmony_ci * @man: Pointer to a command buffer manager.
13708c2ecf20Sopenharmony_ci *
13718c2ecf20Sopenharmony_ci * This function removes the main buffer space pool, and should be called
13728c2ecf20Sopenharmony_ci * before MOB memory management is removed. When this function has been called,
13738c2ecf20Sopenharmony_ci * only small command buffer submissions of size VMW_CMDBUF_INLINE_SIZE or
13748c2ecf20Sopenharmony_ci * less are allowed, and the default size of the command buffer for small kernel
13758c2ecf20Sopenharmony_ci * submissions is also set to this size.
13768c2ecf20Sopenharmony_ci */
13778c2ecf20Sopenharmony_civoid vmw_cmdbuf_remove_pool(struct vmw_cmdbuf_man *man)
13788c2ecf20Sopenharmony_ci{
13798c2ecf20Sopenharmony_ci	if (!man->has_pool)
13808c2ecf20Sopenharmony_ci		return;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	man->has_pool = false;
13838c2ecf20Sopenharmony_ci	man->default_size = VMW_CMDBUF_INLINE_SIZE;
13848c2ecf20Sopenharmony_ci	(void) vmw_cmdbuf_idle(man, false, 10*HZ);
13858c2ecf20Sopenharmony_ci	if (man->using_mob) {
13868c2ecf20Sopenharmony_ci		(void) ttm_bo_kunmap(&man->map_obj);
13878c2ecf20Sopenharmony_ci		ttm_bo_put(man->cmd_space);
13888c2ecf20Sopenharmony_ci		man->cmd_space = NULL;
13898c2ecf20Sopenharmony_ci	} else {
13908c2ecf20Sopenharmony_ci		dma_free_coherent(&man->dev_priv->dev->pdev->dev,
13918c2ecf20Sopenharmony_ci				  man->size, man->map, man->handle);
13928c2ecf20Sopenharmony_ci	}
13938c2ecf20Sopenharmony_ci}
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci/**
13968c2ecf20Sopenharmony_ci * vmw_cmdbuf_man_destroy - Take down a command buffer manager.
13978c2ecf20Sopenharmony_ci *
13988c2ecf20Sopenharmony_ci * @man: Pointer to a command buffer manager.
13998c2ecf20Sopenharmony_ci *
14008c2ecf20Sopenharmony_ci * This function idles and then destroys a command buffer manager.
14018c2ecf20Sopenharmony_ci */
14028c2ecf20Sopenharmony_civoid vmw_cmdbuf_man_destroy(struct vmw_cmdbuf_man *man)
14038c2ecf20Sopenharmony_ci{
14048c2ecf20Sopenharmony_ci	WARN_ON_ONCE(man->has_pool);
14058c2ecf20Sopenharmony_ci	(void) vmw_cmdbuf_idle(man, false, 10*HZ);
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	if (vmw_cmdbuf_startstop(man, 0, false))
14088c2ecf20Sopenharmony_ci		DRM_ERROR("Failed stopping command buffer contexts.\n");
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	vmw_generic_waiter_remove(man->dev_priv, SVGA_IRQFLAG_ERROR,
14118c2ecf20Sopenharmony_ci				  &man->dev_priv->error_waiters);
14128c2ecf20Sopenharmony_ci	(void) cancel_work_sync(&man->work);
14138c2ecf20Sopenharmony_ci	dma_pool_destroy(man->dheaders);
14148c2ecf20Sopenharmony_ci	dma_pool_destroy(man->headers);
14158c2ecf20Sopenharmony_ci	mutex_destroy(&man->cur_mutex);
14168c2ecf20Sopenharmony_ci	mutex_destroy(&man->space_mutex);
14178c2ecf20Sopenharmony_ci	mutex_destroy(&man->error_mutex);
14188c2ecf20Sopenharmony_ci	kfree(man);
14198c2ecf20Sopenharmony_ci}
1420