/*
 * Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
 *
 * SPDX-License-Identifier: MIT
 */

#include "cl_common.hpp"
#include "vdi_common.hpp"
#include "device/device.hpp"
#include "platform/runtime.hpp"
#include "rocclr/utils/versions.hpp"
#include "os/os.hpp"
#include "cl_semaphore_amd.h"

#include "CL/cl_ext.h"

#include <cstdlib>  // for alloca

/*! \addtogroup API
 *  @{
 *
 *  \addtogroup CL_PlatformInfo
 *  @{
 */

/*! \brief Get the list of available platforms.
 *
 * \param num_entries is the number of cl_platform_id entries that can be added
 * to platforms. If \a platforms is not NULL, the \a num_entries must be greater
 * than zero.
 *
 * \param platforms returns a list of OpenCL platforms found. The cl_platform_id
 * values returned in \a platforms can be used to identify a specific OpenCL
 * platform. If \a platforms argument is NULL, this argument is ignored. The
 * number of OpenCL platforms returned is the mininum of the value specified by
 * \a num_entries or the number of OpenCL platforms available.
 *
 * \param num_platforms returns the number of OpenCL platforms available. If
 * \a num_platforms is NULL, this argument is ignored.
 *
 * \return CL_INVALID_VALUE if num_entries is equal to zero and platforms is not
 * NULL or if both num_platforms and platforms are NULL, and returns CL_SUCCESS
 * if the function is executed successfully.
 *
 * \version 1.0r33
 */

RUNTIME_ENTRY(cl_int, clGetPlatformIDs,
              (cl_uint num_entries, cl_platform_id* platforms, cl_uint* num_platforms)) {
  if (!amd::Runtime::initialized()) {
    amd::Runtime::init();
  }

  if (((num_entries > 0 || num_platforms == NULL) && platforms == NULL) ||
      (num_entries == 0 && platforms != NULL)) {
    return CL_INVALID_VALUE;
  }
  if (num_platforms != NULL && platforms == NULL) {
    *num_platforms = 1;
    return CL_SUCCESS;
  }

  assert(platforms != NULL && "check the code above");
  *platforms = AMD_PLATFORM;

  *not_null(num_platforms) = 1;
  return CL_SUCCESS;
}
RUNTIME_EXIT

/*! \brief Get specific information about the OpenCL platform.
 *
 *  \param param_name is an enum that identifies the platform information being
 *  queried.
 *
 *  \param param_value is a pointer to memory location where appropriate values
 *  for a given \a param_name will be returned. If \a param_value is NULL,
 *  it is ignored.
 *
 *  \param param_value_size specifies the size in bytes of memory pointed to by
 *  \a param_value. This size in bytes must be >= size of return type.
 *
 *  \param param_value_size_ret returns the actual size in bytes of data being
 *  queried by param_value. If \a param_value_size_ret is NULL, it is ignored.
 *
 *  \return One of the following values:
 *    - CL_INVALID_VALUE if \a param_name is not one of the supported
 *      values or if size in bytes specified by \a param_value_size is < size of
 *      return type and \a param_value is not a NULL value.
 *    - CL_SUCCESS if the function is executed successfully.
 *
 *  \version 1.0r33
 */
RUNTIME_ENTRY(cl_int, clGetPlatformInfo,
              (cl_platform_id platform, cl_platform_info param_name, size_t param_value_size,
               void* param_value, size_t* param_value_size_ret)) {
  if (platform != NULL && platform != AMD_PLATFORM) {
    return CL_INVALID_PLATFORM;
  }

  const char* value = NULL;
  switch (param_name) {
    case CL_PLATFORM_PROFILE:
      value = "FULL_PROFILE";
      break;
    case CL_PLATFORM_VERSION:
      value = "OpenCL " XSTR(OPENCL_MAJOR) "." XSTR(OPENCL_MINOR) " " AMD_PLATFORM_INFO;
      break;
    case CL_PLATFORM_NAME:
      value = AMD_PLATFORM_NAME;
      break;
    case CL_PLATFORM_VENDOR:
      value = "Advanced Micro Devices, Inc.";
      break;
    case CL_PLATFORM_EXTENSIONS:
      value =
          "cl_khr_icd "
#ifdef _WIN32
          "cl_khr_d3d10_sharing "
          "cl_khr_d3d11_sharing "
          "cl_khr_dx9_media_sharing "
#endif  //_WIN32
          "cl_amd_event_callback "
          ;
      break;
    case CL_PLATFORM_ICD_SUFFIX_KHR:
      value = "AMD";
      break;
    case CL_PLATFORM_HOST_TIMER_RESOLUTION: {
      cl_ulong resolution = (cl_ulong)amd::Os::timerResolutionNanos();
      return amd::clGetInfo(resolution, param_value_size, param_value, param_value_size_ret);
    }
    default:
      break;
  }
  if (value != NULL) {
    return amd::clGetInfo(value, param_value_size, param_value, param_value_size_ret);
  }

  return CL_INVALID_VALUE;
}
RUNTIME_EXIT

