/*
 * A GEM style device manager for PCIe based OpenCL accelerators.
 *
 * Copyright (C) 2016-2019 Xilinx, Inc. All rights reserved.
 * Copyright (C) 2022 Advanced Micro Devices, Inc.
 *
 * Authors:
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#ifndef _XOCL_DRM_H
#define	_XOCL_DRM_H

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
#include <linux/hashtable.h>
#endif

#define MAX_MEM_BANK_COUNT 	128

typedef void (*xocl_execbuf_callback)(unsigned long data, int error);

/**
 * struct drm_xocl_exec_metadata - Meta data for exec bo
 *
 * @state: State of exec buffer object
 * @active: Reverse mapping to kds command object managed exclusively by kds
 */
struct drm_xocl_exec_metadata {
	enum drm_xocl_execbuf_state state;
	struct xocl_cmd            *active;
	struct work_struct	    compltn_work;
	xocl_execbuf_callback	    execbuf_cb_fn;
	void			   *execbuf_cb_data;
};

struct xocl_cma_memory {
	uint64_t		paddr;
	struct page		**pages;
	struct page		*regular_page;
	uint64_t		size;
	struct sg_table 	*sgt;
};

struct xocl_cma_bank {
	uint64_t		entry_sz;
	uint64_t		entry_num;
	uint64_t 		*phys_addrs;
	struct xocl_cma_memory	cma_mem[1];
};

struct xocl_mm {
	/* Memory manager */
	struct drm_mm           *mm;

	/* Array of bo stats for whole device memory manager */
	struct drm_xocl_mm_stat *bo_usage_stat;
};

struct xocl_mem_stat {
	struct list_head        link;
	uint32_t 		mem_idx;
	uint32_t 		slot_idx;
	
	/* Memory usage stats for each memory bank per slot */
	struct drm_xocl_mm_stat mm_usage_stat;
};

struct xocl_drm {
	xdev_handle_t		xdev;
	struct drm_device       *ddev;
	struct mutex            mm_lock;

	/* Memory manager */
	struct xocl_mm		*xocl_mm;
	struct drm_xocl_mm_stat mm_usage_stat[MAX_MEM_BANK_COUNT];

	/* Xocl driver memory list head */
	struct list_head        mem_list_head;
	struct mem_data		ps_mem_data;
	int			cma_bank_idx;
};

struct drm_xocl_bo {
	/* drm base object */
	struct drm_gem_object base;
	struct drm_mm_node    *mm_node;
	struct drm_xocl_exec_metadata metadata;
	struct page           **pages;
	struct sg_table       *sgt;
	void                  *vmapping;
	u64                   p2p_bar_offset;
	struct dma_buf        *dmabuf;
	const struct vm_operations_struct *dmabuf_vm_ops;
	unsigned              dma_nsg;
	unsigned              flags;
	unsigned              mem_idx;
	unsigned	      user_flags;
	/* variable to store the actual bo size instead of aligned size*/
	uint64_t 	      actual_bo_size;
};

struct drm_xocl_unmgd {
	struct page         **pages;
	struct sg_table      *sgt;
	unsigned int          npages;
	unsigned              flags;
};

struct drm_xocl_bo *__xocl_create_bo_ioctl(struct drm_device *dev,
	struct drm_file *filp, struct drm_xocl_create_bo *args);
struct drm_xocl_bo *xocl_drm_create_bo(struct xocl_drm *drm_p,
	uint64_t unaligned_size, unsigned user_flags);
void xocl_drm_free_bo(struct drm_gem_object *obj);


void xocl_mm_get_usage_stat(struct xocl_drm *drm_p, u32 ddr,
        struct drm_xocl_mm_stat *pstat);
void xocl_mm_update_usage_stat(struct xocl_drm *drm_p, u32 ddr,
        u64 size, int count);

int xocl_mm_insert_node(struct xocl_drm *drm_p, unsigned memidx,
                uint32_t slotidx, struct drm_xocl_bo *xobj, u64 size);

void *xocl_drm_init(xdev_handle_t xdev);
void xocl_drm_fini(struct xocl_drm *drm_p);
int xocl_init_mem(struct xocl_drm *drm_p, uint32_t slot_id);
int xocl_cleanup_mem_all(struct xocl_drm *drm_p);
int xocl_cleanup_mem(struct xocl_drm *drm_p, uint32_t slot_id);

int xocl_check_topology(struct xocl_drm *drm_p);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
vm_fault_t xocl_gem_fault(struct vm_fault *vmf);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
int xocl_gem_fault(struct vm_fault *vmf);
#else
int xocl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
#endif

static inline struct sg_table *xocl_prime_pages_to_sg(struct drm_device *dev,
						      struct page **pages, unsigned int nr_pages)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
	return drm_prime_pages_to_sg(dev, pages, nr_pages);
#else
	#if defined(RHEL_RELEASE_CODE)
		#if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,4)
			(void)dev;
			return drm_prime_pages_to_sg(dev, pages, nr_pages);
		#else
			(void)dev;
			return drm_prime_pages_to_sg(pages, nr_pages);
		#endif
	#else
                (void)dev;
                return drm_prime_pages_to_sg(pages, nr_pages);
	#endif
#endif
}

static inline struct drm_xocl_bo *to_xocl_bo(struct drm_gem_object *bo)
{
	return (struct drm_xocl_bo *)bo;
}

int xocl_init_unmgd(struct drm_xocl_unmgd *unmgd, uint64_t data_ptr,
		        uint64_t size, u32 write);
void xocl_finish_unmgd(struct drm_xocl_unmgd *unmgd);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) || defined(RHEL_8_5_GE)
extern const struct drm_gem_object_funcs xocl_gem_object_funcs;
#endif

#endif
