/*
 * A GEM style device manager for PCIe based OpenCL accelerators.
 *
 * Copyright (C) 2016-2019 Xilinx, Inc. All rights reserved.
 *
 * 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

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

#define IS_HOST_MEM(m_tag)	(!strncmp(m_tag, "HOST[0]", 7))
#define IS_PLRAM(m_tag)		(!strncmp(m_tag, "PLRAM[", 6))

/**
 * 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_drm {
	xdev_handle_t		xdev;
	/* memory management */
	struct drm_device       *ddev;
	/* Memory manager */
	struct drm_mm           *mm;
	struct mutex            mm_lock;
	struct drm_xocl_mm_stat **mm_usage_stat;
	/* Array of bo usage stats */
	struct drm_xocl_mm_stat *bo_usage_stat;

	int			cma_bank_idx;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
	DECLARE_HASHTABLE(mm_range, 6);
#endif
};

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;
};

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_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, u32 ddr,
                struct drm_mm_node *node, u64 size);

void *xocl_drm_init(xdev_handle_t xdev);
void xocl_drm_fini(struct xocl_drm *drm_p);
uint32_t xocl_get_shared_ddr(struct xocl_drm *drm_p, struct mem_data *m_data);
int xocl_init_mem(struct xocl_drm *drm_p);
int xocl_cleanup_mem(struct xocl_drm *drm_p);

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
