~thopiekar/arm-mali/libvdpau-sunxi

« back to all changes in this revision

Viewing changes to ve.c

  • Committer: Jens Kuske
  • Date: 2016-02-16 13:12:22 UTC
  • Revision ID: git-v1:685769372a8281bf67046a958883323aaf14d232
Use libcedrus

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (c) 2013-2014 Jens Kuske <jenskuske@gmail.com>
3
 
 *
4
 
 * This library is free software; you can redistribute it and/or
5
 
 * modify it under the terms of the GNU Lesser General Public
6
 
 * License as published by the Free Software Foundation; either
7
 
 * version 2.1 of the License, or (at your option) any later version.
8
 
 *
9
 
 * This library is distributed in the hope that it will be useful,
10
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 
 * Lesser General Public License for more details.
13
 
 *
14
 
 * You should have received a copy of the GNU Lesser General Public
15
 
 * License along with this library; if not, write to the Free Software
16
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17
 
 *
18
 
 */
19
 
 
20
 
#include <fcntl.h>
21
 
#include <pthread.h>
22
 
#include <stddef.h>
23
 
#include <stdint.h>
24
 
#include <stdio.h>
25
 
#include <stdlib.h>
26
 
#include <unistd.h>
27
 
#include <sys/ioctl.h>
28
 
#include <sys/mman.h>
29
 
#include "ve.h"
30
 
#include "kernel-headers/ion.h"
31
 
#include "kernel-headers/ion_sunxi.h"
32
 
 
33
 
#define DEVICE "/dev/cedar_dev"
34
 
#define PAGE_OFFSET (0xc0000000) // from kernel
35
 
#define PAGE_SIZE (4096)
36
 
 
37
 
#define container_of(ptr, type, member) ({                      \
38
 
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
39
 
        (type *)( (char *)__mptr - offsetof(type,member) );})
40
 
 
41
 
enum IOCTL_CMD
42
 
{
43
 
        IOCTL_UNKOWN = 0x100,
44
 
        IOCTL_GET_ENV_INFO,
45
 
        IOCTL_WAIT_VE,
46
 
        IOCTL_RESET_VE,
47
 
        IOCTL_ENABLE_VE,
48
 
        IOCTL_DISABLE_VE,
49
 
        IOCTL_SET_VE_FREQ,
50
 
 
51
 
        IOCTL_CONFIG_AVS2 = 0x200,
52
 
        IOCTL_GETVALUE_AVS2 ,
53
 
        IOCTL_PAUSE_AVS2 ,
54
 
        IOCTL_START_AVS2 ,
55
 
        IOCTL_RESET_AVS2 ,
56
 
        IOCTL_ADJUST_AVS2,
57
 
        IOCTL_ENGINE_REQ,
58
 
        IOCTL_ENGINE_REL,
59
 
        IOCTL_ENGINE_CHECK_DELAY,
60
 
        IOCTL_GET_IC_VER,
61
 
        IOCTL_ADJUST_AVS2_ABS,
62
 
        IOCTL_FLUSH_CACHE
63
 
};
64
 
 
65
 
struct ve_info
66
 
{
67
 
        uint32_t reserved_mem;
68
 
        int reserved_mem_size;
69
 
        uint32_t registers;
70
 
};
71
 
 
72
 
struct cedarv_cache_range
73
 
{
74
 
        long start;
75
 
        long end;
76
 
};
77
 
 
78
 
struct memchunk_t
79
 
{
80
 
        struct ve_mem mem;
81
 
        struct memchunk_t *next;
82
 
};
83
 
 
84
 
struct ion_mem
85
 
{
86
 
        struct ion_handle *handle;
87
 
        int fd;
88
 
        struct ve_mem mem;
89
 
};
90
 
 
91
 
static struct
92
 
{
93
 
        int fd;
94
 
        int ion_fd;
95
 
        void *regs;
96
 
        int version;
97
 
        int ioctl_offset;
98
 
        struct memchunk_t first_memchunk;
99
 
        pthread_rwlock_t memory_lock;
100
 
        pthread_mutex_t device_lock;
101
 
} ve = { .fd = -1, .ion_fd = -1, .memory_lock = PTHREAD_RWLOCK_INITIALIZER, .device_lock = PTHREAD_MUTEX_INITIALIZER };
102
 
 
103
 
