/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * SPDX-FileCopyrightText: Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES.
 * All rights reserved.
 */

#ifndef __VMAP_H__
#define __VMAP_H__

#include "common.h"

/*
 * Virtual mapping abstraction offers pinning functionalities for the very
 * the specific use-case(s) to achieve NvStreams streaming over NvSciC2cPcie
 * on Tegra PCIe EP or Tegra PCIe RP. It's scope is specific to/for and limited
 * to NvSciC2cPcie.
 *
 * Virtual mapping interfaces primarily offer mapping(also referred as pinning
 * in tegra context) and unmapping(also referred as unpinning in tegra context)
 * dma_buf backed NvRmMemHandles or Syncpoint Shim backed NvRmHost1xSyncpoints.
 * In this abstraction, Memory/Mem objects/objs are NvRmMemHandles(or it's FD),
 * and Sync objects/objs are NvRmHost1xSyncpointHandles (or it's FD).
 *
 * This SW shall run either on Tegra PCIe RP or Tegra PCIe EP.
 *
 * ASSUMPTION: Once pages are mapped/pinned, on tegra they shall not swap out.
 *
 * On tegra, Mem objects are the NvRmMemHandles with struct dma_buf backing.
 * On tegra, Sync objects are the NvRmHost1xSyncpointHandles with Syncpoint
 * shim (aperture) backing.
 *
 * Each object is considered one of the following:
 *
 * 1. Local - The object visibility is limited to local SoC on which it is
 *    allocated. Also sometimes referred as Unexported. These objects are mapped
 *    to PCIe device address space and it's whereabouts are NOT shared with
 *    remote SoC.
 * 2. Export/Exported - The object visibility is across PCIe to remote SoC
 *    and subsequently remote SoC can initiate writes to it. For tegra,
 *    exported objects are never read over PCIe. These objects are mapped to
 *    PCIe device address space and it's whereabouts shall be shared with remote
 *    SoC.
 * 3. Import/Imported - This is a virtual object which points to the
 *    corresponding object exported by remote SoC. Being virtual, it shall be
 *    similar for both Mem and Sync objects - therefore Imported object is
 *    just an import/imported object and NOT imported Mem/Sync obj.
 *
 * Protection/Permission flags:
 * a. Local Mem objects map to PCIe device with READ access.
 * b. Export Mem objects map to PCIe device with WRITE access (We export for
 *    remote to write to it via CPU or PCIe eDMA(zero-copy).
 * c. Local Sync objects are not mapped to PCIe device as NvSciC2cPcie KMD
 *    shall signal(write) them via NvHost1x interface.
 * d. Export Sync objects map to PCIe device with WRITE access (We export for
 *    remote to signal(write) to it via CPU or PCIe eDMA).
 *
 * Mapping:
 * Tegra PCIe EP exposes three BAR memory windows towards PCIe RP. Of these,
 * Only one (1) (BAR0) is available for NvSciC2cPcie access. Therefore, all
 * Export objects must be mapped to a PCIe address which this PCIe EP BAR0
 * translation is programmed with. With the overall PCIe address space being
 * much bigger than the PCIe BAR0 space, there is an explicit need to stitch
 * all Exported objects to a single region. This requires Export objects be
 * mapped using iommu apis to achieve BAR stitching and this mapping is
 * referred as client managed with NvSciC2cPcie managing the iova region.
 *
 * Tegra PCIe EP has limited set of translation registers for it's CPU to raise
 * PCIe transactions towards a PCIe RP. Therefore, when Sync objects are
 * exported from PCIe RP towards PCIe EP to CPU signal them, they must be mapped
 * to a single iova region which PCIe EP has setup for it's translaction
 * registers. This is strictly not applicable for Exported Mem objects as they
 * are written by eDMA always by the importing SoC. However, to keep parity and
 * symmetry in the behavior of Exported Mem objects from Tegra PCIe EE->PCIe RP,
 * Exported Mem objects from Tegra PCIe RP->Tegra PCIe EP shall also be mapped
 * to a client managed iova region.
 *
 * For Local Mem objects which are accessed by local SoC PCIe eDMA, they
 * can be mapped to any pcie address outside the reserved iova region by
 * NvSciC2cPcie for exports. This doesn't require any iova management by
 * client and is prefectly fine to use pci device (smmu) managed iova.
 * This mapping is referred as device(dev) managed mapping. Only on Tegra PCIe
 * RP, Exported Mem objects can be mapped using dev managed iova as Tegra PCIe
 * EP shall write them using eDMA, but as stated before, to keep parity and
 * symmetry in the behavior of Exported Mem objects from Tegra PCIe EE->PCIe RP,
 * Exported Mem objects from Tegra PCIe RP->Tegra PCIe EP shall also be mapped
 * to a client managed iova region.
 *
 * All Sync objects (local or export) are mapped to NvSciC2cPcie for signalling
 * (Write access), based on QNX security policy, there shall be only one
 * signaler allowed and, therefore, Sync objects are pinned/mapped just once.
 * Export Mem objects are mapped to NvSciC2cPcie for remote SoC to produce data
 * (Write access), also in the lack of N producer -> 1 consumer user-case,
 * remote Mem objects are pinned/mapped just once. However, Local Mem objects
 * which have Read access, can be mapped/pinned again. Essentially, all objects
 * requiring Write access by NvSciC2cPcie (PCIe device) are pinned/mapped once.
 *
 * Summary:
 *  i.   Types:
 *        a. Local Mem objects.
 *        b. Export Mem objects.
 *        c. Local Sync objects.
 *        d. Export Sync objects.
 *        e. Import objects.
 *  ii.  Mapping:
 *        a. Local Mem objects - dev managed (READ only).
 *        b. Export Mem objects - client managed (WRITE only).
 *            On Tegra PCIe EP:- compulsarily client managed.
 *            On Tegra PCIe RP:- Can be either dev managed or client managed.
 *            Choose client manged to be in symmetry with Tegra PCIe EP.
 *        c. Local Sync objects - Not mapped but pinned (tracked).
 *        d. Export Sync objects - client managed (WRITE ONLY).
 *            On Tegra PCIe EP:- compulsarily client managed.
 *            On Tegra PCIe RP:- Can be either dev managed (if Tegra PCIe RP use
 *            only eDMA for signaling, but we have use-cases for CPU signaling
 *            also from Tegra PCIe RP. Therefore choose client managed as it can
 *            satisfy CPU and eDMA signaling needs from remote Tegra PCIe EP.
 *        e. Import Objects - virtual objects pointing to exported objects by
 *            remote. Every exported object from a SoC must have a corresponding
 *            import object on remote SoC. Not mapped but pinned(tracked).
 */

