// SPDX-License-Identifier: GPL-2.0-or-later AND LGPL-2.0-or-later AND LGPL-2.1-or-later

#include "mvb_heap.h"
#include "mvb_heap_impl.h"

#include <linux/ion.h>
#include <linux/ion_mtk.h>
#include <ion/ion.h>

#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include <gst/gst.h>

#include "mvb_heap_debug.h"

static int
_ion_ioctl(int fd, int req, void *arg)
{
  int ret = ioctl (fd, req, arg);

  if (ret < 0)
  {
    g_print ("[fdprovider] error: ioctl\n");
    return -errno;
  }

  return ret;
}

static int
_ion_handle_alloc (int device_fd, size_t len, size_t align, unsigned int heap_mask,
    unsigned int flags, ion_user_handle_t *handle)
{
  struct ion_allocation_data data = {
    .len          = len,
    .align        = align,
    .heap_id_mask = heap_mask,
    .flags        = flags,
  };
  int ret;

  if (handle == NULL)
  {
    g_print ("[fdprovider] invalid handle");
    return -EINVAL;
  }

  ret = _ion_ioctl(device_fd, ION_IOC_ALLOC, &data);
  if (ret < 0) {
    g_print ("[fdprovider] can not alloc\n");
    return ret;
  }

  *handle = data.handle;
  return ret;
}

static int
_ion_handle_free (int device_fd, ion_user_handle_t handle)
{
  struct ion_handle_data data = {
    .handle = handle,
  };

  return _ion_ioctl(device_fd, ION_IOC_FREE, &data);
}

static int
_ion_share_fd(int device_fd, ion_user_handle_t handle, int *dma_fd)
{
  int ret;
  struct ion_fd_data data = {
    .handle = handle,
  };

  if (dma_fd == NULL)
    return -EINVAL;

  ret = _ion_ioctl(device_fd, ION_IOC_SHARE, &data);
  if (ret < 0) {
    g_print ("[fdprovider] ion share failed: %s\n", strerror(errno));
    return ret;
  }

  if (data.fd < 0) {
    g_print ("[fdprovider] share ioctl returned negative fd\n");
    return -EINVAL;
  }

  *dma_fd = data.fd;
  return ret;
}

static const char*
_get_ion_alloc_param (uint32_t mvb_flag,
                          uint32_t* ion_flag,
                          uint32_t* ion_heap_mask)
{
  const char* ion_heap_name = NULL;

  if (!ion_flag || !ion_heap_mask)
    return ion_heap_name;

  if ((mvb_flag & MVB_HEAP_FLAG_SECURE) != 0)
  {
    ion_heap_name = "ion_svp_heap";
    *ion_heap_mask = ION_HEAP_MULTIMEDIA_SEC_MASK;
    *ion_flag = 0;
  }
  else
  {
    ion_heap_name = "mtk_ion_mm_heap";
    *ion_heap_mask = ION_HEAP_MULTIMEDIA_MASK;

    if ((mvb_flag & MVB_HEAP_FLAG_CACHED) != 0)
      *ion_flag = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
    else
      *ion_flag = 0;
  }

  return ion_heap_name;
}

struct mvb_heap_ion_legacy_info_t
{
  int device_fd;
};

struct mvb_slot_ion_legacy_info_t
{
  ion_user_handle_t ion_handle;
  uint32_t          ion_flag;
  uint32_t          ion_mask;
};