int ve_open(void)
104
 
{
105
 
        if (ve.fd != -1)
106
 
                return 0;
107
 
 
108
 
        struct ve_info info;
109
 
 
110
 
        ve.fd = open(DEVICE, O_RDWR);
111
 
        if (ve.fd == -1)
112
 
                return 0;
113
 
 
114
 
        if (ioctl(ve.fd, IOCTL_GET_ENV_INFO, (void *)(&info)) == -1)
115
 
                goto close;
116
 
 
117
 
        ve.regs = mmap(NULL, 0x800, PROT_READ | PROT_WRITE, MAP_SHARED, ve.fd, info.registers);
118
 
        if (ve.regs == MAP_FAILED)
119
 
                goto close;
120
 
 
121
 
        ve.first_memchunk.mem.phys = info.reserved_mem - PAGE_OFFSET;
122
 
        ve.first_memchunk.mem.size = info.reserved_mem_size;
123
 
 
124
 
        if (ve.first_memchunk.mem.size == 0)
125
 
        {
126
 
                ve.ion_fd = open("/dev/ion", O_RDONLY);
127
 
                if (ve.ion_fd == -1)
128
 
                        goto unmap;
129
 
        }
130
 
 
131
 
        ioctl(ve.fd, IOCTL_ENGINE_REQ, 0);
132
 
 
133
 
        ve.version = readl(ve.regs + VE_VERSION) >> 16;
134
 
 
135
 
        if (ve.version >= 0x1667)
136
 
                ve.ioctl_offset = 1;
137
 
 
138
 
        ioctl(ve.fd, IOCTL_ENABLE_VE + ve.ioctl_offset, 0);
139
 
        ioctl(ve.fd, IOCTL_SET_VE_FREQ + ve.ioctl_offset, 320);
140
 
        ioctl(ve.fd, IOCTL_RESET_VE + ve.ioctl_offset, 0);
141
 
 
142
 
        writel(0x00130007, ve.regs + VE_CTRL);
143
 
 
144
 
        printf("[VDPAU SUNXI] VE version 0x%04x opened.\n", ve.version);
145
 
 
146
 
        return 1;
147
 
 
148
 
unmap:
149
 
        munmap(ve.regs, 0x800);
150
 
close:
151
 
        close(ve.fd);
152
 
        ve.fd = -1;
153
 
        return 0;
154
 
}
155
 
 
156
 
void ve_close(void)
157
 
{
158
 
        if (ve.fd == -1)
159
 
                return;
160
 
 
161
 
        ioctl(ve.fd, IOCTL_DISABLE_VE + ve.ioctl_offset, 0);
162
 
        ioctl(ve.fd, IOCTL_ENGINE_REL, 0);
163
 
 
164
 
        munmap(ve.regs, 0x800);
165
 
        ve.regs = NULL;
166
 
 
167
 
        if (ve.ion_fd != -1)
168
 
                close(ve.ion_fd);
169
 
 
170
 
        close(ve.fd);
171
 
        ve.fd = -1;
172
 
}
173
 
 
174
 
int ve_get_version(void)
175
 
{
176
 
        return ve.version;
177
 
}
178
 
 
179
 
int ve_wait(int timeout)
180
 
{
181
 
        if (ve.fd == -1)
182
 
                return 0;
183
 
 
184
 
        return ioctl(ve.fd, IOCTL_WAIT_VE, timeout);
185
 
}
186
 
 
187
 
void *ve_get(int engine, uint32_t flags)
188
 
{
189
 
        if (pthread_mutex_lock(&ve.device_lock))
190
 
                return NULL;
191
 
 
192
 
        writel(0x00130000 | (engine & 0xf) | (flags & ~0xf), ve.regs + VE_CTRL);
193
 
 
194
 
        return ve.regs;
195
 
}
196
 
 
197
 
void ve_put(void)
198
 