/* forward declaration. */
struct driver_ctx_t;

/* Object type that shall be virtually mapped for PCIe access. */
enum vmap_obj_type {
	/* NvRmMemHandle (struct dma_buf *), aka, memobj.*/
	VMAP_OBJ_TYPE_MEM = STREAM_OBJ_TYPE_MEM,

	/* NvRmHost1xSyncpointHandle ( syncpt id), aka, memobj.*/
	VMAP_OBJ_TYPE_SYNC = STREAM_OBJ_TYPE_SYNC,

	/*(virtual) objects imported from remote SoC.*/
	VMAP_OBJ_TYPE_IMPORT,
};

/*
 * Permissions for pin/mapping Buff/Sync objs to PCIe device
 *
 * at the moment, WRITE for all EXPORT*
 * at the moment, READ for all LOCAL*
 */
enum vmap_obj_prot {
	/* read-only access by PCIe device. */
	VMAP_OBJ_PROT_READ = 1,

	/* write only access to PCIe device. */
	VMAP_OBJ_PROT_WRITE = 2,

	/*
	 * no use-case known today.
	 * VMAP_OBJ_PERM_READWRITE = 4,
	 */
};

/* Which IOVA to use for mapping Mem/Sync objs.*/
enum vmap_mngd {
	/*
	 * Stitching of all exported objects is done by reserving an IOVA region
	 * and mapping Mem and Sync objs to it. The reserved IOVA region is
	 * managed by client/user (NvSciC2cPcie) and use iommu apis to map Mem
	 * or Sync objects to the specific IOVA.
	 */
	VMAP_MNGD_CLIENT = 0,

	/*
	 * The IOVA is managed by pci dev, therefore dev managed. Used only for
	 * Mem objects (Local and possible for exported too).
	 */
	VMAP_MNGD_DEV,
};

/* Returned object attributes after mapping.*/
struct vmap_obj_attributes {
	enum vmap_obj_type type;
	s32 id;
	u64 iova;
	size_t size;
	size_t offsetof;
	/* only for local sync obj.*/
	u32 syncpt_id;
};

/* Parameters to map Mem object.*/
struct vmap_memobj_map_params {
	s32 fd;
	/* To allow mapping Export Mem objects as dev-managed - Tegra PCIe RP.*/
	enum vmap_mngd mngd;
	/* local/source mem as read-only, remote/export as write-only.*/
	enum vmap_obj_prot prot;
};

/* Parameters to map Sync object.*/
struct vmap_syncobj_map_params {
	s32 fd;
	u32 id;
	/* client mngd only.*/
	enum vmap_mngd mngd;
	/* write-only.*/
	enum vmap_obj_prot prot;
	/* local sync objs will not be pinned to pcie address space.*/
	bool pin_reqd;
};

/* Parameters to map Import object.*/
struct vmap_importobj_map_params {
	u64 export_desc;
};

struct vmap_obj_map_params {
	enum vmap_obj_type type;
	union vmap_params {
		struct vmap_memobj_map_params memobj;
		struct vmap_syncobj_map_params syncobj;
		struct vmap_importobj_map_params importobj;
	} u;
};

/* Parameters to register Import object, as received from remote NvSciC2cPcie.*/
struct vmap_importobj_reg_params {
	u64 export_desc;
	u64 iova;
	size_t size;
	size_t offsetof;
};

/* Entry point for the virtual mapping sub-module/abstraction. */
int
vmap_init(struct driver_ctx_t *drv_ctx, void **vmap_h);

/* Exit point for nvscic2c-pcie vmap sub-module/abstraction. */
void
vmap_deinit(void **vmap_h);

/* Map objects to pcie device.*/
int
vmap_obj_map(void *vmap_h, struct vmap_obj_map_params *params,
	     struct vmap_obj_attributes *attrib, uint64_t aperture_limit,
	     uint64_t *const aperture_inuse);

/* Unmap objects from pcie device.*/
int
vmap_obj_unmap(void *vmap_h, enum vmap_obj_type type, s32 obj_id);

/* Increment reference count for objects. */
int
vmap_obj_getref(void *vmap_h, enum vmap_obj_type type, s32 obj_id);

/* Decrement reference count for objects. */
int
vmap_obj_putref(void *vmap_h, enum vmap_obj_type type, s32 obj_id);
#endif // __VMAP_H__
