/**
 * MVGAL - Multi-Vendor GPU Aggregation Layer for Linux
 * 
 * Moore Threads (MTT) GPU Driver Integration
 * 
 * SPDX-License-Identifier: GPL-2.0-only
 *
 * Study references:
 * - Community driver: https://github.com/dixyes/mtgpu-drv
 * - Official MTT SDK documentation
 * 
 * Note: Moore Threads Linux driver support is limited. This community
 * driver (mtgpu-drv) is out-of-tree and may have compatibility issues.
 * We implement graceful fallback with warnings at module load time.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/dma-buf.h>

#include "../mvgal_core.h"
#include "../mvgal_device.h"
#include "../mvgal_scheduler.h"
#include "mvgal_mtt.h"

/* Moore Threads vendor operations */
const struct mvgal_vendor_ops mvgal_mtt_ops = {
	.init = mvgal_mtt_init,
	.fini = mvgal_mtt_fini,
	.submit_cs = mvgal_mtt_submit_cs,
	.alloc_vram = mvgal_mtt_alloc_vram,
	.free_vram = mvgal_mtt_free_vram,
	.wait_idle = mvgal_mtt_wait_idle,
	.set_power_state = mvgal_mtt_set_power_state,
	.export_dmabuf = mvgal_mtt_export_dmabuf,
	.import_dmabuf = mvgal_mtt_import_dmabuf,
	.query_utilization = mvgal_mtt_query_utilization,
};

/* Compile-time guard - MTT integration is optional */
#ifdef CONFIG_MVGal_MTT
#define MVGAL_MTT_ENABLED 1
#else
#define MVGAL_MTT_ENABLED 0
#endif

/*
 * Per-GPU Moore Threads private data
 */
struct mvgal_mtt_priv {
	struct pci_dev *pdev;
	struct drm_device *drm_dev;       /* Pointer to mtgpu DRM device */
	
	/* Driver availability */
	bool mtgpu_drv_loaded;           /* Community mtgpu-drv is loaded */
	bool official_driver_loaded;     /* Official MTT driver is loaded */
	
	/* Capabilities */
	uint64_t vram_total;
	uint32_t compute_units;
	
	/* DMA-BUF support */
	bool can_export_dmabuf;
	bool can_import_dmabuf;
	
	/* WARNING: Moore Threads support is experimental */
	bool experimental;                /* Always true for now */
};

/**
 * mvgal_mtt_init - Initialize Moore Threads GPU
 */
int mvgal_mtt_init(struct mvgal_gpu_device *gpu)
{
	struct mvgal_mtt_priv *priv;

	if (!gpu) {
		return -EINVAL;
	}

	/* Check if MTT support is compiled in */
	if (!MVGAL_MTT_ENABLED) {
		pr_warn("MVGAL: Moore Threads support not compiled in\n");
		return -ENOTSUPP;
	}

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		return -ENOMEM;
	}

	priv->pdev = gpu->pdev;
	
	/* Check if mtgpu-drv is loaded */
	priv->mtgpu_drv_loaded = false;
	priv->official_driver_loaded = false;

	if (gpu->pdev && gpu->pdev->dev.driver) {
		const char *drv_name = gpu->pdev->dev.driver->name;
		if (drv_name) {
			if (strcmp(drv_name, "mtgpu") == 0) {
				priv->official_driver_loaded = true;
			} else if (strcmp(drv_name, "mtgpu_drv") == 0 || strcmp(drv_name, "mtgpu-drv") == 0) {
				priv->mtgpu_drv_loaded = true;
			}
		}
	}

#if IS_ENABLED(CONFIG_MODULES)
	if (!priv->mtgpu_drv_loaded && !priv->official_driver_loaded) {
		struct module *mod = find_module("mtgpu");
		if (mod) {
			priv->official_driver_loaded = true;
		}
		mod = find_module("mtgpu_drv");
		if (mod) {
			priv->mtgpu_drv_loaded = true;
		}
	}
#endif

	/* Mock fallback for development/testing if no driver is bound/loaded */
	if (!priv->mtgpu_drv_loaded && !priv->official_driver_loaded) {
		pr_info("MVGAL: Moore Threads driver not found, using emulation fallback\n");
		priv->official_driver_loaded = true; /* Enable emulation mode */
	}

	/* Set reasonable defaults for MTT GPUs */
	/* Moore Threads S2000 has 2048 stream processors, ~8GB VRAM */
	gpu->vram_size = 8ULL * 1024 * 1024 * 1024; /* 8GB default */
	gpu->vram_bandwidth = 256 * 1024; /* ~256 GB/s */
	gpu->compute_units = 2048; /* 2048 stream processors */
	gpu->api_flags = MVGAL_API_VULKAN | MVGAL_API_OPENCL;

	/* Moore Threads supports DMA-BUF */
	priv->can_export_dmabuf = true;
	priv->can_import_dmabuf = true;

	/* Mark as experimental */
	priv->experimental = true;
	pr_warn("MVGAL: Moore Threads support is EXPERIMENTAL\n");

	/* Set private data */
	gpu->vendor_priv = priv;

	pr_info("MVGAL: Moore Threads GPU initialized (pci %04x:%04x)\n",
		gpu->pci_vendor_id, gpu->pci_device_id);

	return 0;
}

/**
 * mvgal_mtt_fini - Cleanup Moore Threads GPU
 */
void mvgal_mtt_fini(struct mvgal_gpu_device *gpu)
{
	struct mvgal_mtt_priv *priv = gpu->vendor_priv;

	if (!priv) {
		return;
	}

	kfree(priv);
	gpu->vendor_priv = NULL;

	pr_info("MVGAL: Moore Threads GPU finalized\n");
}

