/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
 * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
 */

#ifndef __HGSL_H_
#define __HGSL_H_

#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/dma-buf.h>
#include <linux/spinlock.h>
#include <linux/sync_file.h>
#include "hgsl_hyp.h"
#include "hgsl_memory.h"
#include "hgsl_tcsr.h"

#define HGSL_TIMELINE_NAME_LEN 64

#define HGSL_ISYNC_32BITS_TIMELINE 0
#define HGSL_ISYNC_64BITS_TIMELINE 1

/* Support upto 3 GVMs: 3 DBQs(Low/Medium/High priority) per GVM */
#define MAX_DB_QUEUE 9
#define HGSL_TCSR_NUM 4

#define HGSL_CONTEXT_NUM 256

#define HGSL_IOCTL_FUNC(_cmd, _func) \
	[_IOC_NR((_cmd))] = \
		{ .cmd = (_cmd), .func = (_func) }

struct qcom_hgsl;
struct hgsl_hsync_timeline;

struct hgsl_ioctl {
	unsigned int cmd;
	int (*func)(struct file *filep, unsigned long arg);
};

#pragma pack(push, 4)
struct shadow_ts {
	unsigned int sop;
	unsigned int unused1;
	unsigned int eop;
	unsigned int unused2;
	unsigned int reserved[6];
};
#pragma pack(pop)

struct reg {
	unsigned long paddr;
	unsigned long size;
	void __iomem *vaddr;
};

struct hw_version {
	unsigned int version;
	unsigned int release;
};

struct db_buffer {
	int32_t dwords;
	void  *vaddr;
};

struct dbq_ibdesc_priv {
	bool   buf_inuse;
	uint32_t context_id;
	uint32_t timestamp;
};

struct doorbell_queue {
	struct dma_buf *dma;
	struct dma_buf_map map;
	void *vbase;
	uint64_t  gmuaddr;
	struct db_buffer data;
	uint32_t state;
	int tcsr_idx;
	uint32_t dbq_idx;
	struct dbq_ibdesc_priv ibdesc_priv;
	uint32_t  ibdesc_max_size;
	struct mutex lock;
	atomic_t seq_num;
};

struct doorbell_context_queue {
	struct hgsl_mem_node *queue_mem;
	struct dma_buf_map map;
	uint32_t db_signal;
	uint32_t seq_num;
	void *queue_header;
	void *queue_body;
	void *indirect_ibs;
	uint32_t queue_header_gmuaddr;
	uint32_t queue_body_gmuaddr;
	uint32_t indirect_ibs_gmuaddr;
	uint32_t queue_size;
	int irq_idx;
};

struct qcom_hgsl {
	struct device *dev;

	/* character device info */
	struct cdev cdev;
	dev_t device_no;
	struct class *driver_class;
	struct device *class_dev;

	/* registers mapping */
	struct reg reg_ver;
	struct reg reg_dbidx;

	struct doorbell_queue dbq[MAX_DB_QUEUE];
	struct hgsl_dbq_info dbq_info[MAX_DB_QUEUE];

	/* Could disable db and use isync only */
	bool db_off;

	/* global doorbell tcsr */
	struct hgsl_tcsr *tcsr[HGSL_TCSR_NUM][HGSL_TCSR_ROLE_MAX];
	int tcsr_idx;
	struct hgsl_context **contexts;
	rwlock_t ctxt_lock;

	struct list_head active_wait_list;
	spinlock_t active_wait_lock;

	struct workqueue_struct *wq;
	struct work_struct ts_retire_work;

	struct hw_version *ver;
	struct hgsl_hyp_priv_t global_hyp;
	bool global_hyp_inited;
	struct mutex mutex;
	struct list_head active_list;
	struct list_head release_list;
	struct workqueue_struct *release_wq;
	struct work_struct release_work;
	struct idr isync_timeline_idr;
	spinlock_t isync_timeline_lock;
	atomic64_t total_mem_size;
	bool default_iocoherency;

	/* Debug nodes */
	struct kobject sysfs;
	struct kobject *clients_sysfs;
	struct dentry *debugfs;
	struct dentry *clients_debugfs;
};

/**
 * HGSL context define
 **/
struct hgsl_context {
	struct hgsl_priv *priv;
	struct dma_buf_map map;
	uint32_t context_id;
	uint32_t devhandle;
	uint32_t flags;
	struct shadow_ts *shadow_ts;
	wait_queue_head_t wait_q;
	pid_t pid;
	bool dbq_assigned;
	uint32_t dbq_info;
	struct doorbell_queue *dbq;
	struct hgsl_mem_node *shadow_ts_node;
	uint32_t shadow_ts_flags;
	bool is_fe_shadow;
	bool in_destroy;
	bool destroyed;
	struct kref kref;

	uint32_t last_ts;
	struct hgsl_hsync_timeline *timeline;
	uint32_t queued_ts;
	bool is_killed;
	int tcsr_idx;
	struct mutex lock;
	struct doorbell_context_queue *dbcq;
	uint32_t dbcq_export_id;
};

