/*
 * Copyright © 2020 Raspberry Pi Ltd
 * based on intel anv code:
 * Copyright © 2015 Intel Corporation

 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "v3dv_device.h"
#include "v3dv_entrypoints.h"
#include "vk_util.h"
#include "wsi_common_drm.h"
#include "wsi_common_entrypoints.h"

static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
v3dv_wsi_proc_addr(VkPhysicalDevice physicalDevice, const char *pName)
{
   V3DV_FROM_HANDLE(v3dv_physical_device, pdevice, physicalDevice);
   return vk_instance_get_proc_addr_unchecked(pdevice->vk.instance, pName);
}

static bool
v3dv_wsi_can_present_on_device(VkPhysicalDevice _pdevice, int fd)
{
   V3DV_FROM_HANDLE(v3dv_physical_device, pdevice, _pdevice);
   assert(pdevice->display_fd != -1);
   assert(pdevice->render_fd != -1);

   /* v3dv handles presentation by allocating wsi buffers in the display
    * device, so both render and display devices can match here.
    * In particular, this callback receives the render device when
    * running in an XWayland environment.
    */
   return wsi_common_drm_devices_equal(fd, pdevice->display_fd) ||
          wsi_common_drm_devices_equal(fd, pdevice->render_fd);
}


static void
filter_surface_capabilities(VkSurfaceKHR _surface,
                            VkSurfaceCapabilitiesKHR *caps)
{
   ICD_FROM_HANDLE(VkIcdSurfaceBase, surface, _surface);

   /* Display images must be linear so they are restricted. This would
    * affect sampling usages too, but we don't restrict those since we
    * support on-the-fly conversion to UIF when sampling for simple 2D
    * images at a performance penalty.
    */
   if (surface->platform == VK_ICD_WSI_PLATFORM_DISPLAY)
      caps->supportedUsageFlags &= ~VK_IMAGE_USAGE_STORAGE_BIT;
}

VKAPI_ATTR VkResult VKAPI_CALL
v3dv_GetPhysicalDeviceSurfaceCapabilitiesKHR(
    VkPhysicalDevice                            physicalDevice,
    VkSurfaceKHR                                surface,
    VkSurfaceCapabilitiesKHR*                   pSurfaceCapabilities)
{
   VkResult result;
   result = wsi_GetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice,
                                                        surface,
                                                        pSurfaceCapabilities);
   filter_surface_capabilities(surface, pSurfaceCapabilities);
   return result;
}

VKAPI_ATTR VkResult VKAPI_CALL
v3dv_GetPhysicalDeviceSurfaceCapabilities2KHR(
    VkPhysicalDevice                            physicalDevice,
    const VkPhysicalDeviceSurfaceInfo2KHR*      pSurfaceInfo,
    VkSurfaceCapabilities2KHR*                  pSurfaceCapabilities)
{
   VkResult result;
   result = wsi_GetPhysicalDeviceSurfaceCapabilities2KHR(physicalDevice,
                                                         pSurfaceInfo,
                                                         pSurfaceCapabilities);
   filter_surface_capabilities(pSurfaceInfo->surface,
                               &pSurfaceCapabilities->surfaceCapabilities);
   return result;
}

VkResult
v3dv_wsi_init(struct v3dv_physical_device *physical_device)
{
   VkResult result;
   const struct v3dv_instance *v3dv_instance =
      container_of(physical_device->vk.instance, struct v3dv_instance, vk);

   result = wsi_device_init(&physical_device->wsi_device,
                            v3dv_physical_device_to_handle(physical_device),
                            v3dv_wsi_proc_addr,
                            &physical_device->vk.instance->alloc,
                            physical_device->display_fd,
                            &v3dv_instance->dri_options,
                            &(struct wsi_device_options){.sw_device = false});

   if (result != VK_SUCCESS)
      return result;

   physical_device->wsi_device.supports_modifiers = true;
   physical_device->wsi_device.can_present_on_device =
      v3dv_wsi_can_present_on_device;

   physical_device->vk.wsi_device = &physical_device->wsi_device;

   return VK_SUCCESS;
}

void
v3dv_wsi_finish(struct v3dv_physical_device *physical_device)
{
   physical_device->vk.wsi_device = NULL;
   wsi_device_finish(&physical_device->wsi_device,
                     &physical_device->vk.instance->alloc);
}