{
199
 
        writel(0x00130007, ve.regs + VE_CTRL);
200
 
        pthread_mutex_unlock(&ve.device_lock);
201
 
}
202
 
 
203
 
static struct ve_mem *ion_malloc(int size)
204
 
{
205
 
        struct ion_mem *imem = calloc(1, sizeof(struct ion_mem));
206
 
        if (!imem)
207
 
        {
208
 
                perror("calloc ion_buffer failed");
209
 
                return NULL;
210
 
        }
211
 
 
212
 
        struct ion_allocation_data alloc = {
213
 
                .len = size,
214
 
                .align = 4096,
215
 
                .heap_id_mask = ION_HEAP_TYPE_DMA,
216
 
                .flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC,
217
 
        };
218
 
 
219
 
        if (ioctl(ve.ion_fd, ION_IOC_ALLOC, &alloc))
220
 
        {
221
 
                perror("ION_IOC_ALLOC failed");
222
 
                free(imem);
223
 
                return NULL;
224
 
        }
225
 
 
226
 
        imem->handle = alloc.handle;
227
 
        imem->mem.size = size;
228
 
 
229
 
        struct ion_fd_data map = {
230
 
                .handle = imem->handle,
231
 
        };
232
 
 
233
 
        if (ioctl(ve.ion_fd, ION_IOC_MAP, &map))
234
 
        {
235
 
                perror("ION_IOC_MAP failed");
236
 
                free(imem);
237
 
                return NULL;
238
 
        }
239
 
 
240
 
        imem->fd = map.fd;
241
 
 
242
 
        imem->mem.virt = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, imem->fd, 0);
243
 
        if (imem->mem.virt == MAP_FAILED)
244
 
        {
245
 
                perror("mmap failed");
246
 
                return NULL;
247
 
        }
248
 
 
249
 
        sunxi_phys_data phys = {
250
 
                .handle = imem->handle,
251
 
        };
252
 
 
253
 
        struct ion_custom_data custom = {
254
 
                .cmd = ION_IOC_SUNXI_PHYS_ADDR,
255
 
                .arg = (unsigned long)(&phys),
256
 
        };
257
 
 
258
 
        if (ioctl(ve.ion_fd, ION_IOC_CUSTOM, &custom))
259
 
        {
260
 
                perror("ION_IOC_CUSTOM(SUNXI_PHYS_ADDR) failed");
261
 
                free(imem);
262
 
                return NULL;
263
 
        }
264
 
 
265
 
        imem->mem.phys = phys.phys_addr - 0x40000000;
266
 
 
267
 
        return &imem->mem;
268
 
}
269
 
 
270
 
struct ve_mem *ve_malloc(int size)
271
 
{
272
 
        if (ve.fd == -1)
273
 
                return NULL;
274
 
 
275
 
        if (ve.ion_fd != -1)
276
 
                return ion_malloc(size);
277
 
 
278
 
        if (pthread_rwlock_wrlock(&ve.memory_lock))
279
 
                return NULL;
280
 
 
281
 
        void *addr = NULL;
282
 
        struct ve_mem *ret = NULL;
283
 
 
284
 
        size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
285
 
        struct memchunk_t *c, *best_chunk = NULL;
286
 
        for (c = &ve.first_memchunk; c != NULL; c = c->next)
287
 
        {
288
 
                if(c->mem.virt == NULL && c->mem.size >= size)
289
 
                {
290
 
                        if (best_chunk == NULL || c->mem.size < best_chunk->mem.size)
291
 
                                best_chunk = c;
292
 
 
293
 
                        if (c->mem.size == size)
294
 
                                break;
295
 
                }
296
 
        }
297
 
 
298
 
        if (!best_chunk)
299
 
                goto out;
300
 
 
301
 
        int left_size = best_chunk->mem.size - size;
302
 
 
303
 
        addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ve.fd, best_chunk->mem.phys + PAGE_OFFSET);
304
 
        if (addr == MAP_FAILED)
305
 
        {
306
 
                ret = NULL;
307
 
                goto out;
308
 
        }
309
 
 
310
 
        best_chunk->mem.virt = addr;
311
 
        best_chunk->mem.size = size;
312
 
 
313
 
        if (left_size > 0)
