162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
462306a36Sopenharmony_ci/* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/bitfield.h>
762306a36Sopenharmony_ci#include <linux/bits.h>
862306a36Sopenharmony_ci#include <linux/completion.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/dma-buf.h>
1162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/kref.h>
1462306a36Sopenharmony_ci#include <linux/list.h>
1562306a36Sopenharmony_ci#include <linux/math64.h>
1662306a36Sopenharmony_ci#include <linux/mm.h>
1762306a36Sopenharmony_ci#include <linux/moduleparam.h>
1862306a36Sopenharmony_ci#include <linux/scatterlist.h>
1962306a36Sopenharmony_ci#include <linux/spinlock.h>
2062306a36Sopenharmony_ci#include <linux/srcu.h>
2162306a36Sopenharmony_ci#include <linux/types.h>
2262306a36Sopenharmony_ci#include <linux/uaccess.h>
2362306a36Sopenharmony_ci#include <linux/wait.h>
2462306a36Sopenharmony_ci#include <drm/drm_file.h>
2562306a36Sopenharmony_ci#include <drm/drm_gem.h>
2662306a36Sopenharmony_ci#include <drm/drm_prime.h>
2762306a36Sopenharmony_ci#include <drm/drm_print.h>
2862306a36Sopenharmony_ci#include <uapi/drm/qaic_accel.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "qaic.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define SEM_VAL_MASK	GENMASK_ULL(11, 0)
3362306a36Sopenharmony_ci#define SEM_INDEX_MASK	GENMASK_ULL(4, 0)
3462306a36Sopenharmony_ci#define BULK_XFER	BIT(3)
3562306a36Sopenharmony_ci#define GEN_COMPLETION	BIT(4)
3662306a36Sopenharmony_ci#define INBOUND_XFER	1
3762306a36Sopenharmony_ci#define OUTBOUND_XFER	2
3862306a36Sopenharmony_ci#define REQHP_OFF	0x0 /* we read this */
3962306a36Sopenharmony_ci#define REQTP_OFF	0x4 /* we write this */
4062306a36Sopenharmony_ci#define RSPHP_OFF	0x8 /* we write this */
4162306a36Sopenharmony_ci#define RSPTP_OFF	0xc /* we read this */
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define ENCODE_SEM(val, index, sync, cmd, flags)			\
4462306a36Sopenharmony_ci		({							\
4562306a36Sopenharmony_ci			FIELD_PREP(GENMASK(11, 0), (val)) |		\
4662306a36Sopenharmony_ci			FIELD_PREP(GENMASK(20, 16), (index)) |		\
4762306a36Sopenharmony_ci			FIELD_PREP(BIT(22), (sync)) |			\
4862306a36Sopenharmony_ci			FIELD_PREP(GENMASK(26, 24), (cmd)) |		\
4962306a36Sopenharmony_ci			FIELD_PREP(GENMASK(30, 29), (flags)) |		\
5062306a36Sopenharmony_ci			FIELD_PREP(BIT(31), (cmd) ? 1 : 0);		\
5162306a36Sopenharmony_ci		})
5262306a36Sopenharmony_ci#define NUM_EVENTS	128
5362306a36Sopenharmony_ci#define NUM_DELAYS	10
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic unsigned int wait_exec_default_timeout_ms = 5000; /* 5 sec default */
5662306a36Sopenharmony_cimodule_param(wait_exec_default_timeout_ms, uint, 0600);
5762306a36Sopenharmony_ciMODULE_PARM_DESC(wait_exec_default_timeout_ms, "Default timeout for DRM_IOCTL_QAIC_WAIT_BO");
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic unsigned int datapath_poll_interval_us = 100; /* 100 usec default */
6062306a36Sopenharmony_cimodule_param(datapath_poll_interval_us, uint, 0600);
6162306a36Sopenharmony_ciMODULE_PARM_DESC(datapath_poll_interval_us,
6262306a36Sopenharmony_ci		 "Amount of time to sleep between activity when datapath polling is enabled");
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistruct dbc_req {
6562306a36Sopenharmony_ci	/*
6662306a36Sopenharmony_ci	 * A request ID is assigned to each memory handle going in DMA queue.
6762306a36Sopenharmony_ci	 * As a single memory handle can enqueue multiple elements in DMA queue
6862306a36Sopenharmony_ci	 * all of them will have the same request ID.
6962306a36Sopenharmony_ci	 */
7062306a36Sopenharmony_ci	__le16	req_id;
7162306a36Sopenharmony_ci	/* Future use */
7262306a36Sopenharmony_ci	__u8	seq_id;
7362306a36Sopenharmony_ci	/*
7462306a36Sopenharmony_ci	 * Special encoded variable
7562306a36Sopenharmony_ci	 * 7	0 - Do not force to generate MSI after DMA is completed
7662306a36Sopenharmony_ci	 *	1 - Force to generate MSI after DMA is completed
7762306a36Sopenharmony_ci	 * 6:5	Reserved
7862306a36Sopenharmony_ci	 * 4	1 - Generate completion element in the response queue
7962306a36Sopenharmony_ci	 *	0 - No Completion Code
8062306a36Sopenharmony_ci	 * 3	0 - DMA request is a Link list transfer
8162306a36Sopenharmony_ci	 *	1 - DMA request is a Bulk transfer
8262306a36Sopenharmony_ci	 * 2	Reserved
8362306a36Sopenharmony_ci	 * 1:0	00 - No DMA transfer involved
8462306a36Sopenharmony_ci	 *	01 - DMA transfer is part of inbound transfer
8562306a36Sopenharmony_ci	 *	10 - DMA transfer has outbound transfer
8662306a36Sopenharmony_ci	 *	11 - NA
8762306a36Sopenharmony_ci	 */
8862306a36Sopenharmony_ci	__u8	cmd;
8962306a36Sopenharmony_ci	__le32	resv;
9062306a36Sopenharmony_ci	/* Source address for the transfer */
9162306a36Sopenharmony_ci	__le64	src_addr;
9262306a36Sopenharmony_ci	/* Destination address for the transfer */
9362306a36Sopenharmony_ci	__le64	dest_addr;
9462306a36Sopenharmony_ci	/* Length of transfer request */
9562306a36Sopenharmony_ci	__le32	len;
9662306a36Sopenharmony_ci	__le32	resv2;
9762306a36Sopenharmony_ci	/* Doorbell address */
9862306a36Sopenharmony_ci	__le64	db_addr;
9962306a36Sopenharmony_ci	/*
10062306a36Sopenharmony_ci	 * Special encoded variable
10162306a36Sopenharmony_ci	 * 7	1 - Doorbell(db) write
10262306a36Sopenharmony_ci	 *	0 - No doorbell write
10362306a36Sopenharmony_ci	 * 6:2	Reserved
10462306a36Sopenharmony_ci	 * 1:0	00 - 32 bit access, db address must be aligned to 32bit-boundary
10562306a36Sopenharmony_ci	 *	01 - 16 bit access, db address must be aligned to 16bit-boundary
10662306a36Sopenharmony_ci	 *	10 - 8 bit access, db address must be aligned to 8bit-boundary
10762306a36Sopenharmony_ci	 *	11 - Reserved
10862306a36Sopenharmony_ci	 */
10962306a36Sopenharmony_ci	__u8	db_len;
11062306a36Sopenharmony_ci	__u8	resv3;
11162306a36Sopenharmony_ci	__le16	resv4;
11262306a36Sopenharmony_ci	/* 32 bit data written to doorbell address */
11362306a36Sopenharmony_ci	__le32	db_data;
11462306a36Sopenharmony_ci	/*
11562306a36Sopenharmony_ci	 * Special encoded variable
11662306a36Sopenharmony_ci	 * All the fields of sem_cmdX are passed from user and all are ORed
11762306a36Sopenharmony_ci	 * together to form sem_cmd.
11862306a36Sopenharmony_ci	 * 0:11		Semaphore value
11962306a36Sopenharmony_ci	 * 15:12	Reserved
12062306a36Sopenharmony_ci	 * 20:16	Semaphore index
12162306a36Sopenharmony_ci	 * 21		Reserved
12262306a36Sopenharmony_ci	 * 22		Semaphore Sync
12362306a36Sopenharmony_ci	 * 23		Reserved
12462306a36Sopenharmony_ci	 * 26:24	Semaphore command
12562306a36Sopenharmony_ci	 * 28:27	Reserved
12662306a36Sopenharmony_ci	 * 29		Semaphore DMA out bound sync fence
12762306a36Sopenharmony_ci	 * 30		Semaphore DMA in bound sync fence
12862306a36Sopenharmony_ci	 * 31		Enable semaphore command
12962306a36Sopenharmony_ci	 */
13062306a36Sopenharmony_ci	__le32	sem_cmd0;
13162306a36Sopenharmony_ci	__le32	sem_cmd1;
13262306a36Sopenharmony_ci	__le32	sem_cmd2;
13362306a36Sopenharmony_ci	__le32	sem_cmd3;
13462306a36Sopenharmony_ci} __packed;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistruct dbc_rsp {
13762306a36Sopenharmony_ci	/* Request ID of the memory handle whose DMA transaction is completed */
13862306a36Sopenharmony_ci	__le16	req_id;
13962306a36Sopenharmony_ci	/* Status of the DMA transaction. 0 : Success otherwise failure */
14062306a36Sopenharmony_ci	__le16	status;
14162306a36Sopenharmony_ci} __packed;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ciinline int get_dbc_req_elem_size(void)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	return sizeof(struct dbc_req);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ciinline int get_dbc_rsp_elem_size(void)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	return sizeof(struct dbc_rsp);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic void free_slice(struct kref *kref)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct bo_slice *slice = container_of(kref, struct bo_slice, ref_count);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	list_del(&slice->slice);
15862306a36Sopenharmony_ci	drm_gem_object_put(&slice->bo->base);
15962306a36Sopenharmony_ci	sg_free_table(slice->sgt);
16062306a36Sopenharmony_ci	kfree(slice->sgt);
16162306a36Sopenharmony_ci	kfree(slice->reqs);
16262306a36Sopenharmony_ci	kfree(slice);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic int clone_range_of_sgt_for_slice(struct qaic_device *qdev, struct sg_table **sgt_out,
16662306a36Sopenharmony_ci					struct sg_table *sgt_in, u64 size, u64 offset)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	int total_len, len, nents, offf = 0, offl = 0;
16962306a36Sopenharmony_ci	struct scatterlist *sg, *sgn, *sgf, *sgl;
17062306a36Sopenharmony_ci	struct sg_table *sgt;
17162306a36Sopenharmony_ci	int ret, j;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* find out number of relevant nents needed for this mem */
17462306a36Sopenharmony_ci	total_len = 0;
17562306a36Sopenharmony_ci	sgf = NULL;
17662306a36Sopenharmony_ci	sgl = NULL;
17762306a36Sopenharmony_ci	nents = 0;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	size = size ? size : PAGE_SIZE;
18062306a36Sopenharmony_ci	for (sg = sgt_in->sgl; sg; sg = sg_next(sg)) {
18162306a36Sopenharmony_ci		len = sg_dma_len(sg);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		if (!len)
18462306a36Sopenharmony_ci			continue;
18562306a36Sopenharmony_ci		if (offset >= total_len && offset < total_len + len) {
18662306a36Sopenharmony_ci			sgf = sg;
18762306a36Sopenharmony_ci			offf = offset - total_len;
18862306a36Sopenharmony_ci		}
18962306a36Sopenharmony_ci		if (sgf)
19062306a36Sopenharmony_ci			nents++;
19162306a36Sopenharmony_ci		if (offset + size >= total_len &&
19262306a36Sopenharmony_ci		    offset + size <= total_len + len) {
19362306a36Sopenharmony_ci			sgl = sg;
19462306a36Sopenharmony_ci			offl = offset + size - total_len;
19562306a36Sopenharmony_ci			break;
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci		total_len += len;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (!sgf || !sgl) {
20162306a36Sopenharmony_ci		ret = -EINVAL;
20262306a36Sopenharmony_ci		goto out;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
20662306a36Sopenharmony_ci	if (!sgt) {
20762306a36Sopenharmony_ci		ret = -ENOMEM;
20862306a36Sopenharmony_ci		goto out;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ret = sg_alloc_table(sgt, nents, GFP_KERNEL);
21262306a36Sopenharmony_ci	if (ret)
21362306a36Sopenharmony_ci		goto free_sgt;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* copy relevant sg node and fix page and length */
21662306a36Sopenharmony_ci	sgn = sgf;
21762306a36Sopenharmony_ci	for_each_sgtable_sg(sgt, sg, j) {
21862306a36Sopenharmony_ci		memcpy(sg, sgn, sizeof(*sg));
21962306a36Sopenharmony_ci		if (sgn == sgf) {
22062306a36Sopenharmony_ci			sg_dma_address(sg) += offf;
22162306a36Sopenharmony_ci			sg_dma_len(sg) -= offf;
22262306a36Sopenharmony_ci			sg_set_page(sg, sg_page(sgn), sg_dma_len(sg), offf);
22362306a36Sopenharmony_ci		} else {
22462306a36Sopenharmony_ci			offf = 0;
22562306a36Sopenharmony_ci		}
22662306a36Sopenharmony_ci		if (sgn == sgl) {
22762306a36Sopenharmony_ci			sg_dma_len(sg) = offl - offf;
22862306a36Sopenharmony_ci			sg_set_page(sg, sg_page(sgn), offl - offf, offf);
22962306a36Sopenharmony_ci			sg_mark_end(sg);
23062306a36Sopenharmony_ci			break;
23162306a36Sopenharmony_ci		}
23262306a36Sopenharmony_ci		sgn = sg_next(sgn);
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	*sgt_out = sgt;
23662306a36Sopenharmony_ci	return ret;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cifree_sgt:
23962306a36Sopenharmony_ci	kfree(sgt);
24062306a36Sopenharmony_ciout:
24162306a36Sopenharmony_ci	*sgt_out = NULL;
24262306a36Sopenharmony_ci	return ret;
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic int encode_reqs(struct qaic_device *qdev, struct bo_slice *slice,
24662306a36Sopenharmony_ci		       struct qaic_attach_slice_entry *req)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	__le64 db_addr = cpu_to_le64(req->db_addr);
24962306a36Sopenharmony_ci	__le32 db_data = cpu_to_le32(req->db_data);
25062306a36Sopenharmony_ci	struct scatterlist *sg;
25162306a36Sopenharmony_ci	__u8 cmd = BULK_XFER;
25262306a36Sopenharmony_ci	int presync_sem;
25362306a36Sopenharmony_ci	u64 dev_addr;
25462306a36Sopenharmony_ci	__u8 db_len;
25562306a36Sopenharmony_ci	int i;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (!slice->no_xfer)
25862306a36Sopenharmony_ci		cmd |= (slice->dir == DMA_TO_DEVICE ? INBOUND_XFER : OUTBOUND_XFER);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (req->db_len && !IS_ALIGNED(req->db_addr, req->db_len / 8))
26162306a36Sopenharmony_ci		return -EINVAL;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	presync_sem = req->sem0.presync + req->sem1.presync + req->sem2.presync + req->sem3.presync;
26462306a36Sopenharmony_ci	if (presync_sem > 1)
26562306a36Sopenharmony_ci		return -EINVAL;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	presync_sem = req->sem0.presync << 0 | req->sem1.presync << 1 |
26862306a36Sopenharmony_ci		      req->sem2.presync << 2 | req->sem3.presync << 3;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	switch (req->db_len) {
27162306a36Sopenharmony_ci	case 32:
27262306a36Sopenharmony_ci		db_len = BIT(7);
27362306a36Sopenharmony_ci		break;
27462306a36Sopenharmony_ci	case 16:
27562306a36Sopenharmony_ci		db_len = BIT(7) | 1;
27662306a36Sopenharmony_ci		break;
27762306a36Sopenharmony_ci	case 8:
27862306a36Sopenharmony_ci		db_len = BIT(7) | 2;
27962306a36Sopenharmony_ci		break;
28062306a36Sopenharmony_ci	case 0:
28162306a36Sopenharmony_ci		db_len = 0; /* doorbell is not active for this command */
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci	default:
28462306a36Sopenharmony_ci		return -EINVAL; /* should never hit this */
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/*
28862306a36Sopenharmony_ci	 * When we end up splitting up a single request (ie a buf slice) into
28962306a36Sopenharmony_ci	 * multiple DMA requests, we have to manage the sync data carefully.
29062306a36Sopenharmony_ci	 * There can only be one presync sem. That needs to be on every xfer
29162306a36Sopenharmony_ci	 * so that the DMA engine doesn't transfer data before the receiver is
29262306a36Sopenharmony_ci	 * ready. We only do the doorbell and postsync sems after the xfer.
29362306a36Sopenharmony_ci	 * To guarantee previous xfers for the request are complete, we use a
29462306a36Sopenharmony_ci	 * fence.
29562306a36Sopenharmony_ci	 */
29662306a36Sopenharmony_ci	dev_addr = req->dev_addr;
29762306a36Sopenharmony_ci	for_each_sgtable_sg(slice->sgt, sg, i) {
29862306a36Sopenharmony_ci		slice->reqs[i].cmd = cmd;
29962306a36Sopenharmony_ci		slice->reqs[i].src_addr = cpu_to_le64(slice->dir == DMA_TO_DEVICE ?
30062306a36Sopenharmony_ci						      sg_dma_address(sg) : dev_addr);
30162306a36Sopenharmony_ci		slice->reqs[i].dest_addr = cpu_to_le64(slice->dir == DMA_TO_DEVICE ?
30262306a36Sopenharmony_ci						       dev_addr : sg_dma_address(sg));
30362306a36Sopenharmony_ci		/*
30462306a36Sopenharmony_ci		 * sg_dma_len(sg) returns size of a DMA segment, maximum DMA
30562306a36Sopenharmony_ci		 * segment size is set to UINT_MAX by qaic and hence return
30662306a36Sopenharmony_ci		 * values of sg_dma_len(sg) can never exceed u32 range. So,
30762306a36Sopenharmony_ci		 * by down sizing we are not corrupting the value.
30862306a36Sopenharmony_ci		 */
30962306a36Sopenharmony_ci		slice->reqs[i].len = cpu_to_le32((u32)sg_dma_len(sg));
31062306a36Sopenharmony_ci		switch (presync_sem) {
31162306a36Sopenharmony_ci		case BIT(0):
31262306a36Sopenharmony_ci			slice->reqs[i].sem_cmd0 = cpu_to_le32(ENCODE_SEM(req->sem0.val,
31362306a36Sopenharmony_ci									 req->sem0.index,
31462306a36Sopenharmony_ci									 req->sem0.presync,
31562306a36Sopenharmony_ci									 req->sem0.cmd,
31662306a36Sopenharmony_ci									 req->sem0.flags));
31762306a36Sopenharmony_ci			break;
31862306a36Sopenharmony_ci		case BIT(1):
31962306a36Sopenharmony_ci			slice->reqs[i].sem_cmd1 = cpu_to_le32(ENCODE_SEM(req->sem1.val,
32062306a36Sopenharmony_ci									 req->sem1.index,
32162306a36Sopenharmony_ci									 req->sem1.presync,
32262306a36Sopenharmony_ci									 req->sem1.cmd,
32362306a36Sopenharmony_ci									 req->sem1.flags));
32462306a36Sopenharmony_ci			break;
32562306a36Sopenharmony_ci		case BIT(2):
32662306a36Sopenharmony_ci			slice->reqs[i].sem_cmd2 = cpu_to_le32(ENCODE_SEM(req->sem2.val,
32762306a36Sopenharmony_ci									 req->sem2.index,
32862306a36Sopenharmony_ci									 req->sem2.presync,
32962306a36Sopenharmony_ci									 req->sem2.cmd,
33062306a36Sopenharmony_ci									 req->sem2.flags));
33162306a36Sopenharmony_ci			break;
33262306a36Sopenharmony_ci		case BIT(3):
33362306a36Sopenharmony_ci			slice->reqs[i].sem_cmd3 = cpu_to_le32(ENCODE_SEM(req->sem3.val,
33462306a36Sopenharmony_ci									 req->sem3.index,
33562306a36Sopenharmony_ci									 req->sem3.presync,
33662306a36Sopenharmony_ci									 req->sem3.cmd,
33762306a36Sopenharmony_ci									 req->sem3.flags));
33862306a36Sopenharmony_ci			break;
33962306a36Sopenharmony_ci		}
34062306a36Sopenharmony_ci		dev_addr += sg_dma_len(sg);
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci	/* add post transfer stuff to last segment */
34362306a36Sopenharmony_ci	i--;
34462306a36Sopenharmony_ci	slice->reqs[i].cmd |= GEN_COMPLETION;
34562306a36Sopenharmony_ci	slice->reqs[i].db_addr = db_addr;
34662306a36Sopenharmony_ci	slice->reqs[i].db_len = db_len;
34762306a36Sopenharmony_ci	slice->reqs[i].db_data = db_data;
34862306a36Sopenharmony_ci	/*
34962306a36Sopenharmony_ci	 * Add a fence if we have more than one request going to the hardware
35062306a36Sopenharmony_ci	 * representing the entirety of the user request, and the user request
35162306a36Sopenharmony_ci	 * has no presync condition.
35262306a36Sopenharmony_ci	 * Fences are expensive, so we try to avoid them. We rely on the
35362306a36Sopenharmony_ci	 * hardware behavior to avoid needing one when there is a presync
35462306a36Sopenharmony_ci	 * condition. When a presync exists, all requests for that same
35562306a36Sopenharmony_ci	 * presync will be queued into a fifo. Thus, since we queue the
35662306a36Sopenharmony_ci	 * post xfer activity only on the last request we queue, the hardware
35762306a36Sopenharmony_ci	 * will ensure that the last queued request is processed last, thus
35862306a36Sopenharmony_ci	 * making sure the post xfer activity happens at the right time without
35962306a36Sopenharmony_ci	 * a fence.
36062306a36Sopenharmony_ci	 */
36162306a36Sopenharmony_ci	if (i && !presync_sem)
36262306a36Sopenharmony_ci		req->sem0.flags |= (slice->dir == DMA_TO_DEVICE ?
36362306a36Sopenharmony_ci				    QAIC_SEM_INSYNCFENCE : QAIC_SEM_OUTSYNCFENCE);
36462306a36Sopenharmony_ci	slice->reqs[i].sem_cmd0 = cpu_to_le32(ENCODE_SEM(req->sem0.val, req->sem0.index,
36562306a36Sopenharmony_ci							 req->sem0.presync, req->sem0.cmd,
36662306a36Sopenharmony_ci							 req->sem0.flags));
36762306a36Sopenharmony_ci	slice->reqs[i].sem_cmd1 = cpu_to_le32(ENCODE_SEM(req->sem1.val, req->sem1.index,
36862306a36Sopenharmony_ci							 req->sem1.presync, req->sem1.cmd,
36962306a36Sopenharmony_ci							 req->sem1.flags));
37062306a36Sopenharmony_ci	slice->reqs[i].sem_cmd2 = cpu_to_le32(ENCODE_SEM(req->sem2.val, req->sem2.index,
37162306a36Sopenharmony_ci							 req->sem2.presync, req->sem2.cmd,
37262306a36Sopenharmony_ci							 req->sem2.flags));
37362306a36Sopenharmony_ci	slice->reqs[i].sem_cmd3 = cpu_to_le32(ENCODE_SEM(req->sem3.val, req->sem3.index,
37462306a36Sopenharmony_ci							 req->sem3.presync, req->sem3.cmd,
37562306a36Sopenharmony_ci							 req->sem3.flags));
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	return 0;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic int qaic_map_one_slice(struct qaic_device *qdev, struct qaic_bo *bo,
38162306a36Sopenharmony_ci			      struct qaic_attach_slice_entry *slice_ent)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct sg_table *sgt = NULL;
38462306a36Sopenharmony_ci	struct bo_slice *slice;
38562306a36Sopenharmony_ci	int ret;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	ret = clone_range_of_sgt_for_slice(qdev, &sgt, bo->sgt, slice_ent->size, slice_ent->offset);
38862306a36Sopenharmony_ci	if (ret)
38962306a36Sopenharmony_ci		goto out;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	slice = kmalloc(sizeof(*slice), GFP_KERNEL);
39262306a36Sopenharmony_ci	if (!slice) {
39362306a36Sopenharmony_ci		ret = -ENOMEM;
39462306a36Sopenharmony_ci		goto free_sgt;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	slice->reqs = kcalloc(sgt->nents, sizeof(*slice->reqs), GFP_KERNEL);
39862306a36Sopenharmony_ci	if (!slice->reqs) {
39962306a36Sopenharmony_ci		ret = -ENOMEM;
40062306a36Sopenharmony_ci		goto free_slice;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	slice->no_xfer = !slice_ent->size;
40462306a36Sopenharmony_ci	slice->sgt = sgt;
40562306a36Sopenharmony_ci	slice->nents = sgt->nents;
40662306a36Sopenharmony_ci	slice->dir = bo->dir;
40762306a36Sopenharmony_ci	slice->bo = bo;
40862306a36Sopenharmony_ci	slice->size = slice_ent->size;
40962306a36Sopenharmony_ci	slice->offset = slice_ent->offset;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	ret = encode_reqs(qdev, slice, slice_ent);
41262306a36Sopenharmony_ci	if (ret)
41362306a36Sopenharmony_ci		goto free_req;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	bo->total_slice_nents += sgt->nents;
41662306a36Sopenharmony_ci	kref_init(&slice->ref_count);
41762306a36Sopenharmony_ci	drm_gem_object_get(&bo->base);
41862306a36Sopenharmony_ci	list_add_tail(&slice->slice, &bo->slices);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return 0;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cifree_req:
42362306a36Sopenharmony_ci	kfree(slice->reqs);
42462306a36Sopenharmony_cifree_slice:
42562306a36Sopenharmony_ci	kfree(slice);
42662306a36Sopenharmony_cifree_sgt:
42762306a36Sopenharmony_ci	sg_free_table(sgt);
42862306a36Sopenharmony_ci	kfree(sgt);
42962306a36Sopenharmony_ciout:
43062306a36Sopenharmony_ci	return ret;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int create_sgt(struct qaic_device *qdev, struct sg_table **sgt_out, u64 size)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct scatterlist *sg;
43662306a36Sopenharmony_ci	struct sg_table *sgt;
43762306a36Sopenharmony_ci	struct page **pages;
43862306a36Sopenharmony_ci	int *pages_order;
43962306a36Sopenharmony_ci	int buf_extra;
44062306a36Sopenharmony_ci	int max_order;
44162306a36Sopenharmony_ci	int nr_pages;
44262306a36Sopenharmony_ci	int ret = 0;
44362306a36Sopenharmony_ci	int i, j, k;
44462306a36Sopenharmony_ci	int order;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (size) {
44762306a36Sopenharmony_ci		nr_pages = DIV_ROUND_UP(size, PAGE_SIZE);
44862306a36Sopenharmony_ci		/*
44962306a36Sopenharmony_ci		 * calculate how much extra we are going to allocate, to remove
45062306a36Sopenharmony_ci		 * later
45162306a36Sopenharmony_ci		 */
45262306a36Sopenharmony_ci		buf_extra = (PAGE_SIZE - size % PAGE_SIZE) % PAGE_SIZE;
45362306a36Sopenharmony_ci		max_order = min(MAX_ORDER - 1, get_order(size));
45462306a36Sopenharmony_ci	} else {
45562306a36Sopenharmony_ci		/* allocate a single page for book keeping */
45662306a36Sopenharmony_ci		nr_pages = 1;
45762306a36Sopenharmony_ci		buf_extra = 0;
45862306a36Sopenharmony_ci		max_order = 0;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	pages = kvmalloc_array(nr_pages, sizeof(*pages) + sizeof(*pages_order), GFP_KERNEL);
46262306a36Sopenharmony_ci	if (!pages) {
46362306a36Sopenharmony_ci		ret = -ENOMEM;
46462306a36Sopenharmony_ci		goto out;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci	pages_order = (void *)pages + sizeof(*pages) * nr_pages;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/*
46962306a36Sopenharmony_ci	 * Allocate requested memory using alloc_pages. It is possible to allocate
47062306a36Sopenharmony_ci	 * the requested memory in multiple chunks by calling alloc_pages
47162306a36Sopenharmony_ci	 * multiple times. Use SG table to handle multiple allocated pages.
47262306a36Sopenharmony_ci	 */
47362306a36Sopenharmony_ci	i = 0;
47462306a36Sopenharmony_ci	while (nr_pages > 0) {
47562306a36Sopenharmony_ci		order = min(get_order(nr_pages * PAGE_SIZE), max_order);
47662306a36Sopenharmony_ci		while (1) {
47762306a36Sopenharmony_ci			pages[i] = alloc_pages(GFP_KERNEL | GFP_HIGHUSER |
47862306a36Sopenharmony_ci					       __GFP_NOWARN | __GFP_ZERO |
47962306a36Sopenharmony_ci					       (order ? __GFP_NORETRY : __GFP_RETRY_MAYFAIL),
48062306a36Sopenharmony_ci					       order);
48162306a36Sopenharmony_ci			if (pages[i])
48262306a36Sopenharmony_ci				break;
48362306a36Sopenharmony_ci			if (!order--) {
48462306a36Sopenharmony_ci				ret = -ENOMEM;
48562306a36Sopenharmony_ci				goto free_partial_alloc;
48662306a36Sopenharmony_ci			}
48762306a36Sopenharmony_ci		}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		max_order = order;
49062306a36Sopenharmony_ci		pages_order[i] = order;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		nr_pages -= 1 << order;
49362306a36Sopenharmony_ci		if (nr_pages <= 0)
49462306a36Sopenharmony_ci			/* account for over allocation */
49562306a36Sopenharmony_ci			buf_extra += abs(nr_pages) * PAGE_SIZE;
49662306a36Sopenharmony_ci		i++;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
50062306a36Sopenharmony_ci	if (!sgt) {
50162306a36Sopenharmony_ci		ret = -ENOMEM;
50262306a36Sopenharmony_ci		goto free_partial_alloc;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	if (sg_alloc_table(sgt, i, GFP_KERNEL)) {
50662306a36Sopenharmony_ci		ret = -ENOMEM;
50762306a36Sopenharmony_ci		goto free_sgt;
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	/* Populate the SG table with the allocated memory pages */
51162306a36Sopenharmony_ci	sg = sgt->sgl;
51262306a36Sopenharmony_ci	for (k = 0; k < i; k++, sg = sg_next(sg)) {
51362306a36Sopenharmony_ci		/* Last entry requires special handling */
51462306a36Sopenharmony_ci		if (k < i - 1) {
51562306a36Sopenharmony_ci			sg_set_page(sg, pages[k], PAGE_SIZE << pages_order[k], 0);
51662306a36Sopenharmony_ci		} else {
51762306a36Sopenharmony_ci			sg_set_page(sg, pages[k], (PAGE_SIZE << pages_order[k]) - buf_extra, 0);
51862306a36Sopenharmony_ci			sg_mark_end(sg);
51962306a36Sopenharmony_ci		}
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	kvfree(pages);
52362306a36Sopenharmony_ci	*sgt_out = sgt;
52462306a36Sopenharmony_ci	return ret;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cifree_sgt:
52762306a36Sopenharmony_ci	kfree(sgt);
52862306a36Sopenharmony_cifree_partial_alloc:
52962306a36Sopenharmony_ci	for (j = 0; j < i; j++)
53062306a36Sopenharmony_ci		__free_pages(pages[j], pages_order[j]);
53162306a36Sopenharmony_ci	kvfree(pages);
53262306a36Sopenharmony_ciout:
53362306a36Sopenharmony_ci	*sgt_out = NULL;
53462306a36Sopenharmony_ci	return ret;
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic bool invalid_sem(struct qaic_sem *sem)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	if (sem->val & ~SEM_VAL_MASK || sem->index & ~SEM_INDEX_MASK ||
54062306a36Sopenharmony_ci	    !(sem->presync == 0 || sem->presync == 1) || sem->pad ||
54162306a36Sopenharmony_ci	    sem->flags & ~(QAIC_SEM_INSYNCFENCE | QAIC_SEM_OUTSYNCFENCE) ||
54262306a36Sopenharmony_ci	    sem->cmd > QAIC_SEM_WAIT_GT_0)
54362306a36Sopenharmony_ci		return true;
54462306a36Sopenharmony_ci	return false;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic int qaic_validate_req(struct qaic_device *qdev, struct qaic_attach_slice_entry *slice_ent,
54862306a36Sopenharmony_ci			     u32 count, u64 total_size)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	int i;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
55362306a36Sopenharmony_ci		if (!(slice_ent[i].db_len == 32 || slice_ent[i].db_len == 16 ||
55462306a36Sopenharmony_ci		      slice_ent[i].db_len == 8 || slice_ent[i].db_len == 0) ||
55562306a36Sopenharmony_ci		      invalid_sem(&slice_ent[i].sem0) || invalid_sem(&slice_ent[i].sem1) ||
55662306a36Sopenharmony_ci		      invalid_sem(&slice_ent[i].sem2) || invalid_sem(&slice_ent[i].sem3))
55762306a36Sopenharmony_ci			return -EINVAL;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		if (slice_ent[i].offset + slice_ent[i].size > total_size)
56062306a36Sopenharmony_ci			return -EINVAL;
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	return 0;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic void qaic_free_sgt(struct sg_table *sgt)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct scatterlist *sg;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	for (sg = sgt->sgl; sg; sg = sg_next(sg))
57162306a36Sopenharmony_ci		if (sg_page(sg))
57262306a36Sopenharmony_ci			__free_pages(sg_page(sg), get_order(sg->length));
57362306a36Sopenharmony_ci	sg_free_table(sgt);
57462306a36Sopenharmony_ci	kfree(sgt);
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic void qaic_gem_print_info(struct drm_printer *p, unsigned int indent,
57862306a36Sopenharmony_ci				const struct drm_gem_object *obj)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct qaic_bo *bo = to_qaic_bo(obj);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	drm_printf_indent(p, indent, "user requested size=%llu\n", bo->size);
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic const struct vm_operations_struct drm_vm_ops = {
58662306a36Sopenharmony_ci	.open = drm_gem_vm_open,
58762306a36Sopenharmony_ci	.close = drm_gem_vm_close,
58862306a36Sopenharmony_ci};
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	struct qaic_bo *bo = to_qaic_bo(obj);
59362306a36Sopenharmony_ci	unsigned long offset = 0;
59462306a36Sopenharmony_ci	struct scatterlist *sg;
59562306a36Sopenharmony_ci	int ret = 0;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (obj->import_attach)
59862306a36Sopenharmony_ci		return -EINVAL;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	for (sg = bo->sgt->sgl; sg; sg = sg_next(sg)) {
60162306a36Sopenharmony_ci		if (sg_page(sg)) {
60262306a36Sopenharmony_ci			ret = remap_pfn_range(vma, vma->vm_start + offset, page_to_pfn(sg_page(sg)),
60362306a36Sopenharmony_ci					      sg->length, vma->vm_page_prot);
60462306a36Sopenharmony_ci			if (ret)
60562306a36Sopenharmony_ci				goto out;
60662306a36Sopenharmony_ci			offset += sg->length;
60762306a36Sopenharmony_ci		}
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ciout:
61162306a36Sopenharmony_ci	return ret;
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic void qaic_free_object(struct drm_gem_object *obj)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	struct qaic_bo *bo = to_qaic_bo(obj);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	if (obj->import_attach) {
61962306a36Sopenharmony_ci		/* DMABUF/PRIME Path */
62062306a36Sopenharmony_ci		drm_prime_gem_destroy(obj, NULL);
62162306a36Sopenharmony_ci	} else {
62262306a36Sopenharmony_ci		/* Private buffer allocation path */
62362306a36Sopenharmony_ci		qaic_free_sgt(bo->sgt);
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	drm_gem_object_release(obj);
62762306a36Sopenharmony_ci	kfree(bo);
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic const struct drm_gem_object_funcs qaic_gem_funcs = {
63162306a36Sopenharmony_ci	.free = qaic_free_object,
63262306a36Sopenharmony_ci	.print_info = qaic_gem_print_info,
63362306a36Sopenharmony_ci	.mmap = qaic_gem_object_mmap,
63462306a36Sopenharmony_ci	.vm_ops = &drm_vm_ops,
63562306a36Sopenharmony_ci};
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic struct qaic_bo *qaic_alloc_init_bo(void)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	struct qaic_bo *bo;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	bo = kzalloc(sizeof(*bo), GFP_KERNEL);
64262306a36Sopenharmony_ci	if (!bo)
64362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	INIT_LIST_HEAD(&bo->slices);
64662306a36Sopenharmony_ci	init_completion(&bo->xfer_done);
64762306a36Sopenharmony_ci	complete_all(&bo->xfer_done);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	return bo;
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ciint qaic_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	struct qaic_create_bo *args = data;
65562306a36Sopenharmony_ci	int usr_rcu_id, qdev_rcu_id;
65662306a36Sopenharmony_ci	struct drm_gem_object *obj;
65762306a36Sopenharmony_ci	struct qaic_device *qdev;
65862306a36Sopenharmony_ci	struct qaic_user *usr;
65962306a36Sopenharmony_ci	struct qaic_bo *bo;
66062306a36Sopenharmony_ci	size_t size;
66162306a36Sopenharmony_ci	int ret;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (args->pad)
66462306a36Sopenharmony_ci		return -EINVAL;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	size = PAGE_ALIGN(args->size);
66762306a36Sopenharmony_ci	if (size == 0)
66862306a36Sopenharmony_ci		return -EINVAL;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	usr = file_priv->driver_priv;
67162306a36Sopenharmony_ci	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
67262306a36Sopenharmony_ci	if (!usr->qddev) {
67362306a36Sopenharmony_ci		ret = -ENODEV;
67462306a36Sopenharmony_ci		goto unlock_usr_srcu;
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	qdev = usr->qddev->qdev;
67862306a36Sopenharmony_ci	qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
67962306a36Sopenharmony_ci	if (qdev->in_reset) {
68062306a36Sopenharmony_ci		ret = -ENODEV;
68162306a36Sopenharmony_ci		goto unlock_dev_srcu;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	bo = qaic_alloc_init_bo();
68562306a36Sopenharmony_ci	if (IS_ERR(bo)) {
68662306a36Sopenharmony_ci		ret = PTR_ERR(bo);
68762306a36Sopenharmony_ci		goto unlock_dev_srcu;
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci	obj = &bo->base;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	drm_gem_private_object_init(dev, obj, size);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	obj->funcs = &qaic_gem_funcs;
69462306a36Sopenharmony_ci	ret = create_sgt(qdev, &bo->sgt, size);
69562306a36Sopenharmony_ci	if (ret)
69662306a36Sopenharmony_ci		goto free_bo;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	bo->size = args->size;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	ret = drm_gem_handle_create(file_priv, obj, &args->handle);
70162306a36Sopenharmony_ci	if (ret)
70262306a36Sopenharmony_ci		goto free_sgt;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	bo->handle = args->handle;
70562306a36Sopenharmony_ci	drm_gem_object_put(obj);
70662306a36Sopenharmony_ci	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
70762306a36Sopenharmony_ci	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	return 0;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cifree_sgt:
71262306a36Sopenharmony_ci	qaic_free_sgt(bo->sgt);
71362306a36Sopenharmony_cifree_bo:
71462306a36Sopenharmony_ci	kfree(bo);
71562306a36Sopenharmony_ciunlock_dev_srcu:
71662306a36Sopenharmony_ci	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
71762306a36Sopenharmony_ciunlock_usr_srcu:
71862306a36Sopenharmony_ci	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
71962306a36Sopenharmony_ci	return ret;
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ciint qaic_mmap_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct qaic_mmap_bo *args = data;
72562306a36Sopenharmony_ci	int usr_rcu_id, qdev_rcu_id;
72662306a36Sopenharmony_ci	struct drm_gem_object *obj;
72762306a36Sopenharmony_ci	struct qaic_device *qdev;
72862306a36Sopenharmony_ci	struct qaic_user *usr;
72962306a36Sopenharmony_ci	int ret;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	usr = file_priv->driver_priv;
73262306a36Sopenharmony_ci	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
73362306a36Sopenharmony_ci	if (!usr->qddev) {
73462306a36Sopenharmony_ci		ret = -ENODEV;
73562306a36Sopenharmony_ci		goto unlock_usr_srcu;
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	qdev = usr->qddev->qdev;
73962306a36Sopenharmony_ci	qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
74062306a36Sopenharmony_ci	if (qdev->in_reset) {
74162306a36Sopenharmony_ci		ret = -ENODEV;
74262306a36Sopenharmony_ci		goto unlock_dev_srcu;
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	obj = drm_gem_object_lookup(file_priv, args->handle);
74662306a36Sopenharmony_ci	if (!obj) {
74762306a36Sopenharmony_ci		ret = -ENOENT;
74862306a36Sopenharmony_ci		goto unlock_dev_srcu;
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	ret = drm_gem_create_mmap_offset(obj);
75262306a36Sopenharmony_ci	if (ret == 0)
75362306a36Sopenharmony_ci		args->offset = drm_vma_node_offset_addr(&obj->vma_node);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	drm_gem_object_put(obj);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ciunlock_dev_srcu:
75862306a36Sopenharmony_ci	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
75962306a36Sopenharmony_ciunlock_usr_srcu:
76062306a36Sopenharmony_ci	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
76162306a36Sopenharmony_ci	return ret;
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_cistruct drm_gem_object *qaic_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	struct dma_buf_attachment *attach;
76762306a36Sopenharmony_ci	struct drm_gem_object *obj;
76862306a36Sopenharmony_ci	struct qaic_bo *bo;
76962306a36Sopenharmony_ci	int ret;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	bo = qaic_alloc_init_bo();
77262306a36Sopenharmony_ci	if (IS_ERR(bo)) {
77362306a36Sopenharmony_ci		ret = PTR_ERR(bo);
77462306a36Sopenharmony_ci		goto out;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	obj = &bo->base;
77862306a36Sopenharmony_ci	get_dma_buf(dma_buf);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	attach = dma_buf_attach(dma_buf, dev->dev);
78162306a36Sopenharmony_ci	if (IS_ERR(attach)) {
78262306a36Sopenharmony_ci		ret = PTR_ERR(attach);
78362306a36Sopenharmony_ci		goto attach_fail;
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	if (!attach->dmabuf->size) {
78762306a36Sopenharmony_ci		ret = -EINVAL;
78862306a36Sopenharmony_ci		goto size_align_fail;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	drm_gem_private_object_init(dev, obj, attach->dmabuf->size);
79262306a36Sopenharmony_ci	/*
79362306a36Sopenharmony_ci	 * skipping dma_buf_map_attachment() as we do not know the direction
79462306a36Sopenharmony_ci	 * just yet. Once the direction is known in the subsequent IOCTL to
79562306a36Sopenharmony_ci	 * attach slicing, we can do it then.
79662306a36Sopenharmony_ci	 */
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	obj->funcs = &qaic_gem_funcs;
79962306a36Sopenharmony_ci	obj->import_attach = attach;
80062306a36Sopenharmony_ci	obj->resv = dma_buf->resv;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	return obj;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cisize_align_fail:
80562306a36Sopenharmony_ci	dma_buf_detach(dma_buf, attach);
80662306a36Sopenharmony_ciattach_fail:
80762306a36Sopenharmony_ci	dma_buf_put(dma_buf);
80862306a36Sopenharmony_ci	kfree(bo);
80962306a36Sopenharmony_ciout:
81062306a36Sopenharmony_ci	return ERR_PTR(ret);
81162306a36Sopenharmony_ci}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_cistatic int qaic_prepare_import_bo(struct qaic_bo *bo, struct qaic_attach_slice_hdr *hdr)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	struct drm_gem_object *obj = &bo->base;
81662306a36Sopenharmony_ci	struct sg_table *sgt;
81762306a36Sopenharmony_ci	int ret;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if (obj->import_attach->dmabuf->size < hdr->size)
82062306a36Sopenharmony_ci		return -EINVAL;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	sgt = dma_buf_map_attachment(obj->import_attach, hdr->dir);
82362306a36Sopenharmony_ci	if (IS_ERR(sgt)) {
82462306a36Sopenharmony_ci		ret = PTR_ERR(sgt);
82562306a36Sopenharmony_ci		return ret;
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	bo->sgt = sgt;
82962306a36Sopenharmony_ci	bo->size = hdr->size;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	return 0;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cistatic int qaic_prepare_export_bo(struct qaic_device *qdev, struct qaic_bo *bo,
83562306a36Sopenharmony_ci				  struct qaic_attach_slice_hdr *hdr)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	int ret;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (bo->size != hdr->size)
84062306a36Sopenharmony_ci		return -EINVAL;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	ret = dma_map_sgtable(&qdev->pdev->dev, bo->sgt, hdr->dir, 0);
84362306a36Sopenharmony_ci	if (ret)
84462306a36Sopenharmony_ci		return -EFAULT;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	return 0;
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cistatic int qaic_prepare_bo(struct qaic_device *qdev, struct qaic_bo *bo,
85062306a36Sopenharmony_ci			   struct qaic_attach_slice_hdr *hdr)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	int ret;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	if (bo->base.import_attach)
85562306a36Sopenharmony_ci		ret = qaic_prepare_import_bo(bo, hdr);
85662306a36Sopenharmony_ci	else
85762306a36Sopenharmony_ci		ret = qaic_prepare_export_bo(qdev, bo, hdr);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (ret == 0)
86062306a36Sopenharmony_ci		bo->dir = hdr->dir;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	return ret;
86362306a36Sopenharmony_ci}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_cistatic void qaic_unprepare_import_bo(struct qaic_bo *bo)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	dma_buf_unmap_attachment(bo->base.import_attach, bo->sgt, bo->dir);
86862306a36Sopenharmony_ci	bo->sgt = NULL;
86962306a36Sopenharmony_ci	bo->size = 0;
87062306a36Sopenharmony_ci}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_cistatic void qaic_unprepare_export_bo(struct qaic_device *qdev, struct qaic_bo *bo)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	dma_unmap_sgtable(&qdev->pdev->dev, bo->sgt, bo->dir, 0);
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_cistatic void qaic_unprepare_bo(struct qaic_device *qdev, struct qaic_bo *bo)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	if (bo->base.import_attach)
88062306a36Sopenharmony_ci		qaic_unprepare_import_bo(bo);
88162306a36Sopenharmony_ci	else
88262306a36Sopenharmony_ci		qaic_unprepare_export_bo(qdev, bo);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	bo->dir = 0;
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic void qaic_free_slices_bo(struct qaic_bo *bo)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct bo_slice *slice, *temp;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	list_for_each_entry_safe(slice, temp, &bo->slices, slice)
89262306a36Sopenharmony_ci		kref_put(&slice->ref_count, free_slice);
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistatic int qaic_attach_slicing_bo(struct qaic_device *qdev, struct qaic_bo *bo,
89662306a36Sopenharmony_ci				  struct qaic_attach_slice_hdr *hdr,
89762306a36Sopenharmony_ci				  struct qaic_attach_slice_entry *slice_ent)
89862306a36Sopenharmony_ci{
89962306a36Sopenharmony_ci	int ret, i;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	for (i = 0; i < hdr->count; i++) {
90262306a36Sopenharmony_ci		ret = qaic_map_one_slice(qdev, bo, &slice_ent[i]);
90362306a36Sopenharmony_ci		if (ret) {
90462306a36Sopenharmony_ci			qaic_free_slices_bo(bo);
90562306a36Sopenharmony_ci			return ret;
90662306a36Sopenharmony_ci		}
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	if (bo->total_slice_nents > qdev->dbc[hdr->dbc_id].nelem) {
91062306a36Sopenharmony_ci		qaic_free_slices_bo(bo);
91162306a36Sopenharmony_ci		return -ENOSPC;
91262306a36Sopenharmony_ci	}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	bo->sliced = true;
91562306a36Sopenharmony_ci	bo->nr_slice = hdr->count;
91662306a36Sopenharmony_ci	list_add_tail(&bo->bo_list, &qdev->dbc[hdr->dbc_id].bo_lists);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	return 0;
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ciint qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	struct qaic_attach_slice_entry *slice_ent;
92462306a36Sopenharmony_ci	struct qaic_attach_slice *args = data;
92562306a36Sopenharmony_ci	int rcu_id, usr_rcu_id, qdev_rcu_id;
92662306a36Sopenharmony_ci	struct dma_bridge_chan	*dbc;
92762306a36Sopenharmony_ci	struct drm_gem_object *obj;
92862306a36Sopenharmony_ci	struct qaic_device *qdev;
92962306a36Sopenharmony_ci	unsigned long arg_size;
93062306a36Sopenharmony_ci	struct qaic_user *usr;
93162306a36Sopenharmony_ci	u8 __user *user_data;
93262306a36Sopenharmony_ci	struct qaic_bo *bo;
93362306a36Sopenharmony_ci	int ret;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	if (args->hdr.count == 0)
93662306a36Sopenharmony_ci		return -EINVAL;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	arg_size = args->hdr.count * sizeof(*slice_ent);
93962306a36Sopenharmony_ci	if (arg_size / args->hdr.count != sizeof(*slice_ent))
94062306a36Sopenharmony_ci		return -EINVAL;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	if (args->hdr.size == 0)
94362306a36Sopenharmony_ci		return -EINVAL;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (!(args->hdr.dir == DMA_TO_DEVICE || args->hdr.dir == DMA_FROM_DEVICE))
94662306a36Sopenharmony_ci		return -EINVAL;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	if (args->data == 0)
94962306a36Sopenharmony_ci		return -EINVAL;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	usr = file_priv->driver_priv;
95262306a36Sopenharmony_ci	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
95362306a36Sopenharmony_ci	if (!usr->qddev) {
95462306a36Sopenharmony_ci		ret = -ENODEV;
95562306a36Sopenharmony_ci		goto unlock_usr_srcu;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	qdev = usr->qddev->qdev;
95962306a36Sopenharmony_ci	qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
96062306a36Sopenharmony_ci	if (qdev->in_reset) {
96162306a36Sopenharmony_ci		ret = -ENODEV;
96262306a36Sopenharmony_ci		goto unlock_dev_srcu;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	if (args->hdr.dbc_id >= qdev->num_dbc) {
96662306a36Sopenharmony_ci		ret = -EINVAL;
96762306a36Sopenharmony_ci		goto unlock_dev_srcu;
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	user_data = u64_to_user_ptr(args->data);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	slice_ent = kzalloc(arg_size, GFP_KERNEL);
97362306a36Sopenharmony_ci	if (!slice_ent) {
97462306a36Sopenharmony_ci		ret = -EINVAL;
97562306a36Sopenharmony_ci		goto unlock_dev_srcu;
97662306a36Sopenharmony_ci	}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	ret = copy_from_user(slice_ent, user_data, arg_size);
97962306a36Sopenharmony_ci	if (ret) {
98062306a36Sopenharmony_ci		ret = -EFAULT;
98162306a36Sopenharmony_ci		goto free_slice_ent;
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	ret = qaic_validate_req(qdev, slice_ent, args->hdr.count, args->hdr.size);
98562306a36Sopenharmony_ci	if (ret)
98662306a36Sopenharmony_ci		goto free_slice_ent;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	obj = drm_gem_object_lookup(file_priv, args->hdr.handle);
98962306a36Sopenharmony_ci	if (!obj) {
99062306a36Sopenharmony_ci		ret = -ENOENT;
99162306a36Sopenharmony_ci		goto free_slice_ent;
99262306a36Sopenharmony_ci	}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	bo = to_qaic_bo(obj);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (bo->sliced) {
99762306a36Sopenharmony_ci		ret = -EINVAL;
99862306a36Sopenharmony_ci		goto put_bo;
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	dbc = &qdev->dbc[args->hdr.dbc_id];
100262306a36Sopenharmony_ci	rcu_id = srcu_read_lock(&dbc->ch_lock);
100362306a36Sopenharmony_ci	if (dbc->usr != usr) {
100462306a36Sopenharmony_ci		ret = -EINVAL;
100562306a36Sopenharmony_ci		goto unlock_ch_srcu;
100662306a36Sopenharmony_ci	}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	ret = qaic_prepare_bo(qdev, bo, &args->hdr);
100962306a36Sopenharmony_ci	if (ret)
101062306a36Sopenharmony_ci		goto unlock_ch_srcu;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	ret = qaic_attach_slicing_bo(qdev, bo, &args->hdr, slice_ent);
101362306a36Sopenharmony_ci	if (ret)
101462306a36Sopenharmony_ci		goto unprepare_bo;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	if (args->hdr.dir == DMA_TO_DEVICE)
101762306a36Sopenharmony_ci		dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, args->hdr.dir);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	bo->dbc = dbc;
102062306a36Sopenharmony_ci	srcu_read_unlock(&dbc->ch_lock, rcu_id);
102162306a36Sopenharmony_ci	drm_gem_object_put(obj);
102262306a36Sopenharmony_ci	kfree(slice_ent);
102362306a36Sopenharmony_ci	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
102462306a36Sopenharmony_ci	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	return 0;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ciunprepare_bo:
102962306a36Sopenharmony_ci	qaic_unprepare_bo(qdev, bo);
103062306a36Sopenharmony_ciunlock_ch_srcu:
103162306a36Sopenharmony_ci	srcu_read_unlock(&dbc->ch_lock, rcu_id);
103262306a36Sopenharmony_ciput_bo:
103362306a36Sopenharmony_ci	drm_gem_object_put(obj);
103462306a36Sopenharmony_cifree_slice_ent:
103562306a36Sopenharmony_ci	kfree(slice_ent);
103662306a36Sopenharmony_ciunlock_dev_srcu:
103762306a36Sopenharmony_ci	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
103862306a36Sopenharmony_ciunlock_usr_srcu:
103962306a36Sopenharmony_ci	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
104062306a36Sopenharmony_ci	return ret;
104162306a36Sopenharmony_ci}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_cistatic inline int copy_exec_reqs(struct qaic_device *qdev, struct bo_slice *slice, u32 dbc_id,
104462306a36Sopenharmony_ci				 u32 head, u32 *ptail)
104562306a36Sopenharmony_ci{
104662306a36Sopenharmony_ci	struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id];
104762306a36Sopenharmony_ci	struct dbc_req *reqs = slice->reqs;
104862306a36Sopenharmony_ci	u32 tail = *ptail;
104962306a36Sopenharmony_ci	u32 avail;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	avail = head - tail;
105262306a36Sopenharmony_ci	if (head <= tail)
105362306a36Sopenharmony_ci		avail += dbc->nelem;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	--avail;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	if (avail < slice->nents)
105862306a36Sopenharmony_ci		return -EAGAIN;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	if (tail + slice->nents > dbc->nelem) {
106162306a36Sopenharmony_ci		avail = dbc->nelem - tail;
106262306a36Sopenharmony_ci		avail = min_t(u32, avail, slice->nents);
106362306a36Sopenharmony_ci		memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs,
106462306a36Sopenharmony_ci		       sizeof(*reqs) * avail);
106562306a36Sopenharmony_ci		reqs += avail;
106662306a36Sopenharmony_ci		avail = slice->nents - avail;
106762306a36Sopenharmony_ci		if (avail)
106862306a36Sopenharmony_ci			memcpy(dbc->req_q_base, reqs, sizeof(*reqs) * avail);
106962306a36Sopenharmony_ci	} else {
107062306a36Sopenharmony_ci		memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs,
107162306a36Sopenharmony_ci		       sizeof(*reqs) * slice->nents);
107262306a36Sopenharmony_ci	}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	*ptail = (tail + slice->nents) % dbc->nelem;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	return 0;
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci/*
108062306a36Sopenharmony_ci * Based on the value of resize we may only need to transmit first_n
108162306a36Sopenharmony_ci * entries and the last entry, with last_bytes to send from the last entry.
108262306a36Sopenharmony_ci * Note that first_n could be 0.
108362306a36Sopenharmony_ci */
108462306a36Sopenharmony_cistatic inline int copy_partial_exec_reqs(struct qaic_device *qdev, struct bo_slice *slice,
108562306a36Sopenharmony_ci					 u64 resize, u32 dbc_id, u32 head, u32 *ptail)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id];
108862306a36Sopenharmony_ci	struct dbc_req *reqs = slice->reqs;
108962306a36Sopenharmony_ci	struct dbc_req *last_req;
109062306a36Sopenharmony_ci	u32 tail = *ptail;
109162306a36Sopenharmony_ci	u64 total_bytes;
109262306a36Sopenharmony_ci	u64 last_bytes;
109362306a36Sopenharmony_ci	u32 first_n;
109462306a36Sopenharmony_ci	u32 avail;
109562306a36Sopenharmony_ci	int ret;
109662306a36Sopenharmony_ci	int i;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	avail = head - tail;
109962306a36Sopenharmony_ci	if (head <= tail)
110062306a36Sopenharmony_ci		avail += dbc->nelem;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	--avail;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	total_bytes = 0;
110562306a36Sopenharmony_ci	for (i = 0; i < slice->nents; i++) {
110662306a36Sopenharmony_ci		total_bytes += le32_to_cpu(reqs[i].len);
110762306a36Sopenharmony_ci		if (total_bytes >= resize)
110862306a36Sopenharmony_ci			break;
110962306a36Sopenharmony_ci	}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	if (total_bytes < resize) {
111262306a36Sopenharmony_ci		/* User space should have used the full buffer path. */
111362306a36Sopenharmony_ci		ret = -EINVAL;
111462306a36Sopenharmony_ci		return ret;
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	first_n = i;
111862306a36Sopenharmony_ci	last_bytes = i ? resize + le32_to_cpu(reqs[i].len) - total_bytes : resize;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	if (avail < (first_n + 1))
112162306a36Sopenharmony_ci		return -EAGAIN;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	if (first_n) {
112462306a36Sopenharmony_ci		if (tail + first_n > dbc->nelem) {
112562306a36Sopenharmony_ci			avail = dbc->nelem - tail;
112662306a36Sopenharmony_ci			avail = min_t(u32, avail, first_n);
112762306a36Sopenharmony_ci			memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs,
112862306a36Sopenharmony_ci			       sizeof(*reqs) * avail);
112962306a36Sopenharmony_ci			last_req = reqs + avail;
113062306a36Sopenharmony_ci			avail = first_n - avail;
113162306a36Sopenharmony_ci			if (avail)
113262306a36Sopenharmony_ci				memcpy(dbc->req_q_base, last_req, sizeof(*reqs) * avail);
113362306a36Sopenharmony_ci		} else {
113462306a36Sopenharmony_ci			memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs,
113562306a36Sopenharmony_ci			       sizeof(*reqs) * first_n);
113662306a36Sopenharmony_ci		}
113762306a36Sopenharmony_ci	}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	/* Copy over the last entry. Here we need to adjust len to the left over
114062306a36Sopenharmony_ci	 * size, and set src and dst to the entry it is copied to.
114162306a36Sopenharmony_ci	 */
114262306a36Sopenharmony_ci	last_req = dbc->req_q_base + (tail + first_n) % dbc->nelem * get_dbc_req_elem_size();
114362306a36Sopenharmony_ci	memcpy(last_req, reqs + slice->nents - 1, sizeof(*reqs));
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	/*
114662306a36Sopenharmony_ci	 * last_bytes holds size of a DMA segment, maximum DMA segment size is
114762306a36Sopenharmony_ci	 * set to UINT_MAX by qaic and hence last_bytes can never exceed u32
114862306a36Sopenharmony_ci	 * range. So, by down sizing we are not corrupting the value.
114962306a36Sopenharmony_ci	 */
115062306a36Sopenharmony_ci	last_req->len = cpu_to_le32((u32)last_bytes);
115162306a36Sopenharmony_ci	last_req->src_addr = reqs[first_n].src_addr;
115262306a36Sopenharmony_ci	last_req->dest_addr = reqs[first_n].dest_addr;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	*ptail = (tail + first_n + 1) % dbc->nelem;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	return 0;
115762306a36Sopenharmony_ci}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_cistatic int send_bo_list_to_device(struct qaic_device *qdev, struct drm_file *file_priv,
116062306a36Sopenharmony_ci				  struct qaic_execute_entry *exec, unsigned int count,
116162306a36Sopenharmony_ci				  bool is_partial, struct dma_bridge_chan *dbc, u32 head,
116262306a36Sopenharmony_ci				  u32 *tail)
116362306a36Sopenharmony_ci{
116462306a36Sopenharmony_ci	struct qaic_partial_execute_entry *pexec = (struct qaic_partial_execute_entry *)exec;
116562306a36Sopenharmony_ci	struct drm_gem_object *obj;
116662306a36Sopenharmony_ci	struct bo_slice *slice;
116762306a36Sopenharmony_ci	unsigned long flags;
116862306a36Sopenharmony_ci	struct qaic_bo *bo;
116962306a36Sopenharmony_ci	bool queued;
117062306a36Sopenharmony_ci	int i, j;
117162306a36Sopenharmony_ci	int ret;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
117462306a36Sopenharmony_ci		/*
117562306a36Sopenharmony_ci		 * ref count will be decremented when the transfer of this
117662306a36Sopenharmony_ci		 * buffer is complete. It is inside dbc_irq_threaded_fn().
117762306a36Sopenharmony_ci		 */
117862306a36Sopenharmony_ci		obj = drm_gem_object_lookup(file_priv,
117962306a36Sopenharmony_ci					    is_partial ? pexec[i].handle : exec[i].handle);
118062306a36Sopenharmony_ci		if (!obj) {
118162306a36Sopenharmony_ci			ret = -ENOENT;
118262306a36Sopenharmony_ci			goto failed_to_send_bo;
118362306a36Sopenharmony_ci		}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci		bo = to_qaic_bo(obj);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		if (!bo->sliced) {
118862306a36Sopenharmony_ci			ret = -EINVAL;
118962306a36Sopenharmony_ci			goto failed_to_send_bo;
119062306a36Sopenharmony_ci		}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci		if (is_partial && pexec[i].resize > bo->size) {
119362306a36Sopenharmony_ci			ret = -EINVAL;
119462306a36Sopenharmony_ci			goto failed_to_send_bo;
119562306a36Sopenharmony_ci		}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci		spin_lock_irqsave(&dbc->xfer_lock, flags);
119862306a36Sopenharmony_ci		queued = bo->queued;
119962306a36Sopenharmony_ci		bo->queued = true;
120062306a36Sopenharmony_ci		if (queued) {
120162306a36Sopenharmony_ci			spin_unlock_irqrestore(&dbc->xfer_lock, flags);
120262306a36Sopenharmony_ci			ret = -EINVAL;
120362306a36Sopenharmony_ci			goto failed_to_send_bo;
120462306a36Sopenharmony_ci		}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci		bo->req_id = dbc->next_req_id++;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci		list_for_each_entry(slice, &bo->slices, slice) {
120962306a36Sopenharmony_ci			/*
121062306a36Sopenharmony_ci			 * If this slice does not fall under the given
121162306a36Sopenharmony_ci			 * resize then skip this slice and continue the loop
121262306a36Sopenharmony_ci			 */
121362306a36Sopenharmony_ci			if (is_partial && pexec[i].resize && pexec[i].resize <= slice->offset)
121462306a36Sopenharmony_ci				continue;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci			for (j = 0; j < slice->nents; j++)
121762306a36Sopenharmony_ci				slice->reqs[j].req_id = cpu_to_le16(bo->req_id);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci			/*
122062306a36Sopenharmony_ci			 * If it is a partial execute ioctl call then check if
122162306a36Sopenharmony_ci			 * resize has cut this slice short then do a partial copy
122262306a36Sopenharmony_ci			 * else do complete copy
122362306a36Sopenharmony_ci			 */
122462306a36Sopenharmony_ci			if (is_partial && pexec[i].resize &&
122562306a36Sopenharmony_ci			    pexec[i].resize < slice->offset + slice->size)
122662306a36Sopenharmony_ci				ret = copy_partial_exec_reqs(qdev, slice,
122762306a36Sopenharmony_ci							     pexec[i].resize - slice->offset,
122862306a36Sopenharmony_ci							     dbc->id, head, tail);
122962306a36Sopenharmony_ci			else
123062306a36Sopenharmony_ci				ret = copy_exec_reqs(qdev, slice, dbc->id, head, tail);
123162306a36Sopenharmony_ci			if (ret) {
123262306a36Sopenharmony_ci				bo->queued = false;
123362306a36Sopenharmony_ci				spin_unlock_irqrestore(&dbc->xfer_lock, flags);
123462306a36Sopenharmony_ci				goto failed_to_send_bo;
123562306a36Sopenharmony_ci			}
123662306a36Sopenharmony_ci		}
123762306a36Sopenharmony_ci		reinit_completion(&bo->xfer_done);
123862306a36Sopenharmony_ci		list_add_tail(&bo->xfer_list, &dbc->xfer_list);
123962306a36Sopenharmony_ci		spin_unlock_irqrestore(&dbc->xfer_lock, flags);
124062306a36Sopenharmony_ci		dma_sync_sgtable_for_device(&qdev->pdev->dev, bo->sgt, bo->dir);
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	return 0;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_cifailed_to_send_bo:
124662306a36Sopenharmony_ci	if (likely(obj))
124762306a36Sopenharmony_ci		drm_gem_object_put(obj);
124862306a36Sopenharmony_ci	for (j = 0; j < i; j++) {
124962306a36Sopenharmony_ci		spin_lock_irqsave(&dbc->xfer_lock, flags);
125062306a36Sopenharmony_ci		bo = list_last_entry(&dbc->xfer_list, struct qaic_bo, xfer_list);
125162306a36Sopenharmony_ci		obj = &bo->base;
125262306a36Sopenharmony_ci		bo->queued = false;
125362306a36Sopenharmony_ci		list_del(&bo->xfer_list);
125462306a36Sopenharmony_ci		spin_unlock_irqrestore(&dbc->xfer_lock, flags);
125562306a36Sopenharmony_ci		dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir);
125662306a36Sopenharmony_ci		drm_gem_object_put(obj);
125762306a36Sopenharmony_ci	}
125862306a36Sopenharmony_ci	return ret;
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_cistatic void update_profiling_data(struct drm_file *file_priv,
126262306a36Sopenharmony_ci				  struct qaic_execute_entry *exec, unsigned int count,
126362306a36Sopenharmony_ci				  bool is_partial, u64 received_ts, u64 submit_ts, u32 queue_level)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	struct qaic_partial_execute_entry *pexec = (struct qaic_partial_execute_entry *)exec;
126662306a36Sopenharmony_ci	struct drm_gem_object *obj;
126762306a36Sopenharmony_ci	struct qaic_bo *bo;
126862306a36Sopenharmony_ci	int i;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
127162306a36Sopenharmony_ci		/*
127262306a36Sopenharmony_ci		 * Since we already committed the BO to hardware, the only way
127362306a36Sopenharmony_ci		 * this should fail is a pending signal. We can't cancel the
127462306a36Sopenharmony_ci		 * submit to hardware, so we have to just skip the profiling
127562306a36Sopenharmony_ci		 * data. In case the signal is not fatal to the process, we
127662306a36Sopenharmony_ci		 * return success so that the user doesn't try to resubmit.
127762306a36Sopenharmony_ci		 */
127862306a36Sopenharmony_ci		obj = drm_gem_object_lookup(file_priv,
127962306a36Sopenharmony_ci					    is_partial ? pexec[i].handle : exec[i].handle);
128062306a36Sopenharmony_ci		if (!obj)
128162306a36Sopenharmony_ci			break;
128262306a36Sopenharmony_ci		bo = to_qaic_bo(obj);
128362306a36Sopenharmony_ci		bo->perf_stats.req_received_ts = received_ts;
128462306a36Sopenharmony_ci		bo->perf_stats.req_submit_ts = submit_ts;
128562306a36Sopenharmony_ci		bo->perf_stats.queue_level_before = queue_level;
128662306a36Sopenharmony_ci		queue_level += bo->total_slice_nents;
128762306a36Sopenharmony_ci		drm_gem_object_put(obj);
128862306a36Sopenharmony_ci	}
128962306a36Sopenharmony_ci}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_cistatic int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv,
129262306a36Sopenharmony_ci				   bool is_partial)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	struct qaic_execute *args = data;
129562306a36Sopenharmony_ci	struct qaic_execute_entry *exec;
129662306a36Sopenharmony_ci	struct dma_bridge_chan *dbc;
129762306a36Sopenharmony_ci	int usr_rcu_id, qdev_rcu_id;
129862306a36Sopenharmony_ci	struct qaic_device *qdev;
129962306a36Sopenharmony_ci	struct qaic_user *usr;
130062306a36Sopenharmony_ci	u8 __user *user_data;
130162306a36Sopenharmony_ci	unsigned long n;
130262306a36Sopenharmony_ci	u64 received_ts;
130362306a36Sopenharmony_ci	u32 queue_level;
130462306a36Sopenharmony_ci	u64 submit_ts;
130562306a36Sopenharmony_ci	int rcu_id;
130662306a36Sopenharmony_ci	u32 head;
130762306a36Sopenharmony_ci	u32 tail;
130862306a36Sopenharmony_ci	u64 size;
130962306a36Sopenharmony_ci	int ret;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	received_ts = ktime_get_ns();
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	size = is_partial ? sizeof(struct qaic_partial_execute_entry) : sizeof(*exec);
131462306a36Sopenharmony_ci	n = (unsigned long)size * args->hdr.count;
131562306a36Sopenharmony_ci	if (args->hdr.count == 0 || n / args->hdr.count != size)
131662306a36Sopenharmony_ci		return -EINVAL;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	user_data = u64_to_user_ptr(args->data);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	exec = kcalloc(args->hdr.count, size, GFP_KERNEL);
132162306a36Sopenharmony_ci	if (!exec)
132262306a36Sopenharmony_ci		return -ENOMEM;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	if (copy_from_user(exec, user_data, n)) {
132562306a36Sopenharmony_ci		ret = -EFAULT;
132662306a36Sopenharmony_ci		goto free_exec;
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	usr = file_priv->driver_priv;
133062306a36Sopenharmony_ci	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
133162306a36Sopenharmony_ci	if (!usr->qddev) {
133262306a36Sopenharmony_ci		ret = -ENODEV;
133362306a36Sopenharmony_ci		goto unlock_usr_srcu;
133462306a36Sopenharmony_ci	}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	qdev = usr->qddev->qdev;
133762306a36Sopenharmony_ci	qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
133862306a36Sopenharmony_ci	if (qdev->in_reset) {
133962306a36Sopenharmony_ci		ret = -ENODEV;
134062306a36Sopenharmony_ci		goto unlock_dev_srcu;
134162306a36Sopenharmony_ci	}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	if (args->hdr.dbc_id >= qdev->num_dbc) {
134462306a36Sopenharmony_ci		ret = -EINVAL;
134562306a36Sopenharmony_ci		goto unlock_dev_srcu;
134662306a36Sopenharmony_ci	}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	dbc = &qdev->dbc[args->hdr.dbc_id];
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	rcu_id = srcu_read_lock(&dbc->ch_lock);
135162306a36Sopenharmony_ci	if (!dbc->usr || dbc->usr->handle != usr->handle) {
135262306a36Sopenharmony_ci		ret = -EPERM;
135362306a36Sopenharmony_ci		goto release_ch_rcu;
135462306a36Sopenharmony_ci	}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	head = readl(dbc->dbc_base + REQHP_OFF);
135762306a36Sopenharmony_ci	tail = readl(dbc->dbc_base + REQTP_OFF);
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	if (head == U32_MAX || tail == U32_MAX) {
136062306a36Sopenharmony_ci		/* PCI link error */
136162306a36Sopenharmony_ci		ret = -ENODEV;
136262306a36Sopenharmony_ci		goto release_ch_rcu;
136362306a36Sopenharmony_ci	}
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	queue_level = head <= tail ? tail - head : dbc->nelem - (head - tail);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	ret = send_bo_list_to_device(qdev, file_priv, exec, args->hdr.count, is_partial, dbc,
136862306a36Sopenharmony_ci				     head, &tail);
136962306a36Sopenharmony_ci	if (ret)
137062306a36Sopenharmony_ci		goto release_ch_rcu;
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	/* Finalize commit to hardware */
137362306a36Sopenharmony_ci	submit_ts = ktime_get_ns();
137462306a36Sopenharmony_ci	writel(tail, dbc->dbc_base + REQTP_OFF);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	update_profiling_data(file_priv, exec, args->hdr.count, is_partial, received_ts,
137762306a36Sopenharmony_ci			      submit_ts, queue_level);
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	if (datapath_polling)
138062306a36Sopenharmony_ci		schedule_work(&dbc->poll_work);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_cirelease_ch_rcu:
138362306a36Sopenharmony_ci	srcu_read_unlock(&dbc->ch_lock, rcu_id);
138462306a36Sopenharmony_ciunlock_dev_srcu:
138562306a36Sopenharmony_ci	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
138662306a36Sopenharmony_ciunlock_usr_srcu:
138762306a36Sopenharmony_ci	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
138862306a36Sopenharmony_cifree_exec:
138962306a36Sopenharmony_ci	kfree(exec);
139062306a36Sopenharmony_ci	return ret;
139162306a36Sopenharmony_ci}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ciint qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
139462306a36Sopenharmony_ci{
139562306a36Sopenharmony_ci	return __qaic_execute_bo_ioctl(dev, data, file_priv, false);
139662306a36Sopenharmony_ci}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ciint qaic_partial_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
139962306a36Sopenharmony_ci{
140062306a36Sopenharmony_ci	return __qaic_execute_bo_ioctl(dev, data, file_priv, true);
140162306a36Sopenharmony_ci}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci/*
140462306a36Sopenharmony_ci * Our interrupt handling is a bit more complicated than a simple ideal, but
140562306a36Sopenharmony_ci * sadly necessary.
140662306a36Sopenharmony_ci *
140762306a36Sopenharmony_ci * Each dbc has a completion queue. Entries in the queue correspond to DMA
140862306a36Sopenharmony_ci * requests which the device has processed. The hardware already has a built
140962306a36Sopenharmony_ci * in irq mitigation. When the device puts an entry into the queue, it will
141062306a36Sopenharmony_ci * only trigger an interrupt if the queue was empty. Therefore, when adding
141162306a36Sopenharmony_ci * the Nth event to a non-empty queue, the hardware doesn't trigger an
141262306a36Sopenharmony_ci * interrupt. This means the host doesn't get additional interrupts signaling
141362306a36Sopenharmony_ci * the same thing - the queue has something to process.
141462306a36Sopenharmony_ci * This behavior can be overridden in the DMA request.
141562306a36Sopenharmony_ci * This means that when the host receives an interrupt, it is required to
141662306a36Sopenharmony_ci * drain the queue.
141762306a36Sopenharmony_ci *
141862306a36Sopenharmony_ci * This behavior is what NAPI attempts to accomplish, although we can't use
141962306a36Sopenharmony_ci * NAPI as we don't have a netdev. We use threaded irqs instead.
142062306a36Sopenharmony_ci *
142162306a36Sopenharmony_ci * However, there is a situation where the host drains the queue fast enough
142262306a36Sopenharmony_ci * that every event causes an interrupt. Typically this is not a problem as
142362306a36Sopenharmony_ci * the rate of events would be low. However, that is not the case with
142462306a36Sopenharmony_ci * lprnet for example. On an Intel Xeon D-2191 where we run 8 instances of
142562306a36Sopenharmony_ci * lprnet, the host receives roughly 80k interrupts per second from the device
142662306a36Sopenharmony_ci * (per /proc/interrupts). While NAPI documentation indicates the host should
142762306a36Sopenharmony_ci * just chug along, sadly that behavior causes instability in some hosts.
142862306a36Sopenharmony_ci *
142962306a36Sopenharmony_ci * Therefore, we implement an interrupt disable scheme similar to NAPI. The
143062306a36Sopenharmony_ci * key difference is that we will delay after draining the queue for a small
143162306a36Sopenharmony_ci * time to allow additional events to come in via polling. Using the above
143262306a36Sopenharmony_ci * lprnet workload, this reduces the number of interrupts processed from
143362306a36Sopenharmony_ci * ~80k/sec to about 64 in 5 minutes and appears to solve the system
143462306a36Sopenharmony_ci * instability.
143562306a36Sopenharmony_ci */
143662306a36Sopenharmony_ciirqreturn_t dbc_irq_handler(int irq, void *data)
143762306a36Sopenharmony_ci{
143862306a36Sopenharmony_ci	struct dma_bridge_chan *dbc = data;
143962306a36Sopenharmony_ci	int rcu_id;
144062306a36Sopenharmony_ci	u32 head;
144162306a36Sopenharmony_ci	u32 tail;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	rcu_id = srcu_read_lock(&dbc->ch_lock);
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	if (!dbc->usr) {
144662306a36Sopenharmony_ci		srcu_read_unlock(&dbc->ch_lock, rcu_id);
144762306a36Sopenharmony_ci		return IRQ_HANDLED;
144862306a36Sopenharmony_ci	}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	head = readl(dbc->dbc_base + RSPHP_OFF);
145162306a36Sopenharmony_ci	if (head == U32_MAX) { /* PCI link error */
145262306a36Sopenharmony_ci		srcu_read_unlock(&dbc->ch_lock, rcu_id);
145362306a36Sopenharmony_ci		return IRQ_NONE;
145462306a36Sopenharmony_ci	}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	tail = readl(dbc->dbc_base + RSPTP_OFF);
145762306a36Sopenharmony_ci	if (tail == U32_MAX) { /* PCI link error */
145862306a36Sopenharmony_ci		srcu_read_unlock(&dbc->ch_lock, rcu_id);
145962306a36Sopenharmony_ci		return IRQ_NONE;
146062306a36Sopenharmony_ci	}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	if (head == tail) { /* queue empty */
146362306a36Sopenharmony_ci		srcu_read_unlock(&dbc->ch_lock, rcu_id);
146462306a36Sopenharmony_ci		return IRQ_NONE;
146562306a36Sopenharmony_ci	}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	disable_irq_nosync(irq);
146862306a36Sopenharmony_ci	srcu_read_unlock(&dbc->ch_lock, rcu_id);
146962306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
147062306a36Sopenharmony_ci}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_civoid irq_polling_work(struct work_struct *work)
147362306a36Sopenharmony_ci{
147462306a36Sopenharmony_ci	struct dma_bridge_chan *dbc = container_of(work, struct dma_bridge_chan,  poll_work);
147562306a36Sopenharmony_ci	unsigned long flags;
147662306a36Sopenharmony_ci	int rcu_id;
147762306a36Sopenharmony_ci	u32 head;
147862306a36Sopenharmony_ci	u32 tail;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	rcu_id = srcu_read_lock(&dbc->ch_lock);
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	while (1) {
148362306a36Sopenharmony_ci		if (dbc->qdev->in_reset) {
148462306a36Sopenharmony_ci			srcu_read_unlock(&dbc->ch_lock, rcu_id);
148562306a36Sopenharmony_ci			return;
148662306a36Sopenharmony_ci		}
148762306a36Sopenharmony_ci		if (!dbc->usr) {
148862306a36Sopenharmony_ci			srcu_read_unlock(&dbc->ch_lock, rcu_id);
148962306a36Sopenharmony_ci			return;
149062306a36Sopenharmony_ci		}
149162306a36Sopenharmony_ci		spin_lock_irqsave(&dbc->xfer_lock, flags);
149262306a36Sopenharmony_ci		if (list_empty(&dbc->xfer_list)) {
149362306a36Sopenharmony_ci			spin_unlock_irqrestore(&dbc->xfer_lock, flags);
149462306a36Sopenharmony_ci			srcu_read_unlock(&dbc->ch_lock, rcu_id);
149562306a36Sopenharmony_ci			return;
149662306a36Sopenharmony_ci		}
149762306a36Sopenharmony_ci		spin_unlock_irqrestore(&dbc->xfer_lock, flags);
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci		head = readl(dbc->dbc_base + RSPHP_OFF);
150062306a36Sopenharmony_ci		if (head == U32_MAX) { /* PCI link error */
150162306a36Sopenharmony_ci			srcu_read_unlock(&dbc->ch_lock, rcu_id);
150262306a36Sopenharmony_ci			return;
150362306a36Sopenharmony_ci		}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci		tail = readl(dbc->dbc_base + RSPTP_OFF);
150662306a36Sopenharmony_ci		if (tail == U32_MAX) { /* PCI link error */
150762306a36Sopenharmony_ci			srcu_read_unlock(&dbc->ch_lock, rcu_id);
150862306a36Sopenharmony_ci			return;
150962306a36Sopenharmony_ci		}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci		if (head != tail) {
151262306a36Sopenharmony_ci			irq_wake_thread(dbc->irq, dbc);
151362306a36Sopenharmony_ci			srcu_read_unlock(&dbc->ch_lock, rcu_id);
151462306a36Sopenharmony_ci			return;
151562306a36Sopenharmony_ci		}
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci		cond_resched();
151862306a36Sopenharmony_ci		usleep_range(datapath_poll_interval_us, 2 * datapath_poll_interval_us);
151962306a36Sopenharmony_ci	}
152062306a36Sopenharmony_ci}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ciirqreturn_t dbc_irq_threaded_fn(int irq, void *data)
152362306a36Sopenharmony_ci{
152462306a36Sopenharmony_ci	struct dma_bridge_chan *dbc = data;
152562306a36Sopenharmony_ci	int event_count = NUM_EVENTS;
152662306a36Sopenharmony_ci	int delay_count = NUM_DELAYS;
152762306a36Sopenharmony_ci	struct qaic_device *qdev;
152862306a36Sopenharmony_ci	struct qaic_bo *bo, *i;
152962306a36Sopenharmony_ci	struct dbc_rsp *rsp;
153062306a36Sopenharmony_ci	unsigned long flags;
153162306a36Sopenharmony_ci	int rcu_id;
153262306a36Sopenharmony_ci	u16 status;
153362306a36Sopenharmony_ci	u16 req_id;
153462306a36Sopenharmony_ci	u32 head;
153562306a36Sopenharmony_ci	u32 tail;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	rcu_id = srcu_read_lock(&dbc->ch_lock);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	head = readl(dbc->dbc_base + RSPHP_OFF);
154062306a36Sopenharmony_ci	if (head == U32_MAX) /* PCI link error */
154162306a36Sopenharmony_ci		goto error_out;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	qdev = dbc->qdev;
154462306a36Sopenharmony_ciread_fifo:
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	if (!event_count) {
154762306a36Sopenharmony_ci		event_count = NUM_EVENTS;
154862306a36Sopenharmony_ci		cond_resched();
154962306a36Sopenharmony_ci	}
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	/*
155262306a36Sopenharmony_ci	 * if this channel isn't assigned or gets unassigned during processing
155362306a36Sopenharmony_ci	 * we have nothing further to do
155462306a36Sopenharmony_ci	 */
155562306a36Sopenharmony_ci	if (!dbc->usr)
155662306a36Sopenharmony_ci		goto error_out;
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	tail = readl(dbc->dbc_base + RSPTP_OFF);
155962306a36Sopenharmony_ci	if (tail == U32_MAX) /* PCI link error */
156062306a36Sopenharmony_ci		goto error_out;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	if (head == tail) { /* queue empty */
156362306a36Sopenharmony_ci		if (delay_count) {
156462306a36Sopenharmony_ci			--delay_count;
156562306a36Sopenharmony_ci			usleep_range(100, 200);
156662306a36Sopenharmony_ci			goto read_fifo; /* check for a new event */
156762306a36Sopenharmony_ci		}
156862306a36Sopenharmony_ci		goto normal_out;
156962306a36Sopenharmony_ci	}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	delay_count = NUM_DELAYS;
157262306a36Sopenharmony_ci	while (head != tail) {
157362306a36Sopenharmony_ci		if (!event_count)
157462306a36Sopenharmony_ci			break;
157562306a36Sopenharmony_ci		--event_count;
157662306a36Sopenharmony_ci		rsp = dbc->rsp_q_base + head * sizeof(*rsp);
157762306a36Sopenharmony_ci		req_id = le16_to_cpu(rsp->req_id);
157862306a36Sopenharmony_ci		status = le16_to_cpu(rsp->status);
157962306a36Sopenharmony_ci		if (status)
158062306a36Sopenharmony_ci			pci_dbg(qdev->pdev, "req_id %d failed with status %d\n", req_id, status);
158162306a36Sopenharmony_ci		spin_lock_irqsave(&dbc->xfer_lock, flags);
158262306a36Sopenharmony_ci		/*
158362306a36Sopenharmony_ci		 * A BO can receive multiple interrupts, since a BO can be
158462306a36Sopenharmony_ci		 * divided into multiple slices and a buffer receives as many
158562306a36Sopenharmony_ci		 * interrupts as slices. So until it receives interrupts for
158662306a36Sopenharmony_ci		 * all the slices we cannot mark that buffer complete.
158762306a36Sopenharmony_ci		 */
158862306a36Sopenharmony_ci		list_for_each_entry_safe(bo, i, &dbc->xfer_list, xfer_list) {
158962306a36Sopenharmony_ci			if (bo->req_id == req_id)
159062306a36Sopenharmony_ci				bo->nr_slice_xfer_done++;
159162306a36Sopenharmony_ci			else
159262306a36Sopenharmony_ci				continue;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci			if (bo->nr_slice_xfer_done < bo->nr_slice)
159562306a36Sopenharmony_ci				break;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci			/*
159862306a36Sopenharmony_ci			 * At this point we have received all the interrupts for
159962306a36Sopenharmony_ci			 * BO, which means BO execution is complete.
160062306a36Sopenharmony_ci			 */
160162306a36Sopenharmony_ci			dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir);
160262306a36Sopenharmony_ci			bo->nr_slice_xfer_done = 0;
160362306a36Sopenharmony_ci			bo->queued = false;
160462306a36Sopenharmony_ci			list_del(&bo->xfer_list);
160562306a36Sopenharmony_ci			bo->perf_stats.req_processed_ts = ktime_get_ns();
160662306a36Sopenharmony_ci			complete_all(&bo->xfer_done);
160762306a36Sopenharmony_ci			drm_gem_object_put(&bo->base);
160862306a36Sopenharmony_ci			break;
160962306a36Sopenharmony_ci		}
161062306a36Sopenharmony_ci		spin_unlock_irqrestore(&dbc->xfer_lock, flags);
161162306a36Sopenharmony_ci		head = (head + 1) % dbc->nelem;
161262306a36Sopenharmony_ci	}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	/*
161562306a36Sopenharmony_ci	 * Update the head pointer of response queue and let the device know
161662306a36Sopenharmony_ci	 * that we have consumed elements from the queue.
161762306a36Sopenharmony_ci	 */
161862306a36Sopenharmony_ci	writel(head, dbc->dbc_base + RSPHP_OFF);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	/* elements might have been put in the queue while we were processing */
162162306a36Sopenharmony_ci	goto read_fifo;
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_cinormal_out:
162462306a36Sopenharmony_ci	if (likely(!datapath_polling))
162562306a36Sopenharmony_ci		enable_irq(irq);
162662306a36Sopenharmony_ci	else
162762306a36Sopenharmony_ci		schedule_work(&dbc->poll_work);
162862306a36Sopenharmony_ci	/* checking the fifo and enabling irqs is a race, missed event check */
162962306a36Sopenharmony_ci	tail = readl(dbc->dbc_base + RSPTP_OFF);
163062306a36Sopenharmony_ci	if (tail != U32_MAX && head != tail) {
163162306a36Sopenharmony_ci		if (likely(!datapath_polling))
163262306a36Sopenharmony_ci			disable_irq_nosync(irq);
163362306a36Sopenharmony_ci		goto read_fifo;
163462306a36Sopenharmony_ci	}
163562306a36Sopenharmony_ci	srcu_read_unlock(&dbc->ch_lock, rcu_id);
163662306a36Sopenharmony_ci	return IRQ_HANDLED;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_cierror_out:
163962306a36Sopenharmony_ci	srcu_read_unlock(&dbc->ch_lock, rcu_id);
164062306a36Sopenharmony_ci	if (likely(!datapath_polling))
164162306a36Sopenharmony_ci		enable_irq(irq);
164262306a36Sopenharmony_ci	else
164362306a36Sopenharmony_ci		schedule_work(&dbc->poll_work);
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	return IRQ_HANDLED;
164662306a36Sopenharmony_ci}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ciint qaic_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
164962306a36Sopenharmony_ci{
165062306a36Sopenharmony_ci	struct qaic_wait *args = data;
165162306a36Sopenharmony_ci	int usr_rcu_id, qdev_rcu_id;
165262306a36Sopenharmony_ci	struct dma_bridge_chan *dbc;
165362306a36Sopenharmony_ci	struct drm_gem_object *obj;
165462306a36Sopenharmony_ci	struct qaic_device *qdev;
165562306a36Sopenharmony_ci	unsigned long timeout;
165662306a36Sopenharmony_ci	struct qaic_user *usr;
165762306a36Sopenharmony_ci	struct qaic_bo *bo;
165862306a36Sopenharmony_ci	int rcu_id;
165962306a36Sopenharmony_ci	int ret;
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	if (args->pad != 0)
166262306a36Sopenharmony_ci		return -EINVAL;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	usr = file_priv->driver_priv;
166562306a36Sopenharmony_ci	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
166662306a36Sopenharmony_ci	if (!usr->qddev) {
166762306a36Sopenharmony_ci		ret = -ENODEV;
166862306a36Sopenharmony_ci		goto unlock_usr_srcu;
166962306a36Sopenharmony_ci	}
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	qdev = usr->qddev->qdev;
167262306a36Sopenharmony_ci	qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
167362306a36Sopenharmony_ci	if (qdev->in_reset) {
167462306a36Sopenharmony_ci		ret = -ENODEV;
167562306a36Sopenharmony_ci		goto unlock_dev_srcu;
167662306a36Sopenharmony_ci	}
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	if (args->dbc_id >= qdev->num_dbc) {
167962306a36Sopenharmony_ci		ret = -EINVAL;
168062306a36Sopenharmony_ci		goto unlock_dev_srcu;
168162306a36Sopenharmony_ci	}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	dbc = &qdev->dbc[args->dbc_id];
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	rcu_id = srcu_read_lock(&dbc->ch_lock);
168662306a36Sopenharmony_ci	if (dbc->usr != usr) {
168762306a36Sopenharmony_ci		ret = -EPERM;
168862306a36Sopenharmony_ci		goto unlock_ch_srcu;
168962306a36Sopenharmony_ci	}
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	obj = drm_gem_object_lookup(file_priv, args->handle);
169262306a36Sopenharmony_ci	if (!obj) {
169362306a36Sopenharmony_ci		ret = -ENOENT;
169462306a36Sopenharmony_ci		goto unlock_ch_srcu;
169562306a36Sopenharmony_ci	}
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	bo = to_qaic_bo(obj);
169862306a36Sopenharmony_ci	timeout = args->timeout ? args->timeout : wait_exec_default_timeout_ms;
169962306a36Sopenharmony_ci	timeout = msecs_to_jiffies(timeout);
170062306a36Sopenharmony_ci	ret = wait_for_completion_interruptible_timeout(&bo->xfer_done, timeout);
170162306a36Sopenharmony_ci	if (!ret) {
170262306a36Sopenharmony_ci		ret = -ETIMEDOUT;
170362306a36Sopenharmony_ci		goto put_obj;
170462306a36Sopenharmony_ci	}
170562306a36Sopenharmony_ci	if (ret > 0)
170662306a36Sopenharmony_ci		ret = 0;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	if (!dbc->usr)
170962306a36Sopenharmony_ci		ret = -EPERM;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ciput_obj:
171262306a36Sopenharmony_ci	drm_gem_object_put(obj);
171362306a36Sopenharmony_ciunlock_ch_srcu:
171462306a36Sopenharmony_ci	srcu_read_unlock(&dbc->ch_lock, rcu_id);
171562306a36Sopenharmony_ciunlock_dev_srcu:
171662306a36Sopenharmony_ci	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
171762306a36Sopenharmony_ciunlock_usr_srcu:
171862306a36Sopenharmony_ci	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
171962306a36Sopenharmony_ci	return ret;
172062306a36Sopenharmony_ci}
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ciint qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
172362306a36Sopenharmony_ci{
172462306a36Sopenharmony_ci	struct qaic_perf_stats_entry *ent = NULL;
172562306a36Sopenharmony_ci	struct qaic_perf_stats *args = data;
172662306a36Sopenharmony_ci	int usr_rcu_id, qdev_rcu_id;
172762306a36Sopenharmony_ci	struct drm_gem_object *obj;
172862306a36Sopenharmony_ci	struct qaic_device *qdev;
172962306a36Sopenharmony_ci	struct qaic_user *usr;
173062306a36Sopenharmony_ci	struct qaic_bo *bo;
173162306a36Sopenharmony_ci	int ret, i;
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	usr = file_priv->driver_priv;
173462306a36Sopenharmony_ci	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
173562306a36Sopenharmony_ci	if (!usr->qddev) {
173662306a36Sopenharmony_ci		ret = -ENODEV;
173762306a36Sopenharmony_ci		goto unlock_usr_srcu;
173862306a36Sopenharmony_ci	}
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	qdev = usr->qddev->qdev;
174162306a36Sopenharmony_ci	qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
174262306a36Sopenharmony_ci	if (qdev->in_reset) {
174362306a36Sopenharmony_ci		ret = -ENODEV;
174462306a36Sopenharmony_ci		goto unlock_dev_srcu;
174562306a36Sopenharmony_ci	}
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	if (args->hdr.dbc_id >= qdev->num_dbc) {
174862306a36Sopenharmony_ci		ret = -EINVAL;
174962306a36Sopenharmony_ci		goto unlock_dev_srcu;
175062306a36Sopenharmony_ci	}
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	ent = kcalloc(args->hdr.count, sizeof(*ent), GFP_KERNEL);
175362306a36Sopenharmony_ci	if (!ent) {
175462306a36Sopenharmony_ci		ret = -EINVAL;
175562306a36Sopenharmony_ci		goto unlock_dev_srcu;
175662306a36Sopenharmony_ci	}
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	ret = copy_from_user(ent, u64_to_user_ptr(args->data), args->hdr.count * sizeof(*ent));
175962306a36Sopenharmony_ci	if (ret) {
176062306a36Sopenharmony_ci		ret = -EFAULT;
176162306a36Sopenharmony_ci		goto free_ent;
176262306a36Sopenharmony_ci	}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	for (i = 0; i < args->hdr.count; i++) {
176562306a36Sopenharmony_ci		obj = drm_gem_object_lookup(file_priv, ent[i].handle);
176662306a36Sopenharmony_ci		if (!obj) {
176762306a36Sopenharmony_ci			ret = -ENOENT;
176862306a36Sopenharmony_ci			goto free_ent;
176962306a36Sopenharmony_ci		}
177062306a36Sopenharmony_ci		bo = to_qaic_bo(obj);
177162306a36Sopenharmony_ci		/*
177262306a36Sopenharmony_ci		 * perf stats ioctl is called before wait ioctl is complete then
177362306a36Sopenharmony_ci		 * the latency information is invalid.
177462306a36Sopenharmony_ci		 */
177562306a36Sopenharmony_ci		if (bo->perf_stats.req_processed_ts < bo->perf_stats.req_submit_ts) {
177662306a36Sopenharmony_ci			ent[i].device_latency_us = 0;
177762306a36Sopenharmony_ci		} else {
177862306a36Sopenharmony_ci			ent[i].device_latency_us = div_u64((bo->perf_stats.req_processed_ts -
177962306a36Sopenharmony_ci							    bo->perf_stats.req_submit_ts), 1000);
178062306a36Sopenharmony_ci		}
178162306a36Sopenharmony_ci		ent[i].submit_latency_us = div_u64((bo->perf_stats.req_submit_ts -
178262306a36Sopenharmony_ci						    bo->perf_stats.req_received_ts), 1000);
178362306a36Sopenharmony_ci		ent[i].queue_level_before = bo->perf_stats.queue_level_before;
178462306a36Sopenharmony_ci		ent[i].num_queue_element = bo->total_slice_nents;
178562306a36Sopenharmony_ci		drm_gem_object_put(obj);
178662306a36Sopenharmony_ci	}
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	if (copy_to_user(u64_to_user_ptr(args->data), ent, args->hdr.count * sizeof(*ent)))
178962306a36Sopenharmony_ci		ret = -EFAULT;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_cifree_ent:
179262306a36Sopenharmony_ci	kfree(ent);
179362306a36Sopenharmony_ciunlock_dev_srcu:
179462306a36Sopenharmony_ci	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
179562306a36Sopenharmony_ciunlock_usr_srcu:
179662306a36Sopenharmony_ci	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
179762306a36Sopenharmony_ci	return ret;
179862306a36Sopenharmony_ci}
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_cistatic void empty_xfer_list(struct qaic_device *qdev, struct dma_bridge_chan *dbc)
180162306a36Sopenharmony_ci{
180262306a36Sopenharmony_ci	unsigned long flags;
180362306a36Sopenharmony_ci	struct qaic_bo *bo;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	spin_lock_irqsave(&dbc->xfer_lock, flags);
180662306a36Sopenharmony_ci	while (!list_empty(&dbc->xfer_list)) {
180762306a36Sopenharmony_ci		bo = list_first_entry(&dbc->xfer_list, typeof(*bo), xfer_list);
180862306a36Sopenharmony_ci		bo->queued = false;
180962306a36Sopenharmony_ci		list_del(&bo->xfer_list);
181062306a36Sopenharmony_ci		spin_unlock_irqrestore(&dbc->xfer_lock, flags);
181162306a36Sopenharmony_ci		dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir);
181262306a36Sopenharmony_ci		complete_all(&bo->xfer_done);
181362306a36Sopenharmony_ci		drm_gem_object_put(&bo->base);
181462306a36Sopenharmony_ci		spin_lock_irqsave(&dbc->xfer_lock, flags);
181562306a36Sopenharmony_ci	}
181662306a36Sopenharmony_ci	spin_unlock_irqrestore(&dbc->xfer_lock, flags);
181762306a36Sopenharmony_ci}
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ciint disable_dbc(struct qaic_device *qdev, u32 dbc_id, struct qaic_user *usr)
182062306a36Sopenharmony_ci{
182162306a36Sopenharmony_ci	if (!qdev->dbc[dbc_id].usr || qdev->dbc[dbc_id].usr->handle != usr->handle)
182262306a36Sopenharmony_ci		return -EPERM;
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	qdev->dbc[dbc_id].usr = NULL;
182562306a36Sopenharmony_ci	synchronize_srcu(&qdev->dbc[dbc_id].ch_lock);
182662306a36Sopenharmony_ci	return 0;
182762306a36Sopenharmony_ci}
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci/**
183062306a36Sopenharmony_ci * enable_dbc - Enable the DBC. DBCs are disabled by removing the context of
183162306a36Sopenharmony_ci * user. Add user context back to DBC to enable it. This function trusts the
183262306a36Sopenharmony_ci * DBC ID passed and expects the DBC to be disabled.
183362306a36Sopenharmony_ci * @qdev: Qranium device handle
183462306a36Sopenharmony_ci * @dbc_id: ID of the DBC
183562306a36Sopenharmony_ci * @usr: User context
183662306a36Sopenharmony_ci */
183762306a36Sopenharmony_civoid enable_dbc(struct qaic_device *qdev, u32 dbc_id, struct qaic_user *usr)
183862306a36Sopenharmony_ci{
183962306a36Sopenharmony_ci	qdev->dbc[dbc_id].usr = usr;
184062306a36Sopenharmony_ci}
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_civoid wakeup_dbc(struct qaic_device *qdev, u32 dbc_id)
184362306a36Sopenharmony_ci{
184462306a36Sopenharmony_ci	struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id];
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	dbc->usr = NULL;
184762306a36Sopenharmony_ci	empty_xfer_list(qdev, dbc);
184862306a36Sopenharmony_ci	synchronize_srcu(&dbc->ch_lock);
184962306a36Sopenharmony_ci	/*
185062306a36Sopenharmony_ci	 * Threads holding channel lock, may add more elements in the xfer_list.
185162306a36Sopenharmony_ci	 * Flush out these elements from xfer_list.
185262306a36Sopenharmony_ci	 */
185362306a36Sopenharmony_ci	empty_xfer_list(qdev, dbc);
185462306a36Sopenharmony_ci}
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_civoid release_dbc(struct qaic_device *qdev, u32 dbc_id)
185762306a36Sopenharmony_ci{
185862306a36Sopenharmony_ci	struct bo_slice *slice, *slice_temp;
185962306a36Sopenharmony_ci	struct qaic_bo *bo, *bo_temp;
186062306a36Sopenharmony_ci	struct dma_bridge_chan *dbc;
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	dbc = &qdev->dbc[dbc_id];
186362306a36Sopenharmony_ci	if (!dbc->in_use)
186462306a36Sopenharmony_ci		return;
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	wakeup_dbc(qdev, dbc_id);
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	dma_free_coherent(&qdev->pdev->dev, dbc->total_size, dbc->req_q_base, dbc->dma_addr);
186962306a36Sopenharmony_ci	dbc->total_size = 0;
187062306a36Sopenharmony_ci	dbc->req_q_base = NULL;
187162306a36Sopenharmony_ci	dbc->dma_addr = 0;
187262306a36Sopenharmony_ci	dbc->nelem = 0;
187362306a36Sopenharmony_ci	dbc->usr = NULL;
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	list_for_each_entry_safe(bo, bo_temp, &dbc->bo_lists, bo_list) {
187662306a36Sopenharmony_ci		list_for_each_entry_safe(slice, slice_temp, &bo->slices, slice)
187762306a36Sopenharmony_ci			kref_put(&slice->ref_count, free_slice);
187862306a36Sopenharmony_ci		bo->sliced = false;
187962306a36Sopenharmony_ci		INIT_LIST_HEAD(&bo->slices);
188062306a36Sopenharmony_ci		bo->total_slice_nents = 0;
188162306a36Sopenharmony_ci		bo->dir = 0;
188262306a36Sopenharmony_ci		bo->dbc = NULL;
188362306a36Sopenharmony_ci		bo->nr_slice = 0;
188462306a36Sopenharmony_ci		bo->nr_slice_xfer_done = 0;
188562306a36Sopenharmony_ci		bo->queued = false;
188662306a36Sopenharmony_ci		bo->req_id = 0;
188762306a36Sopenharmony_ci		init_completion(&bo->xfer_done);
188862306a36Sopenharmony_ci		complete_all(&bo->xfer_done);
188962306a36Sopenharmony_ci		list_del(&bo->bo_list);
189062306a36Sopenharmony_ci		bo->perf_stats.req_received_ts = 0;
189162306a36Sopenharmony_ci		bo->perf_stats.req_submit_ts = 0;
189262306a36Sopenharmony_ci		bo->perf_stats.req_processed_ts = 0;
189362306a36Sopenharmony_ci		bo->perf_stats.queue_level_before = 0;
189462306a36Sopenharmony_ci	}
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci	dbc->in_use = false;
189762306a36Sopenharmony_ci	wake_up(&dbc->dbc_release);
189862306a36Sopenharmony_ci}
1899