Add ION allocator

Add a GstAllocator based on /dev/ion to allocate dma-bufs.

Change-Id: I18cf4ef0204e2de856a84e39f6abfef3e49314b2
diff --git a/plugins/gstcoral.c b/plugins/gstcoral.c
index 07be7cd..7af7dea 100644
--- a/plugins/gstcoral.c
+++ b/plugins/gstcoral.c
@@ -18,6 +18,7 @@
 #include "config.h"
 #endif
 
+#include "gstionallocator.h"
 #include "gstglbox.h"
 #include "gstglsvgoverlay.h"
 
@@ -28,6 +29,16 @@
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
+  GstAllocator *ion;
+
+  ion = (GstAllocator *) g_object_new (GST_TYPE_ALLOCATOR_ION, NULL);
+  gst_object_ref_sink (ion);
+  if (gst_allocator_ion_init_check (GST_ALLOCATOR_ION (ion))) {
+    gst_allocator_register (GST_ALLOCATOR_ION_NAME, ion);
+  } else {
+    /* ION allocator not supported. */
+    gst_object_unref (ion);
+  }
 
   if (!gst_element_register (plugin, "glbox",
           GST_RANK_NONE, gst_gl_box_get_type ())) {
diff --git a/plugins/gstionallocator.c b/plugins/gstionallocator.c
new file mode 100644
index 0000000..f73eed3
--- /dev/null
+++ b/plugins/gstionallocator.c
@@ -0,0 +1,175 @@
+/*
+ * # Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+
+#include "gstionallocator.h"
+#include "ion_coral.h"
+
+GST_DEBUG_CATEGORY_STATIC (ion_debug);
+#define GST_CAT_DEFAULT ion_debug
+#define gst_allocator_ion_parent_class parent_class
+G_DEFINE_TYPE (GstAllocatorIon, gst_allocator_ion, GST_TYPE_DMABUF_ALLOCATOR);
+
+static void gst_allocator_ion_finalize (GObject * object);
+static GstMemory * gst_allocator_ion_alloc (GstAllocator * allocator,
+    gsize size,GstAllocationParams * params);
+
+static void
+gst_allocator_ion_class_init (GstAllocatorIonClass * klass)
+{
+  GST_ALLOCATOR_CLASS (klass)->alloc = gst_allocator_ion_alloc;
+  G_OBJECT_CLASS (klass)->finalize = gst_allocator_ion_finalize;
+
+  GST_DEBUG_CATEGORY_INIT (ion_debug, "ion", 0,
+      "Linux dma-buf allocator based on ION");
+}
+
+static void
+gst_allocator_ion_init (GstAllocatorIon * ion)
+{
+  GstAllocator *allocator = GST_ALLOCATOR (ion);
+
+  allocator->mem_type = GST_ALLOCATOR_ION_NAME;
+
+  ion->fd = -1;
+  ion->heap_mask = -1;
+  g_mutex_init (&ion->mutex);
+}
+
+static void
+gst_allocator_ion_finalize (GObject * object)
+{
+  GstAllocatorIon *ion = GST_ALLOCATOR_ION (object);
+
+  if (ion->fd > -1) {
+    close (ion->fd);
+    ion->fd = -1;
+  }
+  g_mutex_clear (&ion->mutex);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+gboolean
+gst_allocator_ion_init_check (GstAllocatorIon * ion)
+{
+  int fd, buf_fd, heap_mask, i;
+  gboolean success;
+  struct ion_heap_query query;
+  struct ion_allocation_data alloc_data;
+  struct ion_heap_data *heap_data = NULL;
+
+  g_mutex_lock (&ion->mutex);
+
+  fd = 1;
+  buf_fd = -1;
+  heap_mask = 0;
+  success = FALSE;
+
+  if (ion->fd > -1) {
+    GST_DEBUG_OBJECT (ion, "Already initialized");
+    goto beach;
+  }
+
+  GST_DEBUG_OBJECT (ion, "Opening /dev/ion");
+  fd = open ("/dev/ion", O_RDWR);
+  if (fd == -1) {
+    GST_INFO_OBJECT (ion, "ION allocator not supported");
+    g_mutex_unlock (&ion->mutex);
+    return FALSE;
+  }
+  GST_DEBUG_OBJECT (ion, "/dev/ion opened with fd=%d", fd);
+
+  memset (&query, 0, sizeof(query));
+  if (ioctl (fd, ION_IOC_HEAP_QUERY, &query) < 0) {
+    GST_ERROR_OBJECT (ion, "ION_IOC_HEAP_QUERY failed: %d", errno);
+    goto beach;
+  }
+
+  GST_DEBUG_OBJECT (ion, "ION_IOC_HEAP_QUERY returned %u heaps", query.cnt);
+  heap_data = g_malloc0_n (query.cnt, sizeof(*heap_data));
+  query.heaps = (uintptr_t) heap_data;
+  if (ioctl (fd, ION_IOC_HEAP_QUERY, &query) < 0) {
+    GST_ERROR_OBJECT (ion, "ION_IOC_HEAP_QUERY failed: %d", errno);
+    goto beach;
+  }
+
+  for (i = 0; i < query.cnt; ++i) {
+    if (heap_data[i].type == ION_HEAP_TYPE_DMA) {
+      heap_mask |= 1 << heap_data[i].heap_id;
+    }
+  }
+
+  memset (&alloc_data, 0, sizeof(alloc_data));
+  alloc_data.len = 42;
+  alloc_data.heap_id_mask = heap_mask;
+  alloc_data.flags = ION_FLAG_CACHED;
+
+  if (ioctl (fd, ION_IOC_ALLOC, &alloc_data) < 0) {
+    GST_ERROR_OBJECT (ion, "ION_IOC_ALLOC failed: %d", errno);
+    goto beach;
+  }
+  buf_fd = alloc_data.fd;
+
+beach:
+  success = buf_fd != -1;
+  if (success) {
+    close (buf_fd);
+    ion->fd = fd;
+    ion->heap_mask = heap_mask;
+    GST_INFO_OBJECT (ion, "ION allocator initialized");
+  } else {
+    GST_ERROR_OBJECT (ion, "ION allocator initialization failed");
+  }
+
+  g_mutex_unlock (&ion->mutex);
+
+  return success;
+}
+
+
+
+static GstMemory *
+gst_allocator_ion_alloc (GstAllocator * allocator, gsize size,
+    GstAllocationParams * params)
+{
+  GstAllocatorIon *ion = GST_ALLOCATOR_ION (allocator);
+  struct ion_allocation_data alloc_data = { 0 };
+
+  alloc_data.len = size;
+  alloc_data.heap_id_mask = ion->heap_mask;
+  alloc_data.flags = ION_FLAG_CACHED;
+
+  if (ioctl (ion->fd, ION_IOC_ALLOC, &alloc_data) < 0) {
+    GST_ERROR_OBJECT (ion, "ION_IOC_ALLOC failed: %d", errno);
+    return NULL;
+  }
+
+  GST_DEBUG_OBJECT (ion, "allocated size %" G_GSIZE_FORMAT " b, fd %d",
+      size, alloc_data.fd);
+  return gst_dmabuf_allocator_alloc (allocator, alloc_data.fd, size);
+}
+
+
+gboolean gst_allocator_ion_init_check (GstAllocatorIon * ion);
\ No newline at end of file
diff --git a/plugins/gstionallocator.h b/plugins/gstionallocator.h
new file mode 100644
index 0000000..e125d92
--- /dev/null
+++ b/plugins/gstionallocator.h
@@ -0,0 +1,50 @@
+/*
+ * # Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ALLOCATOR_ION_H__
+#define __ALLOCATOR_ION_H__
+
+#include <gst/gst.h>
+#include <gst/allocators/gstdmabuf.h>
+
+#define GST_TYPE_ALLOCATOR_ION             (gst_allocator_ion_get_type())
+#define GST_ALLOCATOR_ION(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_ALLOCATOR_ION, GstAllocatorIon))
+#define GST_ALLOCATOR_ION_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_ALLOCATOR_ION, GstAllocatorIonClass))
+#define GST_IS_ALLOCATOR_ION(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_ALLOCATOR_ION))
+#define GST_IS_ALLOCATOR_ION_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_ALLOCATOR_ION))
+
+#define GST_ALLOCATOR_ION_NAME "ion"
+
+typedef struct _GstAllocatorIon GstAllocatorIon;
+typedef struct _GstAllocatorIonClass GstAllocatorIonClass;
+
+struct _GstAllocatorIon {
+  GstDmaBufAllocator parent;
+
+  GMutex mutex;
+  int fd;
+  int heap_mask;
+};
+
+struct _GstAllocatorIonClass {
+  GstDmaBufAllocatorClass parent_class;
+};
+
+GType gst_allocator_ion_get_type (void);
+
+gboolean gst_allocator_ion_init_check (GstAllocatorIon * ion);
+
+#endif
diff --git a/plugins/ion_coral.h b/plugins/ion_coral.h
new file mode 100644
index 0000000..8f765af
--- /dev/null
+++ b/plugins/ion_coral.h
@@ -0,0 +1,126 @@
+/*
+ * drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ */
+
+#ifndef _UAPI_LINUX_ION_H
+#define _UAPI_LINUX_ION_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/**
+ * enum ion_heap_types - list of all possible types of heaps
+ * @ION_HEAP_TYPE_SYSTEM:	 memory allocated via vmalloc
+ * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc
+ * @ION_HEAP_TYPE_CARVEOUT:	 memory allocated from a prereserved
+ *				 carveout heap, allocations are physically
+ *				 contiguous
+ * @ION_HEAP_TYPE_DMA:		 memory allocated via DMA API
+ * @ION_NUM_HEAPS:		 helper for iterating over heaps, a bit mask
+ *				 is used to identify the heaps, so only 32
+ *				 total heap types are supported
+ */
+enum ion_heap_type {
+	ION_HEAP_TYPE_SYSTEM,
+	ION_HEAP_TYPE_SYSTEM_CONTIG,
+	ION_HEAP_TYPE_CARVEOUT,
+	ION_HEAP_TYPE_CHUNK,
+	ION_HEAP_TYPE_DMA,
+	ION_HEAP_TYPE_CUSTOM, /*
+			       * must be last so device specific heaps always
+			       * are at the end of this enum
+			       */
+};
+
+#define ION_NUM_HEAP_IDS		(sizeof(unsigned int) * 8)
+
+/**
+ * allocation flags - the lower 16 bits are used by core ion, the upper 16
+ * bits are reserved for use by the heaps themselves.
+ */
+
+/*
+ * mappings of this buffer should be cached, ion will do cache maintenance
+ * when the buffer is mapped for dma
+ */
+#define ION_FLAG_CACHED 1
+
+/**
+ * DOC: Ion Userspace API
+ *
+ * create a client by opening /dev/ion
+ * most operations handled via following ioctls
+ *
+ */
+
+/**
+ * struct ion_allocation_data - metadata passed from userspace for allocations
+ * @len:		size of the allocation
+ * @heap_id_mask:	mask of heap ids to allocate from
+ * @flags:		flags passed to heap
+ * @handle:		pointer that will be populated with a cookie to use to
+ *			refer to this allocation
+ *
+ * Provided by userspace as an argument to the ioctl
+ */
+struct ion_allocation_data {
+	__u64 len;
+	__u32 heap_id_mask;
+	__u32 flags;
+	__u32 fd;
+	__u32 unused;
+};
+
+#define MAX_HEAP_NAME			32
+
+/**
+ * struct ion_heap_data - data about a heap
+ * @name - first 32 characters of the heap name
+ * @type - heap type
+ * @heap_id - heap id for the heap
+ */
+struct ion_heap_data {
+	char name[MAX_HEAP_NAME];
+	__u32 type;
+	__u32 heap_id;
+	__u32 reserved0;
+	__u32 reserved1;
+	__u32 reserved2;
+};
+
+/**
+ * struct ion_heap_query - collection of data about all heaps
+ * @cnt - total number of heaps to be copied
+ * @heaps - buffer to copy heap data
+ */
+struct ion_heap_query {
+	__u32 cnt; /* Total number of heaps to be copied */
+	__u32 reserved0; /* align to 64bits */
+	__u64 heaps; /* buffer to be populated */
+	__u32 reserved1;
+	__u32 reserved2;
+};
+
+#define ION_IOC_MAGIC		'I'
+
+/**
+ * DOC: ION_IOC_ALLOC - allocate memory
+ *
+ * Takes an ion_allocation_data struct and returns it with the handle field
+ * populated with the opaque handle for the allocation.
+ */
+#define ION_IOC_ALLOC		_IOWR(ION_IOC_MAGIC, 0, \
+				      struct ion_allocation_data)
+
+/**
+ * DOC: ION_IOC_HEAP_QUERY - information about available heaps
+ *
+ * Takes an ion_heap_query structure and populates information about
+ * available Ion heaps.
+ */
+#define ION_IOC_HEAP_QUERY     _IOWR(ION_IOC_MAGIC, 8, \
+					struct ion_heap_query)
+
+#endif /* _UAPI_LINUX_ION_H */