314
 
        {
315
 
                c = malloc(sizeof(struct memchunk_t));
316
 
                c->mem.phys = best_chunk->mem.phys + size;
317
 
                c->mem.size = left_size;
318
 
                c->mem.virt = NULL;
319
 
                c->next = best_chunk->next;
320
 
                best_chunk->next = c;
321
 
        }
322
 
 
323
 
        ret = &best_chunk->mem;
324
 
out:
325
 
        pthread_rwlock_unlock(&ve.memory_lock);
326
 
        return ret;
327
 
}
328
 
 
329
 
static void ion_free(struct ve_mem *mem)
330
 
{
331
 
        if (ve.ion_fd == -1 || !mem)
332
 
                return;
333
 
 
334
 
        struct ion_mem *imem = container_of(mem, struct ion_mem, mem);
335
 
 
336
 
        if (munmap(mem->virt, mem->size))
337
 
        {
338
 
                perror("munmap failed");
339
 
                return;
340
 
        }
341
 
 
342
 
        close(imem->fd);
343
 
 
344
 
        struct ion_handle_data handle = {
345
 
                .handle = imem->handle,
346
 
        };
347
 
 
348
 
        if (ioctl(ve.ion_fd, ION_IOC_FREE, &handle))
349
 
        {
350
 
                perror("ION_IOC_FREE failed");
351
 
                free(imem);
352
 
                return;
353
 
        }
354
 
}
355
 
 
356
 
void ve_free(struct ve_mem *mem)
357
 
{
358
 
        if (ve.fd == -1)
359
 
                return;
360
 
 
361
 
        if (mem == NULL)
362
 
                return;
363
 
 
364
 
        if (ve.ion_fd != -1)
365
 
                ion_free(mem);
366
 
 
367
 
        if (pthread_rwlock_wrlock(&ve.memory_lock))
368
 
                return;
369
 
 
370
 
        struct memchunk_t *c;
371
 
        for (c = &ve.first_memchunk; c != NULL; c = c->next)
372
 
        {
373
 
                if (&c->mem == mem)
374
 
                {
375
 
                        munmap(c->mem.virt, c->mem.size);
376
 
                        c->mem.virt = NULL;
377
 
                        break;
378
 
                }
379
 
        }
380
 
 
381
 
        for (c = &ve.first_memchunk; c != NULL; c = c->next)
382
 
        {
383
 
                if (c->mem.virt == NULL)
384
 
                {
385
 
                        while (c->next != NULL && c->next->mem.virt == NULL)
386
 
                        {
387
 
                                struct memchunk_t *n = c->next;
388
 
                                c->mem.size += n->mem.size;
389
 
                                c->next = n->next;
390
 
                                free(n);
391
 
                        }
392
 
                }
393
 
        }
394
 
 
395
 
        pthread_rwlock_unlock(&ve.memory_lock);
396
 
}
397
 
 
398
 
void ve_flush_cache(struct ve_mem *mem)
399
 
{
400
 
        if (ve.fd == -1)
401
 
                return;
402
 
 
403
 
        if (ve.ion_fd != -1)
404
 
        {
405
 
                sunxi_cache_range range = {
406
 
                        .start = (long)mem->virt,
407
 
                        .end = (long)mem->virt + mem->size,
408
 
                };
409
 
 
410
 
                struct ion_custom_data cache = {
411
 
                        .cmd = ION_IOC_SUNXI_FLUSH_RANGE,
412
 
                        .arg = (unsigned long)(&range),
413
 
                };
414
 
 
415
 
                if (ioctl(ve.ion_fd, ION_IOC_CUSTOM, &cache))
416
 
                        perror("ION_IOC_CUSTOM(SUNXI_FLUSH_RANGE) failed");
417
 
        }
418
 
        else
419
 
        {
420
 
                struct cedarv_cache_range range =
421
 
                {
422
 
                        .start = (int)mem->virt,
423
 
                        .end = (int)mem->virt + mem->size
424
 
                };
425
 
 
426
 
                ioctl(ve.fd, IOCTL_FLUSH_CACHE, (void*)(&range));
427
 
        }
428
 
}