/**
 * MVGAL - Multi-Vendor GPU Aggregation Layer for Linux
 * 
 * GPU Virtualization Detection
 * 
 * SPDX-License-Identifier: GPL-2.0-only
 *
 * This module detects and identifies GPU virtualization capabilities:
 * - VFIO passthrough and mediated devices (mdev)
 * - SR-IOV Virtual Functions and Physical Functions
 * - NVIDIA vGPU
 * - AMD MxGPU
 * - Intel GVT-g
 */

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

#include "mvgal_core.h"
#include "mvgal_device.h"

/**
 * mvgal_detect_virtualization_caps - Detect virtualization capabilities for a GPU
 */
int mvgal_detect_virtualization_caps(struct mvgal_gpu_device *gpu)
{
	struct pci_dev *pdev = gpu->pdev;
	u16 vendor_id = pdev->vendor;
	u16 device_id = pdev->device;
	int ret = 0;

	if (!gpu)
		return -EINVAL;

	memset(&gpu->virt_caps, 0, sizeof(gpu->virt_caps));
	gpu->is_vf = false;
	gpu->is_pf = false;
	gpu->is_mdev = false;
	gpu->vgpu_type = 0;
	gpu->vgpu_instance = 0;

	/* Check if this is a Virtual Function */
	if (pdev->is_virtfn) {
		gpu->is_vf = true;
		gpu->virt_caps |= MVGAL_VIRT_SRIOV_VF;
		pr_debug("MVGAL: GPU %s is an SR-IOV VF\n", gpu->name);
	}

	/* Check if this is a Physical Function (has VFs) */
	if (pdev->physfn) {
		gpu->is_pf = true;
		gpu->virt_caps |= MVGAL_VIRT_SRIOV_PF;
		pr_debug("MVGAL: GPU %s is an SR-IOV PF\n", gpu->name);
	}

	/* Check for mediated device (mdev) support */
	if (pdev->driver && pdev->driver->probe) {
		/* Check if the driver supports the mdev bus type */
		if (sysfs_get_dirent(NULL, "/sys/bus/mdev") == NULL) {
			/* mdev bus not available */
		} else {
			/* Check for mdev class in driver */
			gpu->virt_caps |= MVGAL_VIRT_VFIO_MEDIATED;
			gpu->is_mdev = true;
			pr_debug("MVGAL: GPU %s supports mediated devices\n", gpu->name);
		}
	}

	/* Vendor-specific virtualization detection */
	switch (vendor_id) {
	case 0x10DE: /* NVIDIA */
		ret = mvgal_detect_nvidia_virt(gpu);
		break;
	case 0x1002: /* AMD */
		ret = mvgal_detect_amd_virt(gpu);
		break;
	case 0x8086: /* Intel */
		ret = mvgal_detect_intel_virt(gpu);
		break;
	default:
		break;
	}

	/* Check for VFIO passthrough capability */
	/* VFIO passthrough is always possible if IOMMU is available */
	if (iommu_present(&pci_bus_type)) {
		gpu->virt_caps |= MVGAL_VIRT_VFIO_PASSTHROUGH;
		pr_debug("MVGAL: GPU %s supports VFIO passthrough\n", gpu->name);
	}

	return ret;
}

/**
 * mvgal_detect_nvidia_virt - Detect NVIDIA virtualization capabilities
 */
static int mvgal_detect_nvidia_virt(struct mvgal_gpu_device *gpu)
{
	struct pci_dev *pdev = gpu->pdev;
	u16 device_id = pdev->device;

	/* Check for NVIDIA vGPU by examining PCI subsystem ID */
	/* NVIDIA vGPU devices have specific subsystem vendor IDs */
	u16 subsys_vendor = pdev->subsystem_vendor;
	u16 subsys_device = pdev->subsystem_device;

	/* Known NVIDIA vGPU subsystem IDs */
	switch (subsys_vendor) {
	case 0x10DE: /* NVIDIA */
		switch (subsys_device) {
		case 0x1180: /* GRID M60-2Q */
		case 0x1181: /* GRID M60-4Q */
		case 0x1182: /* GRID M60-8Q */
		case 0x1183: /* GRID M60-1Q */
		case 0x1184: /* GRID M60-2A */
		case 0x1185: /* GRID M60-4A */
		case 0x1186: /* GRID M60-8A */
		case 0x1187: /* GRID M60-1A */
		case 0x11C0: /* GRID P4-2Q */
		case 0x11C1: /* GRID P4-4Q */
		case 0x11C2: /* GRID P4-8Q */
		case 0x11C3: /* GRID P4-1Q */
		case 0x11C4: /* GRID P4-2B */
		case 0x11C5: /* GRID P4-4B */
		case 0x11C6: /* GRID P4-8B */
		case 0x11C7: /* GRID P4-1B */
			gpu->virt_caps |= MVGAL_VIRT_VGPU_NVIDIA;
			gpu->vgpu_type = subsys_device;
			pr_info("MVGAL: NVIDIA vGPU detected (type 0x%04x)\n", subsys_device);
			break;
		}
		break;
	}

	/* Check for NVIDIA vGPU driver presence */
	if (sysfs_get_dirent(NULL, "/module/nvidia_vgpu") != NULL) {
		gpu->virt_caps |= MVGAL_VIRT_VGPU_NVIDIA;
		pr_debug("MVGAL: NVIDIA vGPU kernel module detected\n");
	}

	/* Check for NVIDIA vGPU manager */
	if (sysfs_get_dirent(NULL, "/sys/module/nvidia_vgpu") != NULL) {
		gpu->virt_caps |= MVGAL_VIRT_VGPU_NVIDIA;
	}

	return 0;
}

