summaryrefslogtreecommitdiff
path: root/drivers/gpu/arm/mali400/r4p0_rel0/common/mali_dma.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/arm/mali400/r4p0_rel0/common/mali_dma.c')
-rw-r--r--drivers/gpu/arm/mali400/r4p0_rel0/common/mali_dma.c201
1 files changed, 201 insertions, 0 deletions
diff --git a/drivers/gpu/arm/mali400/r4p0_rel0/common/mali_dma.c b/drivers/gpu/arm/mali400/r4p0_rel0/common/mali_dma.c
new file mode 100644
index 00000000000..4d75cc2cab7
--- /dev/null
+++ b/drivers/gpu/arm/mali400/r4p0_rel0/common/mali_dma.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2010-2012 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
+ *
+ * A copy of the licence is included with the program, and can also be obtained from Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "mali_kernel_common.h"
+#include "mali_osk.h"
+#include "mali_hw_core.h"
+#include "mali_dma.h"
+
+/**
+ * Size of the Mali-450 DMA unit registers in bytes.
+ */
+#define MALI450_DMA_REG_SIZE 0x08
+
+/**
+ * Value that appears in MEMSIZE if an error occurs when reading the command list.
+ */
+#define MALI450_DMA_BUS_ERR_VAL 0xffffffff
+
+/**
+ * Mali DMA registers
+ * Used in the register read/write routines.
+ * See the hardware documentation for more information about each register.
+ */
+typedef enum mali_dma_register {
+
+ MALI450_DMA_REG_SOURCE_ADDRESS = 0x0000,
+ MALI450_DMA_REG_SOURCE_SIZE = 0x0004,
+} mali_dma_register;
+
+struct mali_dma_core {
+ struct mali_hw_core hw_core; /**< Common for all HW cores */
+ _mali_osk_spinlock_t *lock; /**< Lock protecting access to DMA core */
+ mali_dma_pool pool; /**< Memory pool for command buffers */
+};
+
+static struct mali_dma_core *mali_global_dma_core = NULL;
+
+struct mali_dma_core *mali_dma_create(_mali_osk_resource_t *resource)
+{
+ struct mali_dma_core* dma;
+ _mali_osk_errcode_t err;
+
+ MALI_DEBUG_ASSERT(NULL == mali_global_dma_core);
+
+ dma = _mali_osk_malloc(sizeof(struct mali_dma_core));
+ if (dma == NULL) goto alloc_failed;
+
+ dma->lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_DMA_COMMAND);
+ if (NULL == dma->lock) goto lock_init_failed;
+
+ dma->pool = mali_dma_pool_create(MALI_DMA_CMD_BUF_SIZE, 4, 0);
+ if (NULL == dma->pool) goto dma_pool_failed;
+
+ err = mali_hw_core_create(&dma->hw_core, resource, MALI450_DMA_REG_SIZE);
+ if (_MALI_OSK_ERR_OK != err) goto hw_core_failed;
+
+ mali_global_dma_core = dma;
+ MALI_DEBUG_PRINT(2, ("Mali DMA: Created Mali APB DMA unit\n"));
+ return dma;
+
+ /* Error handling */
+
+hw_core_failed:
+ mali_dma_pool_destroy(dma->pool);
+dma_pool_failed:
+ _mali_osk_spinlock_term(dma->lock);
+lock_init_failed:
+ _mali_osk_free(dma);
+alloc_failed:
+ MALI_DEBUG_PRINT(2, ("Mali DMA: Failed to create APB DMA unit\n"));
+ return NULL;
+}
+
+void mali_dma_delete(struct mali_dma_core *dma)
+{
+ MALI_DEBUG_ASSERT_POINTER(dma);
+
+ MALI_DEBUG_PRINT(2, ("Mali DMA: Deleted Mali APB DMA unit\n"));
+
+ mali_hw_core_delete(&dma->hw_core);
+ _mali_osk_spinlock_term(dma->lock);
+ mali_dma_pool_destroy(dma->pool);
+ _mali_osk_free(dma);
+}
+
+static void mali_dma_bus_error(struct mali_dma_core *dma)
+{
+ u32 addr = mali_hw_core_register_read(&dma->hw_core, MALI450_DMA_REG_SOURCE_ADDRESS);
+
+ MALI_PRINT_ERROR(("Mali DMA: Bus error when reading command list from 0x%lx\n", addr));
+
+ /* Clear the bus error */
+ mali_hw_core_register_write(&dma->hw_core, MALI450_DMA_REG_SOURCE_SIZE, 0);
+}
+
+static mali_bool mali_dma_is_busy(struct mali_dma_core *dma)
+{
+ u32 val;
+ mali_bool dma_busy_flag = MALI_FALSE;
+
+ MALI_DEBUG_ASSERT_POINTER(dma);
+
+ val = mali_hw_core_register_read(&dma->hw_core, MALI450_DMA_REG_SOURCE_SIZE);
+
+ if (MALI450_DMA_BUS_ERR_VAL == val) {
+ /* Bus error reading command list */
+ mali_dma_bus_error(dma);
+ return MALI_FALSE;
+ }
+ if (val > 0) {
+ dma_busy_flag = MALI_TRUE;
+ }
+
+ return dma_busy_flag;
+}
+
+static void mali_dma_start_transfer(struct mali_dma_core* dma, mali_dma_cmd_buf *buf)
+{
+ u32 memsize = buf->size * 4;
+ u32 addr = buf->phys_addr;
+
+ MALI_DEBUG_ASSERT_POINTER(dma);
+ MALI_DEBUG_ASSERT(memsize < (1 << 16));
+ MALI_DEBUG_ASSERT(0 == (memsize & 0x3)); /* 4 byte aligned */
+
+ MALI_DEBUG_ASSERT(!mali_dma_is_busy(dma));
+
+ /* Writes the physical source memory address of chunk containing command headers and data */
+ mali_hw_core_register_write(&dma->hw_core, MALI450_DMA_REG_SOURCE_ADDRESS, addr);
+
+ /* Writes the length of transfer */
+ mali_hw_core_register_write(&dma->hw_core, MALI450_DMA_REG_SOURCE_SIZE, memsize);
+}
+
+_mali_osk_errcode_t mali_dma_get_cmd_buf(mali_dma_cmd_buf *buf)
+{
+ MALI_DEBUG_ASSERT_POINTER(buf);
+
+ buf->virt_addr = (u32*)mali_dma_pool_alloc(mali_global_dma_core->pool, &buf->phys_addr);
+ if (NULL == buf->virt_addr) {
+ return _MALI_OSK_ERR_NOMEM;
+ }
+
+ /* size contains the number of words in the buffer and is incremented
+ * as commands are added to the buffer. */
+ buf->size = 0;
+
+ return _MALI_OSK_ERR_OK;
+}
+
+void mali_dma_put_cmd_buf(mali_dma_cmd_buf *buf)
+{
+ MALI_DEBUG_ASSERT_POINTER(buf);
+
+ if (NULL == buf->virt_addr) return;
+
+ mali_dma_pool_free(mali_global_dma_core->pool, buf->virt_addr, buf->phys_addr);
+
+ buf->virt_addr = NULL;
+}
+
+_mali_osk_errcode_t mali_dma_start(struct mali_dma_core* dma, mali_dma_cmd_buf *buf)
+{
+ _mali_osk_errcode_t err = _MALI_OSK_ERR_OK;
+
+ _mali_osk_spinlock_lock(dma->lock);
+
+ if (mali_dma_is_busy(dma)) {
+ err = _MALI_OSK_ERR_BUSY;
+ goto out;
+ }
+
+ mali_dma_start_transfer(dma, buf);
+
+out:
+ _mali_osk_spinlock_unlock(dma->lock);
+ return err;
+}
+
+void mali_dma_debug(struct mali_dma_core *dma)
+{
+ MALI_DEBUG_ASSERT_POINTER(dma);
+ MALI_DEBUG_PRINT(1, ("DMA unit registers:\n\t%08x, %08x\n",
+ mali_hw_core_register_read(&dma->hw_core, MALI450_DMA_REG_SOURCE_ADDRESS),
+ mali_hw_core_register_read(&dma->hw_core, MALI450_DMA_REG_SOURCE_SIZE)
+ ));
+
+}
+
+struct mali_dma_core *mali_dma_get_global_dma_core(void)
+{
+ /* Returns the global dma core object */
+ return mali_global_dma_core;
+}