/*
 * Copyright © 2017 Keith Packard
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of the copyright holders not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  The copyright holders make no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 */

#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "radv_private.h"
#include "radv_cs.h"
#include "util/disk_cache.h"
#include "util/strtod.h"
#include "vk_util.h"
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <amdgpu.h>
#include <amdgpu_drm.h>
#include "winsys/amdgpu/radv_amdgpu_winsys_public.h"
#include "ac_llvm_util.h"
#include "vk_format.h"
#include "sid.h"
#include "util/debug.h"
#include "wsi_common_display.h"

#define MM_PER_PIXEL     (1.0/96.0 * 25.4)

VkResult
radv_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physical_device,
                                           uint32_t *property_count,
                                           VkDisplayPropertiesKHR *properties)
{
	RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);

	return wsi_display_get_physical_device_display_properties(
		physical_device,
		&pdevice->wsi_device,
		property_count,
		properties);
}

VkResult
radv_GetPhysicalDeviceDisplayProperties2KHR(VkPhysicalDevice physical_device,
                                            uint32_t *property_count,
                                            VkDisplayProperties2KHR *properties)
{
	RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);

	return wsi_display_get_physical_device_display_properties2(
		physical_device,
		&pdevice->wsi_device,
		property_count,
		properties);
}

VkResult
radv_GetPhysicalDeviceDisplayPlanePropertiesKHR(
	VkPhysicalDevice physical_device,
	uint32_t *property_count,
	VkDisplayPlanePropertiesKHR *properties)
{
	RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);

	return wsi_display_get_physical_device_display_plane_properties(
		physical_device,
		&pdevice->wsi_device,
		property_count,
		properties);
}

VkResult
radv_GetPhysicalDeviceDisplayPlaneProperties2KHR(
	VkPhysicalDevice physical_device,
	uint32_t *property_count,
	VkDisplayPlaneProperties2KHR *properties)
{
	RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);

	return wsi_display_get_physical_device_display_plane_properties2(
		physical_device,
		&pdevice->wsi_device,
		property_count,
		properties);
}

VkResult
radv_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physical_device,
                                         uint32_t plane_index,
                                         uint32_t *display_count,
                                         VkDisplayKHR *displays)
{
	RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);

	return wsi_display_get_display_plane_supported_displays(
		physical_device,
		&pdevice->wsi_device,
		plane_index,
		display_count,
		displays);
}


VkResult
radv_GetDisplayModePropertiesKHR(VkPhysicalDevice physical_device,
                                 VkDisplayKHR display,
                                 uint32_t *property_count,
                                 VkDisplayModePropertiesKHR *properties)
{
	RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);

	return wsi_display_get_display_mode_properties(physical_device,
						       &pdevice->wsi_device,
						       display,
						       property_count,
						       properties);
}

VkResult
radv_GetDisplayModeProperties2KHR(VkPhysicalDevice physical_device,
                                  VkDisplayKHR display,
                                  uint32_t *property_count,
                                  VkDisplayModeProperties2KHR *properties)
{
	RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);

	return wsi_display_get_display_mode_properties2(physical_device,
						        &pdevice->wsi_device,
						        display,
						        property_count,
						        properties);
}

VkResult
radv_CreateDisplayModeKHR(VkPhysicalDevice physical_device,
                          VkDisplayKHR display,
                          const VkDisplayModeCreateInfoKHR *create_info,
                          const VkAllocationCallbacks *allocator,
                          VkDisplayModeKHR *mode)
{
	RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);

	return wsi_display_create_display_mode(physical_device,
					       &pdevice->wsi_device,
					       display,
					       create_info,
					       allocator,
					       mode);
}

VkResult
radv_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physical_device,
                                    VkDisplayModeKHR mode_khr,
                                    uint32_t plane_index,
                                    VkDisplayPlaneCapabilitiesKHR *capabilities)
{
	RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);

	return wsi_get_display_plane_capabilities(physical_device,
						  &pdevice->wsi_device,
						  mode_khr,
						  plane_index,
						  capabilities);
}

VkResult
radv_GetDisplayPlaneCapabilities2KHR(VkPhysicalDevice physical_device,
                                     const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo,
                                     VkDisplayPlaneCapabilities2KHR *capabilities)
{
	RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);

	return wsi_get_display_plane_capabilities2(physical_device,
						   &pdevice->wsi_device,
						   pDisplayPlaneInfo,
						   capabilities);
}