/**
 * mvgal_detect_amd_virt - Detect AMD virtualization capabilities
 */
static int mvgal_detect_amd_virt(struct mvgal_gpu_device *gpu)
{
	struct pci_dev *pdev = gpu->pdev;

	/* Check for AMD MxGPU (virtualization) */
	/* AMD MxGPU is indicated by specific PCI IDs and subsystem IDs */
	u16 subsys_vendor = pdev->subsystem_vendor;

	/* Check for AMD MxGPU driver */
	if (sysfs_get_dirent(NULL, "/module/amdgpu") != NULL) {
		/* Check for MxGPU support in driver */
		if (sysfs_get_dirent(NULL, "/sys/module/amdgpu/parameters/mxgpu_enable") != NULL) {
			gpu->virt_caps |= MVGAL_VIRT_VGPU_AMD_MXGPU;
			pr_debug("MVGAL: AMD MxGPU support detected\n");
		}
	}

	/* Check for SR-IOV support on AMD GPUs */
	/* Most modern AMD GPUs support SR-IOV */
	if (device_id >= 0x6900) { /* Vega and newer */
		gpu->virt_caps |= MVGAL_VIRT_SRIOV_PF;
		pr_debug("MVGAL: AMD GPU supports SR-IOV\n");
	}

	return 0;
}

/**
 * mvgal_detect_intel_virt - Detect Intel virtualization capabilities
 */
static int mvgal_detect_intel_virt(struct mvgal_gpu_device *gpu)
{
	struct pci_dev *pdev = gpu->pdev;
	u16 device_id = pdev->device;

	/* Check for Intel GVT-g (Graphics Virtualization Technology) */
	/* GVT-g is supported on Skylake and newer integrated graphics */
	if (device_id >= 0x1900 && device_id <= 0x9B60) {
		/* Check for i915 GVT driver */
		if (sysfs_get_dirent(NULL, "/module/i915") != NULL) {
			/* Check for GVT enable parameter */
			if (sysfs_get_dirent(NULL, "/sys/module/i915/parameters/enable_gvt") != NULL) {
				gpu->virt_caps |= MVGAL_VIRT_VGPU_INTEL_GVT;
				pr_debug("MVGAL: Intel GVT-g support detected\n");
			}
		}
	}

	/* Check for Xe driver virtualization support */
	if (device_id >= 0x4680 && device_id <= 0x46FF) {
		/* DG2 and newer discrete GPUs */
		if (sysfs_get_dirent(NULL, "/module/xe") != NULL) {
			gpu->virt_caps |= MVGAL_VIRT_VFIO_MEDIATED;
			pr_debug("MVGAL: Intel Xe virtualization support detected\n");
		}
	}

	return 0;
}

/**
 * mvgal_is_vfio_mdev - Check if GPU is a VFIO mediated device
 */
bool mvgal_is_vfio_mdev(struct mvgal_gpu_device *gpu)
{
	return gpu && (gpu->virt_caps & MVGAL_VIRT_VFIO_MEDIATED);
}

/**
 * mvgal_is_sriov_vf - Check if GPU is an SR-IOV Virtual Function
 */
bool mvgal_is_sriov_vf(struct mvgal_gpu_device *gpu)
{
	return gpu && (gpu->virt_caps & MVGAL_VIRT_SRIOV_VF);
}

/**
 * mvgal_is_nvidia_vgpu - Check if GPU is an NVIDIA vGPU
 */
bool mvgal_is_nvidia_vgpu(struct mvgal_gpu_device *gpu)
{
	return gpu && (gpu->virt_caps & MVGAL_VIRT_VGPU_NVIDIA);
}

/**
 * mvgal_is_amd_mxgpu - Check if GPU is AMD MxGPU
 */
bool mvgal_is_amd_mxgpu(struct mvgal_gpu_device *gpu)
{
	return gpu && (gpu->virt_caps & MVGAL_VIRT_VGPU_AMD_MXGPU);
}

/**
 * mvgal_is_intel_gvt - Check if GPU supports Intel GVT-g
 */
bool mvgal_is_intel_gvt(struct mvgal_gpu_device *gpu)
{
	return gpu && (gpu->virt_caps & MVGAL_VIRT_VGPU_INTEL_GVT);
}