162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
262306a36Sopenharmony_ci/**************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright 2015-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
2862306a36Sopenharmony_ci#include "vmwgfx_bo.h"
2962306a36Sopenharmony_ci#include "vmwgfx_drv.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <drm/ttm/ttm_bo.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <linux/dmapool.h>
3462306a36Sopenharmony_ci#include <linux/pci.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/*
3762306a36Sopenharmony_ci * Size of inline command buffers. Try to make sure that a page size is a
3862306a36Sopenharmony_ci * multiple of the DMA pool allocation size.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci#define VMW_CMDBUF_INLINE_ALIGN 64
4162306a36Sopenharmony_ci#define VMW_CMDBUF_INLINE_SIZE \
4262306a36Sopenharmony_ci	(1024 - ALIGN(sizeof(SVGACBHeader), VMW_CMDBUF_INLINE_ALIGN))
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/**
4562306a36Sopenharmony_ci * struct vmw_cmdbuf_context - Command buffer context queues
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci * @submitted: List of command buffers that have been submitted to the
4862306a36Sopenharmony_ci * manager but not yet submitted to hardware.
4962306a36Sopenharmony_ci * @hw_submitted: List of command buffers submitted to hardware.
5062306a36Sopenharmony_ci * @preempted: List of preempted command buffers.
5162306a36Sopenharmony_ci * @num_hw_submitted: Number of buffers currently being processed by hardware
5262306a36Sopenharmony_ci * @block_submission: Identifies a block command submission.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistruct vmw_cmdbuf_context {
5562306a36Sopenharmony_ci	struct list_head submitted;
5662306a36Sopenharmony_ci	struct list_head hw_submitted;
5762306a36Sopenharmony_ci	struct list_head preempted;
5862306a36Sopenharmony_ci	unsigned num_hw_submitted;
5962306a36Sopenharmony_ci	bool block_submission;
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/**
6362306a36Sopenharmony_ci * struct vmw_cmdbuf_man - Command buffer manager
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci * @cur_mutex: Mutex protecting the command buffer used for incremental small
6662306a36Sopenharmony_ci * kernel command submissions, @cur.
6762306a36Sopenharmony_ci * @space_mutex: Mutex to protect against starvation when we allocate
6862306a36Sopenharmony_ci * main pool buffer space.
6962306a36Sopenharmony_ci * @error_mutex: Mutex to serialize the work queue error handling.
7062306a36Sopenharmony_ci * Note this is not needed if the same workqueue handler
7162306a36Sopenharmony_ci * can't race with itself...
7262306a36Sopenharmony_ci * @work: A struct work_struct implementeing command buffer error handling.
7362306a36Sopenharmony_ci * Immutable.
7462306a36Sopenharmony_ci * @dev_priv: Pointer to the device private struct. Immutable.
7562306a36Sopenharmony_ci * @ctx: Array of command buffer context queues. The queues and the context
7662306a36Sopenharmony_ci * data is protected by @lock.
7762306a36Sopenharmony_ci * @error: List of command buffers that have caused device errors.
7862306a36Sopenharmony_ci * Protected by @lock.
7962306a36Sopenharmony_ci * @mm: Range manager for the command buffer space. Manager allocations and
8062306a36Sopenharmony_ci * frees are protected by @lock.
8162306a36Sopenharmony_ci * @cmd_space: Buffer object for the command buffer space, unless we were
8262306a36Sopenharmony_ci * able to make a contigous coherent DMA memory allocation, @handle. Immutable.
8362306a36Sopenharmony_ci * @map: Pointer to command buffer space. May be a mapped buffer object or
8462306a36Sopenharmony_ci * a contigous coherent DMA memory allocation. Immutable.
8562306a36Sopenharmony_ci * @cur: Command buffer for small kernel command submissions. Protected by
8662306a36Sopenharmony_ci * the @cur_mutex.
8762306a36Sopenharmony_ci * @cur_pos: Space already used in @cur. Protected by @cur_mutex.
8862306a36Sopenharmony_ci * @default_size: Default size for the @cur command buffer. Immutable.
8962306a36Sopenharmony_ci * @max_hw_submitted: Max number of in-flight command buffers the device can
9062306a36Sopenharmony_ci * handle. Immutable.
9162306a36Sopenharmony_ci * @lock: Spinlock protecting command submission queues.
9262306a36Sopenharmony_ci * @headers: Pool of DMA memory for device command buffer headers.
9362306a36Sopenharmony_ci * Internal protection.
9462306a36Sopenharmony_ci * @dheaders: Pool of DMA memory for device command buffer headers with trailing
9562306a36Sopenharmony_ci * space for inline data. Internal protection.
9662306a36Sopenharmony_ci * @alloc_queue: Wait queue for processes waiting to allocate command buffer
9762306a36Sopenharmony_ci * space.
9862306a36Sopenharmony_ci * @idle_queue: Wait queue for processes waiting for command buffer idle.
9962306a36Sopenharmony_ci * @irq_on: Whether the process function has requested irq to be turned on.
10062306a36Sopenharmony_ci * Protected by @lock.
10162306a36Sopenharmony_ci * @using_mob: Whether the command buffer space is a MOB or a contigous DMA
10262306a36Sopenharmony_ci * allocation. Immutable.
10362306a36Sopenharmony_ci * @has_pool: Has a large pool of DMA memory which allows larger allocations.
10462306a36Sopenharmony_ci * Typically this is false only during bootstrap.
10562306a36Sopenharmony_ci * @handle: DMA address handle for the command buffer space if @using_mob is
10662306a36Sopenharmony_ci * false. Immutable.
10762306a36Sopenharmony_ci * @size: The size of the command buffer space. Immutable.
10862306a36Sopenharmony_ci * @num_contexts: Number of contexts actually enabled.
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_cistruct vmw_cmdbuf_man {
11162306a36Sopenharmony_ci	struct mutex cur_mutex;
11262306a36Sopenharmony_ci	struct mutex space_mutex;
11362306a36Sopenharmony_ci	struct mutex error_mutex;
11462306a36Sopenharmony_ci	struct work_struct work;
11562306a36Sopenharmony_ci	struct vmw_private *dev_priv;
11662306a36Sopenharmony_ci	struct vmw_cmdbuf_context ctx[SVGA_CB_CONTEXT_MAX];
11762306a36Sopenharmony_ci	struct list_head error;
11862306a36Sopenharmony_ci	struct drm_mm mm;
11962306a36Sopenharmony_ci	struct vmw_bo *cmd_space;
12062306a36Sopenharmony_ci	u8 *map;
12162306a36Sopenharmony_ci	struct vmw_cmdbuf_header *cur;
12262306a36Sopenharmony_ci	size_t cur_pos;
12362306a36Sopenharmony_ci	size_t default_size;
12462306a36Sopenharmony_ci	unsigned max_hw_submitted;
12562306a36Sopenharmony_ci	spinlock_t lock;
12662306a36Sopenharmony_ci	struct dma_pool *headers;
12762306a36Sopenharmony_ci	struct dma_pool *dheaders;
12862306a36Sopenharmony_ci	wait_queue_head_t alloc_queue;
12962306a36Sopenharmony_ci	wait_queue_head_t idle_queue;
13062306a36Sopenharmony_ci	bool irq_on;
13162306a36Sopenharmony_ci	bool using_mob;
13262306a36Sopenharmony_ci	bool has_pool;
13362306a36Sopenharmony_ci	dma_addr_t handle;
13462306a36Sopenharmony_ci	size_t size;
13562306a36Sopenharmony_ci	u32 num_contexts;
13662306a36Sopenharmony_ci};
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/**
13962306a36Sopenharmony_ci * struct vmw_cmdbuf_header - Command buffer metadata
14062306a36Sopenharmony_ci *
14162306a36Sopenharmony_ci * @man: The command buffer manager.
14262306a36Sopenharmony_ci * @cb_header: Device command buffer header, allocated from a DMA pool.
14362306a36Sopenharmony_ci * @cb_context: The device command buffer context.
14462306a36Sopenharmony_ci * @list: List head for attaching to the manager lists.
14562306a36Sopenharmony_ci * @node: The range manager node.
14662306a36Sopenharmony_ci * @handle: The DMA address of @cb_header. Handed to the device on command
14762306a36Sopenharmony_ci * buffer submission.
14862306a36Sopenharmony_ci * @cmd: Pointer to the command buffer space of this buffer.
14962306a36Sopenharmony_ci * @size: Size of the command buffer space of this buffer.
15062306a36Sopenharmony_ci * @reserved: Reserved space of this buffer.
15162306a36Sopenharmony_ci * @inline_space: Whether inline command buffer space is used.
15262306a36Sopenharmony_ci */
15362306a36Sopenharmony_cistruct vmw_cmdbuf_header {
15462306a36Sopenharmony_ci	struct vmw_cmdbuf_man *man;
15562306a36Sopenharmony_ci	SVGACBHeader *cb_header;
15662306a36Sopenharmony_ci	SVGACBContext cb_context;
15762306a36Sopenharmony_ci	struct list_head list;
15862306a36Sopenharmony_ci	struct drm_mm_node node;
15962306a36Sopenharmony_ci	dma_addr_t handle;
16062306a36Sopenharmony_ci	u8 *cmd;
16162306a36Sopenharmony_ci	size_t size;
16262306a36Sopenharmony_ci	size_t reserved;
16362306a36Sopenharmony_ci	bool inline_space;
16462306a36Sopenharmony_ci};
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/**
16762306a36Sopenharmony_ci * struct vmw_cmdbuf_dheader - Device command buffer header with inline
16862306a36Sopenharmony_ci * command buffer space.
16962306a36Sopenharmony_ci *
17062306a36Sopenharmony_ci * @cb_header: Device command buffer header.
17162306a36Sopenharmony_ci * @cmd: Inline command buffer space.
17262306a36Sopenharmony_ci */
17362306a36Sopenharmony_cistruct vmw_cmdbuf_dheader {
17462306a36Sopenharmony_ci	SVGACBHeader cb_header;
17562306a36Sopenharmony_ci	u8 cmd[VMW_CMDBUF_INLINE_SIZE] __aligned(VMW_CMDBUF_INLINE_ALIGN);
17662306a36Sopenharmony_ci};
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/**
17962306a36Sopenharmony_ci * struct vmw_cmdbuf_alloc_info - Command buffer space allocation metadata
18062306a36Sopenharmony_ci *
18162306a36Sopenharmony_ci * @page_size: Size of requested command buffer space in pages.
18262306a36Sopenharmony_ci * @node: Pointer to the range manager node.
18362306a36Sopenharmony_ci * @done: True if this allocation has succeeded.
18462306a36Sopenharmony_ci */
18562306a36Sopenharmony_cistruct vmw_cmdbuf_alloc_info {
18662306a36Sopenharmony_ci	size_t page_size;
18762306a36Sopenharmony_ci	struct drm_mm_node *node;
18862306a36Sopenharmony_ci	bool done;
18962306a36Sopenharmony_ci};
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci/* Loop over each context in the command buffer manager. */
19262306a36Sopenharmony_ci#define for_each_cmdbuf_ctx(_man, _i, _ctx)				\
19362306a36Sopenharmony_ci	for (_i = 0, _ctx = &(_man)->ctx[0]; (_i) < (_man)->num_contexts; \
19462306a36Sopenharmony_ci	     ++(_i), ++(_ctx))
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, u32 context,
19762306a36Sopenharmony_ci				bool enable);
19862306a36Sopenharmony_cistatic int vmw_cmdbuf_preempt(struct vmw_cmdbuf_man *man, u32 context);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci/**
20162306a36Sopenharmony_ci * vmw_cmdbuf_cur_lock - Helper to lock the cur_mutex.
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * @man: The range manager.
20462306a36Sopenharmony_ci * @interruptible: Whether to wait interruptible when locking.
20562306a36Sopenharmony_ci */
20662306a36Sopenharmony_cistatic int vmw_cmdbuf_cur_lock(struct vmw_cmdbuf_man *man, bool interruptible)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	if (interruptible) {
20962306a36Sopenharmony_ci		if (mutex_lock_interruptible(&man->cur_mutex))
21062306a36Sopenharmony_ci			return -ERESTARTSYS;
21162306a36Sopenharmony_ci	} else {
21262306a36Sopenharmony_ci		mutex_lock(&man->cur_mutex);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return 0;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/**
21962306a36Sopenharmony_ci * vmw_cmdbuf_cur_unlock - Helper to unlock the cur_mutex.
22062306a36Sopenharmony_ci *
22162306a36Sopenharmony_ci * @man: The range manager.
22262306a36Sopenharmony_ci */
22362306a36Sopenharmony_cistatic void vmw_cmdbuf_cur_unlock(struct vmw_cmdbuf_man *man)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	mutex_unlock(&man->cur_mutex);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/**
22962306a36Sopenharmony_ci * vmw_cmdbuf_header_inline_free - Free a struct vmw_cmdbuf_header that has
23062306a36Sopenharmony_ci * been used for the device context with inline command buffers.
23162306a36Sopenharmony_ci * Need not be called locked.
23262306a36Sopenharmony_ci *
23362306a36Sopenharmony_ci * @header: Pointer to the header to free.
23462306a36Sopenharmony_ci */
23562306a36Sopenharmony_cistatic void vmw_cmdbuf_header_inline_free(struct vmw_cmdbuf_header *header)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct vmw_cmdbuf_dheader *dheader;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (WARN_ON_ONCE(!header->inline_space))
24062306a36Sopenharmony_ci		return;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	dheader = container_of(header->cb_header, struct vmw_cmdbuf_dheader,
24362306a36Sopenharmony_ci			       cb_header);
24462306a36Sopenharmony_ci	dma_pool_free(header->man->dheaders, dheader, header->handle);
24562306a36Sopenharmony_ci	kfree(header);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/**
24962306a36Sopenharmony_ci * __vmw_cmdbuf_header_free - Free a struct vmw_cmdbuf_header  and its
25062306a36Sopenharmony_ci * associated structures.
25162306a36Sopenharmony_ci *
25262306a36Sopenharmony_ci * @header: Pointer to the header to free.
25362306a36Sopenharmony_ci *
25462306a36Sopenharmony_ci * For internal use. Must be called with man::lock held.
25562306a36Sopenharmony_ci */
25662306a36Sopenharmony_cistatic void __vmw_cmdbuf_header_free(struct vmw_cmdbuf_header *header)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct vmw_cmdbuf_man *man = header->man;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	lockdep_assert_held_once(&man->lock);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (header->inline_space) {
26362306a36Sopenharmony_ci		vmw_cmdbuf_header_inline_free(header);
26462306a36Sopenharmony_ci		return;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	drm_mm_remove_node(&header->node);
26862306a36Sopenharmony_ci	wake_up_all(&man->alloc_queue);
26962306a36Sopenharmony_ci	if (header->cb_header)
27062306a36Sopenharmony_ci		dma_pool_free(man->headers, header->cb_header,
27162306a36Sopenharmony_ci			      header->handle);
27262306a36Sopenharmony_ci	kfree(header);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci/**
27662306a36Sopenharmony_ci * vmw_cmdbuf_header_free - Free a struct vmw_cmdbuf_header  and its
27762306a36Sopenharmony_ci * associated structures.
27862306a36Sopenharmony_ci *
27962306a36Sopenharmony_ci * @header: Pointer to the header to free.
28062306a36Sopenharmony_ci */
28162306a36Sopenharmony_civoid vmw_cmdbuf_header_free(struct vmw_cmdbuf_header *header)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct vmw_cmdbuf_man *man = header->man;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* Avoid locking if inline_space */
28662306a36Sopenharmony_ci	if (header->inline_space) {
28762306a36Sopenharmony_ci		vmw_cmdbuf_header_inline_free(header);
28862306a36Sopenharmony_ci		return;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci	spin_lock(&man->lock);
29162306a36Sopenharmony_ci	__vmw_cmdbuf_header_free(header);
29262306a36Sopenharmony_ci	spin_unlock(&man->lock);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci/**
29762306a36Sopenharmony_ci * vmw_cmdbuf_header_submit: Submit a command buffer to hardware.
29862306a36Sopenharmony_ci *
29962306a36Sopenharmony_ci * @header: The header of the buffer to submit.
30062306a36Sopenharmony_ci */
30162306a36Sopenharmony_cistatic int vmw_cmdbuf_header_submit(struct vmw_cmdbuf_header *header)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct vmw_cmdbuf_man *man = header->man;
30462306a36Sopenharmony_ci	u32 val;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	val = upper_32_bits(header->handle);
30762306a36Sopenharmony_ci	vmw_write(man->dev_priv, SVGA_REG_COMMAND_HIGH, val);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	val = lower_32_bits(header->handle);
31062306a36Sopenharmony_ci	val |= header->cb_context & SVGA_CB_CONTEXT_MASK;
31162306a36Sopenharmony_ci	vmw_write(man->dev_priv, SVGA_REG_COMMAND_LOW, val);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	return header->cb_header->status;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci/**
31762306a36Sopenharmony_ci * vmw_cmdbuf_ctx_init: Initialize a command buffer context.
31862306a36Sopenharmony_ci *
31962306a36Sopenharmony_ci * @ctx: The command buffer context to initialize
32062306a36Sopenharmony_ci */
32162306a36Sopenharmony_cistatic void vmw_cmdbuf_ctx_init(struct vmw_cmdbuf_context *ctx)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctx->hw_submitted);
32462306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctx->submitted);
32562306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctx->preempted);
32662306a36Sopenharmony_ci	ctx->num_hw_submitted = 0;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci/**
33062306a36Sopenharmony_ci * vmw_cmdbuf_ctx_submit: Submit command buffers from a command buffer
33162306a36Sopenharmony_ci * context.
33262306a36Sopenharmony_ci *
33362306a36Sopenharmony_ci * @man: The command buffer manager.
33462306a36Sopenharmony_ci * @ctx: The command buffer context.
33562306a36Sopenharmony_ci *
33662306a36Sopenharmony_ci * Submits command buffers to hardware until there are no more command
33762306a36Sopenharmony_ci * buffers to submit or the hardware can't handle more command buffers.
33862306a36Sopenharmony_ci */
33962306a36Sopenharmony_cistatic void vmw_cmdbuf_ctx_submit(struct vmw_cmdbuf_man *man,
34062306a36Sopenharmony_ci				  struct vmw_cmdbuf_context *ctx)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	while (ctx->num_hw_submitted < man->max_hw_submitted &&
34362306a36Sopenharmony_ci	       !list_empty(&ctx->submitted) &&
34462306a36Sopenharmony_ci	       !ctx->block_submission) {
34562306a36Sopenharmony_ci		struct vmw_cmdbuf_header *entry;
34662306a36Sopenharmony_ci		SVGACBStatus status;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		entry = list_first_entry(&ctx->submitted,
34962306a36Sopenharmony_ci					 struct vmw_cmdbuf_header,
35062306a36Sopenharmony_ci					 list);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci		status = vmw_cmdbuf_header_submit(entry);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		/* This should never happen */
35562306a36Sopenharmony_ci		if (WARN_ON_ONCE(status == SVGA_CB_STATUS_QUEUE_FULL)) {
35662306a36Sopenharmony_ci			entry->cb_header->status = SVGA_CB_STATUS_NONE;
35762306a36Sopenharmony_ci			break;
35862306a36Sopenharmony_ci		}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		list_move_tail(&entry->list, &ctx->hw_submitted);
36162306a36Sopenharmony_ci		ctx->num_hw_submitted++;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci/**
36762306a36Sopenharmony_ci * vmw_cmdbuf_ctx_process - Process a command buffer context.
36862306a36Sopenharmony_ci *
36962306a36Sopenharmony_ci * @man: The command buffer manager.
37062306a36Sopenharmony_ci * @ctx: The command buffer context.
37162306a36Sopenharmony_ci * @notempty: Pass back count of non-empty command submitted lists.
37262306a36Sopenharmony_ci *
37362306a36Sopenharmony_ci * Submit command buffers to hardware if possible, and process finished
37462306a36Sopenharmony_ci * buffers. Typically freeing them, but on preemption or error take
37562306a36Sopenharmony_ci * appropriate action. Wake up waiters if appropriate.
37662306a36Sopenharmony_ci */
37762306a36Sopenharmony_cistatic void vmw_cmdbuf_ctx_process(struct vmw_cmdbuf_man *man,
37862306a36Sopenharmony_ci				   struct vmw_cmdbuf_context *ctx,
37962306a36Sopenharmony_ci				   int *notempty)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct vmw_cmdbuf_header *entry, *next;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	vmw_cmdbuf_ctx_submit(man, ctx);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	list_for_each_entry_safe(entry, next, &ctx->hw_submitted, list) {
38662306a36Sopenharmony_ci		SVGACBStatus status = entry->cb_header->status;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		if (status == SVGA_CB_STATUS_NONE)
38962306a36Sopenharmony_ci			break;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci		list_del(&entry->list);
39262306a36Sopenharmony_ci		wake_up_all(&man->idle_queue);
39362306a36Sopenharmony_ci		ctx->num_hw_submitted--;
39462306a36Sopenharmony_ci		switch (status) {
39562306a36Sopenharmony_ci		case SVGA_CB_STATUS_COMPLETED:
39662306a36Sopenharmony_ci			__vmw_cmdbuf_header_free(entry);
39762306a36Sopenharmony_ci			break;
39862306a36Sopenharmony_ci		case SVGA_CB_STATUS_COMMAND_ERROR:
39962306a36Sopenharmony_ci			WARN_ONCE(true, "Command buffer error.\n");
40062306a36Sopenharmony_ci			entry->cb_header->status = SVGA_CB_STATUS_NONE;
40162306a36Sopenharmony_ci			list_add_tail(&entry->list, &man->error);
40262306a36Sopenharmony_ci			schedule_work(&man->work);
40362306a36Sopenharmony_ci			break;
40462306a36Sopenharmony_ci		case SVGA_CB_STATUS_PREEMPTED:
40562306a36Sopenharmony_ci			entry->cb_header->status = SVGA_CB_STATUS_NONE;
40662306a36Sopenharmony_ci			list_add_tail(&entry->list, &ctx->preempted);
40762306a36Sopenharmony_ci			break;
40862306a36Sopenharmony_ci		case SVGA_CB_STATUS_CB_HEADER_ERROR:
40962306a36Sopenharmony_ci			WARN_ONCE(true, "Command buffer header error.\n");
41062306a36Sopenharmony_ci			__vmw_cmdbuf_header_free(entry);
41162306a36Sopenharmony_ci			break;
41262306a36Sopenharmony_ci		default:
41362306a36Sopenharmony_ci			WARN_ONCE(true, "Undefined command buffer status.\n");
41462306a36Sopenharmony_ci			__vmw_cmdbuf_header_free(entry);
41562306a36Sopenharmony_ci			break;
41662306a36Sopenharmony_ci		}
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	vmw_cmdbuf_ctx_submit(man, ctx);
42062306a36Sopenharmony_ci	if (!list_empty(&ctx->submitted))
42162306a36Sopenharmony_ci		(*notempty)++;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci/**
42562306a36Sopenharmony_ci * vmw_cmdbuf_man_process - Process all command buffer contexts and
42662306a36Sopenharmony_ci * switch on and off irqs as appropriate.
42762306a36Sopenharmony_ci *
42862306a36Sopenharmony_ci * @man: The command buffer manager.
42962306a36Sopenharmony_ci *
43062306a36Sopenharmony_ci * Calls vmw_cmdbuf_ctx_process() on all contexts. If any context has
43162306a36Sopenharmony_ci * command buffers left that are not submitted to hardware, Make sure
43262306a36Sopenharmony_ci * IRQ handling is turned on. Otherwise, make sure it's turned off.
43362306a36Sopenharmony_ci */
43462306a36Sopenharmony_cistatic void vmw_cmdbuf_man_process(struct vmw_cmdbuf_man *man)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	int notempty;
43762306a36Sopenharmony_ci	struct vmw_cmdbuf_context *ctx;
43862306a36Sopenharmony_ci	int i;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ciretry:
44162306a36Sopenharmony_ci	notempty = 0;
44262306a36Sopenharmony_ci	for_each_cmdbuf_ctx(man, i, ctx)
44362306a36Sopenharmony_ci		vmw_cmdbuf_ctx_process(man, ctx, &notempty);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (man->irq_on && !notempty) {
44662306a36Sopenharmony_ci		vmw_generic_waiter_remove(man->dev_priv,
44762306a36Sopenharmony_ci					  SVGA_IRQFLAG_COMMAND_BUFFER,
44862306a36Sopenharmony_ci					  &man->dev_priv->cmdbuf_waiters);
44962306a36Sopenharmony_ci		man->irq_on = false;
45062306a36Sopenharmony_ci	} else if (!man->irq_on && notempty) {
45162306a36Sopenharmony_ci		vmw_generic_waiter_add(man->dev_priv,
45262306a36Sopenharmony_ci				       SVGA_IRQFLAG_COMMAND_BUFFER,
45362306a36Sopenharmony_ci				       &man->dev_priv->cmdbuf_waiters);
45462306a36Sopenharmony_ci		man->irq_on = true;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		/* Rerun in case we just missed an irq. */
45762306a36Sopenharmony_ci		goto retry;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci/**
46262306a36Sopenharmony_ci * vmw_cmdbuf_ctx_add - Schedule a command buffer for submission on a
46362306a36Sopenharmony_ci * command buffer context
46462306a36Sopenharmony_ci *
46562306a36Sopenharmony_ci * @man: The command buffer manager.
46662306a36Sopenharmony_ci * @header: The header of the buffer to submit.
46762306a36Sopenharmony_ci * @cb_context: The command buffer context to use.
46862306a36Sopenharmony_ci *
46962306a36Sopenharmony_ci * This function adds @header to the "submitted" queue of the command
47062306a36Sopenharmony_ci * buffer context identified by @cb_context. It then calls the command buffer
47162306a36Sopenharmony_ci * manager processing to potentially submit the buffer to hardware.
47262306a36Sopenharmony_ci * @man->lock needs to be held when calling this function.
47362306a36Sopenharmony_ci */
47462306a36Sopenharmony_cistatic void vmw_cmdbuf_ctx_add(struct vmw_cmdbuf_man *man,
47562306a36Sopenharmony_ci			       struct vmw_cmdbuf_header *header,
47662306a36Sopenharmony_ci			       SVGACBContext cb_context)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	if (!(header->cb_header->flags & SVGA_CB_FLAG_DX_CONTEXT))
47962306a36Sopenharmony_ci		header->cb_header->dxContext = 0;
48062306a36Sopenharmony_ci	header->cb_context = cb_context;
48162306a36Sopenharmony_ci	list_add_tail(&header->list, &man->ctx[cb_context].submitted);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	vmw_cmdbuf_man_process(man);
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/**
48762306a36Sopenharmony_ci * vmw_cmdbuf_irqthread - The main part of the command buffer interrupt
48862306a36Sopenharmony_ci * handler implemented as a threaded irq task.
48962306a36Sopenharmony_ci *
49062306a36Sopenharmony_ci * @man: Pointer to the command buffer manager.
49162306a36Sopenharmony_ci *
49262306a36Sopenharmony_ci * The bottom half of the interrupt handler simply calls into the
49362306a36Sopenharmony_ci * command buffer processor to free finished buffers and submit any
49462306a36Sopenharmony_ci * queued buffers to hardware.
49562306a36Sopenharmony_ci */
49662306a36Sopenharmony_civoid vmw_cmdbuf_irqthread(struct vmw_cmdbuf_man *man)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	spin_lock(&man->lock);
49962306a36Sopenharmony_ci	vmw_cmdbuf_man_process(man);
50062306a36Sopenharmony_ci	spin_unlock(&man->lock);
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci/**
50462306a36Sopenharmony_ci * vmw_cmdbuf_work_func - The deferred work function that handles
50562306a36Sopenharmony_ci * command buffer errors.
50662306a36Sopenharmony_ci *
50762306a36Sopenharmony_ci * @work: The work func closure argument.
50862306a36Sopenharmony_ci *
50962306a36Sopenharmony_ci * Restarting the command buffer context after an error requires process
51062306a36Sopenharmony_ci * context, so it is deferred to this work function.
51162306a36Sopenharmony_ci */
51262306a36Sopenharmony_cistatic void vmw_cmdbuf_work_func(struct work_struct *work)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct vmw_cmdbuf_man *man =
51562306a36Sopenharmony_ci		container_of(work, struct vmw_cmdbuf_man, work);
51662306a36Sopenharmony_ci	struct vmw_cmdbuf_header *entry, *next;
51762306a36Sopenharmony_ci	uint32_t dummy = 0;
51862306a36Sopenharmony_ci	bool send_fence = false;
51962306a36Sopenharmony_ci	struct list_head restart_head[SVGA_CB_CONTEXT_MAX];
52062306a36Sopenharmony_ci	int i;
52162306a36Sopenharmony_ci	struct vmw_cmdbuf_context *ctx;
52262306a36Sopenharmony_ci	bool global_block = false;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	for_each_cmdbuf_ctx(man, i, ctx)
52562306a36Sopenharmony_ci		INIT_LIST_HEAD(&restart_head[i]);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	mutex_lock(&man->error_mutex);
52862306a36Sopenharmony_ci	spin_lock(&man->lock);
52962306a36Sopenharmony_ci	list_for_each_entry_safe(entry, next, &man->error, list) {
53062306a36Sopenharmony_ci		SVGACBHeader *cb_hdr = entry->cb_header;
53162306a36Sopenharmony_ci		SVGA3dCmdHeader *header = (SVGA3dCmdHeader *)
53262306a36Sopenharmony_ci			(entry->cmd + cb_hdr->errorOffset);
53362306a36Sopenharmony_ci		u32 error_cmd_size, new_start_offset;
53462306a36Sopenharmony_ci		const char *cmd_name;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		list_del_init(&entry->list);
53762306a36Sopenharmony_ci		global_block = true;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		if (!vmw_cmd_describe(header, &error_cmd_size, &cmd_name)) {
54062306a36Sopenharmony_ci			VMW_DEBUG_USER("Unknown command causing device error.\n");
54162306a36Sopenharmony_ci			VMW_DEBUG_USER("Command buffer offset is %lu\n",
54262306a36Sopenharmony_ci				       (unsigned long) cb_hdr->errorOffset);
54362306a36Sopenharmony_ci			__vmw_cmdbuf_header_free(entry);
54462306a36Sopenharmony_ci			send_fence = true;
54562306a36Sopenharmony_ci			continue;
54662306a36Sopenharmony_ci		}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		VMW_DEBUG_USER("Command \"%s\" causing device error.\n",
54962306a36Sopenharmony_ci			       cmd_name);
55062306a36Sopenharmony_ci		VMW_DEBUG_USER("Command buffer offset is %lu\n",
55162306a36Sopenharmony_ci			       (unsigned long) cb_hdr->errorOffset);
55262306a36Sopenharmony_ci		VMW_DEBUG_USER("Command size is %lu\n",
55362306a36Sopenharmony_ci			       (unsigned long) error_cmd_size);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		new_start_offset = cb_hdr->errorOffset + error_cmd_size;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		if (new_start_offset >= cb_hdr->length) {
55862306a36Sopenharmony_ci			__vmw_cmdbuf_header_free(entry);
55962306a36Sopenharmony_ci			send_fence = true;
56062306a36Sopenharmony_ci			continue;
56162306a36Sopenharmony_ci		}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		if (man->using_mob)
56462306a36Sopenharmony_ci			cb_hdr->ptr.mob.mobOffset += new_start_offset;
56562306a36Sopenharmony_ci		else
56662306a36Sopenharmony_ci			cb_hdr->ptr.pa += (u64) new_start_offset;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		entry->cmd += new_start_offset;
56962306a36Sopenharmony_ci		cb_hdr->length -= new_start_offset;
57062306a36Sopenharmony_ci		cb_hdr->errorOffset = 0;
57162306a36Sopenharmony_ci		cb_hdr->offset = 0;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		list_add_tail(&entry->list, &restart_head[entry->cb_context]);
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	for_each_cmdbuf_ctx(man, i, ctx)
57762306a36Sopenharmony_ci		man->ctx[i].block_submission = true;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	spin_unlock(&man->lock);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	/* Preempt all contexts */
58262306a36Sopenharmony_ci	if (global_block && vmw_cmdbuf_preempt(man, 0))
58362306a36Sopenharmony_ci		DRM_ERROR("Failed preempting command buffer contexts\n");
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	spin_lock(&man->lock);
58662306a36Sopenharmony_ci	for_each_cmdbuf_ctx(man, i, ctx) {
58762306a36Sopenharmony_ci		/* Move preempted command buffers to the preempted queue. */
58862306a36Sopenharmony_ci		vmw_cmdbuf_ctx_process(man, ctx, &dummy);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci		/*
59162306a36Sopenharmony_ci		 * Add the preempted queue after the command buffer
59262306a36Sopenharmony_ci		 * that caused an error.
59362306a36Sopenharmony_ci		 */
59462306a36Sopenharmony_ci		list_splice_init(&ctx->preempted, restart_head[i].prev);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		/*
59762306a36Sopenharmony_ci		 * Finally add all command buffers first in the submitted
59862306a36Sopenharmony_ci		 * queue, to rerun them.
59962306a36Sopenharmony_ci		 */
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci		ctx->block_submission = false;
60262306a36Sopenharmony_ci		list_splice_init(&restart_head[i], &ctx->submitted);
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	vmw_cmdbuf_man_process(man);
60662306a36Sopenharmony_ci	spin_unlock(&man->lock);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	if (global_block && vmw_cmdbuf_startstop(man, 0, true))
60962306a36Sopenharmony_ci		DRM_ERROR("Failed restarting command buffer contexts\n");
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/* Send a new fence in case one was removed */
61262306a36Sopenharmony_ci	if (send_fence) {
61362306a36Sopenharmony_ci		vmw_cmd_send_fence(man->dev_priv, &dummy);
61462306a36Sopenharmony_ci		wake_up_all(&man->idle_queue);
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	mutex_unlock(&man->error_mutex);
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci/**
62162306a36Sopenharmony_ci * vmw_cmdbuf_man_idle - Check whether the command buffer manager is idle.
62262306a36Sopenharmony_ci *
62362306a36Sopenharmony_ci * @man: The command buffer manager.
62462306a36Sopenharmony_ci * @check_preempted: Check also the preempted queue for pending command buffers.
62562306a36Sopenharmony_ci *
62662306a36Sopenharmony_ci */
62762306a36Sopenharmony_cistatic bool vmw_cmdbuf_man_idle(struct vmw_cmdbuf_man *man,
62862306a36Sopenharmony_ci				bool check_preempted)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	struct vmw_cmdbuf_context *ctx;
63162306a36Sopenharmony_ci	bool idle = false;
63262306a36Sopenharmony_ci	int i;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	spin_lock(&man->lock);
63562306a36Sopenharmony_ci	vmw_cmdbuf_man_process(man);
63662306a36Sopenharmony_ci	for_each_cmdbuf_ctx(man, i, ctx) {
63762306a36Sopenharmony_ci		if (!list_empty(&ctx->submitted) ||
63862306a36Sopenharmony_ci		    !list_empty(&ctx->hw_submitted) ||
63962306a36Sopenharmony_ci		    (check_preempted && !list_empty(&ctx->preempted)))
64062306a36Sopenharmony_ci			goto out_unlock;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	idle = list_empty(&man->error);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ciout_unlock:
64662306a36Sopenharmony_ci	spin_unlock(&man->lock);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	return idle;
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci/**
65262306a36Sopenharmony_ci * __vmw_cmdbuf_cur_flush - Flush the current command buffer for small kernel
65362306a36Sopenharmony_ci * command submissions
65462306a36Sopenharmony_ci *
65562306a36Sopenharmony_ci * @man: The command buffer manager.
65662306a36Sopenharmony_ci *
65762306a36Sopenharmony_ci * Flushes the current command buffer without allocating a new one. A new one
65862306a36Sopenharmony_ci * is automatically allocated when needed. Call with @man->cur_mutex held.
65962306a36Sopenharmony_ci */
66062306a36Sopenharmony_cistatic void __vmw_cmdbuf_cur_flush(struct vmw_cmdbuf_man *man)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	struct vmw_cmdbuf_header *cur = man->cur;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	lockdep_assert_held_once(&man->cur_mutex);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	if (!cur)
66762306a36Sopenharmony_ci		return;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	spin_lock(&man->lock);
67062306a36Sopenharmony_ci	if (man->cur_pos == 0) {
67162306a36Sopenharmony_ci		__vmw_cmdbuf_header_free(cur);
67262306a36Sopenharmony_ci		goto out_unlock;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	man->cur->cb_header->length = man->cur_pos;
67662306a36Sopenharmony_ci	vmw_cmdbuf_ctx_add(man, man->cur, SVGA_CB_CONTEXT_0);
67762306a36Sopenharmony_ciout_unlock:
67862306a36Sopenharmony_ci	spin_unlock(&man->lock);
67962306a36Sopenharmony_ci	man->cur = NULL;
68062306a36Sopenharmony_ci	man->cur_pos = 0;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci/**
68462306a36Sopenharmony_ci * vmw_cmdbuf_cur_flush - Flush the current command buffer for small kernel
68562306a36Sopenharmony_ci * command submissions
68662306a36Sopenharmony_ci *
68762306a36Sopenharmony_ci * @man: The command buffer manager.
68862306a36Sopenharmony_ci * @interruptible: Whether to sleep interruptible when sleeping.
68962306a36Sopenharmony_ci *
69062306a36Sopenharmony_ci * Flushes the current command buffer without allocating a new one. A new one
69162306a36Sopenharmony_ci * is automatically allocated when needed.
69262306a36Sopenharmony_ci */
69362306a36Sopenharmony_ciint vmw_cmdbuf_cur_flush(struct vmw_cmdbuf_man *man,
69462306a36Sopenharmony_ci			 bool interruptible)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	int ret = vmw_cmdbuf_cur_lock(man, interruptible);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	if (ret)
69962306a36Sopenharmony_ci		return ret;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	__vmw_cmdbuf_cur_flush(man);
70262306a36Sopenharmony_ci	vmw_cmdbuf_cur_unlock(man);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	return 0;
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci/**
70862306a36Sopenharmony_ci * vmw_cmdbuf_idle - Wait for command buffer manager idle.
70962306a36Sopenharmony_ci *
71062306a36Sopenharmony_ci * @man: The command buffer manager.
71162306a36Sopenharmony_ci * @interruptible: Sleep interruptible while waiting.
71262306a36Sopenharmony_ci * @timeout: Time out after this many ticks.
71362306a36Sopenharmony_ci *
71462306a36Sopenharmony_ci * Wait until the command buffer manager has processed all command buffers,
71562306a36Sopenharmony_ci * or until a timeout occurs. If a timeout occurs, the function will return
71662306a36Sopenharmony_ci * -EBUSY.
71762306a36Sopenharmony_ci */
71862306a36Sopenharmony_ciint vmw_cmdbuf_idle(struct vmw_cmdbuf_man *man, bool interruptible,
71962306a36Sopenharmony_ci		    unsigned long timeout)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	int ret;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	ret = vmw_cmdbuf_cur_flush(man, interruptible);
72462306a36Sopenharmony_ci	vmw_generic_waiter_add(man->dev_priv,
72562306a36Sopenharmony_ci			       SVGA_IRQFLAG_COMMAND_BUFFER,
72662306a36Sopenharmony_ci			       &man->dev_priv->cmdbuf_waiters);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	if (interruptible) {
72962306a36Sopenharmony_ci		ret = wait_event_interruptible_timeout
73062306a36Sopenharmony_ci			(man->idle_queue, vmw_cmdbuf_man_idle(man, true),
73162306a36Sopenharmony_ci			 timeout);
73262306a36Sopenharmony_ci	} else {
73362306a36Sopenharmony_ci		ret = wait_event_timeout
73462306a36Sopenharmony_ci			(man->idle_queue, vmw_cmdbuf_man_idle(man, true),
73562306a36Sopenharmony_ci			 timeout);
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci	vmw_generic_waiter_remove(man->dev_priv,
73862306a36Sopenharmony_ci				  SVGA_IRQFLAG_COMMAND_BUFFER,
73962306a36Sopenharmony_ci				  &man->dev_priv->cmdbuf_waiters);
74062306a36Sopenharmony_ci	if (ret == 0) {
74162306a36Sopenharmony_ci		if (!vmw_cmdbuf_man_idle(man, true))
74262306a36Sopenharmony_ci			ret = -EBUSY;
74362306a36Sopenharmony_ci		else
74462306a36Sopenharmony_ci			ret = 0;
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci	if (ret > 0)
74762306a36Sopenharmony_ci		ret = 0;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	return ret;
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci/**
75362306a36Sopenharmony_ci * vmw_cmdbuf_try_alloc - Try to allocate buffer space from the main pool.
75462306a36Sopenharmony_ci *
75562306a36Sopenharmony_ci * @man: The command buffer manager.
75662306a36Sopenharmony_ci * @info: Allocation info. Will hold the size on entry and allocated mm node
75762306a36Sopenharmony_ci * on successful return.
75862306a36Sopenharmony_ci *
75962306a36Sopenharmony_ci * Try to allocate buffer space from the main pool. Returns true if succeeded.
76062306a36Sopenharmony_ci * If a fatal error was hit, the error code is returned in @info->ret.
76162306a36Sopenharmony_ci */
76262306a36Sopenharmony_cistatic bool vmw_cmdbuf_try_alloc(struct vmw_cmdbuf_man *man,
76362306a36Sopenharmony_ci				 struct vmw_cmdbuf_alloc_info *info)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	int ret;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (info->done)
76862306a36Sopenharmony_ci		return true;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	memset(info->node, 0, sizeof(*info->node));
77162306a36Sopenharmony_ci	spin_lock(&man->lock);
77262306a36Sopenharmony_ci	ret = drm_mm_insert_node(&man->mm, info->node, info->page_size);
77362306a36Sopenharmony_ci	if (ret) {
77462306a36Sopenharmony_ci		vmw_cmdbuf_man_process(man);
77562306a36Sopenharmony_ci		ret = drm_mm_insert_node(&man->mm, info->node, info->page_size);
77662306a36Sopenharmony_ci	}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	spin_unlock(&man->lock);
77962306a36Sopenharmony_ci	info->done = !ret;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	return info->done;
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci/**
78562306a36Sopenharmony_ci * vmw_cmdbuf_alloc_space - Allocate buffer space from the main pool.
78662306a36Sopenharmony_ci *
78762306a36Sopenharmony_ci * @man: The command buffer manager.
78862306a36Sopenharmony_ci * @node: Pointer to pre-allocated range-manager node.
78962306a36Sopenharmony_ci * @size: The size of the allocation.
79062306a36Sopenharmony_ci * @interruptible: Whether to sleep interruptible while waiting for space.
79162306a36Sopenharmony_ci *
79262306a36Sopenharmony_ci * This function allocates buffer space from the main pool, and if there is
79362306a36Sopenharmony_ci * no space available ATM, it turns on IRQ handling and sleeps waiting for it to
79462306a36Sopenharmony_ci * become available.
79562306a36Sopenharmony_ci */
79662306a36Sopenharmony_cistatic int vmw_cmdbuf_alloc_space(struct vmw_cmdbuf_man *man,
79762306a36Sopenharmony_ci				  struct drm_mm_node *node,
79862306a36Sopenharmony_ci				  size_t size,
79962306a36Sopenharmony_ci				  bool interruptible)
80062306a36Sopenharmony_ci{
80162306a36Sopenharmony_ci	struct vmw_cmdbuf_alloc_info info;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	info.page_size = PFN_UP(size);
80462306a36Sopenharmony_ci	info.node = node;
80562306a36Sopenharmony_ci	info.done = false;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/*
80862306a36Sopenharmony_ci	 * To prevent starvation of large requests, only one allocating call
80962306a36Sopenharmony_ci	 * at a time waiting for space.
81062306a36Sopenharmony_ci	 */
81162306a36Sopenharmony_ci	if (interruptible) {
81262306a36Sopenharmony_ci		if (mutex_lock_interruptible(&man->space_mutex))
81362306a36Sopenharmony_ci			return -ERESTARTSYS;
81462306a36Sopenharmony_ci	} else {
81562306a36Sopenharmony_ci		mutex_lock(&man->space_mutex);
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	/* Try to allocate space without waiting. */
81962306a36Sopenharmony_ci	if (vmw_cmdbuf_try_alloc(man, &info))
82062306a36Sopenharmony_ci		goto out_unlock;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	vmw_generic_waiter_add(man->dev_priv,
82362306a36Sopenharmony_ci			       SVGA_IRQFLAG_COMMAND_BUFFER,
82462306a36Sopenharmony_ci			       &man->dev_priv->cmdbuf_waiters);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (interruptible) {
82762306a36Sopenharmony_ci		int ret;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci		ret = wait_event_interruptible
83062306a36Sopenharmony_ci			(man->alloc_queue, vmw_cmdbuf_try_alloc(man, &info));
83162306a36Sopenharmony_ci		if (ret) {
83262306a36Sopenharmony_ci			vmw_generic_waiter_remove
83362306a36Sopenharmony_ci				(man->dev_priv, SVGA_IRQFLAG_COMMAND_BUFFER,
83462306a36Sopenharmony_ci				 &man->dev_priv->cmdbuf_waiters);
83562306a36Sopenharmony_ci			mutex_unlock(&man->space_mutex);
83662306a36Sopenharmony_ci			return ret;
83762306a36Sopenharmony_ci		}
83862306a36Sopenharmony_ci	} else {
83962306a36Sopenharmony_ci		wait_event(man->alloc_queue, vmw_cmdbuf_try_alloc(man, &info));
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci	vmw_generic_waiter_remove(man->dev_priv,
84262306a36Sopenharmony_ci				  SVGA_IRQFLAG_COMMAND_BUFFER,
84362306a36Sopenharmony_ci				  &man->dev_priv->cmdbuf_waiters);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ciout_unlock:
84662306a36Sopenharmony_ci	mutex_unlock(&man->space_mutex);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	return 0;
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci/**
85262306a36Sopenharmony_ci * vmw_cmdbuf_space_pool - Set up a command buffer header with command buffer
85362306a36Sopenharmony_ci * space from the main pool.
85462306a36Sopenharmony_ci *
85562306a36Sopenharmony_ci * @man: The command buffer manager.
85662306a36Sopenharmony_ci * @header: Pointer to the header to set up.
85762306a36Sopenharmony_ci * @size: The requested size of the buffer space.
85862306a36Sopenharmony_ci * @interruptible: Whether to sleep interruptible while waiting for space.
85962306a36Sopenharmony_ci */
86062306a36Sopenharmony_cistatic int vmw_cmdbuf_space_pool(struct vmw_cmdbuf_man *man,
86162306a36Sopenharmony_ci				 struct vmw_cmdbuf_header *header,
86262306a36Sopenharmony_ci				 size_t size,
86362306a36Sopenharmony_ci				 bool interruptible)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	SVGACBHeader *cb_hdr;
86662306a36Sopenharmony_ci	size_t offset;
86762306a36Sopenharmony_ci	int ret;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	if (!man->has_pool)
87062306a36Sopenharmony_ci		return -ENOMEM;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	ret = vmw_cmdbuf_alloc_space(man, &header->node,  size, interruptible);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (ret)
87562306a36Sopenharmony_ci		return ret;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	header->cb_header = dma_pool_zalloc(man->headers, GFP_KERNEL,
87862306a36Sopenharmony_ci					    &header->handle);
87962306a36Sopenharmony_ci	if (!header->cb_header) {
88062306a36Sopenharmony_ci		ret = -ENOMEM;
88162306a36Sopenharmony_ci		goto out_no_cb_header;
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	header->size = header->node.size << PAGE_SHIFT;
88562306a36Sopenharmony_ci	cb_hdr = header->cb_header;
88662306a36Sopenharmony_ci	offset = header->node.start << PAGE_SHIFT;
88762306a36Sopenharmony_ci	header->cmd = man->map + offset;
88862306a36Sopenharmony_ci	if (man->using_mob) {
88962306a36Sopenharmony_ci		cb_hdr->flags = SVGA_CB_FLAG_MOB;
89062306a36Sopenharmony_ci		cb_hdr->ptr.mob.mobid = man->cmd_space->tbo.resource->start;
89162306a36Sopenharmony_ci		cb_hdr->ptr.mob.mobOffset = offset;
89262306a36Sopenharmony_ci	} else {
89362306a36Sopenharmony_ci		cb_hdr->ptr.pa = (u64)man->handle + (u64)offset;
89462306a36Sopenharmony_ci	}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	return 0;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ciout_no_cb_header:
89962306a36Sopenharmony_ci	spin_lock(&man->lock);
90062306a36Sopenharmony_ci	drm_mm_remove_node(&header->node);
90162306a36Sopenharmony_ci	spin_unlock(&man->lock);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	return ret;
90462306a36Sopenharmony_ci}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci/**
90762306a36Sopenharmony_ci * vmw_cmdbuf_space_inline - Set up a command buffer header with
90862306a36Sopenharmony_ci * inline command buffer space.
90962306a36Sopenharmony_ci *
91062306a36Sopenharmony_ci * @man: The command buffer manager.
91162306a36Sopenharmony_ci * @header: Pointer to the header to set up.
91262306a36Sopenharmony_ci * @size: The requested size of the buffer space.
91362306a36Sopenharmony_ci */
91462306a36Sopenharmony_cistatic int vmw_cmdbuf_space_inline(struct vmw_cmdbuf_man *man,
91562306a36Sopenharmony_ci				   struct vmw_cmdbuf_header *header,
91662306a36Sopenharmony_ci				   int size)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	struct vmw_cmdbuf_dheader *dheader;
91962306a36Sopenharmony_ci	SVGACBHeader *cb_hdr;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	if (WARN_ON_ONCE(size > VMW_CMDBUF_INLINE_SIZE))
92262306a36Sopenharmony_ci		return -ENOMEM;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	dheader = dma_pool_zalloc(man->dheaders, GFP_KERNEL,
92562306a36Sopenharmony_ci				  &header->handle);
92662306a36Sopenharmony_ci	if (!dheader)
92762306a36Sopenharmony_ci		return -ENOMEM;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	header->inline_space = true;
93062306a36Sopenharmony_ci	header->size = VMW_CMDBUF_INLINE_SIZE;
93162306a36Sopenharmony_ci	cb_hdr = &dheader->cb_header;
93262306a36Sopenharmony_ci	header->cb_header = cb_hdr;
93362306a36Sopenharmony_ci	header->cmd = dheader->cmd;
93462306a36Sopenharmony_ci	cb_hdr->status = SVGA_CB_STATUS_NONE;
93562306a36Sopenharmony_ci	cb_hdr->flags = SVGA_CB_FLAG_NONE;
93662306a36Sopenharmony_ci	cb_hdr->ptr.pa = (u64)header->handle +
93762306a36Sopenharmony_ci		(u64)offsetof(struct vmw_cmdbuf_dheader, cmd);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	return 0;
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci/**
94362306a36Sopenharmony_ci * vmw_cmdbuf_alloc - Allocate a command buffer header complete with
94462306a36Sopenharmony_ci * command buffer space.
94562306a36Sopenharmony_ci *
94662306a36Sopenharmony_ci * @man: The command buffer manager.
94762306a36Sopenharmony_ci * @size: The requested size of the buffer space.
94862306a36Sopenharmony_ci * @interruptible: Whether to sleep interruptible while waiting for space.
94962306a36Sopenharmony_ci * @p_header: points to a header pointer to populate on successful return.
95062306a36Sopenharmony_ci *
95162306a36Sopenharmony_ci * Returns a pointer to command buffer space if successful. Otherwise
95262306a36Sopenharmony_ci * returns an error pointer. The header pointer returned in @p_header should
95362306a36Sopenharmony_ci * be used for upcoming calls to vmw_cmdbuf_reserve() and vmw_cmdbuf_commit().
95462306a36Sopenharmony_ci */
95562306a36Sopenharmony_civoid *vmw_cmdbuf_alloc(struct vmw_cmdbuf_man *man,
95662306a36Sopenharmony_ci		       size_t size, bool interruptible,
95762306a36Sopenharmony_ci		       struct vmw_cmdbuf_header **p_header)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	struct vmw_cmdbuf_header *header;
96062306a36Sopenharmony_ci	int ret = 0;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	*p_header = NULL;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	header = kzalloc(sizeof(*header), GFP_KERNEL);
96562306a36Sopenharmony_ci	if (!header)
96662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (size <= VMW_CMDBUF_INLINE_SIZE)
96962306a36Sopenharmony_ci		ret = vmw_cmdbuf_space_inline(man, header, size);
97062306a36Sopenharmony_ci	else
97162306a36Sopenharmony_ci		ret = vmw_cmdbuf_space_pool(man, header, size, interruptible);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	if (ret) {
97462306a36Sopenharmony_ci		kfree(header);
97562306a36Sopenharmony_ci		return ERR_PTR(ret);
97662306a36Sopenharmony_ci	}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	header->man = man;
97962306a36Sopenharmony_ci	INIT_LIST_HEAD(&header->list);
98062306a36Sopenharmony_ci	header->cb_header->status = SVGA_CB_STATUS_NONE;
98162306a36Sopenharmony_ci	*p_header = header;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	return header->cmd;
98462306a36Sopenharmony_ci}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci/**
98762306a36Sopenharmony_ci * vmw_cmdbuf_reserve_cur - Reserve space for commands in the current
98862306a36Sopenharmony_ci * command buffer.
98962306a36Sopenharmony_ci *
99062306a36Sopenharmony_ci * @man: The command buffer manager.
99162306a36Sopenharmony_ci * @size: The requested size of the commands.
99262306a36Sopenharmony_ci * @ctx_id: The context id if any. Otherwise set to SVGA3D_REG_INVALID.
99362306a36Sopenharmony_ci * @interruptible: Whether to sleep interruptible while waiting for space.
99462306a36Sopenharmony_ci *
99562306a36Sopenharmony_ci * Returns a pointer to command buffer space if successful. Otherwise
99662306a36Sopenharmony_ci * returns an error pointer.
99762306a36Sopenharmony_ci */
99862306a36Sopenharmony_cistatic void *vmw_cmdbuf_reserve_cur(struct vmw_cmdbuf_man *man,
99962306a36Sopenharmony_ci				    size_t size,
100062306a36Sopenharmony_ci				    int ctx_id,
100162306a36Sopenharmony_ci				    bool interruptible)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	struct vmw_cmdbuf_header *cur;
100462306a36Sopenharmony_ci	void *ret;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	if (vmw_cmdbuf_cur_lock(man, interruptible))
100762306a36Sopenharmony_ci		return ERR_PTR(-ERESTARTSYS);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	cur = man->cur;
101062306a36Sopenharmony_ci	if (cur && (size + man->cur_pos > cur->size ||
101162306a36Sopenharmony_ci		    ((cur->cb_header->flags & SVGA_CB_FLAG_DX_CONTEXT) &&
101262306a36Sopenharmony_ci		     ctx_id != cur->cb_header->dxContext)))
101362306a36Sopenharmony_ci		__vmw_cmdbuf_cur_flush(man);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (!man->cur) {
101662306a36Sopenharmony_ci		ret = vmw_cmdbuf_alloc(man,
101762306a36Sopenharmony_ci				       max_t(size_t, size, man->default_size),
101862306a36Sopenharmony_ci				       interruptible, &man->cur);
101962306a36Sopenharmony_ci		if (IS_ERR(ret)) {
102062306a36Sopenharmony_ci			vmw_cmdbuf_cur_unlock(man);
102162306a36Sopenharmony_ci			return ret;
102262306a36Sopenharmony_ci		}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci		cur = man->cur;
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	if (ctx_id != SVGA3D_INVALID_ID) {
102862306a36Sopenharmony_ci		cur->cb_header->flags |= SVGA_CB_FLAG_DX_CONTEXT;
102962306a36Sopenharmony_ci		cur->cb_header->dxContext = ctx_id;
103062306a36Sopenharmony_ci	}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	cur->reserved = size;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	return (void *) (man->cur->cmd + man->cur_pos);
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci/**
103862306a36Sopenharmony_ci * vmw_cmdbuf_commit_cur - Commit commands in the current command buffer.
103962306a36Sopenharmony_ci *
104062306a36Sopenharmony_ci * @man: The command buffer manager.
104162306a36Sopenharmony_ci * @size: The size of the commands actually written.
104262306a36Sopenharmony_ci * @flush: Whether to flush the command buffer immediately.
104362306a36Sopenharmony_ci */
104462306a36Sopenharmony_cistatic void vmw_cmdbuf_commit_cur(struct vmw_cmdbuf_man *man,
104562306a36Sopenharmony_ci				  size_t size, bool flush)
104662306a36Sopenharmony_ci{
104762306a36Sopenharmony_ci	struct vmw_cmdbuf_header *cur = man->cur;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	lockdep_assert_held_once(&man->cur_mutex);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	WARN_ON(size > cur->reserved);
105262306a36Sopenharmony_ci	man->cur_pos += size;
105362306a36Sopenharmony_ci	if (!size)
105462306a36Sopenharmony_ci		cur->cb_header->flags &= ~SVGA_CB_FLAG_DX_CONTEXT;
105562306a36Sopenharmony_ci	if (flush)
105662306a36Sopenharmony_ci		__vmw_cmdbuf_cur_flush(man);
105762306a36Sopenharmony_ci	vmw_cmdbuf_cur_unlock(man);
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci/**
106162306a36Sopenharmony_ci * vmw_cmdbuf_reserve - Reserve space for commands in a command buffer.
106262306a36Sopenharmony_ci *
106362306a36Sopenharmony_ci * @man: The command buffer manager.
106462306a36Sopenharmony_ci * @size: The requested size of the commands.
106562306a36Sopenharmony_ci * @ctx_id: The context id if any. Otherwise set to SVGA3D_REG_INVALID.
106662306a36Sopenharmony_ci * @interruptible: Whether to sleep interruptible while waiting for space.
106762306a36Sopenharmony_ci * @header: Header of the command buffer. NULL if the current command buffer
106862306a36Sopenharmony_ci * should be used.
106962306a36Sopenharmony_ci *
107062306a36Sopenharmony_ci * Returns a pointer to command buffer space if successful. Otherwise
107162306a36Sopenharmony_ci * returns an error pointer.
107262306a36Sopenharmony_ci */
107362306a36Sopenharmony_civoid *vmw_cmdbuf_reserve(struct vmw_cmdbuf_man *man, size_t size,
107462306a36Sopenharmony_ci			 int ctx_id, bool interruptible,
107562306a36Sopenharmony_ci			 struct vmw_cmdbuf_header *header)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	if (!header)
107862306a36Sopenharmony_ci		return vmw_cmdbuf_reserve_cur(man, size, ctx_id, interruptible);
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	if (size > header->size)
108162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	if (ctx_id != SVGA3D_INVALID_ID) {
108462306a36Sopenharmony_ci		header->cb_header->flags |= SVGA_CB_FLAG_DX_CONTEXT;
108562306a36Sopenharmony_ci		header->cb_header->dxContext = ctx_id;
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	header->reserved = size;
108962306a36Sopenharmony_ci	return header->cmd;
109062306a36Sopenharmony_ci}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci/**
109362306a36Sopenharmony_ci * vmw_cmdbuf_commit - Commit commands in a command buffer.
109462306a36Sopenharmony_ci *
109562306a36Sopenharmony_ci * @man: The command buffer manager.
109662306a36Sopenharmony_ci * @size: The size of the commands actually written.
109762306a36Sopenharmony_ci * @header: Header of the command buffer. NULL if the current command buffer
109862306a36Sopenharmony_ci * should be used.
109962306a36Sopenharmony_ci * @flush: Whether to flush the command buffer immediately.
110062306a36Sopenharmony_ci */
110162306a36Sopenharmony_civoid vmw_cmdbuf_commit(struct vmw_cmdbuf_man *man, size_t size,
110262306a36Sopenharmony_ci		       struct vmw_cmdbuf_header *header, bool flush)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	if (!header) {
110562306a36Sopenharmony_ci		vmw_cmdbuf_commit_cur(man, size, flush);
110662306a36Sopenharmony_ci		return;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	(void) vmw_cmdbuf_cur_lock(man, false);
111062306a36Sopenharmony_ci	__vmw_cmdbuf_cur_flush(man);
111162306a36Sopenharmony_ci	WARN_ON(size > header->reserved);
111262306a36Sopenharmony_ci	man->cur = header;
111362306a36Sopenharmony_ci	man->cur_pos = size;
111462306a36Sopenharmony_ci	if (!size)
111562306a36Sopenharmony_ci		header->cb_header->flags &= ~SVGA_CB_FLAG_DX_CONTEXT;
111662306a36Sopenharmony_ci	if (flush)
111762306a36Sopenharmony_ci		__vmw_cmdbuf_cur_flush(man);
111862306a36Sopenharmony_ci	vmw_cmdbuf_cur_unlock(man);
111962306a36Sopenharmony_ci}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci/**
112362306a36Sopenharmony_ci * vmw_cmdbuf_send_device_command - Send a command through the device context.
112462306a36Sopenharmony_ci *
112562306a36Sopenharmony_ci * @man: The command buffer manager.
112662306a36Sopenharmony_ci * @command: Pointer to the command to send.
112762306a36Sopenharmony_ci * @size: Size of the command.
112862306a36Sopenharmony_ci *
112962306a36Sopenharmony_ci * Synchronously sends a device context command.
113062306a36Sopenharmony_ci */
113162306a36Sopenharmony_cistatic int vmw_cmdbuf_send_device_command(struct vmw_cmdbuf_man *man,
113262306a36Sopenharmony_ci					  const void *command,
113362306a36Sopenharmony_ci					  size_t size)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	struct vmw_cmdbuf_header *header;
113662306a36Sopenharmony_ci	int status;
113762306a36Sopenharmony_ci	void *cmd = vmw_cmdbuf_alloc(man, size, false, &header);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	if (IS_ERR(cmd))
114062306a36Sopenharmony_ci		return PTR_ERR(cmd);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	memcpy(cmd, command, size);
114362306a36Sopenharmony_ci	header->cb_header->length = size;
114462306a36Sopenharmony_ci	header->cb_context = SVGA_CB_CONTEXT_DEVICE;
114562306a36Sopenharmony_ci	spin_lock(&man->lock);
114662306a36Sopenharmony_ci	status = vmw_cmdbuf_header_submit(header);
114762306a36Sopenharmony_ci	spin_unlock(&man->lock);
114862306a36Sopenharmony_ci	vmw_cmdbuf_header_free(header);
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	if (status != SVGA_CB_STATUS_COMPLETED) {
115162306a36Sopenharmony_ci		DRM_ERROR("Device context command failed with status %d\n",
115262306a36Sopenharmony_ci			  status);
115362306a36Sopenharmony_ci		return -EINVAL;
115462306a36Sopenharmony_ci	}
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	return 0;
115762306a36Sopenharmony_ci}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci/**
116062306a36Sopenharmony_ci * vmw_cmdbuf_preempt - Send a preempt command through the device
116162306a36Sopenharmony_ci * context.
116262306a36Sopenharmony_ci *
116362306a36Sopenharmony_ci * @man: The command buffer manager.
116462306a36Sopenharmony_ci * @context: Device context to pass command through.
116562306a36Sopenharmony_ci *
116662306a36Sopenharmony_ci * Synchronously sends a preempt command.
116762306a36Sopenharmony_ci */
116862306a36Sopenharmony_cistatic int vmw_cmdbuf_preempt(struct vmw_cmdbuf_man *man, u32 context)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct {
117162306a36Sopenharmony_ci		uint32 id;
117262306a36Sopenharmony_ci		SVGADCCmdPreempt body;
117362306a36Sopenharmony_ci	} __packed cmd;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	cmd.id = SVGA_DC_CMD_PREEMPT;
117662306a36Sopenharmony_ci	cmd.body.context = SVGA_CB_CONTEXT_0 + context;
117762306a36Sopenharmony_ci	cmd.body.ignoreIDZero = 0;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	return vmw_cmdbuf_send_device_command(man, &cmd, sizeof(cmd));
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci/**
118462306a36Sopenharmony_ci * vmw_cmdbuf_startstop - Send a start / stop command through the device
118562306a36Sopenharmony_ci * context.
118662306a36Sopenharmony_ci *
118762306a36Sopenharmony_ci * @man: The command buffer manager.
118862306a36Sopenharmony_ci * @context: Device context to start/stop.
118962306a36Sopenharmony_ci * @enable: Whether to enable or disable the context.
119062306a36Sopenharmony_ci *
119162306a36Sopenharmony_ci * Synchronously sends a device start / stop context command.
119262306a36Sopenharmony_ci */
119362306a36Sopenharmony_cistatic int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, u32 context,
119462306a36Sopenharmony_ci				bool enable)
119562306a36Sopenharmony_ci{
119662306a36Sopenharmony_ci	struct {
119762306a36Sopenharmony_ci		uint32 id;
119862306a36Sopenharmony_ci		SVGADCCmdStartStop body;
119962306a36Sopenharmony_ci	} __packed cmd;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	cmd.id = SVGA_DC_CMD_START_STOP_CONTEXT;
120262306a36Sopenharmony_ci	cmd.body.enable = (enable) ? 1 : 0;
120362306a36Sopenharmony_ci	cmd.body.context = SVGA_CB_CONTEXT_0 + context;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	return vmw_cmdbuf_send_device_command(man, &cmd, sizeof(cmd));
120662306a36Sopenharmony_ci}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci/**
120962306a36Sopenharmony_ci * vmw_cmdbuf_set_pool_size - Set command buffer manager sizes
121062306a36Sopenharmony_ci *
121162306a36Sopenharmony_ci * @man: The command buffer manager.
121262306a36Sopenharmony_ci * @size: The size of the main space pool.
121362306a36Sopenharmony_ci *
121462306a36Sopenharmony_ci * Set the size and allocate the main command buffer space pool.
121562306a36Sopenharmony_ci * If successful, this enables large command submissions.
121662306a36Sopenharmony_ci * Note that this function requires that rudimentary command
121762306a36Sopenharmony_ci * submission is already available and that the MOB memory manager is alive.
121862306a36Sopenharmony_ci * Returns 0 on success. Negative error code on failure.
121962306a36Sopenharmony_ci */
122062306a36Sopenharmony_ciint vmw_cmdbuf_set_pool_size(struct vmw_cmdbuf_man *man, size_t size)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	struct vmw_private *dev_priv = man->dev_priv;
122362306a36Sopenharmony_ci	int ret;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	if (man->has_pool)
122662306a36Sopenharmony_ci		return -EINVAL;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	/* First, try to allocate a huge chunk of DMA memory */
122962306a36Sopenharmony_ci	size = PAGE_ALIGN(size);
123062306a36Sopenharmony_ci	man->map = dma_alloc_coherent(dev_priv->drm.dev, size,
123162306a36Sopenharmony_ci				      &man->handle, GFP_KERNEL);
123262306a36Sopenharmony_ci	if (man->map) {
123362306a36Sopenharmony_ci		man->using_mob = false;
123462306a36Sopenharmony_ci	} else {
123562306a36Sopenharmony_ci		struct vmw_bo_params bo_params = {
123662306a36Sopenharmony_ci			.domain = VMW_BO_DOMAIN_MOB,
123762306a36Sopenharmony_ci			.busy_domain = VMW_BO_DOMAIN_MOB,
123862306a36Sopenharmony_ci			.bo_type = ttm_bo_type_kernel,
123962306a36Sopenharmony_ci			.size = size,
124062306a36Sopenharmony_ci			.pin = true
124162306a36Sopenharmony_ci		};
124262306a36Sopenharmony_ci		/*
124362306a36Sopenharmony_ci		 * DMA memory failed. If we can have command buffers in a
124462306a36Sopenharmony_ci		 * MOB, try to use that instead. Note that this will
124562306a36Sopenharmony_ci		 * actually call into the already enabled manager, when
124662306a36Sopenharmony_ci		 * binding the MOB.
124762306a36Sopenharmony_ci		 */
124862306a36Sopenharmony_ci		if (!(dev_priv->capabilities & SVGA_CAP_DX) ||
124962306a36Sopenharmony_ci		    !dev_priv->has_mob)
125062306a36Sopenharmony_ci			return -ENOMEM;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci		ret = vmw_bo_create(dev_priv, &bo_params, &man->cmd_space);
125362306a36Sopenharmony_ci		if (ret)
125462306a36Sopenharmony_ci			return ret;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci		man->map = vmw_bo_map_and_cache(man->cmd_space);
125762306a36Sopenharmony_ci		man->using_mob = man->map;
125862306a36Sopenharmony_ci	}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	man->size = size;
126162306a36Sopenharmony_ci	drm_mm_init(&man->mm, 0, size >> PAGE_SHIFT);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	man->has_pool = true;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	/*
126662306a36Sopenharmony_ci	 * For now, set the default size to VMW_CMDBUF_INLINE_SIZE to
126762306a36Sopenharmony_ci	 * prevent deadlocks from happening when vmw_cmdbuf_space_pool()
126862306a36Sopenharmony_ci	 * needs to wait for space and we block on further command
126962306a36Sopenharmony_ci	 * submissions to be able to free up space.
127062306a36Sopenharmony_ci	 */
127162306a36Sopenharmony_ci	man->default_size = VMW_CMDBUF_INLINE_SIZE;
127262306a36Sopenharmony_ci	drm_info(&dev_priv->drm,
127362306a36Sopenharmony_ci		 "Using command buffers with %s pool.\n",
127462306a36Sopenharmony_ci		 (man->using_mob) ? "MOB" : "DMA");
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	return 0;
127762306a36Sopenharmony_ci}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci/**
128062306a36Sopenharmony_ci * vmw_cmdbuf_man_create: Create a command buffer manager and enable it for
128162306a36Sopenharmony_ci * inline command buffer submissions only.
128262306a36Sopenharmony_ci *
128362306a36Sopenharmony_ci * @dev_priv: Pointer to device private structure.
128462306a36Sopenharmony_ci *
128562306a36Sopenharmony_ci * Returns a pointer to a cummand buffer manager to success or error pointer
128662306a36Sopenharmony_ci * on failure. The command buffer manager will be enabled for submissions of
128762306a36Sopenharmony_ci * size VMW_CMDBUF_INLINE_SIZE only.
128862306a36Sopenharmony_ci */
128962306a36Sopenharmony_cistruct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv)
129062306a36Sopenharmony_ci{
129162306a36Sopenharmony_ci	struct vmw_cmdbuf_man *man;
129262306a36Sopenharmony_ci	struct vmw_cmdbuf_context *ctx;
129362306a36Sopenharmony_ci	unsigned int i;
129462306a36Sopenharmony_ci	int ret;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	if (!(dev_priv->capabilities & SVGA_CAP_COMMAND_BUFFERS))
129762306a36Sopenharmony_ci		return ERR_PTR(-ENOSYS);
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	man = kzalloc(sizeof(*man), GFP_KERNEL);
130062306a36Sopenharmony_ci	if (!man)
130162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	man->num_contexts = (dev_priv->capabilities & SVGA_CAP_HP_CMD_QUEUE) ?
130462306a36Sopenharmony_ci		2 : 1;
130562306a36Sopenharmony_ci	man->headers = dma_pool_create("vmwgfx cmdbuf",
130662306a36Sopenharmony_ci				       dev_priv->drm.dev,
130762306a36Sopenharmony_ci				       sizeof(SVGACBHeader),
130862306a36Sopenharmony_ci				       64, PAGE_SIZE);
130962306a36Sopenharmony_ci	if (!man->headers) {
131062306a36Sopenharmony_ci		ret = -ENOMEM;
131162306a36Sopenharmony_ci		goto out_no_pool;
131262306a36Sopenharmony_ci	}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	man->dheaders = dma_pool_create("vmwgfx inline cmdbuf",
131562306a36Sopenharmony_ci					dev_priv->drm.dev,
131662306a36Sopenharmony_ci					sizeof(struct vmw_cmdbuf_dheader),
131762306a36Sopenharmony_ci					64, PAGE_SIZE);
131862306a36Sopenharmony_ci	if (!man->dheaders) {
131962306a36Sopenharmony_ci		ret = -ENOMEM;
132062306a36Sopenharmony_ci		goto out_no_dpool;
132162306a36Sopenharmony_ci	}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	for_each_cmdbuf_ctx(man, i, ctx)
132462306a36Sopenharmony_ci		vmw_cmdbuf_ctx_init(ctx);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	INIT_LIST_HEAD(&man->error);
132762306a36Sopenharmony_ci	spin_lock_init(&man->lock);
132862306a36Sopenharmony_ci	mutex_init(&man->cur_mutex);
132962306a36Sopenharmony_ci	mutex_init(&man->space_mutex);
133062306a36Sopenharmony_ci	mutex_init(&man->error_mutex);
133162306a36Sopenharmony_ci	man->default_size = VMW_CMDBUF_INLINE_SIZE;
133262306a36Sopenharmony_ci	init_waitqueue_head(&man->alloc_queue);
133362306a36Sopenharmony_ci	init_waitqueue_head(&man->idle_queue);
133462306a36Sopenharmony_ci	man->dev_priv = dev_priv;
133562306a36Sopenharmony_ci	man->max_hw_submitted = SVGA_CB_MAX_QUEUED_PER_CONTEXT - 1;
133662306a36Sopenharmony_ci	INIT_WORK(&man->work, &vmw_cmdbuf_work_func);
133762306a36Sopenharmony_ci	vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_ERROR,
133862306a36Sopenharmony_ci			       &dev_priv->error_waiters);
133962306a36Sopenharmony_ci	ret = vmw_cmdbuf_startstop(man, 0, true);
134062306a36Sopenharmony_ci	if (ret) {
134162306a36Sopenharmony_ci		DRM_ERROR("Failed starting command buffer contexts\n");
134262306a36Sopenharmony_ci		vmw_cmdbuf_man_destroy(man);
134362306a36Sopenharmony_ci		return ERR_PTR(ret);
134462306a36Sopenharmony_ci	}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	return man;
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ciout_no_dpool:
134962306a36Sopenharmony_ci	dma_pool_destroy(man->headers);
135062306a36Sopenharmony_ciout_no_pool:
135162306a36Sopenharmony_ci	kfree(man);
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	return ERR_PTR(ret);
135462306a36Sopenharmony_ci}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci/**
135762306a36Sopenharmony_ci * vmw_cmdbuf_remove_pool - Take down the main buffer space pool.
135862306a36Sopenharmony_ci *
135962306a36Sopenharmony_ci * @man: Pointer to a command buffer manager.
136062306a36Sopenharmony_ci *
136162306a36Sopenharmony_ci * This function removes the main buffer space pool, and should be called
136262306a36Sopenharmony_ci * before MOB memory management is removed. When this function has been called,
136362306a36Sopenharmony_ci * only small command buffer submissions of size VMW_CMDBUF_INLINE_SIZE or
136462306a36Sopenharmony_ci * less are allowed, and the default size of the command buffer for small kernel
136562306a36Sopenharmony_ci * submissions is also set to this size.
136662306a36Sopenharmony_ci */
136762306a36Sopenharmony_civoid vmw_cmdbuf_remove_pool(struct vmw_cmdbuf_man *man)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	if (!man->has_pool)
137062306a36Sopenharmony_ci		return;
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	man->has_pool = false;
137362306a36Sopenharmony_ci	man->default_size = VMW_CMDBUF_INLINE_SIZE;
137462306a36Sopenharmony_ci	(void) vmw_cmdbuf_idle(man, false, 10*HZ);
137562306a36Sopenharmony_ci	if (man->using_mob)
137662306a36Sopenharmony_ci		vmw_bo_unreference(&man->cmd_space);
137762306a36Sopenharmony_ci	else
137862306a36Sopenharmony_ci		dma_free_coherent(man->dev_priv->drm.dev,
137962306a36Sopenharmony_ci				  man->size, man->map, man->handle);
138062306a36Sopenharmony_ci}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci/**
138362306a36Sopenharmony_ci * vmw_cmdbuf_man_destroy - Take down a command buffer manager.
138462306a36Sopenharmony_ci *
138562306a36Sopenharmony_ci * @man: Pointer to a command buffer manager.
138662306a36Sopenharmony_ci *
138762306a36Sopenharmony_ci * This function idles and then destroys a command buffer manager.
138862306a36Sopenharmony_ci */
138962306a36Sopenharmony_civoid vmw_cmdbuf_man_destroy(struct vmw_cmdbuf_man *man)
139062306a36Sopenharmony_ci{
139162306a36Sopenharmony_ci	WARN_ON_ONCE(man->has_pool);
139262306a36Sopenharmony_ci	(void) vmw_cmdbuf_idle(man, false, 10*HZ);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	if (vmw_cmdbuf_startstop(man, 0, false))
139562306a36Sopenharmony_ci		DRM_ERROR("Failed stopping command buffer contexts.\n");
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	vmw_generic_waiter_remove(man->dev_priv, SVGA_IRQFLAG_ERROR,
139862306a36Sopenharmony_ci				  &man->dev_priv->error_waiters);
139962306a36Sopenharmony_ci	(void) cancel_work_sync(&man->work);
140062306a36Sopenharmony_ci	dma_pool_destroy(man->dheaders);
140162306a36Sopenharmony_ci	dma_pool_destroy(man->headers);
140262306a36Sopenharmony_ci	mutex_destroy(&man->cur_mutex);
140362306a36Sopenharmony_ci	mutex_destroy(&man->space_mutex);
140462306a36Sopenharmony_ci	mutex_destroy(&man->error_mutex);
140562306a36Sopenharmony_ci	kfree(man);
140662306a36Sopenharmony_ci}
1407