static int mvb_heap_ion_legacy_slot_init (struct mvb_slot_t* slot, int size, uint32_t mvb_flag)
{
  struct mvb_heap_ion_legacy_info_t* priv_heap_info = slot->parent->priv;
  struct mvb_slot_ion_legacy_info_t* priv_slot_info = slot->priv;
  const char* ion_heap_name;
  int ret;

  MVB_LOGD ("begin");

  if (priv_heap_info == NULL) {
    MVB_LOGE ("invalid pointer to priv heap info");
    return MVB_RET_FAIL;
  }

  /* fill following info
        1) slot->size
        2) slot->mvb_flag
     */
  slot->size     = size;
  slot->mvb_flag = mvb_flag;

  /* malloc for
        1) slot->priv
   */
  priv_slot_info = slot->priv = (struct mvb_slot_ion_legacy_info_t*) malloc (
      sizeof (struct mvb_slot_ion_legacy_info_t));
  if (priv_slot_info == NULL) {
    MVB_LOGE ("[mvb][ion_legacy] fail to malloc for mvb_slot_ion_legacy_info_t\n");
    return MVB_RET_FAIL;
  }

  /* fill following by mvb_flag
        1) priv_slot_info->ion_flag
        2) priv_slot_info->ion_mask
     */
  ion_heap_name = _get_ion_alloc_param (slot->mvb_flag, &(priv_slot_info->ion_flag), &(priv_slot_info->ion_mask));
  if (ion_heap_name == NULL) {
    MVB_LOGE ("[mvb][ion_legacy] fail to get alloc param\n");
    return MVB_RET_FAIL;
  }
  MVB_LOGD ("find ion heap: %s", ion_heap_name);

  /* fill following info
        1) priv_slot_info->handle
     */
  ret = _ion_handle_alloc (priv_heap_info->device_fd,
                           slot->size,
                           32,
                           priv_slot_info->ion_mask,
                           priv_slot_info->ion_flag,
                           &priv_slot_info->ion_handle); /* out param */
  if (ret) {
    MVB_LOGE ("[mvb][ion_legacy] fail to alloc\n");
    return MVB_RET_FAIL;
  }
  MVB_LOGD ("create ion handle: %d", priv_slot_info->ion_handle);

  /* fill following info
        1) slot->fd
     */
  ret = _ion_share_fd (priv_heap_info->device_fd,
                       priv_slot_info->ion_handle,
                       &slot->fd); /* out param */
  if (ret) {
    MVB_LOGE ("[mvb][ion_legacy] fail to share\n");
    return MVB_RET_FAIL;
  }
  MVB_LOGD ("create ion fd: %d", slot->fd);
  return MVB_RET_OK;
}

static int mvb_heap_ion_legacy_slot_deinit (struct mvb_slot_t* slot)
{
  struct mvb_heap_ion_legacy_info_t* priv_heap_info = slot->parent->priv;
  struct mvb_slot_ion_legacy_info_t* priv_slot_info = slot->priv;

  MVB_LOGD ("begin: %p", slot);

  if (slot->priv)
  {
    MVB_LOGD ("close fd: %d", slot->fd);
    close (slot->fd);
    slot->fd = -1;

    _ion_handle_free (priv_heap_info->device_fd, priv_slot_info->ion_handle);

    free (slot->priv);
    slot->priv = NULL;
  }

  MVB_LOGD ("end");
  return MVB_RET_OK;
}

static int mvb_heap_ion_legacy_init (struct mvb_heap_t* heap)
{
  struct mvb_heap_ion_legacy_info_t* priv_info;

  MVB_LOGD ("begin");

  heap->priv = priv_info = (struct mvb_heap_ion_legacy_info_t*) malloc (sizeof (struct mvb_heap_ion_legacy_info_t));

  priv_info->device_fd = open ("/dev/ion", O_RDWR);
  if (priv_info->device_fd < 0) {
    g_print ("[mvb][ion_legacy] fail to open /dev/ion\n");
    free (priv_info);
    return MVB_RET_FAIL;
  }

  MVB_LOGD ("end");
  return MVB_RET_OK;
}

static int mvb_heap_ion_legacy_deinit (struct mvb_heap_t* heap)
{
  struct mvb_heap_ion_legacy_info_t* priv_info = heap->priv;

  MVB_LOGD ("begin");

  if (priv_info) {
    close (priv_info->device_fd);
    priv_info->device_fd = -1;

    free (heap->priv);
    heap->priv = NULL;
  }

  MVB_LOGD ("end");

  return MVB_RET_OK;
}


struct mvb_heap_ops_t mvb_heap_ops_ion_legacy = {
  .slot_init   = mvb_heap_ion_legacy_slot_init,
  .slot_deinit = mvb_heap_ion_legacy_slot_deinit,
  .heap_init   = mvb_heap_ion_legacy_init,
  .heap_deinit = mvb_heap_ion_legacy_deinit,
};

#if 0
static int i4UTIonDevFd = -1;

#if 0
void
alloc_ion_buffer(unsigned int size, unsigned int alignment, unsigned int heap_mask,
    int *dmabuf_fd, ion_user_handle_t *pIonBufhandle)
{
    int share_fd = -1, retVal;

    if (i4UTIonDevFd == -1) {
        i4UTIonDevFd = open("/dev/ion", O_RDWR);
        GST_INFO("mt_ion_open, ion_dev_fd = %d \n", i4UTIonDevFd);
    }

    if (i4UTIonDevFd < 0) {
        GST_ERROR("cannot open ION device.\n");
        return;
    }

    retVal = _ion_alloc(i4UTIonDevFd, size, alignment, heap_mask,
        ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, pIonBufhandle);

    if (retVal) {
        GST_ERROR("ion_alloc failed %d\n", retVal);
        return;
    }

    retVal = _ion_share(i4UTIonDevFd, *pIonBufhandle, &share_fd);
    if (retVal) {
        GST_ERROR("ion_share failed: %d\n", retVal);
        return;
    }
    *dmabuf_fd = share_fd;

  return;
}
#endif


