xref: /openbmc/linux/drivers/accel/qaic/qaic_data.c (revision efcedd56)
1ff13be83SJeffrey Hugo // SPDX-License-Identifier: GPL-2.0-only
2ff13be83SJeffrey Hugo 
3ff13be83SJeffrey Hugo /* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
4ff13be83SJeffrey Hugo /* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
5ff13be83SJeffrey Hugo 
6ff13be83SJeffrey Hugo #include <linux/bitfield.h>
7ff13be83SJeffrey Hugo #include <linux/bits.h>
8ff13be83SJeffrey Hugo #include <linux/completion.h>
9ff13be83SJeffrey Hugo #include <linux/delay.h>
10ff13be83SJeffrey Hugo #include <linux/dma-buf.h>
11ff13be83SJeffrey Hugo #include <linux/dma-mapping.h>
12ff13be83SJeffrey Hugo #include <linux/interrupt.h>
13ff13be83SJeffrey Hugo #include <linux/kref.h>
14ff13be83SJeffrey Hugo #include <linux/list.h>
15ff13be83SJeffrey Hugo #include <linux/math64.h>
16ff13be83SJeffrey Hugo #include <linux/mm.h>
17ff13be83SJeffrey Hugo #include <linux/moduleparam.h>
18ff13be83SJeffrey Hugo #include <linux/scatterlist.h>
19ff13be83SJeffrey Hugo #include <linux/spinlock.h>
20ff13be83SJeffrey Hugo #include <linux/srcu.h>
21ff13be83SJeffrey Hugo #include <linux/types.h>
22ff13be83SJeffrey Hugo #include <linux/uaccess.h>
23ff13be83SJeffrey Hugo #include <linux/wait.h>
24ff13be83SJeffrey Hugo #include <drm/drm_file.h>
25ff13be83SJeffrey Hugo #include <drm/drm_gem.h>
268d0d16a3SPranjal Ramajor Asha Kanojiya #include <drm/drm_prime.h>
27ff13be83SJeffrey Hugo #include <drm/drm_print.h>
28ff13be83SJeffrey Hugo #include <uapi/drm/qaic_accel.h>
29ff13be83SJeffrey Hugo 
30ff13be83SJeffrey Hugo #include "qaic.h"
31ff13be83SJeffrey Hugo 
32ff13be83SJeffrey Hugo #define SEM_VAL_MASK	GENMASK_ULL(11, 0)
33ff13be83SJeffrey Hugo #define SEM_INDEX_MASK	GENMASK_ULL(4, 0)
34ff13be83SJeffrey Hugo #define BULK_XFER	BIT(3)
35ff13be83SJeffrey Hugo #define GEN_COMPLETION	BIT(4)
36ff13be83SJeffrey Hugo #define INBOUND_XFER	1
37ff13be83SJeffrey Hugo #define OUTBOUND_XFER	2
38ff13be83SJeffrey Hugo #define REQHP_OFF	0x0 /* we read this */
39ff13be83SJeffrey Hugo #define REQTP_OFF	0x4 /* we write this */
40ff13be83SJeffrey Hugo #define RSPHP_OFF	0x8 /* we write this */
41ff13be83SJeffrey Hugo #define RSPTP_OFF	0xc /* we read this */
42ff13be83SJeffrey Hugo 
43ff13be83SJeffrey Hugo #define ENCODE_SEM(val, index, sync, cmd, flags)			\
44ff13be83SJeffrey Hugo 		({							\
45ff13be83SJeffrey Hugo 			FIELD_PREP(GENMASK(11, 0), (val)) |		\
46ff13be83SJeffrey Hugo 			FIELD_PREP(GENMASK(20, 16), (index)) |		\
47ff13be83SJeffrey Hugo 			FIELD_PREP(BIT(22), (sync)) |			\
48ff13be83SJeffrey Hugo 			FIELD_PREP(GENMASK(26, 24), (cmd)) |		\
49ff13be83SJeffrey Hugo 			FIELD_PREP(GENMASK(30, 29), (flags)) |		\
50ff13be83SJeffrey Hugo 			FIELD_PREP(BIT(31), (cmd) ? 1 : 0);		\
51ff13be83SJeffrey Hugo 		})
52ff13be83SJeffrey Hugo #define NUM_EVENTS	128
53ff13be83SJeffrey Hugo #define NUM_DELAYS	10
54ff13be83SJeffrey Hugo 
55ff13be83SJeffrey Hugo static unsigned int wait_exec_default_timeout_ms = 5000; /* 5 sec default */
56ff13be83SJeffrey Hugo module_param(wait_exec_default_timeout_ms, uint, 0600);
57ff13be83SJeffrey Hugo MODULE_PARM_DESC(wait_exec_default_timeout_ms, "Default timeout for DRM_IOCTL_QAIC_WAIT_BO");
58ff13be83SJeffrey Hugo 
59ff13be83SJeffrey Hugo static unsigned int datapath_poll_interval_us = 100; /* 100 usec default */
60ff13be83SJeffrey Hugo module_param(datapath_poll_interval_us, uint, 0600);
61ff13be83SJeffrey Hugo MODULE_PARM_DESC(datapath_poll_interval_us,
62ff13be83SJeffrey Hugo 		 "Amount of time to sleep between activity when datapath polling is enabled");
63ff13be83SJeffrey Hugo 
64ff13be83SJeffrey Hugo struct dbc_req {
65ff13be83SJeffrey Hugo 	/*
66ff13be83SJeffrey Hugo 	 * A request ID is assigned to each memory handle going in DMA queue.
67ff13be83SJeffrey Hugo 	 * As a single memory handle can enqueue multiple elements in DMA queue
68ff13be83SJeffrey Hugo 	 * all of them will have the same request ID.
69ff13be83SJeffrey Hugo 	 */
70ff13be83SJeffrey Hugo 	__le16	req_id;
71ff13be83SJeffrey Hugo 	/* Future use */
72ff13be83SJeffrey Hugo 	__u8	seq_id;
73ff13be83SJeffrey Hugo 	/*
74ff13be83SJeffrey Hugo 	 * Special encoded variable
75ff13be83SJeffrey Hugo 	 * 7	0 - Do not force to generate MSI after DMA is completed
76ff13be83SJeffrey Hugo 	 *	1 - Force to generate MSI after DMA is completed
77ff13be83SJeffrey Hugo 	 * 6:5	Reserved
78ff13be83SJeffrey Hugo 	 * 4	1 - Generate completion element in the response queue
79ff13be83SJeffrey Hugo 	 *	0 - No Completion Code
80ff13be83SJeffrey Hugo 	 * 3	0 - DMA request is a Link list transfer
81ff13be83SJeffrey Hugo 	 *	1 - DMA request is a Bulk transfer
82ff13be83SJeffrey Hugo 	 * 2	Reserved
83ff13be83SJeffrey Hugo 	 * 1:0	00 - No DMA transfer involved
84ff13be83SJeffrey Hugo 	 *	01 - DMA transfer is part of inbound transfer
85ff13be83SJeffrey Hugo 	 *	10 - DMA transfer has outbound transfer
86ff13be83SJeffrey Hugo 	 *	11 - NA
87ff13be83SJeffrey Hugo 	 */
88ff13be83SJeffrey Hugo 	__u8	cmd;
89ff13be83SJeffrey Hugo 	__le32	resv;
90ff13be83SJeffrey Hugo 	/* Source address for the transfer */
91ff13be83SJeffrey Hugo 	__le64	src_addr;
92ff13be83SJeffrey Hugo 	/* Destination address for the transfer */
93ff13be83SJeffrey Hugo 	__le64	dest_addr;
94ff13be83SJeffrey Hugo 	/* Length of transfer request */
95ff13be83SJeffrey Hugo 	__le32	len;
96ff13be83SJeffrey Hugo 	__le32	resv2;
97ff13be83SJeffrey Hugo 	/* Doorbell address */
98ff13be83SJeffrey Hugo 	__le64	db_addr;
99ff13be83SJeffrey Hugo 	/*
100ff13be83SJeffrey Hugo 	 * Special encoded variable
101ff13be83SJeffrey Hugo 	 * 7	1 - Doorbell(db) write
102ff13be83SJeffrey Hugo 	 *	0 - No doorbell write
103ff13be83SJeffrey Hugo 	 * 6:2	Reserved
104ff13be83SJeffrey Hugo 	 * 1:0	00 - 32 bit access, db address must be aligned to 32bit-boundary
105ff13be83SJeffrey Hugo 	 *	01 - 16 bit access, db address must be aligned to 16bit-boundary
106ff13be83SJeffrey Hugo 	 *	10 - 8 bit access, db address must be aligned to 8bit-boundary
107ff13be83SJeffrey Hugo 	 *	11 - Reserved
108ff13be83SJeffrey Hugo 	 */
109ff13be83SJeffrey Hugo 	__u8	db_len;
110ff13be83SJeffrey Hugo 	__u8	resv3;
111ff13be83SJeffrey Hugo 	__le16	resv4;
112ff13be83SJeffrey Hugo 	/* 32 bit data written to doorbell address */
113ff13be83SJeffrey Hugo 	__le32	db_data;
114ff13be83SJeffrey Hugo 	/*
115ff13be83SJeffrey Hugo 	 * Special encoded variable
116ff13be83SJeffrey Hugo 	 * All the fields of sem_cmdX are passed from user and all are ORed
117ff13be83SJeffrey Hugo 	 * together to form sem_cmd.
118ff13be83SJeffrey Hugo 	 * 0:11		Semaphore value
119ff13be83SJeffrey Hugo 	 * 15:12	Reserved
120ff13be83SJeffrey Hugo 	 * 20:16	Semaphore index
121ff13be83SJeffrey Hugo 	 * 21		Reserved
122ff13be83SJeffrey Hugo 	 * 22		Semaphore Sync
123ff13be83SJeffrey Hugo 	 * 23		Reserved
124ff13be83SJeffrey Hugo 	 * 26:24	Semaphore command
125ff13be83SJeffrey Hugo 	 * 28:27	Reserved
126ff13be83SJeffrey Hugo 	 * 29		Semaphore DMA out bound sync fence
127ff13be83SJeffrey Hugo 	 * 30		Semaphore DMA in bound sync fence
128ff13be83SJeffrey Hugo 	 * 31		Enable semaphore command
129ff13be83SJeffrey Hugo 	 */
130ff13be83SJeffrey Hugo 	__le32	sem_cmd0;
131ff13be83SJeffrey Hugo 	__le32	sem_cmd1;
132ff13be83SJeffrey Hugo 	__le32	sem_cmd2;
133ff13be83SJeffrey Hugo 	__le32	sem_cmd3;
134ff13be83SJeffrey Hugo } __packed;
135ff13be83SJeffrey Hugo 
136ff13be83SJeffrey Hugo struct dbc_rsp {
137ff13be83SJeffrey Hugo 	/* Request ID of the memory handle whose DMA transaction is completed */
138ff13be83SJeffrey Hugo 	__le16	req_id;
139ff13be83SJeffrey Hugo 	/* Status of the DMA transaction. 0 : Success otherwise failure */
140ff13be83SJeffrey Hugo 	__le16	status;
141ff13be83SJeffrey Hugo } __packed;
142ff13be83SJeffrey Hugo 
get_dbc_req_elem_size(void)143ff13be83SJeffrey Hugo inline int get_dbc_req_elem_size(void)
144ff13be83SJeffrey Hugo {
145ff13be83SJeffrey Hugo 	return sizeof(struct dbc_req);
146ff13be83SJeffrey Hugo }
147ff13be83SJeffrey Hugo 
get_dbc_rsp_elem_size(void)148ff13be83SJeffrey Hugo inline int get_dbc_rsp_elem_size(void)
149ff13be83SJeffrey Hugo {
150ff13be83SJeffrey Hugo 	return sizeof(struct dbc_rsp);
151ff13be83SJeffrey Hugo }
152ff13be83SJeffrey Hugo 
free_slice(struct kref * kref)153ff13be83SJeffrey Hugo static void free_slice(struct kref *kref)
154ff13be83SJeffrey Hugo {
155ff13be83SJeffrey Hugo 	struct bo_slice *slice = container_of(kref, struct bo_slice, ref_count);
156ff13be83SJeffrey Hugo 
157ff13be83SJeffrey Hugo 	list_del(&slice->slice);
158ff13be83SJeffrey Hugo 	drm_gem_object_put(&slice->bo->base);
159ff13be83SJeffrey Hugo 	sg_free_table(slice->sgt);
160ff13be83SJeffrey Hugo 	kfree(slice->sgt);
161ff13be83SJeffrey Hugo 	kfree(slice->reqs);
162ff13be83SJeffrey Hugo 	kfree(slice);
163ff13be83SJeffrey Hugo }
164ff13be83SJeffrey Hugo 
clone_range_of_sgt_for_slice(struct qaic_device * qdev,struct sg_table ** sgt_out,struct sg_table * sgt_in,u64 size,u64 offset)165ff13be83SJeffrey Hugo static int clone_range_of_sgt_for_slice(struct qaic_device *qdev, struct sg_table **sgt_out,
166ff13be83SJeffrey Hugo 					struct sg_table *sgt_in, u64 size, u64 offset)
167ff13be83SJeffrey Hugo {
168ff13be83SJeffrey Hugo 	int total_len, len, nents, offf = 0, offl = 0;
169ff13be83SJeffrey Hugo 	struct scatterlist *sg, *sgn, *sgf, *sgl;
170ff13be83SJeffrey Hugo 	struct sg_table *sgt;
171ff13be83SJeffrey Hugo 	int ret, j;
172ff13be83SJeffrey Hugo 
173ff13be83SJeffrey Hugo 	/* find out number of relevant nents needed for this mem */
174ff13be83SJeffrey Hugo 	total_len = 0;
175ff13be83SJeffrey Hugo 	sgf = NULL;
176ff13be83SJeffrey Hugo 	sgl = NULL;
177ff13be83SJeffrey Hugo 	nents = 0;
178ff13be83SJeffrey Hugo 
179ff13be83SJeffrey Hugo 	size = size ? size : PAGE_SIZE;
180ff13be83SJeffrey Hugo 	for (sg = sgt_in->sgl; sg; sg = sg_next(sg)) {
181ff13be83SJeffrey Hugo 		len = sg_dma_len(sg);
182ff13be83SJeffrey Hugo 
183ff13be83SJeffrey Hugo 		if (!len)
184ff13be83SJeffrey Hugo 			continue;
185ff13be83SJeffrey Hugo 		if (offset >= total_len && offset < total_len + len) {
186ff13be83SJeffrey Hugo 			sgf = sg;
187ff13be83SJeffrey Hugo 			offf = offset - total_len;
188ff13be83SJeffrey Hugo 		}
189ff13be83SJeffrey Hugo 		if (sgf)
190ff13be83SJeffrey Hugo 			nents++;
191ff13be83SJeffrey Hugo 		if (offset + size >= total_len &&
192ff13be83SJeffrey Hugo 		    offset + size <= total_len + len) {
193ff13be83SJeffrey Hugo 			sgl = sg;
194ff13be83SJeffrey Hugo 			offl = offset + size - total_len;
195ff13be83SJeffrey Hugo 			break;
196ff13be83SJeffrey Hugo 		}
197ff13be83SJeffrey Hugo 		total_len += len;
198ff13be83SJeffrey Hugo 	}
199ff13be83SJeffrey Hugo 
200ff13be83SJeffrey Hugo 	if (!sgf || !sgl) {
201ff13be83SJeffrey Hugo 		ret = -EINVAL;
202ff13be83SJeffrey Hugo 		goto out;
203ff13be83SJeffrey Hugo 	}
204ff13be83SJeffrey Hugo 
205ff13be83SJeffrey Hugo 	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
206ff13be83SJeffrey Hugo 	if (!sgt) {
207ff13be83SJeffrey Hugo 		ret = -ENOMEM;
208ff13be83SJeffrey Hugo 		goto out;
209ff13be83SJeffrey Hugo 	}
210ff13be83SJeffrey Hugo 
211ff13be83SJeffrey Hugo 	ret = sg_alloc_table(sgt, nents, GFP_KERNEL);
212ff13be83SJeffrey Hugo 	if (ret)
213ff13be83SJeffrey Hugo 		goto free_sgt;
214ff13be83SJeffrey Hugo 
215ff13be83SJeffrey Hugo 	/* copy relevant sg node and fix page and length */
216ff13be83SJeffrey Hugo 	sgn = sgf;
217ff13be83SJeffrey Hugo 	for_each_sgtable_sg(sgt, sg, j) {
218ff13be83SJeffrey Hugo 		memcpy(sg, sgn, sizeof(*sg));
219ff13be83SJeffrey Hugo 		if (sgn == sgf) {
220ff13be83SJeffrey Hugo 			sg_dma_address(sg) += offf;
221ff13be83SJeffrey Hugo 			sg_dma_len(sg) -= offf;
222ff13be83SJeffrey Hugo 			sg_set_page(sg, sg_page(sgn), sg_dma_len(sg), offf);
223ff13be83SJeffrey Hugo 		} else {
224ff13be83SJeffrey Hugo 			offf = 0;
225ff13be83SJeffrey Hugo 		}
226ff13be83SJeffrey Hugo 		if (sgn == sgl) {
227ff13be83SJeffrey Hugo 			sg_dma_len(sg) = offl - offf;
228ff13be83SJeffrey Hugo 			sg_set_page(sg, sg_page(sgn), offl - offf, offf);
229ff13be83SJeffrey Hugo 			sg_mark_end(sg);
230ff13be83SJeffrey Hugo 			break;
231ff13be83SJeffrey Hugo 		}
232ff13be83SJeffrey Hugo 		sgn = sg_next(sgn);
233ff13be83SJeffrey Hugo 	}
234ff13be83SJeffrey Hugo 
235ff13be83SJeffrey Hugo 	*sgt_out = sgt;
236ff13be83SJeffrey Hugo 	return ret;
237ff13be83SJeffrey Hugo 
238ff13be83SJeffrey Hugo free_sgt:
239ff13be83SJeffrey Hugo 	kfree(sgt);
240ff13be83SJeffrey Hugo out:
241ff13be83SJeffrey Hugo 	*sgt_out = NULL;
242ff13be83SJeffrey Hugo 	return ret;
243ff13be83SJeffrey Hugo }
244ff13be83SJeffrey Hugo 
encode_reqs(struct qaic_device * qdev,struct bo_slice * slice,struct qaic_attach_slice_entry * req)245ff13be83SJeffrey Hugo static int encode_reqs(struct qaic_device *qdev, struct bo_slice *slice,
246ff13be83SJeffrey Hugo 		       struct qaic_attach_slice_entry *req)
247ff13be83SJeffrey Hugo {
248ff13be83SJeffrey Hugo 	__le64 db_addr = cpu_to_le64(req->db_addr);
249ff13be83SJeffrey Hugo 	__le32 db_data = cpu_to_le32(req->db_data);
250ff13be83SJeffrey Hugo 	struct scatterlist *sg;
251ff13be83SJeffrey Hugo 	__u8 cmd = BULK_XFER;
252ff13be83SJeffrey Hugo 	int presync_sem;
253ff13be83SJeffrey Hugo 	u64 dev_addr;
254ff13be83SJeffrey Hugo 	__u8 db_len;
255ff13be83SJeffrey Hugo 	int i;
256ff13be83SJeffrey Hugo 
257ff13be83SJeffrey Hugo 	if (!slice->no_xfer)
258ff13be83SJeffrey Hugo 		cmd |= (slice->dir == DMA_TO_DEVICE ? INBOUND_XFER : OUTBOUND_XFER);
259ff13be83SJeffrey Hugo 
260ff13be83SJeffrey Hugo 	if (req->db_len && !IS_ALIGNED(req->db_addr, req->db_len / 8))
261ff13be83SJeffrey Hugo 		return -EINVAL;
262ff13be83SJeffrey Hugo 
263ff13be83SJeffrey Hugo 	presync_sem = req->sem0.presync + req->sem1.presync + req->sem2.presync + req->sem3.presync;
264ff13be83SJeffrey Hugo 	if (presync_sem > 1)
265ff13be83SJeffrey Hugo 		return -EINVAL;
266ff13be83SJeffrey Hugo 
267ff13be83SJeffrey Hugo 	presync_sem = req->sem0.presync << 0 | req->sem1.presync << 1 |
268ff13be83SJeffrey Hugo 		      req->sem2.presync << 2 | req->sem3.presync << 3;
269ff13be83SJeffrey Hugo 
270ff13be83SJeffrey Hugo 	switch (req->db_len) {
271ff13be83SJeffrey Hugo 	case 32:
272ff13be83SJeffrey Hugo 		db_len = BIT(7);
273ff13be83SJeffrey Hugo 		break;
274ff13be83SJeffrey Hugo 	case 16:
275ff13be83SJeffrey Hugo 		db_len = BIT(7) | 1;
276ff13be83SJeffrey Hugo 		break;
277ff13be83SJeffrey Hugo 	case 8:
278ff13be83SJeffrey Hugo 		db_len = BIT(7) | 2;
279ff13be83SJeffrey Hugo 		break;
280ff13be83SJeffrey Hugo 	case 0:
281ff13be83SJeffrey Hugo 		db_len = 0; /* doorbell is not active for this command */
282ff13be83SJeffrey Hugo 		break;
283ff13be83SJeffrey Hugo 	default:
284ff13be83SJeffrey Hugo 		return -EINVAL; /* should never hit this */
285ff13be83SJeffrey Hugo 	}
286ff13be83SJeffrey Hugo 
287ff13be83SJeffrey Hugo 	/*
288ff13be83SJeffrey Hugo 	 * When we end up splitting up a single request (ie a buf slice) into
289ff13be83SJeffrey Hugo 	 * multiple DMA requests, we have to manage the sync data carefully.
290ff13be83SJeffrey Hugo 	 * There can only be one presync sem. That needs to be on every xfer
291ff13be83SJeffrey Hugo 	 * so that the DMA engine doesn't transfer data before the receiver is
292ff13be83SJeffrey Hugo 	 * ready. We only do the doorbell and postsync sems after the xfer.
293ff13be83SJeffrey Hugo 	 * To guarantee previous xfers for the request are complete, we use a
294ff13be83SJeffrey Hugo 	 * fence.
295ff13be83SJeffrey Hugo 	 */
296ff13be83SJeffrey Hugo 	dev_addr = req->dev_addr;
297ff13be83SJeffrey Hugo 	for_each_sgtable_sg(slice->sgt, sg, i) {
298ff13be83SJeffrey Hugo 		slice->reqs[i].cmd = cmd;
299ff13be83SJeffrey Hugo 		slice->reqs[i].src_addr = cpu_to_le64(slice->dir == DMA_TO_DEVICE ?
300ff13be83SJeffrey Hugo 						      sg_dma_address(sg) : dev_addr);
301ff13be83SJeffrey Hugo 		slice->reqs[i].dest_addr = cpu_to_le64(slice->dir == DMA_TO_DEVICE ?
302ff13be83SJeffrey Hugo 						       dev_addr : sg_dma_address(sg));
303ff13be83SJeffrey Hugo 		/*
304ff13be83SJeffrey Hugo 		 * sg_dma_len(sg) returns size of a DMA segment, maximum DMA
305ff13be83SJeffrey Hugo 		 * segment size is set to UINT_MAX by qaic and hence return
306ff13be83SJeffrey Hugo 		 * values of sg_dma_len(sg) can never exceed u32 range. So,
307ff13be83SJeffrey Hugo 		 * by down sizing we are not corrupting the value.
308ff13be83SJeffrey Hugo 		 */
309ff13be83SJeffrey Hugo 		slice->reqs[i].len = cpu_to_le32((u32)sg_dma_len(sg));
310ff13be83SJeffrey Hugo 		switch (presync_sem) {
311ff13be83SJeffrey Hugo 		case BIT(0):
312ff13be83SJeffrey Hugo 			slice->reqs[i].sem_cmd0 = cpu_to_le32(ENCODE_SEM(req->sem0.val,
313ff13be83SJeffrey Hugo 									 req->sem0.index,
314ff13be83SJeffrey Hugo 									 req->sem0.presync,
315ff13be83SJeffrey Hugo 									 req->sem0.cmd,
316ff13be83SJeffrey Hugo 									 req->sem0.flags));
317ff13be83SJeffrey Hugo 			break;
318ff13be83SJeffrey Hugo 		case BIT(1):
319ff13be83SJeffrey Hugo 			slice->reqs[i].sem_cmd1 = cpu_to_le32(ENCODE_SEM(req->sem1.val,
320ff13be83SJeffrey Hugo 									 req->sem1.index,
321ff13be83SJeffrey Hugo 									 req->sem1.presync,
322ff13be83SJeffrey Hugo 									 req->sem1.cmd,
323ff13be83SJeffrey Hugo 									 req->sem1.flags));
324ff13be83SJeffrey Hugo 			break;
325ff13be83SJeffrey Hugo 		case BIT(2):
326ff13be83SJeffrey Hugo 			slice->reqs[i].sem_cmd2 = cpu_to_le32(ENCODE_SEM(req->sem2.val,
327ff13be83SJeffrey Hugo 									 req->sem2.index,
328ff13be83SJeffrey Hugo 									 req->sem2.presync,
329ff13be83SJeffrey Hugo 									 req->sem2.cmd,
330ff13be83SJeffrey Hugo 									 req->sem2.flags));
331ff13be83SJeffrey Hugo 			break;
332ff13be83SJeffrey Hugo 		case BIT(3):
333ff13be83SJeffrey Hugo 			slice->reqs[i].sem_cmd3 = cpu_to_le32(ENCODE_SEM(req->sem3.val,
334ff13be83SJeffrey Hugo 									 req->sem3.index,
335ff13be83SJeffrey Hugo 									 req->sem3.presync,
336ff13be83SJeffrey Hugo 									 req->sem3.cmd,
337ff13be83SJeffrey Hugo 									 req->sem3.flags));
338ff13be83SJeffrey Hugo 			break;
339ff13be83SJeffrey Hugo 		}
340ff13be83SJeffrey Hugo 		dev_addr += sg_dma_len(sg);
341ff13be83SJeffrey Hugo 	}
342ff13be83SJeffrey Hugo 	/* add post transfer stuff to last segment */
343ff13be83SJeffrey Hugo 	i--;
344ff13be83SJeffrey Hugo 	slice->reqs[i].cmd |= GEN_COMPLETION;
345ff13be83SJeffrey Hugo 	slice->reqs[i].db_addr = db_addr;
346ff13be83SJeffrey Hugo 	slice->reqs[i].db_len = db_len;
347ff13be83SJeffrey Hugo 	slice->reqs[i].db_data = db_data;
348ff13be83SJeffrey Hugo 	/*
349ff13be83SJeffrey Hugo 	 * Add a fence if we have more than one request going to the hardware
350ff13be83SJeffrey Hugo 	 * representing the entirety of the user request, and the user request
351ff13be83SJeffrey Hugo 	 * has no presync condition.
352ff13be83SJeffrey Hugo 	 * Fences are expensive, so we try to avoid them. We rely on the
353ff13be83SJeffrey Hugo 	 * hardware behavior to avoid needing one when there is a presync
354ff13be83SJeffrey Hugo 	 * condition. When a presync exists, all requests for that same
355ff13be83SJeffrey Hugo 	 * presync will be queued into a fifo. Thus, since we queue the
356ff13be83SJeffrey Hugo 	 * post xfer activity only on the last request we queue, the hardware
357ff13be83SJeffrey Hugo 	 * will ensure that the last queued request is processed last, thus
358ff13be83SJeffrey Hugo 	 * making sure the post xfer activity happens at the right time without
359ff13be83SJeffrey Hugo 	 * a fence.
360ff13be83SJeffrey Hugo 	 */
361ff13be83SJeffrey Hugo 	if (i && !presync_sem)
362ff13be83SJeffrey Hugo 		req->sem0.flags |= (slice->dir == DMA_TO_DEVICE ?
363ff13be83SJeffrey Hugo 				    QAIC_SEM_INSYNCFENCE : QAIC_SEM_OUTSYNCFENCE);
364ff13be83SJeffrey Hugo 	slice->reqs[i].sem_cmd0 = cpu_to_le32(ENCODE_SEM(req->sem0.val, req->sem0.index,
365ff13be83SJeffrey Hugo 							 req->sem0.presync, req->sem0.cmd,
366ff13be83SJeffrey Hugo 							 req->sem0.flags));
367ff13be83SJeffrey Hugo 	slice->reqs[i].sem_cmd1 = cpu_to_le32(ENCODE_SEM(req->sem1.val, req->sem1.index,
368ff13be83SJeffrey Hugo 							 req->sem1.presync, req->sem1.cmd,
369ff13be83SJeffrey Hugo 							 req->sem1.flags));
370ff13be83SJeffrey Hugo 	slice->reqs[i].sem_cmd2 = cpu_to_le32(ENCODE_SEM(req->sem2.val, req->sem2.index,
371ff13be83SJeffrey Hugo 							 req->sem2.presync, req->sem2.cmd,
372ff13be83SJeffrey Hugo 							 req->sem2.flags));
373ff13be83SJeffrey Hugo 	slice->reqs[i].sem_cmd3 = cpu_to_le32(ENCODE_SEM(req->sem3.val, req->sem3.index,
374ff13be83SJeffrey Hugo 							 req->sem3.presync, req->sem3.cmd,
375ff13be83SJeffrey Hugo 							 req->sem3.flags));
376ff13be83SJeffrey Hugo 
377ff13be83SJeffrey Hugo 	return 0;
378ff13be83SJeffrey Hugo }
379ff13be83SJeffrey Hugo 
qaic_map_one_slice(struct qaic_device * qdev,struct qaic_bo * bo,struct qaic_attach_slice_entry * slice_ent)380ff13be83SJeffrey Hugo static int qaic_map_one_slice(struct qaic_device *qdev, struct qaic_bo *bo,
381ff13be83SJeffrey Hugo 			      struct qaic_attach_slice_entry *slice_ent)
382ff13be83SJeffrey Hugo {
383ff13be83SJeffrey Hugo 	struct sg_table *sgt = NULL;
384ff13be83SJeffrey Hugo 	struct bo_slice *slice;
385ff13be83SJeffrey Hugo 	int ret;
386ff13be83SJeffrey Hugo 
387ff13be83SJeffrey Hugo 	ret = clone_range_of_sgt_for_slice(qdev, &sgt, bo->sgt, slice_ent->size, slice_ent->offset);
388ff13be83SJeffrey Hugo 	if (ret)
389ff13be83SJeffrey Hugo 		goto out;
390ff13be83SJeffrey Hugo 
391ff13be83SJeffrey Hugo 	slice = kmalloc(sizeof(*slice), GFP_KERNEL);
392ff13be83SJeffrey Hugo 	if (!slice) {
393ff13be83SJeffrey Hugo 		ret = -ENOMEM;
394ff13be83SJeffrey Hugo 		goto free_sgt;
395ff13be83SJeffrey Hugo 	}
396ff13be83SJeffrey Hugo 
397ff13be83SJeffrey Hugo 	slice->reqs = kcalloc(sgt->nents, sizeof(*slice->reqs), GFP_KERNEL);
398ff13be83SJeffrey Hugo 	if (!slice->reqs) {
399ff13be83SJeffrey Hugo 		ret = -ENOMEM;
400ff13be83SJeffrey Hugo 		goto free_slice;
401ff13be83SJeffrey Hugo 	}
402ff13be83SJeffrey Hugo 
403ff13be83SJeffrey Hugo 	slice->no_xfer = !slice_ent->size;
404ff13be83SJeffrey Hugo 	slice->sgt = sgt;
405ff13be83SJeffrey Hugo 	slice->nents = sgt->nents;
406ff13be83SJeffrey Hugo 	slice->dir = bo->dir;
407ff13be83SJeffrey Hugo 	slice->bo = bo;
408ff13be83SJeffrey Hugo 	slice->size = slice_ent->size;
409ff13be83SJeffrey Hugo 	slice->offset = slice_ent->offset;
410ff13be83SJeffrey Hugo 
411ff13be83SJeffrey Hugo 	ret = encode_reqs(qdev, slice, slice_ent);
412ff13be83SJeffrey Hugo 	if (ret)
413ff13be83SJeffrey Hugo 		goto free_req;
414ff13be83SJeffrey Hugo 
415ff13be83SJeffrey Hugo 	bo->total_slice_nents += sgt->nents;
416ff13be83SJeffrey Hugo 	kref_init(&slice->ref_count);
417ff13be83SJeffrey Hugo 	drm_gem_object_get(&bo->base);
418ff13be83SJeffrey Hugo 	list_add_tail(&slice->slice, &bo->slices);
419ff13be83SJeffrey Hugo 
420ff13be83SJeffrey Hugo 	return 0;
421ff13be83SJeffrey Hugo 
422ff13be83SJeffrey Hugo free_req:
423ff13be83SJeffrey Hugo 	kfree(slice->reqs);
424ff13be83SJeffrey Hugo free_slice:
425ff13be83SJeffrey Hugo 	kfree(slice);
426ff13be83SJeffrey Hugo free_sgt:
427ff13be83SJeffrey Hugo 	sg_free_table(sgt);
428ff13be83SJeffrey Hugo 	kfree(sgt);
429ff13be83SJeffrey Hugo out:
430ff13be83SJeffrey Hugo 	return ret;
431ff13be83SJeffrey Hugo }
432ff13be83SJeffrey Hugo 
create_sgt(struct qaic_device * qdev,struct sg_table ** sgt_out,u64 size)433ff13be83SJeffrey Hugo static int create_sgt(struct qaic_device *qdev, struct sg_table **sgt_out, u64 size)
434ff13be83SJeffrey Hugo {
435ff13be83SJeffrey Hugo 	struct scatterlist *sg;
436ff13be83SJeffrey Hugo 	struct sg_table *sgt;
437ff13be83SJeffrey Hugo 	struct page **pages;
438ff13be83SJeffrey Hugo 	int *pages_order;
439ff13be83SJeffrey Hugo 	int buf_extra;
440ff13be83SJeffrey Hugo 	int max_order;
441ff13be83SJeffrey Hugo 	int nr_pages;
442ff13be83SJeffrey Hugo 	int ret = 0;
443ff13be83SJeffrey Hugo 	int i, j, k;
444ff13be83SJeffrey Hugo 	int order;
445ff13be83SJeffrey Hugo 
446ff13be83SJeffrey Hugo 	if (size) {
447ff13be83SJeffrey Hugo 		nr_pages = DIV_ROUND_UP(size, PAGE_SIZE);
448ff13be83SJeffrey Hugo 		/*
449ff13be83SJeffrey Hugo 		 * calculate how much extra we are going to allocate, to remove
450ff13be83SJeffrey Hugo 		 * later
451ff13be83SJeffrey Hugo 		 */
452ff13be83SJeffrey Hugo 		buf_extra = (PAGE_SIZE - size % PAGE_SIZE) % PAGE_SIZE;
453ff13be83SJeffrey Hugo 		max_order = min(MAX_ORDER - 1, get_order(size));
454ff13be83SJeffrey Hugo 	} else {
455ff13be83SJeffrey Hugo 		/* allocate a single page for book keeping */
456ff13be83SJeffrey Hugo 		nr_pages = 1;
457ff13be83SJeffrey Hugo 		buf_extra = 0;
458ff13be83SJeffrey Hugo 		max_order = 0;
459ff13be83SJeffrey Hugo 	}
460ff13be83SJeffrey Hugo 
461ff13be83SJeffrey Hugo 	pages = kvmalloc_array(nr_pages, sizeof(*pages) + sizeof(*pages_order), GFP_KERNEL);
462ff13be83SJeffrey Hugo 	if (!pages) {
463ff13be83SJeffrey Hugo 		ret = -ENOMEM;
464ff13be83SJeffrey Hugo 		goto out;
465ff13be83SJeffrey Hugo 	}
466ff13be83SJeffrey Hugo 	pages_order = (void *)pages + sizeof(*pages) * nr_pages;
467ff13be83SJeffrey Hugo 
468ff13be83SJeffrey Hugo 	/*
469ff13be83SJeffrey Hugo 	 * Allocate requested memory using alloc_pages. It is possible to allocate
470ff13be83SJeffrey Hugo 	 * the requested memory in multiple chunks by calling alloc_pages
471ff13be83SJeffrey Hugo 	 * multiple times. Use SG table to handle multiple allocated pages.
472ff13be83SJeffrey Hugo 	 */
473ff13be83SJeffrey Hugo 	i = 0;
474ff13be83SJeffrey Hugo 	while (nr_pages > 0) {
475ff13be83SJeffrey Hugo 		order = min(get_order(nr_pages * PAGE_SIZE), max_order);
476ff13be83SJeffrey Hugo 		while (1) {
477ff13be83SJeffrey Hugo 			pages[i] = alloc_pages(GFP_KERNEL | GFP_HIGHUSER |
478ff13be83SJeffrey Hugo 					       __GFP_NOWARN | __GFP_ZERO |
479ff13be83SJeffrey Hugo 					       (order ? __GFP_NORETRY : __GFP_RETRY_MAYFAIL),
480ff13be83SJeffrey Hugo 					       order);
481ff13be83SJeffrey Hugo 			if (pages[i])
482ff13be83SJeffrey Hugo 				break;
483ff13be83SJeffrey Hugo 			if (!order--) {
484ff13be83SJeffrey Hugo 				ret = -ENOMEM;
485ff13be83SJeffrey Hugo 				goto free_partial_alloc;
486ff13be83SJeffrey Hugo 			}
487ff13be83SJeffrey Hugo 		}
488ff13be83SJeffrey Hugo 
489ff13be83SJeffrey Hugo 		max_order = order;
490ff13be83SJeffrey Hugo 		pages_order[i] = order;
491ff13be83SJeffrey Hugo 
492ff13be83SJeffrey Hugo 		nr_pages -= 1 << order;
493ff13be83SJeffrey Hugo 		if (nr_pages <= 0)
494ff13be83SJeffrey Hugo 			/* account for over allocation */
495ff13be83SJeffrey Hugo 			buf_extra += abs(nr_pages) * PAGE_SIZE;
496ff13be83SJeffrey Hugo 		i++;
497ff13be83SJeffrey Hugo 	}
498ff13be83SJeffrey Hugo 
499ff13be83SJeffrey Hugo 	sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
500ff13be83SJeffrey Hugo 	if (!sgt) {
501ff13be83SJeffrey Hugo 		ret = -ENOMEM;
502ff13be83SJeffrey Hugo 		goto free_partial_alloc;
503ff13be83SJeffrey Hugo 	}
504ff13be83SJeffrey Hugo 
505ff13be83SJeffrey Hugo 	if (sg_alloc_table(sgt, i, GFP_KERNEL)) {
506ff13be83SJeffrey Hugo 		ret = -ENOMEM;
507ff13be83SJeffrey Hugo 		goto free_sgt;
508ff13be83SJeffrey Hugo 	}
509ff13be83SJeffrey Hugo 
510ff13be83SJeffrey Hugo 	/* Populate the SG table with the allocated memory pages */
511ff13be83SJeffrey Hugo 	sg = sgt->sgl;
512ff13be83SJeffrey Hugo 	for (k = 0; k < i; k++, sg = sg_next(sg)) {
513ff13be83SJeffrey Hugo 		/* Last entry requires special handling */
514ff13be83SJeffrey Hugo 		if (k < i - 1) {
515ff13be83SJeffrey Hugo 			sg_set_page(sg, pages[k], PAGE_SIZE << pages_order[k], 0);
516ff13be83SJeffrey Hugo 		} else {
517ff13be83SJeffrey Hugo 			sg_set_page(sg, pages[k], (PAGE_SIZE << pages_order[k]) - buf_extra, 0);
518ff13be83SJeffrey Hugo 			sg_mark_end(sg);
519ff13be83SJeffrey Hugo 		}
520ff13be83SJeffrey Hugo 	}
521ff13be83SJeffrey Hugo 
522ff13be83SJeffrey Hugo 	kvfree(pages);
523ff13be83SJeffrey Hugo 	*sgt_out = sgt;
524ff13be83SJeffrey Hugo 	return ret;
525ff13be83SJeffrey Hugo 
526ff13be83SJeffrey Hugo free_sgt:
527ff13be83SJeffrey Hugo 	kfree(sgt);
528ff13be83SJeffrey Hugo free_partial_alloc:
529ff13be83SJeffrey Hugo 	for (j = 0; j < i; j++)
530ff13be83SJeffrey Hugo 		__free_pages(pages[j], pages_order[j]);
531ff13be83SJeffrey Hugo 	kvfree(pages);
532ff13be83SJeffrey Hugo out:
533ff13be83SJeffrey Hugo 	*sgt_out = NULL;
534ff13be83SJeffrey Hugo 	return ret;
535ff13be83SJeffrey Hugo }
536ff13be83SJeffrey Hugo 
invalid_sem(struct qaic_sem * sem)537ff13be83SJeffrey Hugo static bool invalid_sem(struct qaic_sem *sem)
538ff13be83SJeffrey Hugo {
539ff13be83SJeffrey Hugo 	if (sem->val & ~SEM_VAL_MASK || sem->index & ~SEM_INDEX_MASK ||
540ff13be83SJeffrey Hugo 	    !(sem->presync == 0 || sem->presync == 1) || sem->pad ||
541ff13be83SJeffrey Hugo 	    sem->flags & ~(QAIC_SEM_INSYNCFENCE | QAIC_SEM_OUTSYNCFENCE) ||
542ff13be83SJeffrey Hugo 	    sem->cmd > QAIC_SEM_WAIT_GT_0)
543ff13be83SJeffrey Hugo 		return true;
544ff13be83SJeffrey Hugo 	return false;
545ff13be83SJeffrey Hugo }
546ff13be83SJeffrey Hugo 
qaic_validate_req(struct qaic_device * qdev,struct qaic_attach_slice_entry * slice_ent,u32 count,u64 total_size)547ff13be83SJeffrey Hugo static int qaic_validate_req(struct qaic_device *qdev, struct qaic_attach_slice_entry *slice_ent,
548ff13be83SJeffrey Hugo 			     u32 count, u64 total_size)
549ff13be83SJeffrey Hugo {
550ff13be83SJeffrey Hugo 	int i;
551ff13be83SJeffrey Hugo 
552ff13be83SJeffrey Hugo 	for (i = 0; i < count; i++) {
553ff13be83SJeffrey Hugo 		if (!(slice_ent[i].db_len == 32 || slice_ent[i].db_len == 16 ||
554ff13be83SJeffrey Hugo 		      slice_ent[i].db_len == 8 || slice_ent[i].db_len == 0) ||
555ff13be83SJeffrey Hugo 		      invalid_sem(&slice_ent[i].sem0) || invalid_sem(&slice_ent[i].sem1) ||
556ff13be83SJeffrey Hugo 		      invalid_sem(&slice_ent[i].sem2) || invalid_sem(&slice_ent[i].sem3))
557ff13be83SJeffrey Hugo 			return -EINVAL;
558ff13be83SJeffrey Hugo 
559ff13be83SJeffrey Hugo 		if (slice_ent[i].offset + slice_ent[i].size > total_size)
560ff13be83SJeffrey Hugo 			return -EINVAL;
561ff13be83SJeffrey Hugo 	}
562ff13be83SJeffrey Hugo 
563ff13be83SJeffrey Hugo 	return 0;
564ff13be83SJeffrey Hugo }
565ff13be83SJeffrey Hugo 
qaic_free_sgt(struct sg_table * sgt)566ff13be83SJeffrey Hugo static void qaic_free_sgt(struct sg_table *sgt)
567ff13be83SJeffrey Hugo {
568ff13be83SJeffrey Hugo 	struct scatterlist *sg;
569ff13be83SJeffrey Hugo 
570ff13be83SJeffrey Hugo 	for (sg = sgt->sgl; sg; sg = sg_next(sg))
571ff13be83SJeffrey Hugo 		if (sg_page(sg))
572ff13be83SJeffrey Hugo 			__free_pages(sg_page(sg), get_order(sg->length));
573ff13be83SJeffrey Hugo 	sg_free_table(sgt);
574ff13be83SJeffrey Hugo 	kfree(sgt);
575ff13be83SJeffrey Hugo }
576ff13be83SJeffrey Hugo 
qaic_gem_print_info(struct drm_printer * p,unsigned int indent,const struct drm_gem_object * obj)577ff13be83SJeffrey Hugo static void qaic_gem_print_info(struct drm_printer *p, unsigned int indent,
578ff13be83SJeffrey Hugo 				const struct drm_gem_object *obj)
579ff13be83SJeffrey Hugo {
580ff13be83SJeffrey Hugo 	struct qaic_bo *bo = to_qaic_bo(obj);
581ff13be83SJeffrey Hugo 
582ff13be83SJeffrey Hugo 	drm_printf_indent(p, indent, "user requested size=%llu\n", bo->size);
583ff13be83SJeffrey Hugo }
584ff13be83SJeffrey Hugo 
585ff13be83SJeffrey Hugo static const struct vm_operations_struct drm_vm_ops = {
586ff13be83SJeffrey Hugo 	.open = drm_gem_vm_open,
587ff13be83SJeffrey Hugo 	.close = drm_gem_vm_close,
588ff13be83SJeffrey Hugo };
589ff13be83SJeffrey Hugo 
qaic_gem_object_mmap(struct drm_gem_object * obj,struct vm_area_struct * vma)590ff13be83SJeffrey Hugo static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
591ff13be83SJeffrey Hugo {
592ff13be83SJeffrey Hugo 	struct qaic_bo *bo = to_qaic_bo(obj);
593ff13be83SJeffrey Hugo 	unsigned long offset = 0;
594ff13be83SJeffrey Hugo 	struct scatterlist *sg;
5950e163e54STom Rix 	int ret = 0;
596ff13be83SJeffrey Hugo 
597ff13be83SJeffrey Hugo 	if (obj->import_attach)
598ff13be83SJeffrey Hugo 		return -EINVAL;
599ff13be83SJeffrey Hugo 
600ff13be83SJeffrey Hugo 	for (sg = bo->sgt->sgl; sg; sg = sg_next(sg)) {
601ff13be83SJeffrey Hugo 		if (sg_page(sg)) {
602ff13be83SJeffrey Hugo 			ret = remap_pfn_range(vma, vma->vm_start + offset, page_to_pfn(sg_page(sg)),
603ff13be83SJeffrey Hugo 					      sg->length, vma->vm_page_prot);
604ff13be83SJeffrey Hugo 			if (ret)
605ff13be83SJeffrey Hugo 				goto out;
606ff13be83SJeffrey Hugo 			offset += sg->length;
607ff13be83SJeffrey Hugo 		}
608ff13be83SJeffrey Hugo 	}
609ff13be83SJeffrey Hugo 
610ff13be83SJeffrey Hugo out:
611ff13be83SJeffrey Hugo 	return ret;
612ff13be83SJeffrey Hugo }
613ff13be83SJeffrey Hugo 
qaic_free_object(struct drm_gem_object * obj)614ff13be83SJeffrey Hugo static void qaic_free_object(struct drm_gem_object *obj)
615ff13be83SJeffrey Hugo {
616ff13be83SJeffrey Hugo 	struct qaic_bo *bo = to_qaic_bo(obj);
617ff13be83SJeffrey Hugo 
618ff13be83SJeffrey Hugo 	if (obj->import_attach) {
619ff13be83SJeffrey Hugo 		/* DMABUF/PRIME Path */
6208d0d16a3SPranjal Ramajor Asha Kanojiya 		drm_prime_gem_destroy(obj, NULL);
621ff13be83SJeffrey Hugo 	} else {
622ff13be83SJeffrey Hugo 		/* Private buffer allocation path */
623ff13be83SJeffrey Hugo 		qaic_free_sgt(bo->sgt);
624ff13be83SJeffrey Hugo 	}
625ff13be83SJeffrey Hugo 
626ff13be83SJeffrey Hugo 	drm_gem_object_release(obj);
627ff13be83SJeffrey Hugo 	kfree(bo);
628ff13be83SJeffrey Hugo }
629ff13be83SJeffrey Hugo 
630ff13be83SJeffrey Hugo static const struct drm_gem_object_funcs qaic_gem_funcs = {
631ff13be83SJeffrey Hugo 	.free = qaic_free_object,
632ff13be83SJeffrey Hugo 	.print_info = qaic_gem_print_info,
633ff13be83SJeffrey Hugo 	.mmap = qaic_gem_object_mmap,
634ff13be83SJeffrey Hugo 	.vm_ops = &drm_vm_ops,
635ff13be83SJeffrey Hugo };
636ff13be83SJeffrey Hugo 
qaic_alloc_init_bo(void)637ff13be83SJeffrey Hugo static struct qaic_bo *qaic_alloc_init_bo(void)
638ff13be83SJeffrey Hugo {
639ff13be83SJeffrey Hugo 	struct qaic_bo *bo;
640ff13be83SJeffrey Hugo 
641ff13be83SJeffrey Hugo 	bo = kzalloc(sizeof(*bo), GFP_KERNEL);
642ff13be83SJeffrey Hugo 	if (!bo)
643ff13be83SJeffrey Hugo 		return ERR_PTR(-ENOMEM);
644ff13be83SJeffrey Hugo 
645ff13be83SJeffrey Hugo 	INIT_LIST_HEAD(&bo->slices);
646ff13be83SJeffrey Hugo 	init_completion(&bo->xfer_done);
647ff13be83SJeffrey Hugo 	complete_all(&bo->xfer_done);
648ff13be83SJeffrey Hugo 
649ff13be83SJeffrey Hugo 	return bo;
650ff13be83SJeffrey Hugo }
651ff13be83SJeffrey Hugo 
qaic_create_bo_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)652ff13be83SJeffrey Hugo int qaic_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
653ff13be83SJeffrey Hugo {
654ff13be83SJeffrey Hugo 	struct qaic_create_bo *args = data;
655ff13be83SJeffrey Hugo 	int usr_rcu_id, qdev_rcu_id;
656ff13be83SJeffrey Hugo 	struct drm_gem_object *obj;
657ff13be83SJeffrey Hugo 	struct qaic_device *qdev;
658ff13be83SJeffrey Hugo 	struct qaic_user *usr;
659ff13be83SJeffrey Hugo 	struct qaic_bo *bo;
660ff13be83SJeffrey Hugo 	size_t size;
661ff13be83SJeffrey Hugo 	int ret;
662ff13be83SJeffrey Hugo 
663ff13be83SJeffrey Hugo 	if (args->pad)
664ff13be83SJeffrey Hugo 		return -EINVAL;
665ff13be83SJeffrey Hugo 
666d3b277b7SPranjal Ramajor Asha Kanojiya 	size = PAGE_ALIGN(args->size);
667d3b277b7SPranjal Ramajor Asha Kanojiya 	if (size == 0)
668d3b277b7SPranjal Ramajor Asha Kanojiya 		return -EINVAL;
669d3b277b7SPranjal Ramajor Asha Kanojiya 
670ff13be83SJeffrey Hugo 	usr = file_priv->driver_priv;
671ff13be83SJeffrey Hugo 	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
672ff13be83SJeffrey Hugo 	if (!usr->qddev) {
673ff13be83SJeffrey Hugo 		ret = -ENODEV;
674ff13be83SJeffrey Hugo 		goto unlock_usr_srcu;
675ff13be83SJeffrey Hugo 	}
676ff13be83SJeffrey Hugo 
677ff13be83SJeffrey Hugo 	qdev = usr->qddev->qdev;
678ff13be83SJeffrey Hugo 	qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
679ff13be83SJeffrey Hugo 	if (qdev->in_reset) {
680ff13be83SJeffrey Hugo 		ret = -ENODEV;
681ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
682ff13be83SJeffrey Hugo 	}
683ff13be83SJeffrey Hugo 
684ff13be83SJeffrey Hugo 	bo = qaic_alloc_init_bo();
685ff13be83SJeffrey Hugo 	if (IS_ERR(bo)) {
686ff13be83SJeffrey Hugo 		ret = PTR_ERR(bo);
687ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
688ff13be83SJeffrey Hugo 	}
689ff13be83SJeffrey Hugo 	obj = &bo->base;
690ff13be83SJeffrey Hugo 
691ff13be83SJeffrey Hugo 	drm_gem_private_object_init(dev, obj, size);
692ff13be83SJeffrey Hugo 
693ff13be83SJeffrey Hugo 	obj->funcs = &qaic_gem_funcs;
694ff13be83SJeffrey Hugo 	ret = create_sgt(qdev, &bo->sgt, size);
695ff13be83SJeffrey Hugo 	if (ret)
696ff13be83SJeffrey Hugo 		goto free_bo;
697ff13be83SJeffrey Hugo 
698ff13be83SJeffrey Hugo 	bo->size = args->size;
699ff13be83SJeffrey Hugo 
700ff13be83SJeffrey Hugo 	ret = drm_gem_handle_create(file_priv, obj, &args->handle);
701ff13be83SJeffrey Hugo 	if (ret)
702ff13be83SJeffrey Hugo 		goto free_sgt;
703ff13be83SJeffrey Hugo 
704ff13be83SJeffrey Hugo 	bo->handle = args->handle;
705ff13be83SJeffrey Hugo 	drm_gem_object_put(obj);
706ff13be83SJeffrey Hugo 	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
707ff13be83SJeffrey Hugo 	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
708ff13be83SJeffrey Hugo 
709ff13be83SJeffrey Hugo 	return 0;
710ff13be83SJeffrey Hugo 
711ff13be83SJeffrey Hugo free_sgt:
712ff13be83SJeffrey Hugo 	qaic_free_sgt(bo->sgt);
713ff13be83SJeffrey Hugo free_bo:
714ff13be83SJeffrey Hugo 	kfree(bo);
715ff13be83SJeffrey Hugo unlock_dev_srcu:
716ff13be83SJeffrey Hugo 	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
717ff13be83SJeffrey Hugo unlock_usr_srcu:
718ff13be83SJeffrey Hugo 	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
719ff13be83SJeffrey Hugo 	return ret;
720ff13be83SJeffrey Hugo }
721ff13be83SJeffrey Hugo 
qaic_mmap_bo_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)722ff13be83SJeffrey Hugo int qaic_mmap_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
723ff13be83SJeffrey Hugo {
724ff13be83SJeffrey Hugo 	struct qaic_mmap_bo *args = data;
725ff13be83SJeffrey Hugo 	int usr_rcu_id, qdev_rcu_id;
726ff13be83SJeffrey Hugo 	struct drm_gem_object *obj;
727ff13be83SJeffrey Hugo 	struct qaic_device *qdev;
728ff13be83SJeffrey Hugo 	struct qaic_user *usr;
729ff13be83SJeffrey Hugo 	int ret;
730ff13be83SJeffrey Hugo 
731ff13be83SJeffrey Hugo 	usr = file_priv->driver_priv;
732ff13be83SJeffrey Hugo 	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
733ff13be83SJeffrey Hugo 	if (!usr->qddev) {
734ff13be83SJeffrey Hugo 		ret = -ENODEV;
735ff13be83SJeffrey Hugo 		goto unlock_usr_srcu;
736ff13be83SJeffrey Hugo 	}
737ff13be83SJeffrey Hugo 
738ff13be83SJeffrey Hugo 	qdev = usr->qddev->qdev;
739ff13be83SJeffrey Hugo 	qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
740ff13be83SJeffrey Hugo 	if (qdev->in_reset) {
741ff13be83SJeffrey Hugo 		ret = -ENODEV;
742ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
743ff13be83SJeffrey Hugo 	}
744ff13be83SJeffrey Hugo 
745ff13be83SJeffrey Hugo 	obj = drm_gem_object_lookup(file_priv, args->handle);
746ff13be83SJeffrey Hugo 	if (!obj) {
747ff13be83SJeffrey Hugo 		ret = -ENOENT;
748ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
749ff13be83SJeffrey Hugo 	}
750ff13be83SJeffrey Hugo 
751ff13be83SJeffrey Hugo 	ret = drm_gem_create_mmap_offset(obj);
752ff13be83SJeffrey Hugo 	if (ret == 0)
753ff13be83SJeffrey Hugo 		args->offset = drm_vma_node_offset_addr(&obj->vma_node);
754ff13be83SJeffrey Hugo 
755ff13be83SJeffrey Hugo 	drm_gem_object_put(obj);
756ff13be83SJeffrey Hugo 
757ff13be83SJeffrey Hugo unlock_dev_srcu:
758ff13be83SJeffrey Hugo 	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
759ff13be83SJeffrey Hugo unlock_usr_srcu:
760ff13be83SJeffrey Hugo 	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
761ff13be83SJeffrey Hugo 	return ret;
762ff13be83SJeffrey Hugo }
763ff13be83SJeffrey Hugo 
qaic_gem_prime_import(struct drm_device * dev,struct dma_buf * dma_buf)764ff13be83SJeffrey Hugo struct drm_gem_object *qaic_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf)
765ff13be83SJeffrey Hugo {
766ff13be83SJeffrey Hugo 	struct dma_buf_attachment *attach;
767ff13be83SJeffrey Hugo 	struct drm_gem_object *obj;
768ff13be83SJeffrey Hugo 	struct qaic_bo *bo;
769ff13be83SJeffrey Hugo 	int ret;
770ff13be83SJeffrey Hugo 
771ff13be83SJeffrey Hugo 	bo = qaic_alloc_init_bo();
772ff13be83SJeffrey Hugo 	if (IS_ERR(bo)) {
773ff13be83SJeffrey Hugo 		ret = PTR_ERR(bo);
774ff13be83SJeffrey Hugo 		goto out;
775ff13be83SJeffrey Hugo 	}
776ff13be83SJeffrey Hugo 
777ff13be83SJeffrey Hugo 	obj = &bo->base;
778ff13be83SJeffrey Hugo 	get_dma_buf(dma_buf);
779ff13be83SJeffrey Hugo 
780ff13be83SJeffrey Hugo 	attach = dma_buf_attach(dma_buf, dev->dev);
781ff13be83SJeffrey Hugo 	if (IS_ERR(attach)) {
782ff13be83SJeffrey Hugo 		ret = PTR_ERR(attach);
783ff13be83SJeffrey Hugo 		goto attach_fail;
784ff13be83SJeffrey Hugo 	}
785ff13be83SJeffrey Hugo 
786*efcedd56SPranjal Ramajor Asha Kanojiya 	if (!attach->dmabuf->size) {
787ff13be83SJeffrey Hugo 		ret = -EINVAL;
788ff13be83SJeffrey Hugo 		goto size_align_fail;
789ff13be83SJeffrey Hugo 	}
790ff13be83SJeffrey Hugo 
791*efcedd56SPranjal Ramajor Asha Kanojiya 	drm_gem_private_object_init(dev, obj, attach->dmabuf->size);
792ff13be83SJeffrey Hugo 	/*
793ff13be83SJeffrey Hugo 	 * skipping dma_buf_map_attachment() as we do not know the direction
794ff13be83SJeffrey Hugo 	 * just yet. Once the direction is known in the subsequent IOCTL to
795ff13be83SJeffrey Hugo 	 * attach slicing, we can do it then.
796ff13be83SJeffrey Hugo 	 */
797ff13be83SJeffrey Hugo 
798ff13be83SJeffrey Hugo 	obj->funcs = &qaic_gem_funcs;
799ff13be83SJeffrey Hugo 	obj->import_attach = attach;
800ff13be83SJeffrey Hugo 	obj->resv = dma_buf->resv;
801ff13be83SJeffrey Hugo 
802ff13be83SJeffrey Hugo 	return obj;
803ff13be83SJeffrey Hugo 
804ff13be83SJeffrey Hugo size_align_fail:
805ff13be83SJeffrey Hugo 	dma_buf_detach(dma_buf, attach);
806ff13be83SJeffrey Hugo attach_fail:
807ff13be83SJeffrey Hugo 	dma_buf_put(dma_buf);
808ff13be83SJeffrey Hugo 	kfree(bo);
809ff13be83SJeffrey Hugo out:
810ff13be83SJeffrey Hugo 	return ERR_PTR(ret);
811ff13be83SJeffrey Hugo }
812ff13be83SJeffrey Hugo 
qaic_prepare_import_bo(struct qaic_bo * bo,struct qaic_attach_slice_hdr * hdr)813ff13be83SJeffrey Hugo static int qaic_prepare_import_bo(struct qaic_bo *bo, struct qaic_attach_slice_hdr *hdr)
814ff13be83SJeffrey Hugo {
815ff13be83SJeffrey Hugo 	struct drm_gem_object *obj = &bo->base;
816ff13be83SJeffrey Hugo 	struct sg_table *sgt;
817ff13be83SJeffrey Hugo 	int ret;
818ff13be83SJeffrey Hugo 
819ff13be83SJeffrey Hugo 	if (obj->import_attach->dmabuf->size < hdr->size)
820ff13be83SJeffrey Hugo 		return -EINVAL;
821ff13be83SJeffrey Hugo 
822ff13be83SJeffrey Hugo 	sgt = dma_buf_map_attachment(obj->import_attach, hdr->dir);
823ff13be83SJeffrey Hugo 	if (IS_ERR(sgt)) {
824ff13be83SJeffrey Hugo 		ret = PTR_ERR(sgt);
825ff13be83SJeffrey Hugo 		return ret;
826ff13be83SJeffrey Hugo 	}
827ff13be83SJeffrey Hugo 
828ff13be83SJeffrey Hugo 	bo->sgt = sgt;
829ff13be83SJeffrey Hugo 	bo->size = hdr->size;
830ff13be83SJeffrey Hugo 
831ff13be83SJeffrey Hugo 	return 0;
832ff13be83SJeffrey Hugo }
833ff13be83SJeffrey Hugo 
qaic_prepare_export_bo(struct qaic_device * qdev,struct qaic_bo * bo,struct qaic_attach_slice_hdr * hdr)834ff13be83SJeffrey Hugo static int qaic_prepare_export_bo(struct qaic_device *qdev, struct qaic_bo *bo,
835ff13be83SJeffrey Hugo 				  struct qaic_attach_slice_hdr *hdr)
836ff13be83SJeffrey Hugo {
837ff13be83SJeffrey Hugo 	int ret;
838ff13be83SJeffrey Hugo 
839ff13be83SJeffrey Hugo 	if (bo->size != hdr->size)
840ff13be83SJeffrey Hugo 		return -EINVAL;
841ff13be83SJeffrey Hugo 
842ff13be83SJeffrey Hugo 	ret = dma_map_sgtable(&qdev->pdev->dev, bo->sgt, hdr->dir, 0);
843ff13be83SJeffrey Hugo 	if (ret)
844ff13be83SJeffrey Hugo 		return -EFAULT;
845ff13be83SJeffrey Hugo 
846ff13be83SJeffrey Hugo 	return 0;
847ff13be83SJeffrey Hugo }
848ff13be83SJeffrey Hugo 
qaic_prepare_bo(struct qaic_device * qdev,struct qaic_bo * bo,struct qaic_attach_slice_hdr * hdr)849ff13be83SJeffrey Hugo static int qaic_prepare_bo(struct qaic_device *qdev, struct qaic_bo *bo,
850ff13be83SJeffrey Hugo 			   struct qaic_attach_slice_hdr *hdr)
851ff13be83SJeffrey Hugo {
852ff13be83SJeffrey Hugo 	int ret;
853ff13be83SJeffrey Hugo 
854ff13be83SJeffrey Hugo 	if (bo->base.import_attach)
855ff13be83SJeffrey Hugo 		ret = qaic_prepare_import_bo(bo, hdr);
856ff13be83SJeffrey Hugo 	else
857ff13be83SJeffrey Hugo 		ret = qaic_prepare_export_bo(qdev, bo, hdr);
858ff13be83SJeffrey Hugo 
859ff13be83SJeffrey Hugo 	if (ret == 0)
860ff13be83SJeffrey Hugo 		bo->dir = hdr->dir;
861ff13be83SJeffrey Hugo 
862ff13be83SJeffrey Hugo 	return ret;
863ff13be83SJeffrey Hugo }
864ff13be83SJeffrey Hugo 
qaic_unprepare_import_bo(struct qaic_bo * bo)865ff13be83SJeffrey Hugo static void qaic_unprepare_import_bo(struct qaic_bo *bo)
866ff13be83SJeffrey Hugo {
867ff13be83SJeffrey Hugo 	dma_buf_unmap_attachment(bo->base.import_attach, bo->sgt, bo->dir);
868ff13be83SJeffrey Hugo 	bo->sgt = NULL;
869ff13be83SJeffrey Hugo 	bo->size = 0;
870ff13be83SJeffrey Hugo }
871ff13be83SJeffrey Hugo 
qaic_unprepare_export_bo(struct qaic_device * qdev,struct qaic_bo * bo)872ff13be83SJeffrey Hugo static void qaic_unprepare_export_bo(struct qaic_device *qdev, struct qaic_bo *bo)
873ff13be83SJeffrey Hugo {
874ff13be83SJeffrey Hugo 	dma_unmap_sgtable(&qdev->pdev->dev, bo->sgt, bo->dir, 0);
875ff13be83SJeffrey Hugo }
876ff13be83SJeffrey Hugo 
qaic_unprepare_bo(struct qaic_device * qdev,struct qaic_bo * bo)877ff13be83SJeffrey Hugo static void qaic_unprepare_bo(struct qaic_device *qdev, struct qaic_bo *bo)
878ff13be83SJeffrey Hugo {
879ff13be83SJeffrey Hugo 	if (bo->base.import_attach)
880ff13be83SJeffrey Hugo 		qaic_unprepare_import_bo(bo);
881ff13be83SJeffrey Hugo 	else
882ff13be83SJeffrey Hugo 		qaic_unprepare_export_bo(qdev, bo);
883ff13be83SJeffrey Hugo 
884ff13be83SJeffrey Hugo 	bo->dir = 0;
885ff13be83SJeffrey Hugo }
886ff13be83SJeffrey Hugo 
qaic_free_slices_bo(struct qaic_bo * bo)887ff13be83SJeffrey Hugo static void qaic_free_slices_bo(struct qaic_bo *bo)
888ff13be83SJeffrey Hugo {
889ff13be83SJeffrey Hugo 	struct bo_slice *slice, *temp;
890ff13be83SJeffrey Hugo 
891ff13be83SJeffrey Hugo 	list_for_each_entry_safe(slice, temp, &bo->slices, slice)
892ff13be83SJeffrey Hugo 		kref_put(&slice->ref_count, free_slice);
893ff13be83SJeffrey Hugo }
894ff13be83SJeffrey Hugo 
qaic_attach_slicing_bo(struct qaic_device * qdev,struct qaic_bo * bo,struct qaic_attach_slice_hdr * hdr,struct qaic_attach_slice_entry * slice_ent)895ff13be83SJeffrey Hugo static int qaic_attach_slicing_bo(struct qaic_device *qdev, struct qaic_bo *bo,
896ff13be83SJeffrey Hugo 				  struct qaic_attach_slice_hdr *hdr,
897ff13be83SJeffrey Hugo 				  struct qaic_attach_slice_entry *slice_ent)
898ff13be83SJeffrey Hugo {
899ff13be83SJeffrey Hugo 	int ret, i;
900ff13be83SJeffrey Hugo 
901ff13be83SJeffrey Hugo 	for (i = 0; i < hdr->count; i++) {
902ff13be83SJeffrey Hugo 		ret = qaic_map_one_slice(qdev, bo, &slice_ent[i]);
903ff13be83SJeffrey Hugo 		if (ret) {
904ff13be83SJeffrey Hugo 			qaic_free_slices_bo(bo);
905ff13be83SJeffrey Hugo 			return ret;
906ff13be83SJeffrey Hugo 		}
907ff13be83SJeffrey Hugo 	}
908ff13be83SJeffrey Hugo 
909ff13be83SJeffrey Hugo 	if (bo->total_slice_nents > qdev->dbc[hdr->dbc_id].nelem) {
910ff13be83SJeffrey Hugo 		qaic_free_slices_bo(bo);
911ff13be83SJeffrey Hugo 		return -ENOSPC;
912ff13be83SJeffrey Hugo 	}
913ff13be83SJeffrey Hugo 
914ff13be83SJeffrey Hugo 	bo->sliced = true;
915ff13be83SJeffrey Hugo 	bo->nr_slice = hdr->count;
916ff13be83SJeffrey Hugo 	list_add_tail(&bo->bo_list, &qdev->dbc[hdr->dbc_id].bo_lists);
917ff13be83SJeffrey Hugo 
918ff13be83SJeffrey Hugo 	return 0;
919ff13be83SJeffrey Hugo }
920ff13be83SJeffrey Hugo 
qaic_attach_slice_bo_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)921ff13be83SJeffrey Hugo int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
922ff13be83SJeffrey Hugo {
923ff13be83SJeffrey Hugo 	struct qaic_attach_slice_entry *slice_ent;
924ff13be83SJeffrey Hugo 	struct qaic_attach_slice *args = data;
92575af0a58SPranjal Ramajor Asha Kanojiya 	int rcu_id, usr_rcu_id, qdev_rcu_id;
926ff13be83SJeffrey Hugo 	struct dma_bridge_chan	*dbc;
927ff13be83SJeffrey Hugo 	struct drm_gem_object *obj;
928ff13be83SJeffrey Hugo 	struct qaic_device *qdev;
929ff13be83SJeffrey Hugo 	unsigned long arg_size;
930ff13be83SJeffrey Hugo 	struct qaic_user *usr;
931ff13be83SJeffrey Hugo 	u8 __user *user_data;
932ff13be83SJeffrey Hugo 	struct qaic_bo *bo;
933ff13be83SJeffrey Hugo 	int ret;
934ff13be83SJeffrey Hugo 
935d3b277b7SPranjal Ramajor Asha Kanojiya 	if (args->hdr.count == 0)
936d3b277b7SPranjal Ramajor Asha Kanojiya 		return -EINVAL;
937d3b277b7SPranjal Ramajor Asha Kanojiya 
938d3b277b7SPranjal Ramajor Asha Kanojiya 	arg_size = args->hdr.count * sizeof(*slice_ent);
939d3b277b7SPranjal Ramajor Asha Kanojiya 	if (arg_size / args->hdr.count != sizeof(*slice_ent))
940d3b277b7SPranjal Ramajor Asha Kanojiya 		return -EINVAL;
941d3b277b7SPranjal Ramajor Asha Kanojiya 
942d3b277b7SPranjal Ramajor Asha Kanojiya 	if (args->hdr.size == 0)
943d3b277b7SPranjal Ramajor Asha Kanojiya 		return -EINVAL;
944d3b277b7SPranjal Ramajor Asha Kanojiya 
945d3b277b7SPranjal Ramajor Asha Kanojiya 	if (!(args->hdr.dir == DMA_TO_DEVICE || args->hdr.dir == DMA_FROM_DEVICE))
946d3b277b7SPranjal Ramajor Asha Kanojiya 		return -EINVAL;
947d3b277b7SPranjal Ramajor Asha Kanojiya 
948d3b277b7SPranjal Ramajor Asha Kanojiya 	if (args->data == 0)
949d3b277b7SPranjal Ramajor Asha Kanojiya 		return -EINVAL;
950d3b277b7SPranjal Ramajor Asha Kanojiya 
951ff13be83SJeffrey Hugo 	usr = file_priv->driver_priv;
952ff13be83SJeffrey Hugo 	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
953ff13be83SJeffrey Hugo 	if (!usr->qddev) {
954ff13be83SJeffrey Hugo 		ret = -ENODEV;
955ff13be83SJeffrey Hugo 		goto unlock_usr_srcu;
956ff13be83SJeffrey Hugo 	}
957ff13be83SJeffrey Hugo 
958ff13be83SJeffrey Hugo 	qdev = usr->qddev->qdev;
959ff13be83SJeffrey Hugo 	qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
960ff13be83SJeffrey Hugo 	if (qdev->in_reset) {
961ff13be83SJeffrey Hugo 		ret = -ENODEV;
962ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
963ff13be83SJeffrey Hugo 	}
964ff13be83SJeffrey Hugo 
965ff13be83SJeffrey Hugo 	if (args->hdr.dbc_id >= qdev->num_dbc) {
966ff13be83SJeffrey Hugo 		ret = -EINVAL;
967ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
968ff13be83SJeffrey Hugo 	}
969ff13be83SJeffrey Hugo 
970ff13be83SJeffrey Hugo 	user_data = u64_to_user_ptr(args->data);
971ff13be83SJeffrey Hugo 
972ff13be83SJeffrey Hugo 	slice_ent = kzalloc(arg_size, GFP_KERNEL);
973ff13be83SJeffrey Hugo 	if (!slice_ent) {
974ff13be83SJeffrey Hugo 		ret = -EINVAL;
975ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
976ff13be83SJeffrey Hugo 	}
977ff13be83SJeffrey Hugo 
978ff13be83SJeffrey Hugo 	ret = copy_from_user(slice_ent, user_data, arg_size);
979ff13be83SJeffrey Hugo 	if (ret) {
980ff13be83SJeffrey Hugo 		ret = -EFAULT;
981ff13be83SJeffrey Hugo 		goto free_slice_ent;
982ff13be83SJeffrey Hugo 	}
983ff13be83SJeffrey Hugo 
984ff13be83SJeffrey Hugo 	ret = qaic_validate_req(qdev, slice_ent, args->hdr.count, args->hdr.size);
985ff13be83SJeffrey Hugo 	if (ret)
986ff13be83SJeffrey Hugo 		goto free_slice_ent;
987ff13be83SJeffrey Hugo 
988ff13be83SJeffrey Hugo 	obj = drm_gem_object_lookup(file_priv, args->hdr.handle);
989ff13be83SJeffrey Hugo 	if (!obj) {
990ff13be83SJeffrey Hugo 		ret = -ENOENT;
991ff13be83SJeffrey Hugo 		goto free_slice_ent;
992ff13be83SJeffrey Hugo 	}
993ff13be83SJeffrey Hugo 
994ff13be83SJeffrey Hugo 	bo = to_qaic_bo(obj);
995ff13be83SJeffrey Hugo 
9962e0904efSPranjal Ramajor Asha Kanojiya 	if (bo->sliced) {
9972e0904efSPranjal Ramajor Asha Kanojiya 		ret = -EINVAL;
9982e0904efSPranjal Ramajor Asha Kanojiya 		goto put_bo;
9992e0904efSPranjal Ramajor Asha Kanojiya 	}
10002e0904efSPranjal Ramajor Asha Kanojiya 
100175af0a58SPranjal Ramajor Asha Kanojiya 	dbc = &qdev->dbc[args->hdr.dbc_id];
100275af0a58SPranjal Ramajor Asha Kanojiya 	rcu_id = srcu_read_lock(&dbc->ch_lock);
100375af0a58SPranjal Ramajor Asha Kanojiya 	if (dbc->usr != usr) {
100475af0a58SPranjal Ramajor Asha Kanojiya 		ret = -EINVAL;
100575af0a58SPranjal Ramajor Asha Kanojiya 		goto unlock_ch_srcu;
100675af0a58SPranjal Ramajor Asha Kanojiya 	}
100775af0a58SPranjal Ramajor Asha Kanojiya 
1008ff13be83SJeffrey Hugo 	ret = qaic_prepare_bo(qdev, bo, &args->hdr);
1009ff13be83SJeffrey Hugo 	if (ret)
101075af0a58SPranjal Ramajor Asha Kanojiya 		goto unlock_ch_srcu;
1011ff13be83SJeffrey Hugo 
1012ff13be83SJeffrey Hugo 	ret = qaic_attach_slicing_bo(qdev, bo, &args->hdr, slice_ent);
1013ff13be83SJeffrey Hugo 	if (ret)
1014ff13be83SJeffrey Hugo 		goto unprepare_bo;
1015ff13be83SJeffrey Hugo 
1016ff13be83SJeffrey Hugo 	if (args->hdr.dir == DMA_TO_DEVICE)
1017ff13be83SJeffrey Hugo 		dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, args->hdr.dir);
1018ff13be83SJeffrey Hugo 
1019ff13be83SJeffrey Hugo 	bo->dbc = dbc;
102075af0a58SPranjal Ramajor Asha Kanojiya 	srcu_read_unlock(&dbc->ch_lock, rcu_id);
1021ff13be83SJeffrey Hugo 	drm_gem_object_put(obj);
10222d956177SPranjal Ramajor Asha Kanojiya 	kfree(slice_ent);
1023ff13be83SJeffrey Hugo 	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
1024ff13be83SJeffrey Hugo 	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
1025ff13be83SJeffrey Hugo 
1026ff13be83SJeffrey Hugo 	return 0;
1027ff13be83SJeffrey Hugo 
1028ff13be83SJeffrey Hugo unprepare_bo:
1029ff13be83SJeffrey Hugo 	qaic_unprepare_bo(qdev, bo);
103075af0a58SPranjal Ramajor Asha Kanojiya unlock_ch_srcu:
103175af0a58SPranjal Ramajor Asha Kanojiya 	srcu_read_unlock(&dbc->ch_lock, rcu_id);
1032ff13be83SJeffrey Hugo put_bo:
1033ff13be83SJeffrey Hugo 	drm_gem_object_put(obj);
1034ff13be83SJeffrey Hugo free_slice_ent:
1035ff13be83SJeffrey Hugo 	kfree(slice_ent);
1036ff13be83SJeffrey Hugo unlock_dev_srcu:
1037ff13be83SJeffrey Hugo 	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
1038ff13be83SJeffrey Hugo unlock_usr_srcu:
1039ff13be83SJeffrey Hugo 	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
1040ff13be83SJeffrey Hugo 	return ret;
1041ff13be83SJeffrey Hugo }
1042ff13be83SJeffrey Hugo 
copy_exec_reqs(struct qaic_device * qdev,struct bo_slice * slice,u32 dbc_id,u32 head,u32 * ptail)1043ff13be83SJeffrey Hugo static inline int copy_exec_reqs(struct qaic_device *qdev, struct bo_slice *slice, u32 dbc_id,
1044ff13be83SJeffrey Hugo 				 u32 head, u32 *ptail)
1045ff13be83SJeffrey Hugo {
1046ff13be83SJeffrey Hugo 	struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id];
1047ff13be83SJeffrey Hugo 	struct dbc_req *reqs = slice->reqs;
1048ff13be83SJeffrey Hugo 	u32 tail = *ptail;
1049ff13be83SJeffrey Hugo 	u32 avail;
1050ff13be83SJeffrey Hugo 
1051ff13be83SJeffrey Hugo 	avail = head - tail;
1052ff13be83SJeffrey Hugo 	if (head <= tail)
1053ff13be83SJeffrey Hugo 		avail += dbc->nelem;
1054ff13be83SJeffrey Hugo 
1055ff13be83SJeffrey Hugo 	--avail;
1056ff13be83SJeffrey Hugo 
1057ff13be83SJeffrey Hugo 	if (avail < slice->nents)
1058ff13be83SJeffrey Hugo 		return -EAGAIN;
1059ff13be83SJeffrey Hugo 
1060ff13be83SJeffrey Hugo 	if (tail + slice->nents > dbc->nelem) {
1061ff13be83SJeffrey Hugo 		avail = dbc->nelem - tail;
1062ff13be83SJeffrey Hugo 		avail = min_t(u32, avail, slice->nents);
1063ff13be83SJeffrey Hugo 		memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs,
1064ff13be83SJeffrey Hugo 		       sizeof(*reqs) * avail);
1065ff13be83SJeffrey Hugo 		reqs += avail;
1066ff13be83SJeffrey Hugo 		avail = slice->nents - avail;
1067ff13be83SJeffrey Hugo 		if (avail)
1068ff13be83SJeffrey Hugo 			memcpy(dbc->req_q_base, reqs, sizeof(*reqs) * avail);
1069ff13be83SJeffrey Hugo 	} else {
1070ff13be83SJeffrey Hugo 		memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs,
1071ff13be83SJeffrey Hugo 		       sizeof(*reqs) * slice->nents);
1072ff13be83SJeffrey Hugo 	}
1073ff13be83SJeffrey Hugo 
1074ff13be83SJeffrey Hugo 	*ptail = (tail + slice->nents) % dbc->nelem;
1075ff13be83SJeffrey Hugo 
1076ff13be83SJeffrey Hugo 	return 0;
1077ff13be83SJeffrey Hugo }
1078ff13be83SJeffrey Hugo 
1079ff13be83SJeffrey Hugo /*
1080ff13be83SJeffrey Hugo  * Based on the value of resize we may only need to transmit first_n
1081ff13be83SJeffrey Hugo  * entries and the last entry, with last_bytes to send from the last entry.
1082ff13be83SJeffrey Hugo  * Note that first_n could be 0.
1083ff13be83SJeffrey Hugo  */
copy_partial_exec_reqs(struct qaic_device * qdev,struct bo_slice * slice,u64 resize,u32 dbc_id,u32 head,u32 * ptail)1084ff13be83SJeffrey Hugo static inline int copy_partial_exec_reqs(struct qaic_device *qdev, struct bo_slice *slice,
1085ff13be83SJeffrey Hugo 					 u64 resize, u32 dbc_id, u32 head, u32 *ptail)
1086ff13be83SJeffrey Hugo {
1087ff13be83SJeffrey Hugo 	struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id];
1088ff13be83SJeffrey Hugo 	struct dbc_req *reqs = slice->reqs;
1089ff13be83SJeffrey Hugo 	struct dbc_req *last_req;
1090ff13be83SJeffrey Hugo 	u32 tail = *ptail;
1091ff13be83SJeffrey Hugo 	u64 total_bytes;
1092ff13be83SJeffrey Hugo 	u64 last_bytes;
1093ff13be83SJeffrey Hugo 	u32 first_n;
1094ff13be83SJeffrey Hugo 	u32 avail;
1095ff13be83SJeffrey Hugo 	int ret;
1096ff13be83SJeffrey Hugo 	int i;
1097ff13be83SJeffrey Hugo 
1098ff13be83SJeffrey Hugo 	avail = head - tail;
1099ff13be83SJeffrey Hugo 	if (head <= tail)
1100ff13be83SJeffrey Hugo 		avail += dbc->nelem;
1101ff13be83SJeffrey Hugo 
1102ff13be83SJeffrey Hugo 	--avail;
1103ff13be83SJeffrey Hugo 
1104ff13be83SJeffrey Hugo 	total_bytes = 0;
1105ff13be83SJeffrey Hugo 	for (i = 0; i < slice->nents; i++) {
1106ff13be83SJeffrey Hugo 		total_bytes += le32_to_cpu(reqs[i].len);
1107ff13be83SJeffrey Hugo 		if (total_bytes >= resize)
1108ff13be83SJeffrey Hugo 			break;
1109ff13be83SJeffrey Hugo 	}
1110ff13be83SJeffrey Hugo 
1111ff13be83SJeffrey Hugo 	if (total_bytes < resize) {
1112ff13be83SJeffrey Hugo 		/* User space should have used the full buffer path. */
1113ff13be83SJeffrey Hugo 		ret = -EINVAL;
1114ff13be83SJeffrey Hugo 		return ret;
1115ff13be83SJeffrey Hugo 	}
1116ff13be83SJeffrey Hugo 
1117ff13be83SJeffrey Hugo 	first_n = i;
1118ff13be83SJeffrey Hugo 	last_bytes = i ? resize + le32_to_cpu(reqs[i].len) - total_bytes : resize;
1119ff13be83SJeffrey Hugo 
1120ff13be83SJeffrey Hugo 	if (avail < (first_n + 1))
1121ff13be83SJeffrey Hugo 		return -EAGAIN;
1122ff13be83SJeffrey Hugo 
1123ff13be83SJeffrey Hugo 	if (first_n) {
1124ff13be83SJeffrey Hugo 		if (tail + first_n > dbc->nelem) {
1125ff13be83SJeffrey Hugo 			avail = dbc->nelem - tail;
1126ff13be83SJeffrey Hugo 			avail = min_t(u32, avail, first_n);
1127ff13be83SJeffrey Hugo 			memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs,
1128ff13be83SJeffrey Hugo 			       sizeof(*reqs) * avail);
1129ff13be83SJeffrey Hugo 			last_req = reqs + avail;
1130ff13be83SJeffrey Hugo 			avail = first_n - avail;
1131ff13be83SJeffrey Hugo 			if (avail)
1132ff13be83SJeffrey Hugo 				memcpy(dbc->req_q_base, last_req, sizeof(*reqs) * avail);
1133ff13be83SJeffrey Hugo 		} else {
1134ff13be83SJeffrey Hugo 			memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs,
1135ff13be83SJeffrey Hugo 			       sizeof(*reqs) * first_n);
1136ff13be83SJeffrey Hugo 		}
1137ff13be83SJeffrey Hugo 	}
1138ff13be83SJeffrey Hugo 
1139ff13be83SJeffrey Hugo 	/* Copy over the last entry. Here we need to adjust len to the left over
1140ff13be83SJeffrey Hugo 	 * size, and set src and dst to the entry it is copied to.
1141ff13be83SJeffrey Hugo 	 */
1142ff13be83SJeffrey Hugo 	last_req = dbc->req_q_base + (tail + first_n) % dbc->nelem * get_dbc_req_elem_size();
1143ff13be83SJeffrey Hugo 	memcpy(last_req, reqs + slice->nents - 1, sizeof(*reqs));
1144ff13be83SJeffrey Hugo 
1145ff13be83SJeffrey Hugo 	/*
1146ff13be83SJeffrey Hugo 	 * last_bytes holds size of a DMA segment, maximum DMA segment size is
1147ff13be83SJeffrey Hugo 	 * set to UINT_MAX by qaic and hence last_bytes can never exceed u32
1148ff13be83SJeffrey Hugo 	 * range. So, by down sizing we are not corrupting the value.
1149ff13be83SJeffrey Hugo 	 */
1150ff13be83SJeffrey Hugo 	last_req->len = cpu_to_le32((u32)last_bytes);
1151ff13be83SJeffrey Hugo 	last_req->src_addr = reqs[first_n].src_addr;
1152ff13be83SJeffrey Hugo 	last_req->dest_addr = reqs[first_n].dest_addr;
1153ff13be83SJeffrey Hugo 
1154ff13be83SJeffrey Hugo 	*ptail = (tail + first_n + 1) % dbc->nelem;
1155ff13be83SJeffrey Hugo 
1156ff13be83SJeffrey Hugo 	return 0;
1157ff13be83SJeffrey Hugo }
1158ff13be83SJeffrey Hugo 
send_bo_list_to_device(struct qaic_device * qdev,struct drm_file * file_priv,struct qaic_execute_entry * exec,unsigned int count,bool is_partial,struct dma_bridge_chan * dbc,u32 head,u32 * tail)1159ff13be83SJeffrey Hugo static int send_bo_list_to_device(struct qaic_device *qdev, struct drm_file *file_priv,
1160ff13be83SJeffrey Hugo 				  struct qaic_execute_entry *exec, unsigned int count,
1161ff13be83SJeffrey Hugo 				  bool is_partial, struct dma_bridge_chan *dbc, u32 head,
1162ff13be83SJeffrey Hugo 				  u32 *tail)
1163ff13be83SJeffrey Hugo {
1164ff13be83SJeffrey Hugo 	struct qaic_partial_execute_entry *pexec = (struct qaic_partial_execute_entry *)exec;
1165ff13be83SJeffrey Hugo 	struct drm_gem_object *obj;
1166ff13be83SJeffrey Hugo 	struct bo_slice *slice;
1167ff13be83SJeffrey Hugo 	unsigned long flags;
1168ff13be83SJeffrey Hugo 	struct qaic_bo *bo;
1169ff13be83SJeffrey Hugo 	bool queued;
1170ff13be83SJeffrey Hugo 	int i, j;
1171ff13be83SJeffrey Hugo 	int ret;
1172ff13be83SJeffrey Hugo 
1173ff13be83SJeffrey Hugo 	for (i = 0; i < count; i++) {
1174ff13be83SJeffrey Hugo 		/*
1175ff13be83SJeffrey Hugo 		 * ref count will be decremented when the transfer of this
1176ff13be83SJeffrey Hugo 		 * buffer is complete. It is inside dbc_irq_threaded_fn().
1177ff13be83SJeffrey Hugo 		 */
1178ff13be83SJeffrey Hugo 		obj = drm_gem_object_lookup(file_priv,
1179ff13be83SJeffrey Hugo 					    is_partial ? pexec[i].handle : exec[i].handle);
1180ff13be83SJeffrey Hugo 		if (!obj) {
1181ff13be83SJeffrey Hugo 			ret = -ENOENT;
1182ff13be83SJeffrey Hugo 			goto failed_to_send_bo;
1183ff13be83SJeffrey Hugo 		}
1184ff13be83SJeffrey Hugo 
1185ff13be83SJeffrey Hugo 		bo = to_qaic_bo(obj);
1186ff13be83SJeffrey Hugo 
1187ff13be83SJeffrey Hugo 		if (!bo->sliced) {
1188ff13be83SJeffrey Hugo 			ret = -EINVAL;
1189ff13be83SJeffrey Hugo 			goto failed_to_send_bo;
1190ff13be83SJeffrey Hugo 		}
1191ff13be83SJeffrey Hugo 
1192ff13be83SJeffrey Hugo 		if (is_partial && pexec[i].resize > bo->size) {
1193ff13be83SJeffrey Hugo 			ret = -EINVAL;
1194ff13be83SJeffrey Hugo 			goto failed_to_send_bo;
1195ff13be83SJeffrey Hugo 		}
1196ff13be83SJeffrey Hugo 
1197ff13be83SJeffrey Hugo 		spin_lock_irqsave(&dbc->xfer_lock, flags);
1198ff13be83SJeffrey Hugo 		queued = bo->queued;
1199ff13be83SJeffrey Hugo 		bo->queued = true;
1200ff13be83SJeffrey Hugo 		if (queued) {
1201ff13be83SJeffrey Hugo 			spin_unlock_irqrestore(&dbc->xfer_lock, flags);
1202ff13be83SJeffrey Hugo 			ret = -EINVAL;
1203ff13be83SJeffrey Hugo 			goto failed_to_send_bo;
1204ff13be83SJeffrey Hugo 		}
1205ff13be83SJeffrey Hugo 
1206ff13be83SJeffrey Hugo 		bo->req_id = dbc->next_req_id++;
1207ff13be83SJeffrey Hugo 
1208ff13be83SJeffrey Hugo 		list_for_each_entry(slice, &bo->slices, slice) {
1209ff13be83SJeffrey Hugo 			/*
1210ff13be83SJeffrey Hugo 			 * If this slice does not fall under the given
1211ff13be83SJeffrey Hugo 			 * resize then skip this slice and continue the loop
1212ff13be83SJeffrey Hugo 			 */
1213ff13be83SJeffrey Hugo 			if (is_partial && pexec[i].resize && pexec[i].resize <= slice->offset)
1214ff13be83SJeffrey Hugo 				continue;
1215ff13be83SJeffrey Hugo 
1216ff13be83SJeffrey Hugo 			for (j = 0; j < slice->nents; j++)
1217ff13be83SJeffrey Hugo 				slice->reqs[j].req_id = cpu_to_le16(bo->req_id);
1218ff13be83SJeffrey Hugo 
1219ff13be83SJeffrey Hugo 			/*
1220ff13be83SJeffrey Hugo 			 * If it is a partial execute ioctl call then check if
1221ff13be83SJeffrey Hugo 			 * resize has cut this slice short then do a partial copy
1222ff13be83SJeffrey Hugo 			 * else do complete copy
1223ff13be83SJeffrey Hugo 			 */
1224ff13be83SJeffrey Hugo 			if (is_partial && pexec[i].resize &&
1225ff13be83SJeffrey Hugo 			    pexec[i].resize < slice->offset + slice->size)
1226ff13be83SJeffrey Hugo 				ret = copy_partial_exec_reqs(qdev, slice,
1227ff13be83SJeffrey Hugo 							     pexec[i].resize - slice->offset,
1228ff13be83SJeffrey Hugo 							     dbc->id, head, tail);
1229ff13be83SJeffrey Hugo 			else
1230ff13be83SJeffrey Hugo 				ret = copy_exec_reqs(qdev, slice, dbc->id, head, tail);
1231ff13be83SJeffrey Hugo 			if (ret) {
1232ff13be83SJeffrey Hugo 				bo->queued = false;
1233ff13be83SJeffrey Hugo 				spin_unlock_irqrestore(&dbc->xfer_lock, flags);
1234ff13be83SJeffrey Hugo 				goto failed_to_send_bo;
1235ff13be83SJeffrey Hugo 			}
1236ff13be83SJeffrey Hugo 		}
1237ff13be83SJeffrey Hugo 		reinit_completion(&bo->xfer_done);
1238ff13be83SJeffrey Hugo 		list_add_tail(&bo->xfer_list, &dbc->xfer_list);
1239ff13be83SJeffrey Hugo 		spin_unlock_irqrestore(&dbc->xfer_lock, flags);
1240ff13be83SJeffrey Hugo 		dma_sync_sgtable_for_device(&qdev->pdev->dev, bo->sgt, bo->dir);
1241ff13be83SJeffrey Hugo 	}
1242ff13be83SJeffrey Hugo 
1243ff13be83SJeffrey Hugo 	return 0;
1244ff13be83SJeffrey Hugo 
1245ff13be83SJeffrey Hugo failed_to_send_bo:
1246ff13be83SJeffrey Hugo 	if (likely(obj))
1247ff13be83SJeffrey Hugo 		drm_gem_object_put(obj);
1248ff13be83SJeffrey Hugo 	for (j = 0; j < i; j++) {
1249ff13be83SJeffrey Hugo 		spin_lock_irqsave(&dbc->xfer_lock, flags);
1250ff13be83SJeffrey Hugo 		bo = list_last_entry(&dbc->xfer_list, struct qaic_bo, xfer_list);
1251ff13be83SJeffrey Hugo 		obj = &bo->base;
1252ff13be83SJeffrey Hugo 		bo->queued = false;
1253ff13be83SJeffrey Hugo 		list_del(&bo->xfer_list);
1254ff13be83SJeffrey Hugo 		spin_unlock_irqrestore(&dbc->xfer_lock, flags);
1255ff13be83SJeffrey Hugo 		dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir);
1256ff13be83SJeffrey Hugo 		drm_gem_object_put(obj);
1257ff13be83SJeffrey Hugo 	}
1258ff13be83SJeffrey Hugo 	return ret;
1259ff13be83SJeffrey Hugo }
1260ff13be83SJeffrey Hugo 
update_profiling_data(struct drm_file * file_priv,struct qaic_execute_entry * exec,unsigned int count,bool is_partial,u64 received_ts,u64 submit_ts,u32 queue_level)1261ff13be83SJeffrey Hugo static void update_profiling_data(struct drm_file *file_priv,
1262ff13be83SJeffrey Hugo 				  struct qaic_execute_entry *exec, unsigned int count,
1263ff13be83SJeffrey Hugo 				  bool is_partial, u64 received_ts, u64 submit_ts, u32 queue_level)
1264ff13be83SJeffrey Hugo {
1265ff13be83SJeffrey Hugo 	struct qaic_partial_execute_entry *pexec = (struct qaic_partial_execute_entry *)exec;
1266ff13be83SJeffrey Hugo 	struct drm_gem_object *obj;
1267ff13be83SJeffrey Hugo 	struct qaic_bo *bo;
1268ff13be83SJeffrey Hugo 	int i;
1269ff13be83SJeffrey Hugo 
1270ff13be83SJeffrey Hugo 	for (i = 0; i < count; i++) {
1271ff13be83SJeffrey Hugo 		/*
1272ff13be83SJeffrey Hugo 		 * Since we already committed the BO to hardware, the only way
1273ff13be83SJeffrey Hugo 		 * this should fail is a pending signal. We can't cancel the
1274ff13be83SJeffrey Hugo 		 * submit to hardware, so we have to just skip the profiling
1275ff13be83SJeffrey Hugo 		 * data. In case the signal is not fatal to the process, we
1276ff13be83SJeffrey Hugo 		 * return success so that the user doesn't try to resubmit.
1277ff13be83SJeffrey Hugo 		 */
1278ff13be83SJeffrey Hugo 		obj = drm_gem_object_lookup(file_priv,
1279ff13be83SJeffrey Hugo 					    is_partial ? pexec[i].handle : exec[i].handle);
1280ff13be83SJeffrey Hugo 		if (!obj)
1281ff13be83SJeffrey Hugo 			break;
1282ff13be83SJeffrey Hugo 		bo = to_qaic_bo(obj);
1283ff13be83SJeffrey Hugo 		bo->perf_stats.req_received_ts = received_ts;
1284ff13be83SJeffrey Hugo 		bo->perf_stats.req_submit_ts = submit_ts;
1285ff13be83SJeffrey Hugo 		bo->perf_stats.queue_level_before = queue_level;
1286ff13be83SJeffrey Hugo 		queue_level += bo->total_slice_nents;
1287ff13be83SJeffrey Hugo 		drm_gem_object_put(obj);
1288ff13be83SJeffrey Hugo 	}
1289ff13be83SJeffrey Hugo }
1290ff13be83SJeffrey Hugo 
__qaic_execute_bo_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv,bool is_partial)1291ff13be83SJeffrey Hugo static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv,
1292ff13be83SJeffrey Hugo 				   bool is_partial)
1293ff13be83SJeffrey Hugo {
1294ff13be83SJeffrey Hugo 	struct qaic_execute *args = data;
1295ff13be83SJeffrey Hugo 	struct qaic_execute_entry *exec;
1296ff13be83SJeffrey Hugo 	struct dma_bridge_chan *dbc;
1297ff13be83SJeffrey Hugo 	int usr_rcu_id, qdev_rcu_id;
1298ff13be83SJeffrey Hugo 	struct qaic_device *qdev;
1299ff13be83SJeffrey Hugo 	struct qaic_user *usr;
1300ff13be83SJeffrey Hugo 	u8 __user *user_data;
1301ff13be83SJeffrey Hugo 	unsigned long n;
1302ff13be83SJeffrey Hugo 	u64 received_ts;
1303ff13be83SJeffrey Hugo 	u32 queue_level;
1304ff13be83SJeffrey Hugo 	u64 submit_ts;
1305ff13be83SJeffrey Hugo 	int rcu_id;
1306ff13be83SJeffrey Hugo 	u32 head;
1307ff13be83SJeffrey Hugo 	u32 tail;
1308ff13be83SJeffrey Hugo 	u64 size;
1309ff13be83SJeffrey Hugo 	int ret;
1310ff13be83SJeffrey Hugo 
1311ff13be83SJeffrey Hugo 	received_ts = ktime_get_ns();
1312ff13be83SJeffrey Hugo 
1313cc69c93bSColin Ian King 	size = is_partial ? sizeof(struct qaic_partial_execute_entry) : sizeof(*exec);
1314ff13be83SJeffrey Hugo 	n = (unsigned long)size * args->hdr.count;
1315ff13be83SJeffrey Hugo 	if (args->hdr.count == 0 || n / args->hdr.count != size)
1316ff13be83SJeffrey Hugo 		return -EINVAL;
1317ff13be83SJeffrey Hugo 
1318ff13be83SJeffrey Hugo 	user_data = u64_to_user_ptr(args->data);
1319ff13be83SJeffrey Hugo 
1320ff13be83SJeffrey Hugo 	exec = kcalloc(args->hdr.count, size, GFP_KERNEL);
1321ff13be83SJeffrey Hugo 	if (!exec)
1322ff13be83SJeffrey Hugo 		return -ENOMEM;
1323ff13be83SJeffrey Hugo 
1324ff13be83SJeffrey Hugo 	if (copy_from_user(exec, user_data, n)) {
1325ff13be83SJeffrey Hugo 		ret = -EFAULT;
1326ff13be83SJeffrey Hugo 		goto free_exec;
1327ff13be83SJeffrey Hugo 	}
1328ff13be83SJeffrey Hugo 
1329ff13be83SJeffrey Hugo 	usr = file_priv->driver_priv;
1330ff13be83SJeffrey Hugo 	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
1331ff13be83SJeffrey Hugo 	if (!usr->qddev) {
1332ff13be83SJeffrey Hugo 		ret = -ENODEV;
1333ff13be83SJeffrey Hugo 		goto unlock_usr_srcu;
1334ff13be83SJeffrey Hugo 	}
1335ff13be83SJeffrey Hugo 
1336ff13be83SJeffrey Hugo 	qdev = usr->qddev->qdev;
1337ff13be83SJeffrey Hugo 	qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
1338ff13be83SJeffrey Hugo 	if (qdev->in_reset) {
1339ff13be83SJeffrey Hugo 		ret = -ENODEV;
1340ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
1341ff13be83SJeffrey Hugo 	}
1342ff13be83SJeffrey Hugo 
1343ff13be83SJeffrey Hugo 	if (args->hdr.dbc_id >= qdev->num_dbc) {
1344ff13be83SJeffrey Hugo 		ret = -EINVAL;
1345ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
1346ff13be83SJeffrey Hugo 	}
1347ff13be83SJeffrey Hugo 
1348ff13be83SJeffrey Hugo 	dbc = &qdev->dbc[args->hdr.dbc_id];
1349ff13be83SJeffrey Hugo 
1350ff13be83SJeffrey Hugo 	rcu_id = srcu_read_lock(&dbc->ch_lock);
1351ff13be83SJeffrey Hugo 	if (!dbc->usr || dbc->usr->handle != usr->handle) {
1352ff13be83SJeffrey Hugo 		ret = -EPERM;
1353ff13be83SJeffrey Hugo 		goto release_ch_rcu;
1354ff13be83SJeffrey Hugo 	}
1355ff13be83SJeffrey Hugo 
1356ff13be83SJeffrey Hugo 	head = readl(dbc->dbc_base + REQHP_OFF);
1357ff13be83SJeffrey Hugo 	tail = readl(dbc->dbc_base + REQTP_OFF);
1358ff13be83SJeffrey Hugo 
1359ff13be83SJeffrey Hugo 	if (head == U32_MAX || tail == U32_MAX) {
1360ff13be83SJeffrey Hugo 		/* PCI link error */
1361ff13be83SJeffrey Hugo 		ret = -ENODEV;
1362ff13be83SJeffrey Hugo 		goto release_ch_rcu;
1363ff13be83SJeffrey Hugo 	}
1364ff13be83SJeffrey Hugo 
1365ff13be83SJeffrey Hugo 	queue_level = head <= tail ? tail - head : dbc->nelem - (head - tail);
1366ff13be83SJeffrey Hugo 
1367ff13be83SJeffrey Hugo 	ret = send_bo_list_to_device(qdev, file_priv, exec, args->hdr.count, is_partial, dbc,
1368ff13be83SJeffrey Hugo 				     head, &tail);
1369ff13be83SJeffrey Hugo 	if (ret)
1370ff13be83SJeffrey Hugo 		goto release_ch_rcu;
1371ff13be83SJeffrey Hugo 
1372ff13be83SJeffrey Hugo 	/* Finalize commit to hardware */
1373ff13be83SJeffrey Hugo 	submit_ts = ktime_get_ns();
1374ff13be83SJeffrey Hugo 	writel(tail, dbc->dbc_base + REQTP_OFF);
1375ff13be83SJeffrey Hugo 
1376ff13be83SJeffrey Hugo 	update_profiling_data(file_priv, exec, args->hdr.count, is_partial, received_ts,
1377ff13be83SJeffrey Hugo 			      submit_ts, queue_level);
1378ff13be83SJeffrey Hugo 
1379ff13be83SJeffrey Hugo 	if (datapath_polling)
1380ff13be83SJeffrey Hugo 		schedule_work(&dbc->poll_work);
1381ff13be83SJeffrey Hugo 
1382ff13be83SJeffrey Hugo release_ch_rcu:
1383ff13be83SJeffrey Hugo 	srcu_read_unlock(&dbc->ch_lock, rcu_id);
1384ff13be83SJeffrey Hugo unlock_dev_srcu:
1385ff13be83SJeffrey Hugo 	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
1386ff13be83SJeffrey Hugo unlock_usr_srcu:
1387ff13be83SJeffrey Hugo 	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
1388ff13be83SJeffrey Hugo free_exec:
1389ff13be83SJeffrey Hugo 	kfree(exec);
1390ff13be83SJeffrey Hugo 	return ret;
1391ff13be83SJeffrey Hugo }
1392ff13be83SJeffrey Hugo 
qaic_execute_bo_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)1393ff13be83SJeffrey Hugo int qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
1394ff13be83SJeffrey Hugo {
1395ff13be83SJeffrey Hugo 	return __qaic_execute_bo_ioctl(dev, data, file_priv, false);
1396ff13be83SJeffrey Hugo }
1397ff13be83SJeffrey Hugo 
qaic_partial_execute_bo_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)1398ff13be83SJeffrey Hugo int qaic_partial_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
1399ff13be83SJeffrey Hugo {
1400ff13be83SJeffrey Hugo 	return __qaic_execute_bo_ioctl(dev, data, file_priv, true);
1401ff13be83SJeffrey Hugo }
1402ff13be83SJeffrey Hugo 
1403ff13be83SJeffrey Hugo /*
1404ff13be83SJeffrey Hugo  * Our interrupt handling is a bit more complicated than a simple ideal, but
1405ff13be83SJeffrey Hugo  * sadly necessary.
1406ff13be83SJeffrey Hugo  *
1407ff13be83SJeffrey Hugo  * Each dbc has a completion queue. Entries in the queue correspond to DMA
1408ff13be83SJeffrey Hugo  * requests which the device has processed. The hardware already has a built
1409ff13be83SJeffrey Hugo  * in irq mitigation. When the device puts an entry into the queue, it will
1410ff13be83SJeffrey Hugo  * only trigger an interrupt if the queue was empty. Therefore, when adding
1411ff13be83SJeffrey Hugo  * the Nth event to a non-empty queue, the hardware doesn't trigger an
1412ff13be83SJeffrey Hugo  * interrupt. This means the host doesn't get additional interrupts signaling
1413ff13be83SJeffrey Hugo  * the same thing - the queue has something to process.
1414ff13be83SJeffrey Hugo  * This behavior can be overridden in the DMA request.
1415ff13be83SJeffrey Hugo  * This means that when the host receives an interrupt, it is required to
1416ff13be83SJeffrey Hugo  * drain the queue.
1417ff13be83SJeffrey Hugo  *
1418ff13be83SJeffrey Hugo  * This behavior is what NAPI attempts to accomplish, although we can't use
1419ff13be83SJeffrey Hugo  * NAPI as we don't have a netdev. We use threaded irqs instead.
1420ff13be83SJeffrey Hugo  *
1421ff13be83SJeffrey Hugo  * However, there is a situation where the host drains the queue fast enough
1422ff13be83SJeffrey Hugo  * that every event causes an interrupt. Typically this is not a problem as
1423ff13be83SJeffrey Hugo  * the rate of events would be low. However, that is not the case with
1424ff13be83SJeffrey Hugo  * lprnet for example. On an Intel Xeon D-2191 where we run 8 instances of
1425ff13be83SJeffrey Hugo  * lprnet, the host receives roughly 80k interrupts per second from the device
1426ff13be83SJeffrey Hugo  * (per /proc/interrupts). While NAPI documentation indicates the host should
1427ff13be83SJeffrey Hugo  * just chug along, sadly that behavior causes instability in some hosts.
1428ff13be83SJeffrey Hugo  *
1429ff13be83SJeffrey Hugo  * Therefore, we implement an interrupt disable scheme similar to NAPI. The
1430ff13be83SJeffrey Hugo  * key difference is that we will delay after draining the queue for a small
1431ff13be83SJeffrey Hugo  * time to allow additional events to come in via polling. Using the above
1432ff13be83SJeffrey Hugo  * lprnet workload, this reduces the number of interrupts processed from
1433ff13be83SJeffrey Hugo  * ~80k/sec to about 64 in 5 minutes and appears to solve the system
1434ff13be83SJeffrey Hugo  * instability.
1435ff13be83SJeffrey Hugo  */
dbc_irq_handler(int irq,void * data)1436ff13be83SJeffrey Hugo irqreturn_t dbc_irq_handler(int irq, void *data)
1437ff13be83SJeffrey Hugo {
1438ff13be83SJeffrey Hugo 	struct dma_bridge_chan *dbc = data;
1439ff13be83SJeffrey Hugo 	int rcu_id;
1440ff13be83SJeffrey Hugo 	u32 head;
1441ff13be83SJeffrey Hugo 	u32 tail;
1442ff13be83SJeffrey Hugo 
1443ff13be83SJeffrey Hugo 	rcu_id = srcu_read_lock(&dbc->ch_lock);
1444ff13be83SJeffrey Hugo 
1445ff13be83SJeffrey Hugo 	if (!dbc->usr) {
1446ff13be83SJeffrey Hugo 		srcu_read_unlock(&dbc->ch_lock, rcu_id);
1447ff13be83SJeffrey Hugo 		return IRQ_HANDLED;
1448ff13be83SJeffrey Hugo 	}
1449ff13be83SJeffrey Hugo 
1450ff13be83SJeffrey Hugo 	head = readl(dbc->dbc_base + RSPHP_OFF);
1451ff13be83SJeffrey Hugo 	if (head == U32_MAX) { /* PCI link error */
1452ff13be83SJeffrey Hugo 		srcu_read_unlock(&dbc->ch_lock, rcu_id);
1453ff13be83SJeffrey Hugo 		return IRQ_NONE;
1454ff13be83SJeffrey Hugo 	}
1455ff13be83SJeffrey Hugo 
1456ff13be83SJeffrey Hugo 	tail = readl(dbc->dbc_base + RSPTP_OFF);
1457ff13be83SJeffrey Hugo 	if (tail == U32_MAX) { /* PCI link error */
1458ff13be83SJeffrey Hugo 		srcu_read_unlock(&dbc->ch_lock, rcu_id);
1459ff13be83SJeffrey Hugo 		return IRQ_NONE;
1460ff13be83SJeffrey Hugo 	}
1461ff13be83SJeffrey Hugo 
1462ff13be83SJeffrey Hugo 	if (head == tail) { /* queue empty */
1463ff13be83SJeffrey Hugo 		srcu_read_unlock(&dbc->ch_lock, rcu_id);
1464ff13be83SJeffrey Hugo 		return IRQ_NONE;
1465ff13be83SJeffrey Hugo 	}
1466ff13be83SJeffrey Hugo 
1467ff13be83SJeffrey Hugo 	disable_irq_nosync(irq);
1468ff13be83SJeffrey Hugo 	srcu_read_unlock(&dbc->ch_lock, rcu_id);
1469ff13be83SJeffrey Hugo 	return IRQ_WAKE_THREAD;
1470ff13be83SJeffrey Hugo }
1471ff13be83SJeffrey Hugo 
irq_polling_work(struct work_struct * work)1472ff13be83SJeffrey Hugo void irq_polling_work(struct work_struct *work)
1473ff13be83SJeffrey Hugo {
1474ff13be83SJeffrey Hugo 	struct dma_bridge_chan *dbc = container_of(work, struct dma_bridge_chan,  poll_work);
1475ff13be83SJeffrey Hugo 	unsigned long flags;
1476ff13be83SJeffrey Hugo 	int rcu_id;
1477ff13be83SJeffrey Hugo 	u32 head;
1478ff13be83SJeffrey Hugo 	u32 tail;
1479ff13be83SJeffrey Hugo 
1480ff13be83SJeffrey Hugo 	rcu_id = srcu_read_lock(&dbc->ch_lock);
1481ff13be83SJeffrey Hugo 
1482ff13be83SJeffrey Hugo 	while (1) {
1483ff13be83SJeffrey Hugo 		if (dbc->qdev->in_reset) {
1484ff13be83SJeffrey Hugo 			srcu_read_unlock(&dbc->ch_lock, rcu_id);
1485ff13be83SJeffrey Hugo 			return;
1486ff13be83SJeffrey Hugo 		}
1487ff13be83SJeffrey Hugo 		if (!dbc->usr) {
1488ff13be83SJeffrey Hugo 			srcu_read_unlock(&dbc->ch_lock, rcu_id);
1489ff13be83SJeffrey Hugo 			return;
1490ff13be83SJeffrey Hugo 		}
1491ff13be83SJeffrey Hugo 		spin_lock_irqsave(&dbc->xfer_lock, flags);
1492ff13be83SJeffrey Hugo 		if (list_empty(&dbc->xfer_list)) {
1493ff13be83SJeffrey Hugo 			spin_unlock_irqrestore(&dbc->xfer_lock, flags);
1494ff13be83SJeffrey Hugo 			srcu_read_unlock(&dbc->ch_lock, rcu_id);
1495ff13be83SJeffrey Hugo 			return;
1496ff13be83SJeffrey Hugo 		}
1497ff13be83SJeffrey Hugo 		spin_unlock_irqrestore(&dbc->xfer_lock, flags);
1498ff13be83SJeffrey Hugo 
1499ff13be83SJeffrey Hugo 		head = readl(dbc->dbc_base + RSPHP_OFF);
1500ff13be83SJeffrey Hugo 		if (head == U32_MAX) { /* PCI link error */
1501ff13be83SJeffrey Hugo 			srcu_read_unlock(&dbc->ch_lock, rcu_id);
1502ff13be83SJeffrey Hugo 			return;
1503ff13be83SJeffrey Hugo 		}
1504ff13be83SJeffrey Hugo 
1505ff13be83SJeffrey Hugo 		tail = readl(dbc->dbc_base + RSPTP_OFF);
1506ff13be83SJeffrey Hugo 		if (tail == U32_MAX) { /* PCI link error */
1507ff13be83SJeffrey Hugo 			srcu_read_unlock(&dbc->ch_lock, rcu_id);
1508ff13be83SJeffrey Hugo 			return;
1509ff13be83SJeffrey Hugo 		}
1510ff13be83SJeffrey Hugo 
1511ff13be83SJeffrey Hugo 		if (head != tail) {
1512ff13be83SJeffrey Hugo 			irq_wake_thread(dbc->irq, dbc);
1513ff13be83SJeffrey Hugo 			srcu_read_unlock(&dbc->ch_lock, rcu_id);
1514ff13be83SJeffrey Hugo 			return;
1515ff13be83SJeffrey Hugo 		}
1516ff13be83SJeffrey Hugo 
1517ff13be83SJeffrey Hugo 		cond_resched();
1518ff13be83SJeffrey Hugo 		usleep_range(datapath_poll_interval_us, 2 * datapath_poll_interval_us);
1519ff13be83SJeffrey Hugo 	}
1520ff13be83SJeffrey Hugo }
1521ff13be83SJeffrey Hugo 
dbc_irq_threaded_fn(int irq,void * data)1522ff13be83SJeffrey Hugo irqreturn_t dbc_irq_threaded_fn(int irq, void *data)
1523ff13be83SJeffrey Hugo {
1524ff13be83SJeffrey Hugo 	struct dma_bridge_chan *dbc = data;
1525ff13be83SJeffrey Hugo 	int event_count = NUM_EVENTS;
1526ff13be83SJeffrey Hugo 	int delay_count = NUM_DELAYS;
1527ff13be83SJeffrey Hugo 	struct qaic_device *qdev;
1528ff13be83SJeffrey Hugo 	struct qaic_bo *bo, *i;
1529ff13be83SJeffrey Hugo 	struct dbc_rsp *rsp;
1530ff13be83SJeffrey Hugo 	unsigned long flags;
1531ff13be83SJeffrey Hugo 	int rcu_id;
1532ff13be83SJeffrey Hugo 	u16 status;
1533ff13be83SJeffrey Hugo 	u16 req_id;
1534ff13be83SJeffrey Hugo 	u32 head;
1535ff13be83SJeffrey Hugo 	u32 tail;
1536ff13be83SJeffrey Hugo 
1537ff13be83SJeffrey Hugo 	rcu_id = srcu_read_lock(&dbc->ch_lock);
1538ff13be83SJeffrey Hugo 
1539ff13be83SJeffrey Hugo 	head = readl(dbc->dbc_base + RSPHP_OFF);
1540ff13be83SJeffrey Hugo 	if (head == U32_MAX) /* PCI link error */
1541ff13be83SJeffrey Hugo 		goto error_out;
1542ff13be83SJeffrey Hugo 
1543ff13be83SJeffrey Hugo 	qdev = dbc->qdev;
1544ff13be83SJeffrey Hugo read_fifo:
1545ff13be83SJeffrey Hugo 
1546ff13be83SJeffrey Hugo 	if (!event_count) {
1547ff13be83SJeffrey Hugo 		event_count = NUM_EVENTS;
1548ff13be83SJeffrey Hugo 		cond_resched();
1549ff13be83SJeffrey Hugo 	}
1550ff13be83SJeffrey Hugo 
1551ff13be83SJeffrey Hugo 	/*
1552ff13be83SJeffrey Hugo 	 * if this channel isn't assigned or gets unassigned during processing
1553ff13be83SJeffrey Hugo 	 * we have nothing further to do
1554ff13be83SJeffrey Hugo 	 */
1555ff13be83SJeffrey Hugo 	if (!dbc->usr)
1556ff13be83SJeffrey Hugo 		goto error_out;
1557ff13be83SJeffrey Hugo 
1558ff13be83SJeffrey Hugo 	tail = readl(dbc->dbc_base + RSPTP_OFF);
1559ff13be83SJeffrey Hugo 	if (tail == U32_MAX) /* PCI link error */
1560ff13be83SJeffrey Hugo 		goto error_out;
1561ff13be83SJeffrey Hugo 
1562ff13be83SJeffrey Hugo 	if (head == tail) { /* queue empty */
1563ff13be83SJeffrey Hugo 		if (delay_count) {
1564ff13be83SJeffrey Hugo 			--delay_count;
1565ff13be83SJeffrey Hugo 			usleep_range(100, 200);
1566ff13be83SJeffrey Hugo 			goto read_fifo; /* check for a new event */
1567ff13be83SJeffrey Hugo 		}
1568ff13be83SJeffrey Hugo 		goto normal_out;
1569ff13be83SJeffrey Hugo 	}
1570ff13be83SJeffrey Hugo 
1571ff13be83SJeffrey Hugo 	delay_count = NUM_DELAYS;
1572ff13be83SJeffrey Hugo 	while (head != tail) {
1573ff13be83SJeffrey Hugo 		if (!event_count)
1574ff13be83SJeffrey Hugo 			break;
1575ff13be83SJeffrey Hugo 		--event_count;
1576ff13be83SJeffrey Hugo 		rsp = dbc->rsp_q_base + head * sizeof(*rsp);
1577ff13be83SJeffrey Hugo 		req_id = le16_to_cpu(rsp->req_id);
1578ff13be83SJeffrey Hugo 		status = le16_to_cpu(rsp->status);
1579ff13be83SJeffrey Hugo 		if (status)
1580ff13be83SJeffrey Hugo 			pci_dbg(qdev->pdev, "req_id %d failed with status %d\n", req_id, status);
1581ff13be83SJeffrey Hugo 		spin_lock_irqsave(&dbc->xfer_lock, flags);
1582ff13be83SJeffrey Hugo 		/*
1583ff13be83SJeffrey Hugo 		 * A BO can receive multiple interrupts, since a BO can be
1584ff13be83SJeffrey Hugo 		 * divided into multiple slices and a buffer receives as many
1585ff13be83SJeffrey Hugo 		 * interrupts as slices. So until it receives interrupts for
1586ff13be83SJeffrey Hugo 		 * all the slices we cannot mark that buffer complete.
1587ff13be83SJeffrey Hugo 		 */
1588ff13be83SJeffrey Hugo 		list_for_each_entry_safe(bo, i, &dbc->xfer_list, xfer_list) {
1589ff13be83SJeffrey Hugo 			if (bo->req_id == req_id)
1590ff13be83SJeffrey Hugo 				bo->nr_slice_xfer_done++;
1591ff13be83SJeffrey Hugo 			else
1592ff13be83SJeffrey Hugo 				continue;
1593ff13be83SJeffrey Hugo 
1594ff13be83SJeffrey Hugo 			if (bo->nr_slice_xfer_done < bo->nr_slice)
1595ff13be83SJeffrey Hugo 				break;
1596ff13be83SJeffrey Hugo 
1597ff13be83SJeffrey Hugo 			/*
1598ff13be83SJeffrey Hugo 			 * At this point we have received all the interrupts for
1599ff13be83SJeffrey Hugo 			 * BO, which means BO execution is complete.
1600ff13be83SJeffrey Hugo 			 */
1601ff13be83SJeffrey Hugo 			dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir);
1602ff13be83SJeffrey Hugo 			bo->nr_slice_xfer_done = 0;
1603ff13be83SJeffrey Hugo 			bo->queued = false;
1604ff13be83SJeffrey Hugo 			list_del(&bo->xfer_list);
1605ff13be83SJeffrey Hugo 			bo->perf_stats.req_processed_ts = ktime_get_ns();
1606ff13be83SJeffrey Hugo 			complete_all(&bo->xfer_done);
1607ff13be83SJeffrey Hugo 			drm_gem_object_put(&bo->base);
1608ff13be83SJeffrey Hugo 			break;
1609ff13be83SJeffrey Hugo 		}
1610ff13be83SJeffrey Hugo 		spin_unlock_irqrestore(&dbc->xfer_lock, flags);
1611ff13be83SJeffrey Hugo 		head = (head + 1) % dbc->nelem;
1612ff13be83SJeffrey Hugo 	}
1613ff13be83SJeffrey Hugo 
1614ff13be83SJeffrey Hugo 	/*
1615ff13be83SJeffrey Hugo 	 * Update the head pointer of response queue and let the device know
1616ff13be83SJeffrey Hugo 	 * that we have consumed elements from the queue.
1617ff13be83SJeffrey Hugo 	 */
1618ff13be83SJeffrey Hugo 	writel(head, dbc->dbc_base + RSPHP_OFF);
1619ff13be83SJeffrey Hugo 
1620ff13be83SJeffrey Hugo 	/* elements might have been put in the queue while we were processing */
1621ff13be83SJeffrey Hugo 	goto read_fifo;
1622ff13be83SJeffrey Hugo 
1623ff13be83SJeffrey Hugo normal_out:
1624ff13be83SJeffrey Hugo 	if (likely(!datapath_polling))
1625ff13be83SJeffrey Hugo 		enable_irq(irq);
1626ff13be83SJeffrey Hugo 	else
1627ff13be83SJeffrey Hugo 		schedule_work(&dbc->poll_work);
1628ff13be83SJeffrey Hugo 	/* checking the fifo and enabling irqs is a race, missed event check */
1629ff13be83SJeffrey Hugo 	tail = readl(dbc->dbc_base + RSPTP_OFF);
1630ff13be83SJeffrey Hugo 	if (tail != U32_MAX && head != tail) {
1631ff13be83SJeffrey Hugo 		if (likely(!datapath_polling))
1632ff13be83SJeffrey Hugo 			disable_irq_nosync(irq);
1633ff13be83SJeffrey Hugo 		goto read_fifo;
1634ff13be83SJeffrey Hugo 	}
1635ff13be83SJeffrey Hugo 	srcu_read_unlock(&dbc->ch_lock, rcu_id);
1636ff13be83SJeffrey Hugo 	return IRQ_HANDLED;
1637ff13be83SJeffrey Hugo 
1638ff13be83SJeffrey Hugo error_out:
1639ff13be83SJeffrey Hugo 	srcu_read_unlock(&dbc->ch_lock, rcu_id);
1640ff13be83SJeffrey Hugo 	if (likely(!datapath_polling))
1641ff13be83SJeffrey Hugo 		enable_irq(irq);
1642ff13be83SJeffrey Hugo 	else
1643ff13be83SJeffrey Hugo 		schedule_work(&dbc->poll_work);
1644ff13be83SJeffrey Hugo 
1645ff13be83SJeffrey Hugo 	return IRQ_HANDLED;
1646ff13be83SJeffrey Hugo }
1647ff13be83SJeffrey Hugo 
qaic_wait_bo_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)1648ff13be83SJeffrey Hugo int qaic_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
1649ff13be83SJeffrey Hugo {
1650ff13be83SJeffrey Hugo 	struct qaic_wait *args = data;
1651ff13be83SJeffrey Hugo 	int usr_rcu_id, qdev_rcu_id;
1652ff13be83SJeffrey Hugo 	struct dma_bridge_chan *dbc;
1653ff13be83SJeffrey Hugo 	struct drm_gem_object *obj;
1654ff13be83SJeffrey Hugo 	struct qaic_device *qdev;
1655ff13be83SJeffrey Hugo 	unsigned long timeout;
1656ff13be83SJeffrey Hugo 	struct qaic_user *usr;
1657ff13be83SJeffrey Hugo 	struct qaic_bo *bo;
1658ff13be83SJeffrey Hugo 	int rcu_id;
1659ff13be83SJeffrey Hugo 	int ret;
1660ff13be83SJeffrey Hugo 
1661d3b277b7SPranjal Ramajor Asha Kanojiya 	if (args->pad != 0)
1662d3b277b7SPranjal Ramajor Asha Kanojiya 		return -EINVAL;
1663d3b277b7SPranjal Ramajor Asha Kanojiya 
1664ff13be83SJeffrey Hugo 	usr = file_priv->driver_priv;
1665ff13be83SJeffrey Hugo 	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
1666ff13be83SJeffrey Hugo 	if (!usr->qddev) {
1667ff13be83SJeffrey Hugo 		ret = -ENODEV;
1668ff13be83SJeffrey Hugo 		goto unlock_usr_srcu;
1669ff13be83SJeffrey Hugo 	}
1670ff13be83SJeffrey Hugo 
1671ff13be83SJeffrey Hugo 	qdev = usr->qddev->qdev;
1672ff13be83SJeffrey Hugo 	qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
1673ff13be83SJeffrey Hugo 	if (qdev->in_reset) {
1674ff13be83SJeffrey Hugo 		ret = -ENODEV;
1675ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
1676ff13be83SJeffrey Hugo 	}
1677ff13be83SJeffrey Hugo 
1678ff13be83SJeffrey Hugo 	if (args->dbc_id >= qdev->num_dbc) {
1679ff13be83SJeffrey Hugo 		ret = -EINVAL;
1680ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
1681ff13be83SJeffrey Hugo 	}
1682ff13be83SJeffrey Hugo 
1683ff13be83SJeffrey Hugo 	dbc = &qdev->dbc[args->dbc_id];
1684ff13be83SJeffrey Hugo 
1685ff13be83SJeffrey Hugo 	rcu_id = srcu_read_lock(&dbc->ch_lock);
1686ff13be83SJeffrey Hugo 	if (dbc->usr != usr) {
1687ff13be83SJeffrey Hugo 		ret = -EPERM;
1688ff13be83SJeffrey Hugo 		goto unlock_ch_srcu;
1689ff13be83SJeffrey Hugo 	}
1690ff13be83SJeffrey Hugo 
1691ff13be83SJeffrey Hugo 	obj = drm_gem_object_lookup(file_priv, args->handle);
1692ff13be83SJeffrey Hugo 	if (!obj) {
1693ff13be83SJeffrey Hugo 		ret = -ENOENT;
1694ff13be83SJeffrey Hugo 		goto unlock_ch_srcu;
1695ff13be83SJeffrey Hugo 	}
1696ff13be83SJeffrey Hugo 
1697ff13be83SJeffrey Hugo 	bo = to_qaic_bo(obj);
1698ff13be83SJeffrey Hugo 	timeout = args->timeout ? args->timeout : wait_exec_default_timeout_ms;
1699ff13be83SJeffrey Hugo 	timeout = msecs_to_jiffies(timeout);
1700ff13be83SJeffrey Hugo 	ret = wait_for_completion_interruptible_timeout(&bo->xfer_done, timeout);
1701ff13be83SJeffrey Hugo 	if (!ret) {
1702ff13be83SJeffrey Hugo 		ret = -ETIMEDOUT;
1703ff13be83SJeffrey Hugo 		goto put_obj;
1704ff13be83SJeffrey Hugo 	}
1705ff13be83SJeffrey Hugo 	if (ret > 0)
1706ff13be83SJeffrey Hugo 		ret = 0;
1707ff13be83SJeffrey Hugo 
1708ff13be83SJeffrey Hugo 	if (!dbc->usr)
1709ff13be83SJeffrey Hugo 		ret = -EPERM;
1710ff13be83SJeffrey Hugo 
1711ff13be83SJeffrey Hugo put_obj:
1712ff13be83SJeffrey Hugo 	drm_gem_object_put(obj);
1713ff13be83SJeffrey Hugo unlock_ch_srcu:
1714ff13be83SJeffrey Hugo 	srcu_read_unlock(&dbc->ch_lock, rcu_id);
1715ff13be83SJeffrey Hugo unlock_dev_srcu:
1716ff13be83SJeffrey Hugo 	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
1717ff13be83SJeffrey Hugo unlock_usr_srcu:
1718ff13be83SJeffrey Hugo 	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
1719ff13be83SJeffrey Hugo 	return ret;
1720ff13be83SJeffrey Hugo }
1721ff13be83SJeffrey Hugo 
qaic_perf_stats_bo_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)1722ff13be83SJeffrey Hugo int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
1723ff13be83SJeffrey Hugo {
1724ff13be83SJeffrey Hugo 	struct qaic_perf_stats_entry *ent = NULL;
1725ff13be83SJeffrey Hugo 	struct qaic_perf_stats *args = data;
1726ff13be83SJeffrey Hugo 	int usr_rcu_id, qdev_rcu_id;
1727ff13be83SJeffrey Hugo 	struct drm_gem_object *obj;
1728ff13be83SJeffrey Hugo 	struct qaic_device *qdev;
1729ff13be83SJeffrey Hugo 	struct qaic_user *usr;
1730ff13be83SJeffrey Hugo 	struct qaic_bo *bo;
1731ff13be83SJeffrey Hugo 	int ret, i;
1732ff13be83SJeffrey Hugo 
1733ff13be83SJeffrey Hugo 	usr = file_priv->driver_priv;
1734ff13be83SJeffrey Hugo 	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
1735ff13be83SJeffrey Hugo 	if (!usr->qddev) {
1736ff13be83SJeffrey Hugo 		ret = -ENODEV;
1737ff13be83SJeffrey Hugo 		goto unlock_usr_srcu;
1738ff13be83SJeffrey Hugo 	}
1739ff13be83SJeffrey Hugo 
1740ff13be83SJeffrey Hugo 	qdev = usr->qddev->qdev;
1741ff13be83SJeffrey Hugo 	qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
1742ff13be83SJeffrey Hugo 	if (qdev->in_reset) {
1743ff13be83SJeffrey Hugo 		ret = -ENODEV;
1744ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
1745ff13be83SJeffrey Hugo 	}
1746ff13be83SJeffrey Hugo 
1747ff13be83SJeffrey Hugo 	if (args->hdr.dbc_id >= qdev->num_dbc) {
1748ff13be83SJeffrey Hugo 		ret = -EINVAL;
1749ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
1750ff13be83SJeffrey Hugo 	}
1751ff13be83SJeffrey Hugo 
1752ff13be83SJeffrey Hugo 	ent = kcalloc(args->hdr.count, sizeof(*ent), GFP_KERNEL);
1753ff13be83SJeffrey Hugo 	if (!ent) {
1754ff13be83SJeffrey Hugo 		ret = -EINVAL;
1755ff13be83SJeffrey Hugo 		goto unlock_dev_srcu;
1756ff13be83SJeffrey Hugo 	}
1757ff13be83SJeffrey Hugo 
1758ff13be83SJeffrey Hugo 	ret = copy_from_user(ent, u64_to_user_ptr(args->data), args->hdr.count * sizeof(*ent));
1759ff13be83SJeffrey Hugo 	if (ret) {
1760ff13be83SJeffrey Hugo 		ret = -EFAULT;
1761ff13be83SJeffrey Hugo 		goto free_ent;
1762ff13be83SJeffrey Hugo 	}
1763ff13be83SJeffrey Hugo 
1764ff13be83SJeffrey Hugo 	for (i = 0; i < args->hdr.count; i++) {
1765ff13be83SJeffrey Hugo 		obj = drm_gem_object_lookup(file_priv, ent[i].handle);
1766ff13be83SJeffrey Hugo 		if (!obj) {
1767ff13be83SJeffrey Hugo 			ret = -ENOENT;
1768ff13be83SJeffrey Hugo 			goto free_ent;
1769ff13be83SJeffrey Hugo 		}
1770ff13be83SJeffrey Hugo 		bo = to_qaic_bo(obj);
1771ff13be83SJeffrey Hugo 		/*
1772ff13be83SJeffrey Hugo 		 * perf stats ioctl is called before wait ioctl is complete then
1773ff13be83SJeffrey Hugo 		 * the latency information is invalid.
1774ff13be83SJeffrey Hugo 		 */
1775ff13be83SJeffrey Hugo 		if (bo->perf_stats.req_processed_ts < bo->perf_stats.req_submit_ts) {
1776ff13be83SJeffrey Hugo 			ent[i].device_latency_us = 0;
1777ff13be83SJeffrey Hugo 		} else {
1778ff13be83SJeffrey Hugo 			ent[i].device_latency_us = div_u64((bo->perf_stats.req_processed_ts -
1779ff13be83SJeffrey Hugo 							    bo->perf_stats.req_submit_ts), 1000);
1780ff13be83SJeffrey Hugo 		}
1781ff13be83SJeffrey Hugo 		ent[i].submit_latency_us = div_u64((bo->perf_stats.req_submit_ts -
1782ff13be83SJeffrey Hugo 						    bo->perf_stats.req_received_ts), 1000);
1783ff13be83SJeffrey Hugo 		ent[i].queue_level_before = bo->perf_stats.queue_level_before;
1784ff13be83SJeffrey Hugo 		ent[i].num_queue_element = bo->total_slice_nents;
1785ff13be83SJeffrey Hugo 		drm_gem_object_put(obj);
1786ff13be83SJeffrey Hugo 	}
1787ff13be83SJeffrey Hugo 
1788ff13be83SJeffrey Hugo 	if (copy_to_user(u64_to_user_ptr(args->data), ent, args->hdr.count * sizeof(*ent)))
1789ff13be83SJeffrey Hugo 		ret = -EFAULT;
1790ff13be83SJeffrey Hugo 
1791ff13be83SJeffrey Hugo free_ent:
1792ff13be83SJeffrey Hugo 	kfree(ent);
1793ff13be83SJeffrey Hugo unlock_dev_srcu:
1794ff13be83SJeffrey Hugo 	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
1795ff13be83SJeffrey Hugo unlock_usr_srcu:
1796ff13be83SJeffrey Hugo 	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
1797ff13be83SJeffrey Hugo 	return ret;
1798ff13be83SJeffrey Hugo }
1799ff13be83SJeffrey Hugo 
empty_xfer_list(struct qaic_device * qdev,struct dma_bridge_chan * dbc)1800ff13be83SJeffrey Hugo static void empty_xfer_list(struct qaic_device *qdev, struct dma_bridge_chan *dbc)
1801ff13be83SJeffrey Hugo {
1802ff13be83SJeffrey Hugo 	unsigned long flags;
1803ff13be83SJeffrey Hugo 	struct qaic_bo *bo;
1804ff13be83SJeffrey Hugo 
1805ff13be83SJeffrey Hugo 	spin_lock_irqsave(&dbc->xfer_lock, flags);
1806ff13be83SJeffrey Hugo 	while (!list_empty(&dbc->xfer_list)) {
1807ff13be83SJeffrey Hugo 		bo = list_first_entry(&dbc->xfer_list, typeof(*bo), xfer_list);
1808ff13be83SJeffrey Hugo 		bo->queued = false;
1809ff13be83SJeffrey Hugo 		list_del(&bo->xfer_list);
1810ff13be83SJeffrey Hugo 		spin_unlock_irqrestore(&dbc->xfer_lock, flags);
1811ff13be83SJeffrey Hugo 		dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir);
1812ff13be83SJeffrey Hugo 		complete_all(&bo->xfer_done);
1813ff13be83SJeffrey Hugo 		drm_gem_object_put(&bo->base);
1814ff13be83SJeffrey Hugo 		spin_lock_irqsave(&dbc->xfer_lock, flags);
1815ff13be83SJeffrey Hugo 	}
1816ff13be83SJeffrey Hugo 	spin_unlock_irqrestore(&dbc->xfer_lock, flags);
1817ff13be83SJeffrey Hugo }
1818ff13be83SJeffrey Hugo 
disable_dbc(struct qaic_device * qdev,u32 dbc_id,struct qaic_user * usr)1819ff13be83SJeffrey Hugo int disable_dbc(struct qaic_device *qdev, u32 dbc_id, struct qaic_user *usr)
1820ff13be83SJeffrey Hugo {
1821ff13be83SJeffrey Hugo 	if (!qdev->dbc[dbc_id].usr || qdev->dbc[dbc_id].usr->handle != usr->handle)
1822ff13be83SJeffrey Hugo 		return -EPERM;
1823ff13be83SJeffrey Hugo 
1824ff13be83SJeffrey Hugo 	qdev->dbc[dbc_id].usr = NULL;
1825ff13be83SJeffrey Hugo 	synchronize_srcu(&qdev->dbc[dbc_id].ch_lock);
1826ff13be83SJeffrey Hugo 	return 0;
1827ff13be83SJeffrey Hugo }
1828ff13be83SJeffrey Hugo 
1829ff13be83SJeffrey Hugo /**
1830ff13be83SJeffrey Hugo  * enable_dbc - Enable the DBC. DBCs are disabled by removing the context of
1831ff13be83SJeffrey Hugo  * user. Add user context back to DBC to enable it. This function trusts the
1832ff13be83SJeffrey Hugo  * DBC ID passed and expects the DBC to be disabled.
1833ff13be83SJeffrey Hugo  * @qdev: Qranium device handle
1834ff13be83SJeffrey Hugo  * @dbc_id: ID of the DBC
1835ff13be83SJeffrey Hugo  * @usr: User context
1836ff13be83SJeffrey Hugo  */
enable_dbc(struct qaic_device * qdev,u32 dbc_id,struct qaic_user * usr)1837ff13be83SJeffrey Hugo void enable_dbc(struct qaic_device *qdev, u32 dbc_id, struct qaic_user *usr)
1838ff13be83SJeffrey Hugo {
1839ff13be83SJeffrey Hugo 	qdev->dbc[dbc_id].usr = usr;
1840ff13be83SJeffrey Hugo }
1841ff13be83SJeffrey Hugo 
wakeup_dbc(struct qaic_device * qdev,u32 dbc_id)1842ff13be83SJeffrey Hugo void wakeup_dbc(struct qaic_device *qdev, u32 dbc_id)
1843ff13be83SJeffrey Hugo {
1844ff13be83SJeffrey Hugo 	struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id];
1845ff13be83SJeffrey Hugo 
1846ff13be83SJeffrey Hugo 	dbc->usr = NULL;
1847ff13be83SJeffrey Hugo 	empty_xfer_list(qdev, dbc);
1848ff13be83SJeffrey Hugo 	synchronize_srcu(&dbc->ch_lock);
1849faa7c4eeSPranjal Ramajor Asha Kanojiya 	/*
1850faa7c4eeSPranjal Ramajor Asha Kanojiya 	 * Threads holding channel lock, may add more elements in the xfer_list.
1851faa7c4eeSPranjal Ramajor Asha Kanojiya 	 * Flush out these elements from xfer_list.
1852faa7c4eeSPranjal Ramajor Asha Kanojiya 	 */
1853faa7c4eeSPranjal Ramajor Asha Kanojiya 	empty_xfer_list(qdev, dbc);
1854ff13be83SJeffrey Hugo }
1855ff13be83SJeffrey Hugo 
release_dbc(struct qaic_device * qdev,u32 dbc_id)1856ff13be83SJeffrey Hugo void release_dbc(struct qaic_device *qdev, u32 dbc_id)
1857ff13be83SJeffrey Hugo {
1858ff13be83SJeffrey Hugo 	struct bo_slice *slice, *slice_temp;
1859ff13be83SJeffrey Hugo 	struct qaic_bo *bo, *bo_temp;
1860ff13be83SJeffrey Hugo 	struct dma_bridge_chan *dbc;
1861ff13be83SJeffrey Hugo 
1862ff13be83SJeffrey Hugo 	dbc = &qdev->dbc[dbc_id];
1863ff13be83SJeffrey Hugo 	if (!dbc->in_use)
1864ff13be83SJeffrey Hugo 		return;
1865ff13be83SJeffrey Hugo 
1866ff13be83SJeffrey Hugo 	wakeup_dbc(qdev, dbc_id);
1867ff13be83SJeffrey Hugo 
1868ff13be83SJeffrey Hugo 	dma_free_coherent(&qdev->pdev->dev, dbc->total_size, dbc->req_q_base, dbc->dma_addr);
1869ff13be83SJeffrey Hugo 	dbc->total_size = 0;
1870ff13be83SJeffrey Hugo 	dbc->req_q_base = NULL;
1871ff13be83SJeffrey Hugo 	dbc->dma_addr = 0;
1872ff13be83SJeffrey Hugo 	dbc->nelem = 0;
1873ff13be83SJeffrey Hugo 	dbc->usr = NULL;
1874ff13be83SJeffrey Hugo 
1875ff13be83SJeffrey Hugo 	list_for_each_entry_safe(bo, bo_temp, &dbc->bo_lists, bo_list) {
1876ff13be83SJeffrey Hugo 		list_for_each_entry_safe(slice, slice_temp, &bo->slices, slice)
1877ff13be83SJeffrey Hugo 			kref_put(&slice->ref_count, free_slice);
1878ff13be83SJeffrey Hugo 		bo->sliced = false;
1879ff13be83SJeffrey Hugo 		INIT_LIST_HEAD(&bo->slices);
1880ff13be83SJeffrey Hugo 		bo->total_slice_nents = 0;
1881ff13be83SJeffrey Hugo 		bo->dir = 0;
1882ff13be83SJeffrey Hugo 		bo->dbc = NULL;
1883ff13be83SJeffrey Hugo 		bo->nr_slice = 0;
1884ff13be83SJeffrey Hugo 		bo->nr_slice_xfer_done = 0;
1885ff13be83SJeffrey Hugo 		bo->queued = false;
1886ff13be83SJeffrey Hugo 		bo->req_id = 0;
1887ff13be83SJeffrey Hugo 		init_completion(&bo->xfer_done);
1888ff13be83SJeffrey Hugo 		complete_all(&bo->xfer_done);
1889ff13be83SJeffrey Hugo 		list_del(&bo->bo_list);
1890ff13be83SJeffrey Hugo 		bo->perf_stats.req_received_ts = 0;
1891ff13be83SJeffrey Hugo 		bo->perf_stats.req_submit_ts = 0;
1892ff13be83SJeffrey Hugo 		bo->perf_stats.req_processed_ts = 0;
1893ff13be83SJeffrey Hugo 		bo->perf_stats.queue_level_before = 0;
1894ff13be83SJeffrey Hugo 	}
1895ff13be83SJeffrey Hugo 
1896ff13be83SJeffrey Hugo 	dbc->in_use = false;
1897ff13be83SJeffrey Hugo 	wake_up(&dbc->dbc_release);
1898ff13be83SJeffrey Hugo }
1899