struct hgsl_priv {
	struct qcom_hgsl *dev;
	pid_t pid;
	struct list_head node;
	struct hgsl_hyp_priv_t hyp_priv;
	struct mutex lock;
	struct rb_root mem_mapped;
	struct rb_root mem_allocated;
	int open_count;

	atomic64_t total_mem_size;

	/* sysfs stuff */
	struct kobject kobj;
	struct kobject sysfs_client;
	struct kobject sysfs_mem_size;
	struct dentry *debugfs_client;
	struct dentry *debugfs_mem;
	struct dentry *debugfs_memtype;
};


static inline bool hgsl_ts32_ge(uint32_t a, uint32_t b)
{
	static const uint32_t TIMESTAMP_WINDOW = 0x80000000;

	return (a - b) < TIMESTAMP_WINDOW;
}

static inline bool hgsl_ts64_ge(uint64_t a, uint64_t b)
{
	static const uint64_t TIMESTAMP_WINDOW = 0x8000000000000000LL;

	return (a - b) < TIMESTAMP_WINDOW;
}

static inline bool hgsl_ts_ge(uint64_t a, uint64_t b, bool is64)
{
	if (is64)
		return hgsl_ts64_ge(a, b);
	else
		return hgsl_ts32_ge((uint32_t)a, (uint32_t)b);
}

/**
 * struct hgsl_hsync_timeline - A sync timeline attached under each hgsl context
 * @kref: Refcount to keep the struct alive
 * @name: String to describe this timeline
 * @fence_context: Used by the fence driver to identify fences belonging to
 *		   this context
 * @child_list_head: List head for all fences on this timeline
 * @lock: Spinlock to protect this timeline
 * @last_ts: Last timestamp when signaling fences
 */
struct hgsl_hsync_timeline {
	struct kref kref;
	struct hgsl_context *context;

	char name[HGSL_TIMELINE_NAME_LEN];
	u64 fence_context;

	spinlock_t lock;
	struct list_head fence_list;
	unsigned int last_ts;
};

/**
 * struct hgsl_hsync_fence - A struct containing a fence and other data
 *				associated with it
 * @fence: The fence struct
 * @sync_file: Pointer to the sync file
 * @parent: Pointer to the hgsl sync timeline this fence is on
 * @child_list: List of fences on the same timeline
 * @context_id: hgsl context id
 * @ts: Context timestamp that this fence is associated with
 */
struct hgsl_hsync_fence {
	struct dma_fence fence;
	struct sync_file *sync_file;
	struct hgsl_hsync_timeline *timeline;
	struct list_head child_list;
	u32 context_id;
	unsigned int ts;
};

struct hgsl_isync_timeline {
	struct kref kref;
	struct list_head free_list;
	char name[HGSL_TIMELINE_NAME_LEN];
	int id;
	struct hgsl_priv *priv;
	struct list_head fence_list;
	u64 context;
	spinlock_t lock;
	u64 last_ts;
	u32 flags;
	bool is64bits;
};

struct hgsl_isync_fence {
	struct dma_fence fence;
	struct list_head free_list;  /* For free in batch */
	struct hgsl_isync_timeline *timeline;
	struct list_head child_list;
	u64 ts;
};

/* Fence for commands. */
struct hgsl_hsync_fence *hgsl_hsync_fence_create(
					struct hgsl_context *context,
					uint32_t ts);
int hgsl_hsync_fence_create_fd(struct hgsl_context *context,
				uint32_t ts);
int hgsl_hsync_timeline_create(struct hgsl_context *context);
void hgsl_hsync_timeline_signal(struct hgsl_hsync_timeline *timeline,
						unsigned int ts);
void hgsl_hsync_timeline_put(struct hgsl_hsync_timeline *timeline);
void hgsl_hsync_timeline_fini(struct hgsl_context *context);

/* Fence for process sync. */
int hgsl_isync_timeline_create(struct hgsl_priv *priv,
				    uint32_t *timeline_id,
				    uint32_t flags,
				    uint64_t initial_ts);
int hgsl_isync_timeline_destroy(struct hgsl_priv *priv, uint32_t id);
void hgsl_isync_fini(struct hgsl_priv *priv);
int hgsl_isync_fence_create(struct hgsl_priv *priv, uint32_t timeline_id,
				uint32_t ts, bool ts_is_valid, int *fence_fd);
int hgsl_isync_fence_signal(struct hgsl_priv *priv, uint32_t timeline_id,
							       int fence_fd);
int hgsl_isync_forward(struct hgsl_priv *priv, uint32_t timeline_id,
								uint64_t ts, bool check_owner);
int hgsl_isync_query(struct hgsl_priv *priv, uint32_t timeline_id,
							uint64_t *ts);
int hgsl_isync_wait_multiple(struct hgsl_priv *priv, struct hgsl_timeline_wait *param);

#endif /* __HGSL_H_ */