VkResult
radv_CreateDisplayPlaneSurfaceKHR(
	VkInstance _instance,
	const VkDisplaySurfaceCreateInfoKHR *create_info,
	const VkAllocationCallbacks *allocator,
	VkSurfaceKHR *surface)
{
	RADV_FROM_HANDLE(radv_instance, instance, _instance);
	const VkAllocationCallbacks *alloc;

	if (allocator)
		alloc = allocator;
	else
		alloc = &instance->alloc;

	return wsi_create_display_surface(_instance, alloc,
					  create_info, surface);
}

VkResult
radv_ReleaseDisplayEXT(VkPhysicalDevice physical_device,
		       VkDisplayKHR     display)
{
	RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);

	return wsi_release_display(physical_device,
				   &pdevice->wsi_device,
				   display);
}

#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
VkResult
radv_AcquireXlibDisplayEXT(VkPhysicalDevice     physical_device,
			   Display              *dpy,
			   VkDisplayKHR         display)
{
	RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);

	return wsi_acquire_xlib_display(physical_device,
					&pdevice->wsi_device,
					dpy,
					display);
}

VkResult
radv_GetRandROutputDisplayEXT(VkPhysicalDevice  physical_device,
			      Display           *dpy,
			      RROutput          output,
			      VkDisplayKHR      *display)
{
	RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);

	return wsi_get_randr_output_display(physical_device,
					    &pdevice->wsi_device,
					    dpy,
					    output,
					    display);
}
#endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */

/* VK_EXT_display_control */

VkResult
radv_DisplayPowerControlEXT(VkDevice                    _device,
			    VkDisplayKHR                display,
			    const VkDisplayPowerInfoEXT *display_power_info)
{
	RADV_FROM_HANDLE(radv_device, device, _device);

	return wsi_display_power_control(_device,
					 &device->physical_device->wsi_device,
					 display,
					 display_power_info);
}

VkResult
radv_RegisterDeviceEventEXT(VkDevice                    _device,
			    const VkDeviceEventInfoEXT  *device_event_info,
			    const VkAllocationCallbacks *allocator,
			    VkFence                     *_fence)
{
	RADV_FROM_HANDLE(radv_device, device, _device);
	struct radv_fence            *fence;
	VkResult                     ret;

	fence = vk_alloc2(&device->instance->alloc, allocator, sizeof (*fence),
			  8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
	if (!fence)
		return VK_ERROR_OUT_OF_HOST_MEMORY;

	fence->fence = NULL;
	fence->syncobj = 0;
	fence->temp_syncobj = 0;

	ret = wsi_register_device_event(_device,
					&device->physical_device->wsi_device,
					device_event_info,
					allocator,
					&fence->fence_wsi);
	if (ret == VK_SUCCESS)
		*_fence = radv_fence_to_handle(fence);
	else
		vk_free2(&device->instance->alloc, allocator, fence);
	return ret;
}

VkResult
radv_RegisterDisplayEventEXT(VkDevice                           _device,
			     VkDisplayKHR                       display,
			     const VkDisplayEventInfoEXT        *display_event_info,
			     const VkAllocationCallbacks        *allocator,
			     VkFence                            *_fence)
{
	RADV_FROM_HANDLE(radv_device, device, _device);

	struct radv_fence            *fence;
	VkResult                     ret;

	fence = vk_alloc2(&device->instance->alloc, allocator, sizeof (*fence),
			  8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
	if (!fence)
		return VK_ERROR_OUT_OF_HOST_MEMORY;

	fence->fence = NULL;
	fence->syncobj = 0;
	fence->temp_syncobj = 0;

	ret = wsi_register_display_event(_device,
					 &device->physical_device->wsi_device,
					 display,
					 display_event_info,
					 allocator,
					 &(fence->fence_wsi));

	if (ret == VK_SUCCESS)
		*_fence = radv_fence_to_handle(fence);
	else
		vk_free2(&device->instance->alloc, allocator, fence);
	return ret;
}

VkResult
radv_GetSwapchainCounterEXT(VkDevice                    _device,
			    VkSwapchainKHR              swapchain,
			    VkSurfaceCounterFlagBitsEXT flag_bits,
			    uint64_t                    *value)
{
	RADV_FROM_HANDLE(radv_device, device, _device);

	return wsi_get_swapchain_counter(_device,
					 &device->physical_device->wsi_device,
					 swapchain,
					 flag_bits,
					 value);
}

