summaryrefslogtreecommitdiff
path: root/drivers/gpu/arm/mali400/r4p0_rel0/common/mali_dma.c
blob: 4d75cc2cab7deed571b235e83da0f6074a23aa19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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;
}