static gint
fdprovider_ion_legacy_alloc (struct fd_provider_t* p, gint size, guint32 flag)
{
  struct fd_provider_ext_ion_legacy_t* preal = (struct fd_provider_ext_ion_legacy_t*)p;
  ion_user_handle_t *pIonBufhandle;
  int share_fd;
  int ret;

  ret = _ion_alloc_handle (preal->device_fd,
              size, /* memory size */
              32, /* aligment */
              preal->heap_info.heap_mask,
              preal->heap_info.flag,
              pIonBufhandle); /* out param */
  if (ret) {
    g_print ("[fdprovider] fail to alloc\n");
    return -1;
  }

  ret = _ion_share_fd (preal->device_fd,
              pIonBufhandle,
              &share_fd); /* out param */
  if (ret) {
    g_print ("[fdprovider] fail to share\n");
    return -1;
  }

  return share_fd;
}

static void
fdprovider_ion_legacy_free  (struct fd_provider_t* p, gint fd)
{
  //
}

/*

enum ion_idx {
  _ION_SYSTEM = 0,
  _ION_SYSTEM_NC,
  _ION_MTK_MM,     // this for nomal playback
  _ION_MTK_MM_NC,
  _ION_MTK_SVP,    // this for svp playback
  _ION_MTK_NUM,
};

static const struct ion_heap_info_t ion_info[_ION_MTK_NUM] = {
  [_ION_SYSTEM] = {
    .flag = ION_FLAG_CACHED,
    .heap_name = "ion_system_heap",
  },
  [_ION_SYSTEM_NC] = {
    .flag = 0,
    .heap_name = "ion_system_heap",
  },
  [_ION_MTK_MM] = {
    .flag = ION_FLAG_CACHED,
    .heap_name = "mtk_ion_mm_heap",
  },
  [_ION_MTK_MM_NC] = {
    .flag = 0,
    .heap_name = "mtk_ion_mm_heap",
  },
  [_ION_MTK_SVP] = {
    .flag = 0,
    .heap_name = "ion_svp_heap",
  },
};
*/


#if 0

static int
_fill_ion_heap_info (struct ion_heap_info_t* p_heap_info, int device_fd, guint32 flag)
{
  struct ion_heap_query query;
  struct ion_heap_data  heap_data[MAX_HEAP_COUNT];
  int ret;
  int idx;
  struct fd_provider_ext_ion_legacy_t* preal = (struct fd_provider_ext_ion_legacy_t*)p;

  memset(&query, 0, sizeof(query));
  query.cnt   = MAX_HEAP_COUNT;
  query.heaps = (unsigned long int)&heap_data[0];

  ret = ioctl (device_fd, ION_IOC_HEAP_QUERY, &query);
  if (ret < 0) {
    g_print ("[fdprovider] faild to query ion\n");
    return -1;
  }

  /* fill flag and heap_name */
  if (flag & FD_PROVIDER_FLAG_SECURE == 0)
  {
    p_heap_info->heap_name = "ion_svp_heap";

    p_heap_info->flag = 0;
  }
  else
  {
    preal->heap_name = "mtk_ion_mm_heap";

    if (flag & FD_PROVIDER_FLAG_CACHED != 0)
      p_heap_info->flag = ION_FLAG_CACHED;
    else
      p_heap_info->flag = 0;
  }

  /* fill heap_mask */
  for (idx = 0; idx < query.cnt; idx++)
  {
    if (strcmp (p_heap_info->heap_name, heap_data[idx].heap_name) == 0)
    {
      p_heap_info->heap_mask = heap_data[idx].heap_mask;
      break;
    }
  }

  if (idx >= query.cnt)
    goto err;

  return 0;

err:
  g_print ("[fdprovider] fail to get heap\n");
  return -1;
}

#else

static int
_fill_ion_heap_info (struct ion_heap_info_t* p_heap_info, int device_fd, guint32 flag)
{
  /* fill flag, heap_mask and heap_name */
  if (flag & FD_PROVIDER_FLAG_SECURE != 0)
  {
    p_heap_info->heap_name = "ion_svp_heap";
    p_heap_info->heap_mask = ION_HEAP_MULTIMEDIA_SEC_MASK;
    p_heap_info->flag = 0;
  }
  else
  {
    p_heap_info->heap_name = "mtk_ion_mm_heap";
    p_heap_info->heap_mask = ION_HEAP_MULTIMEDIA_MASK;

    if (flag & FD_PROVIDER_FLAG_CACHED != 0)
      p_heap_info->flag = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
    else
      p_heap_info->flag = 0;
  }

  return 0;
}

#endif

#endif