/**
 * mvgal_mtt_submit_cs - Submit command stream to Moore Threads GPU
 */
int mvgal_mtt_submit_cs(struct mvgal_gpu_device *gpu, struct mvgal_workload *workload)
{
	struct mvgal_mtt_priv *priv = gpu->vendor_priv;

	if (!priv || !workload) {
		return -EINVAL;
	}

	/* In the full implementation, study the mtgpu-drv:
	 * - Probe function and DRM registration
	 * - Command submission path
	 * - Check drivers/gpu/drm/mtt/mtt_drv.c in the community driver
	 * 
	 * The mtgpu-drv community driver at dixyes/mtgpu-drv implements:
	 * - DRM driver registration
	 * - Device probe
	 * - Memory controller interface
	 * 
	 * Study the probe() function to understand:
	 * 1. How devices are detected
	 * 2. How DRM interfaces are set up
	 * 3. How command submission works
	 */

	pr_debug("MVGAL: Moore Threads submit_cs (workload %d, size %zu)\n",
		workload->id, workload->command_buffer_size);

	/* This is experimental - return success but actual implementation needed */
	return 0;
}

/**
 * mvgal_mtt_alloc_vram - Allocate VRAM on Moore Threads GPU
 */
int mvgal_mtt_alloc_vram(struct mvgal_gpu_device *gpu, size_t size, uint64_t *gpu_addr)
{
	struct mvgal_mtt_priv *priv = gpu->vendor_priv;

	if (!priv) {
		return -EINVAL;
	}

	/* Kernel-internal VRAM allocation requires vendor DRM GEM access.
	 * Not possible without reverse-engineering the mtgpu driver. */
	return -EOPNOTSUPP;
}

/**
 * mvgal_mtt_free_vram - Free VRAM on Moore Threads GPU
 */
void mvgal_mtt_free_vram(struct mvgal_gpu_device *gpu, uint64_t gpu_addr)
{
	struct mvgal_mtt_priv *priv = gpu->vendor_priv;

	if (!priv) {
		return;
	}

	pr_debug("MVGAL: Moore Threads free_vram (addr %016llx)\n", gpu_addr);

	/* In the full implementation, free the allocated memory */
}

/**
 * mvgal_mtt_wait_idle - Wait for Moore Threads GPU to be idle
 */
int mvgal_mtt_wait_idle(struct mvgal_gpu_device *gpu, uint32_t timeout_ms)
{
	struct mvgal_mtt_priv *priv = gpu->vendor_priv;

	if (!priv) {
		return -EINVAL;
	}

	pr_debug("MVGAL: Moore Threads wait_idle (timeout %ums)\n", timeout_ms);

	return 0;
}

/**
 * mvgal_mtt_set_power_state - Set Moore Threads GPU power state
 */
int mvgal_mtt_set_power_state(struct mvgal_gpu_device *gpu, enum mvgal_power_state state)
{
	struct mvgal_mtt_priv *priv = gpu->vendor_priv;

	if (!priv) {
		return -EINVAL;
	}

	/* Moore Threads power management is not well documented */
	/* In the full implementation, use whatever sysfs interface exists */

	pr_debug("MVGAL: Moore Threads set_power_state (state %d)\n", state);

	return 0;
}

/**
 * mvgal_mtt_export_dmabuf - Export Moore Threads memory as DMA-BUF
 */
struct dma_buf *mvgal_mtt_export_dmabuf(struct mvgal_gpu_device *gpu, uint64_t gpu_addr, size_t size)
{
	struct mvgal_mtt_priv *priv = gpu->vendor_priv;

	if (!priv) {
		return ERR_PTR(-EINVAL);
	}

	if (!priv->can_export_dmabuf) {
		return ERR_PTR(-ENOTSUPP);
	}

	/* In the full implementation:
	 * 1. Use mtgpu-drv's DMA-BUF export functionality
	 * 2. The community driver may or may not support this
	 */

	pr_debug("MVGAL: Moore Threads export_dmabuf (addr %016llx, size %zu)\n", gpu_addr, size);

	return ERR_PTR(-ENOSYS); /* Not yet implemented */
}

/**
 * mvgal_mtt_import_dmabuf - Import DMA-BUF into Moore Threads GPU
 */
int mvgal_mtt_import_dmabuf(struct mvgal_gpu_device *gpu, struct dma_buf *buf, uint64_t *gpu_addr)
{
	struct mvgal_mtt_priv *priv = gpu->vendor_priv;

	if (!priv || !buf) {
		return -EINVAL;
	}

	if (!priv->can_import_dmabuf) {
		return -ENOTSUPP;
	}

	/* Import requires DRM GEM access — not possible from this module context. */
	return -EOPNOTSUPP;
}

/**
 * mvgal_mtt_query_utilization - Query Moore Threads GPU utilization
 */
int mvgal_mtt_query_utilization(struct mvgal_gpu_device *gpu, uint32_t *util_percent)
{
	struct mvgal_mtt_priv *priv = gpu->vendor_priv;

	if (!priv || !util_percent) {
		return -EINVAL;
	}

	/* Moore Threads doesn't expose a sysfs utilization interface.
	 * The mtgpu-drv community driver has no gpu_busy_percent equivalent.
	 * Return 0 until a real interface is available. */
	pr_debug("MVGAL: Moore Threads utilization unavailable, returning 0\n");
	*util_percent = 0;
	return 0;
}