/*! @}
 *  \addtogroup CL_Devices
 *  @{
 */

/*! \brief Get the list of available devices.
 *
 *  \param device_type is a bitfield that identifies the type of OpenCL device.
 *  The \a device_type can be used to query specific OpenCL devices or all
 *  OpenCL devices available.
 *
 *  \param num_entries is the number of cl_device_id entries that can be added
 *  to devices. If devices is not NULL, the \a num_entries must be greater than
 *  zero.
 *
 *  \param devices returns a list of OpenCL devices found. The cl_device_id
 *  values returned in devices can be used to identify a specific OpenCL device.
 *  If \a devices argument is NULL, this argument is ignored. The number of
 *  OpenCL devices returned is the mininum of value specified by \a num_entries
 *  or the number of OpenCL devices whose type matches device_type.
 *
 *  \param num_devices returns the number of OpenCL devices available that match
 *  device_type. If \a num_devices is NULL, this argument is ignored.
 *
 *  \return One of the following values:
 *    - CL_INVALID_DEVICE_TYPE if \a device_type is not a valid value.
 *    - CL_INVALID_VALUE if \a num_entries is equal to zero and devices is
 *      not NULL or if both \a num_devices and \a devices are NULL.
 *    - CL_DEVICE_ NOT_FOUND if no OpenCL devices that matched \a device_type
 *      were found.
 *    - CL_SUCCESS if the function is executed successfully.
 *
 *  The application can query specific capabilities of the OpenCL device(s)
 *  returned by clGetDeviceIDs. This can be used by the application to
 *  determine which device(s) to use.
 *
 *  \version 1.0r33
 */
RUNTIME_ENTRY(cl_int, clGetDeviceIDs,
              (cl_platform_id platform, cl_device_type device_type, cl_uint num_entries,
               cl_device_id* devices, cl_uint* num_devices)) {
  if (platform != NULL && platform != AMD_PLATFORM) {
    return CL_INVALID_PLATFORM;
  }

  if (((num_entries > 0 || num_devices == NULL) && devices == NULL) ||
      (num_entries == 0 && devices != NULL)) {
    return CL_INVALID_VALUE;
  }
  if (device_type == 0) {
    return CL_INVALID_DEVICE_TYPE;
  }
  // Get all available devices
  if (!amd::Device::getDeviceIDs(device_type, num_entries, devices, num_devices, false)) {
    return CL_DEVICE_NOT_FOUND;
  }

  return CL_SUCCESS;
}
RUNTIME_EXIT

/*! \fn clGetDeviceInfo
 *
 *  \brief Get specific information about an OpenCL device.
 *
 *  \param device is a device returned by clGetDeviceIDs.
 *
 *  \param param_name is an enum that identifies the device information being
 *  queried.
 *
 *  \param param_value is a pointer to memory location where appropriate values
 *  for a given \a param_name will be returned. If \a param_value is NULL,
 *  it is ignored.
 *
 *  \param param_value_size specifies the size in bytes of memory pointed to
 *  by \a param_value. This size in bytes must be >= size of return type.
 *
 *  \param param_value_size_ret returns the actual size in bytes of data being
 *  queried by param_value. If \a param_value_size_ret is NULL, it is ignored.
 *
 *  \return One of the following values:
 *    - CL_INVALID_DEVICE if device is not valid.
 *    - CL_INVALID_VALUE if param_name is not one of the supported values
 *      or if size in bytes specified by \a param_value_size is < size of return
 *      type and \a param_value is not a NULL value.
 *    - CL_SUCCESS if the function is executed successfully.
 *
 *  \version 1.0r33
 */
RUNTIME_ENTRY(cl_int, clGetDeviceInfo,
              (cl_device_id device, cl_device_info param_name, size_t param_value_size,
               void* param_value, size_t* param_value_size_ret)) {
  if (!is_valid(device)) {
    return CL_INVALID_DEVICE;
  }

#define CASE(param_name, field_name)                                                               \
  case param_name:                                                                                 \
    return amd::clGetInfo(as_amd(device)->info().field_name, param_value_size, param_value,        \
                          param_value_size_ret);

  switch (param_name) {
    case CL_DEVICE_TYPE: {
      // For cl_device_type, we need to mask out the default bit.
      cl_device_type device_type = as_amd(device)->type();
      return amd::clGetInfo(device_type, param_value_size, param_value, param_value_size_ret);
    }
      CASE(CL_DEVICE_VENDOR_ID, vendorId_);
      CASE(CL_DEVICE_MAX_COMPUTE_UNITS, maxComputeUnits_);
      CASE(CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, maxWorkItemDimensions_);
      CASE(CL_DEVICE_MAX_WORK_GROUP_SIZE, preferredWorkGroupSize_);
      CASE(CL_DEVICE_PREFERRED_WORK_GROUP_SIZE_AMD, preferredWorkGroupSize_);
      CASE(CL_DEVICE_MAX_WORK_GROUP_SIZE_AMD, maxWorkGroupSize_);
      CASE(CL_DEVICE_MAX_WORK_ITEM_SIZES, maxWorkItemSizes_);
      CASE(CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, preferredVectorWidthChar_);
      CASE(CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, preferredVectorWidthShort_);
      CASE(CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, preferredVectorWidthInt_);
      CASE(CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, preferredVectorWidthLong_);
      CASE(CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, preferredVectorWidthFloat_);
      CASE(CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, preferredVectorWidthDouble_);
      CASE(CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF, preferredVectorWidthDouble_);
      CASE(CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR, nativeVectorWidthChar_);
      CASE(CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT, nativeVectorWidthShort_);
      CASE(CL_DEVICE_NATIVE_VECTOR_WIDTH_INT, nativeVectorWidthInt_);
      CASE(CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG, nativeVectorWidthLong_);
      CASE(CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT, nativeVectorWidthFloat_);
      CASE(CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE, nativeVectorWidthDouble_);
      CASE(CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF, nativeVectorWidthDouble_);
      CASE(CL_DEVICE_MAX_CLOCK_FREQUENCY, maxEngineClockFrequency_);
      CASE(CL_DEVICE_ADDRESS_BITS, addressBits_);
      CASE(CL_DEVICE_MAX_READ_IMAGE_ARGS, maxReadImageArgs_);
      CASE(CL_DEVICE_MAX_WRITE_IMAGE_ARGS, maxWriteImageArgs_);
      CASE(CL_DEVICE_MAX_READ_WRITE_IMAGE_ARGS, maxReadWriteImageArgs_);
      CASE(CL_DEVICE_MAX_MEM_ALLOC_SIZE, maxMemAllocSize_);
      CASE(CL_DEVICE_IMAGE2D_MAX_WIDTH, image2DMaxWidth_);
      CASE(CL_DEVICE_IMAGE2D_MAX_HEIGHT, image2DMaxHeight_);
      CASE(CL_DEVICE_IMAGE3D_MAX_WIDTH, image3DMaxWidth_);
      CASE(CL_DEVICE_IMAGE3D_MAX_HEIGHT, image3DMaxHeight_);
      CASE(CL_DEVICE_IMAGE3D_MAX_DEPTH, image3DMaxDepth_);
      CASE(CL_DEVICE_IMAGE_SUPPORT, imageSupport_);
      CASE(CL_DEVICE_MAX_PARAMETER_SIZE, maxParameterSize_);
      CASE(CL_DEVICE_MAX_SAMPLERS, maxSamplers_);
      CASE(CL_DEVICE_MEM_BASE_ADDR_ALIGN, memBaseAddrAlign_);
      CASE(CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, minDataTypeAlignSize_);
      CASE(CL_DEVICE_HALF_FP_CONFIG, halfFPConfig_);
      CASE(CL_DEVICE_SINGLE_FP_CONFIG, singleFPConfig_);
      CASE(CL_DEVICE_DOUBLE_FP_CONFIG, doubleFPConfig_);
      CASE(CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, globalMemCacheType_);
      CASE(CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, globalMemCacheLineSize_);
      CASE(CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, globalMemCacheSize_);
      CASE(CL_DEVICE_GLOBAL_MEM_SIZE, globalMemSize_);
      CASE(CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, maxConstantBufferSize_);
      CASE(CL_DEVICE_PREFERRED_CONSTANT_BUFFER_SIZE_AMD, preferredConstantBufferSize_);
      CASE(CL_DEVICE_MAX_CONSTANT_ARGS, maxConstantArgs_);
      CASE(CL_DEVICE_LOCAL_MEM_TYPE, localMemType_);
      CASE(CL_DEVICE_LOCAL_MEM_SIZE, localMemSize_);
      CASE(CL_DEVICE_ERROR_CORRECTION_SUPPORT, errorCorrectionSupport_);
      CASE(CL_DEVICE_HOST_UNIFIED_MEMORY, hostUnifiedMemory_);
      CASE(CL_DEVICE_PROFILING_TIMER_RESOLUTION, profilingTimerResolution_);
      CASE(CL_DEVICE_PROFILING_TIMER_OFFSET_AMD, profilingTimerOffset_);
      CASE(CL_DEVICE_ENDIAN_LITTLE, littleEndian_);
      CASE(CL_DEVICE_AVAILABLE, available_);
      CASE(CL_DEVICE_COMPILER_AVAILABLE, compilerAvailable_);
      CASE(CL_DEVICE_EXECUTION_CAPABILITIES, executionCapabilities_);
      CASE(CL_DEVICE_SVM_CAPABILITIES, svmCapabilities_);
      CASE(CL_DEVICE_PREFERRED_PLATFORM_ATOMIC_ALIGNMENT, preferredPlatformAtomicAlignment_);
      CASE(CL_DEVICE_PREFERRED_GLOBAL_ATOMIC_ALIGNMENT, preferredGlobalAtomicAlignment_);
      CASE(CL_DEVICE_PREFERRED_LOCAL_ATOMIC_ALIGNMENT, preferredLocalAtomicAlignment_);
      CASE(CL_DEVICE_QUEUE_ON_HOST_PROPERTIES, queueProperties_);
      CASE(CL_DEVICE_PLATFORM, platform_);
      CASE(CL_DEVICE_NAME, name_);
      CASE(CL_DEVICE_VENDOR, vendor_);
      CASE(CL_DRIVER_VERSION, driverVersion_);
      CASE(CL_DEVICE_PROFILE, profile_);
      CASE(CL_DEVICE_VERSION, version_);
      CASE(CL_DEVICE_OPENCL_C_VERSION, oclcVersion_);
      CASE(CL_DEVICE_EXTENSIONS, extensions_);
      CASE(CL_DEVICE_MAX_ATOMIC_COUNTERS_EXT, maxAtomicCounters_);
      CASE(CL_DEVICE_TOPOLOGY_AMD, deviceTopology_);
      CASE(CL_DEVICE_MAX_SEMAPHORE_SIZE_AMD, maxSemaphoreSize_);
      CASE(CL_DEVICE_BOARD_NAME_AMD, boardName_);
      CASE(CL_DEVICE_SPIR_VERSIONS, spirVersions_);
      CASE(CL_DEVICE_IL_VERSION, spirVersions_);
      CASE(CL_DEVICE_MAX_PIPE_ARGS, maxPipeArgs_);
      CASE(CL_DEVICE_PIPE_MAX_ACTIVE_RESERVATIONS, maxPipeActiveReservations_);
      CASE(CL_DEVICE_PIPE_MAX_PACKET_SIZE, maxPipePacketSize_);
      CASE(CL_DEVICE_MAX_GLOBAL_VARIABLE_SIZE, maxGlobalVariableSize_);
      CASE(CL_DEVICE_GLOBAL_VARIABLE_PREFERRED_TOTAL_SIZE, globalVariablePreferredTotalSize_);
      CASE(CL_DEVICE_QUEUE_ON_DEVICE_PROPERTIES, queueOnDeviceProperties_);
      CASE(CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE, queueOnDevicePreferredSize_);
      CASE(CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE, queueOnDeviceMaxSize_);
      CASE(CL_DEVICE_MAX_ON_DEVICE_QUEUES, maxOnDeviceQueues_);
      CASE(CL_DEVICE_MAX_ON_DEVICE_EVENTS, maxOnDeviceEvents_);
      CASE(CL_DEVICE_LINKER_AVAILABLE, linkerAvailable_);
      CASE(CL_DEVICE_BUILT_IN_KERNELS, builtInKernels_);
      CASE(CL_DEVICE_IMAGE_MAX_BUFFER_SIZE, imageMaxBufferSize_);
      CASE(CL_DEVICE_IMAGE_MAX_ARRAY_SIZE, imageMaxArraySize_);
    case CL_DEVICE_PARENT_DEVICE: {
      cl_device_id parent = (cl_device_id)0;
      return amd::clGetInfo(parent, param_value_size, param_value, param_value_size_ret);
    }
      CASE(CL_DEVICE_PARTITION_MAX_SUB_DEVICES, maxComputeUnits_);
    case CL_DEVICE_PARTITION_PROPERTIES: {
      cl_device_partition_property cl_property = {};
      return amd::clGetInfo(cl_property, param_value_size, param_value, param_value_size_ret);
    }
    case CL_DEVICE_PARTITION_AFFINITY_DOMAIN: {
      cl_device_affinity_domain deviceAffinity = {};
      return amd::clGetInfo(deviceAffinity, param_value_size, param_value, param_value_size_ret);
    }
    case CL_DEVICE_PARTITION_TYPE: {
      cl_device_partition_property cl_property = {};
      return amd::clGetInfo(cl_property, param_value_size, param_value, param_value_size_ret);
    }
    case CL_DEVICE_REFERENCE_COUNT: {
      cl_uint count = as_amd(device)->referenceCount();
      return amd::clGetInfo(count, param_value_size, param_value, param_value_size_ret);
    }
      CASE(CL_DEVICE_PREFERRED_INTEROP_USER_SYNC, preferredInteropUserSync_);
      CASE(CL_DEVICE_PRINTF_BUFFER_SIZE, printfBufferSize_);
      CASE(CL_DEVICE_IMAGE_PITCH_ALIGNMENT, imagePitchAlignment_);
      CASE(CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT, imageBaseAddressAlignment_);

    default:
      break;
  }
  if (as_amd(device)->type() == CL_DEVICE_TYPE_GPU) {
    switch (param_name) {
      case CL_DEVICE_GLOBAL_FREE_MEMORY_AMD: {
        // Free memory should contain 2 values:
        // total free memory and the biggest free block
        size_t freeMemory[2];
        if (!as_amd(device)->globalFreeMemory(freeMemory)) {
          return CL_INVALID_DEVICE;
        }
        if (param_value_size < sizeof(freeMemory)) {
          // Return just total free memory if the app provided space for one value
          return amd::clGetInfo(freeMemory[0], param_value_size, param_value, param_value_size_ret);
        } else {
          return amd::clGetInfo(freeMemory, param_value_size, param_value, param_value_size_ret);
        }
      }
        CASE(CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD, simdPerCU_);
        CASE(CL_DEVICE_SIMD_WIDTH_AMD, simdWidth_);
        CASE(CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD, simdInstructionWidth_);
        CASE(CL_DEVICE_WAVEFRONT_WIDTH_AMD, wavefrontWidth_);
      case CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD: {
        cl_uint globalMemChannels = as_amd(device)->info().vramBusBitWidth_ / 32;
        return amd::clGetInfo(globalMemChannels, param_value_size, param_value,
                              param_value_size_ret);
      }
        CASE(CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD, globalMemChannelBanks_);
        CASE(CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD, globalMemChannelBankWidth_);
        CASE(CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD, localMemSizePerCU_);
        CASE(CL_DEVICE_LOCAL_MEM_BANKS_AMD, localMemBanks_);
        CASE(CL_DEVICE_THREAD_TRACE_SUPPORTED_AMD, threadTraceEnable_);
      case CL_DEVICE_GFXIP_MAJOR_AMD: {
        cl_uint major = as_amd(device)->isa().versionMajor();
        return amd::clGetInfo(major, param_value_size, param_value, param_value_size_ret);
      }
      case CL_DEVICE_GFXIP_MINOR_AMD: {
        cl_uint minor = as_amd(device)->isa().versionMinor();
        return amd::clGetInfo(minor, param_value_size, param_value, param_value_size_ret);
      }
        CASE(CL_DEVICE_AVAILABLE_ASYNC_QUEUES_AMD, numAsyncQueues_);
#define CL_DEVICE_MAX_REAL_TIME_COMPUTE_QUEUES_AMD 0x404D
#define CL_DEVICE_MAX_REAL_TIME_COMPUTE_UNITS_AMD 0x404E
#define CL_DEVICE_MAX_REAL_TIME_COMPUTE_UNITS_GRANULARITY_AMD 0x403A
        CASE(CL_DEVICE_MAX_REAL_TIME_COMPUTE_QUEUES_AMD, numRTQueues_);
        CASE(CL_DEVICE_MAX_REAL_TIME_COMPUTE_UNITS_AMD, numRTCUs_);
        CASE(CL_DEVICE_MAX_REAL_TIME_COMPUTE_UNITS_GRANULARITY_AMD, granularityRTCUs_);
      case CL_DEVICE_NUM_P2P_DEVICES_AMD: {
        cl_uint num_p2p_devices = as_amd(device)->p2pDevices_.size();
        return amd::clGetInfo(num_p2p_devices, param_value_size, param_value, param_value_size_ret);
      }
      case CL_DEVICE_P2P_DEVICES_AMD: {
        uint valueSize = as_amd(device)->p2pDevices_.size() * sizeof(cl_device_id);
        if (param_value != NULL) {
          if ((param_value_size < valueSize) || (param_value_size == 0)) {
            return CL_INVALID_VALUE;
          }
        } else {
          return CL_INVALID_VALUE;
        }
        memcpy(param_value, as_amd(device)->p2pDevices_.data(), valueSize);
        *not_null(param_value_size_ret) = valueSize;
        if (param_value != NULL && param_value_size > valueSize) {
          ::memset(static_cast<char*>(param_value) + valueSize, '\0', param_value_size - valueSize);
        }
        return CL_SUCCESS;
      }
        CASE(CL_DEVICE_PCIE_ID_AMD, pcieDeviceId_);
#define CL_DEVICE_NUMERIC_VERSION 0x105E
      case CL_DEVICE_NUMERIC_VERSION: {
        std::string driverVersion = as_amd(device)->info().driverVersion_;
        std::string drvVersion(driverVersion.c_str());
        drvVersion = drvVersion.substr(0, 4);  // calVersion.find_first_of("(") + 4);
        cl_uint numVersion = atoi(drvVersion.c_str());
        return amd::clGetInfo(numVersion, param_value_size, param_value, param_value_size_ret);
      }
      default:
        break;
    }
  }
#undef CASE

  return CL_INVALID_VALUE;
}
RUNTIME_EXIT

RUNTIME_ENTRY(cl_int, clCreateSubDevices,
              (cl_device_id in_device, const cl_device_partition_property* partition_properties,
               cl_uint num_entries, cl_device_id* out_devices, cl_uint* num_devices)) {
  if (!is_valid(in_device)) {
    return CL_INVALID_DEVICE;
  }
  if (partition_properties == NULL || *partition_properties == 0u) {
    return CL_INVALID_VALUE;
  }
  if ((num_devices == NULL && out_devices == NULL) || (num_entries == 0 && out_devices != NULL)) {
    return CL_INVALID_VALUE;
  }

  return CL_INVALID_VALUE;
}
RUNTIME_EXIT

RUNTIME_ENTRY(cl_int, clRetainDevice, (cl_device_id device)) {
  if (!is_valid(device)) {
    return CL_INVALID_DEVICE;
  }
  as_amd(device)->retain();
  return CL_SUCCESS;
}
RUNTIME_EXIT

RUNTIME_ENTRY(cl_int, clReleaseDevice, (cl_device_id device)) {
  if (!is_valid(device)) {
    return CL_INVALID_DEVICE;
  }
  as_amd(device)->release();
  return CL_SUCCESS;
}
RUNTIME_EXIT

/*! @}
 *  @}
 */
