Skip to content
Snippets Groups Projects
Commit 7f9b20eb authored by Alexandre Bailon's avatar Alexandre Bailon
Browse files

Add support of APU


This adds the support APU (AI Processor Unit).

Signed-off-by: default avatarAlexandre Bailon <abailon@baylibre.com>
parent 1d16e4bd
No related branches found
No related tags found
No related merge requests found
apu_device_del
apu_device_new
apu_device_ref
apu_bo_new
apu_bo_ref
apu_bo_del
apu_bo_handle
apu_bo_dmabuf
apu_bo_map
apu_bo_iommu_map
apu_bo_iommu_unmap
apu_new_job
apu_free_job
apu_job_init
apu_job_wait
apu_queue
apu_dequeue_result
/*
* Copyright (C) 2021 BayLibre SAS
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include <errno.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <libdrm_macros.h>
#include <libsync.h>
#include <util_double_list.h>
#include <xf86drm.h>
#include <xf86atomic.h>
#include "apu_drm.h"
#include "apu_drmif.h"
static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
static void * dev_table;
struct apu_cached_bo {
struct list_head free;
struct list_head allocated;
};
struct apu_drm_device {
int fd;
int device_id;
atomic_t refcnt;
/* The handle_table is used to track GEM bo handles associated w/
* this fd. This is needed, in particular, when importing
* dmabuf's because we don't want multiple 'struct apu_bo's
* floating around with the same handle. Otherwise, when the
* first one is apu_bo_del()'d the handle becomes no longer
* valid, and the remaining 'struct apu_bo's are left pointing
* to an invalid handle (and possible a GEM bo that is already
* free'd).
*/
void *handle_table;
pthread_mutex_t queue_lock;
struct list_head queue;
void *cached_alloc_table;
};
struct apu_drm_job {
struct apu_drm_device *dev;
struct drm_apu_gem_queue *req;
uint32_t syncobj;
struct list_head node;
char data[0];
};
/* a GEM buffer object allocated from the DRM device */
struct apu_bo {
struct apu_drm_device *dev;
int fd; /* dmabuf handle */
uint32_t handle;
uint32_t size;
void *map; /* userspace mmap'ing (if there is one) */
uint64_t offset; /* offset to mmap() */
atomic_t refcnt;
struct list_head cache;
int cached;
};
static void _apu_bo_del(struct apu_bo *bo);
static struct apu_drm_device * apu_device_new_impl(int fd)
{
struct apu_drm_device *dev = calloc(sizeof(*dev), 1);
if (!dev)
return NULL;
dev->fd = fd;
atomic_set(&dev->refcnt, 1);
dev->handle_table = drmHashCreate();
dev->cached_alloc_table = drmHashCreate();
pthread_mutex_init(&dev->queue_lock, NULL);
list_inithead(&dev->queue);
return dev;
}
drm_public struct apu_drm_device * apu_device_new(int fd, int device_id)
{
struct apu_drm_device *dev = NULL;
pthread_mutex_lock(&table_lock);
if (!dev_table)
dev_table = drmHashCreate();
if (drmHashLookup(dev_table, fd, (void **)&dev)) {
/* not found, create new device */
dev = apu_device_new_impl(fd);
dev->device_id = device_id;
drmHashInsert(dev_table, fd, dev);
} else {
/* found, just incr refcnt */
dev = apu_device_ref(dev);
}
pthread_mutex_unlock(&table_lock);
return dev;
}
drm_public struct apu_drm_device * apu_device_ref(struct apu_drm_device *dev)
{
atomic_inc(&dev->refcnt);
return dev;
}
static void free_apu_cached_bo(struct apu_drm_device *dev)
{
struct apu_cached_bo *cached_bo;
struct apu_bo *bo, *tmp;
unsigned long key;
int ret;
ret = drmHashFirst(dev->cached_alloc_table, &key, (void **)&cached_bo);
if (!ret)
return;
do {
LIST_FOR_EACH_ENTRY_SAFE(bo, tmp, &cached_bo->free, cache) {
_apu_bo_del(bo);
}
free(cached_bo);
drmHashDelete(dev->cached_alloc_table, key);
} while (drmHashNext(dev->cached_alloc_table, &key, (void **)&cached_bo));
}
drm_public int apu_device_del(struct apu_drm_device *dev)
{
if (!atomic_dec_and_test(&dev->refcnt))
return 0;
pthread_mutex_lock(&table_lock);
drmHashDestroy(dev->handle_table);
free_apu_cached_bo(dev);
drmHashDestroy(dev->cached_alloc_table);
drmHashDelete(dev_table, dev->fd);
pthread_mutex_unlock(&table_lock);
free(dev);
return 1;
}
/* allocate a new buffer object, call w/ table_lock held */
static struct apu_bo * bo_from_handle(struct apu_drm_device *dev,
uint32_t handle)
{
struct apu_bo *bo = calloc(sizeof(*bo), 1);
if (!bo) {
struct drm_gem_close req = {
.handle = handle,
};
drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
return NULL;
}
bo->dev = apu_device_ref(dev);
bo->handle = handle;
bo->fd = -1;
atomic_set(&bo->refcnt, 1);
/* add ourselves to the handle table: */
drmHashInsert(dev->handle_table, handle, bo);
return bo;
}
/* allocate a new buffer object */
static struct apu_bo * apu_bo_new_impl(struct apu_drm_device *dev,
uint32_t size, uint32_t flags)
{
struct apu_bo *bo = NULL;
struct drm_apu_gem_new req = {
.size = size,
.flags = flags,
};
if (size == 0) {
goto fail;
}
if (drmCommandWriteRead(dev->fd, DRM_APU_GEM_NEW, &req, sizeof(req)))
goto fail;
pthread_mutex_lock(&table_lock);
bo = bo_from_handle(dev, req.handle);
pthread_mutex_unlock(&table_lock);
bo->size = size;
bo->offset = req.offset;
list_inithead(&bo->cache);
bo->cached = 0;
return bo;
fail:
free(bo);
return NULL;
}
/* allocate a new buffer object */
drm_public struct apu_bo *
apu_bo_new(struct apu_drm_device *dev, uint32_t size, uint32_t flags)
{
return apu_bo_new_impl(dev, size, flags);
}
/* allocate a new buffer object */
drm_public struct apu_bo *
apu_cached_bo_new(struct apu_drm_device *dev, uint32_t size, uint32_t flags)
{
struct apu_cached_bo *cached_bo = NULL;
struct apu_bo *bo = NULL;
if (!drmHashLookup(dev->cached_alloc_table, size, (void **)&cached_bo))
{
if (!LIST_IS_EMPTY(&cached_bo->free)) {
bo = LIST_FIRST_ENTRY(&cached_bo->free, struct apu_bo,
cache);
apu_bo_ref(bo);
list_del(&bo->cache);
}
}
if (!bo) {
bo = apu_bo_new_impl(dev, size, flags);
if (!bo)
return NULL;
bo->cached = 1;
}
if (!cached_bo) {
cached_bo = malloc(sizeof(*cached_bo));
if (!cached_bo) {
apu_bo_del(bo);
return NULL;
}
list_inithead(&cached_bo->free);
list_inithead(&cached_bo->allocated);
drmHashInsert(dev->cached_alloc_table, size, cached_bo);
}
list_add(&bo->cache, &cached_bo->allocated);
return bo;
}
/* allocate a new buffer object */
static struct apu_bo * apu_bo_user_new_impl(struct apu_drm_device *dev,
void *hostptr, uint32_t size, uint32_t flags)
{
struct apu_bo *bo = NULL;
struct drm_apu_gem_user_new req = {
.hostptr = (uint64_t)hostptr,
.size = size,
.flags = flags,
};
if (size == 0) {
goto fail;
}
if (drmCommandWriteRead(dev->fd, DRM_APU_GEM_USER_NEW, &req, sizeof(req)))
goto fail;
pthread_mutex_lock(&table_lock);
bo = bo_from_handle(dev, req.handle);
pthread_mutex_unlock(&table_lock);
bo->size = size;
bo->offset = req.offset;
return bo;
fail:
free(bo);
return NULL;
}
/* allocate a new (un-tiled) buffer object */
drm_public struct apu_bo *
apu_bo_user_new(struct apu_drm_device *dev, void *hostptr,
uint32_t size, uint32_t flags)
{
return apu_bo_user_new_impl(dev, hostptr, size, flags);
}
drm_public struct apu_bo *apu_bo_ref(struct apu_bo *bo)
{
atomic_inc(&bo->refcnt);
return bo;
}
/* destroy a buffer object */
drm_public void apu_cached_bo_del(struct apu_bo *bo)
{
struct apu_cached_bo *cached_bo = NULL;
if (drmHashLookup(bo->dev->cached_alloc_table, bo->size,
(void **)&cached_bo))
return;
list_del(&bo->cache);
list_add(&bo->cache, &cached_bo->free);
}
static void _apu_bo_del(struct apu_bo *bo)
{
if (bo->handle) {
struct drm_gem_close req = {
.handle = bo->handle,
};
pthread_mutex_lock(&table_lock);
drmHashDelete(bo->dev->handle_table, bo->handle);
drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
pthread_mutex_unlock(&table_lock);
}
apu_device_del(bo->dev);
free(bo);
}
drm_public void apu_bo_del(struct apu_bo *bo)
{
if (!bo) {
return;
}
if (!atomic_dec_and_test(&bo->refcnt))
return;
if (bo->map) {
munmap(bo->map, bo->size);
bo->map = NULL;
}
/* return the bo to the cache allocator */
if (!LIST_IS_EMPTY(&bo->cache) || bo->cached) {
apu_cached_bo_del(bo);
return;
}
if (bo->fd >= 0) {
close(bo->fd);
bo->fd = -1;
}
_apu_bo_del(bo);
}
drm_public void *apu_bo_map(struct apu_bo *bo)
{
if (!bo->map) {
bo->map = mmap(0, bo->size, PROT_READ | PROT_WRITE,
MAP_SHARED, bo->dev->fd, bo->offset);
if (bo->map == MAP_FAILED) {
bo->map = NULL;
}
}
return bo->map;
}
drm_public uint32_t apu_bo_handle(struct apu_bo *bo)
{
return bo->handle;
}
/* caller owns the dmabuf fd that is returned and is responsible
* to close() it when done
*/
drm_public int apu_bo_dmabuf(struct apu_bo *bo)
{
if (bo->fd < 0) {
struct drm_prime_handle req = {
.handle = bo->handle,
.flags = DRM_CLOEXEC | DRM_RDWR,
};
int ret;
ret = drmIoctl(bo->dev->fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &req);
if (ret)
return ret;
bo->fd = req.fd;
}
return dup(bo->fd);
}
drm_public int apu_bo_iommu_map(struct apu_drm_device *dev,
struct apu_bo **bos, uint64_t *das, uint32_t count)
{
unsigned int i;
int ret;
uint32_t bo_handles[count];
uint32_t bo_das[count];
struct drm_apu_gem_iommu_map req = {
.bo_handles = (uint64_t)bo_handles,
.bo_handle_count = count,
.bo_device_addresses = (uint64_t)bo_das,
};
for (i = 0; i < count; i++)
bo_handles[i] = bos[i]->handle;
ret = drmCommandWrite(dev->fd, DRM_APU_GEM_IOMMU_MAP, &req, sizeof(req));
if (ret)
return ret;
for (i = 0; i < count; i++)
das[i] = bo_das[i];
return 0;
}
drm_public int apu_bo_iommu_unmap(struct apu_drm_device *dev,
struct apu_bo **bos, uint32_t count)
{
unsigned int i;
int ret;
uint32_t bo_handles[count];
struct drm_apu_gem_iommu_map req = {
.bo_handles = (uint64_t)bo_handles,
.bo_handle_count = count,
};
for (i = 0; i < count; i++)
bo_handles[i] = bos[i]->handle;
ret = drmCommandWrite(dev->fd, DRM_APU_GEM_IOMMU_UNMAP, &req, sizeof(req));
if (ret)
return ret;
return 0;
}
drm_public
struct apu_drm_job *apu_new_job(struct apu_drm_device *dev, size_t size)
{
struct apu_drm_job *job;
int ret;
job = malloc(sizeof(*job) + size);
if (!job) {
errno = ENOMEM;
return NULL;
}
job->dev = dev;
ret = drmSyncobjCreate(dev->fd, 0, &job->syncobj);
if (ret) {
free(job);
return NULL;
}
return job;
}
drm_public
int apu_job_init(struct apu_drm_job *job, uint32_t cmd,
struct apu_bo **bos, uint32_t count,
void *data_in, size_t size_in, size_t size_out)
{
uint32_t *bo_handles;
uint32_t i;
job->req = malloc(sizeof(*job->req));
if (!job->req)
return -ENOMEM;
bo_handles = malloc(sizeof(*bo_handles) * count);
if (!bo_handles) {
free(job->req);
return -ENOMEM;
}
job->req->device = job->dev->device_id;
job->req->cmd = cmd;
job->req->bo_handles = (uint64_t)bo_handles;
job->req->bo_handle_count = count;
job->req->out_sync = job->syncobj;
job->req->size_in = size_in;
job->req->size_out = size_out;
job->req->data = (uint64_t)data_in;
for (i = 0; i < count; i++)
bo_handles[i] = bos[i]->handle;
list_inithead(&job->node);
return 0;
}
drm_public
void *apu_job_get_data(struct apu_drm_job *job)
{
return job->data;
}
drm_public
int apu_job_wait(struct apu_drm_job *job)
{
return drmSyncobjWait(job->dev->fd, &job->syncobj,
1, INT64_MAX, 0, NULL);
}
drm_public
struct apu_drm_job *apu_job_wait_any(struct apu_drm_device *dev)
{
int ret;
uint8_t buffer[4096];
struct apu_drm_job *job;
struct apu_job_event *event;
ret = sync_wait(dev->fd, 1000);
if (ret) {
return NULL;
}
ret = read(dev->fd, buffer, sizeof(*event));
if (ret < 0) {
// TODO: log error and eventually, exit
}
event = (struct apu_job_event *)buffer;
if (event->base.type != 0x80000000) {
// TODO: log error and eventually, exit
}
pthread_mutex_lock(&dev->queue_lock);
LIST_FOR_EACH_ENTRY(job, &dev->queue, node) {
if (job->syncobj == event->out_sync) {
pthread_mutex_unlock(&dev->queue_lock);
return job;
}
}
pthread_mutex_unlock(&dev->queue_lock);
return NULL;
}
drm_public
void apu_free_job(struct apu_drm_job *job)
{
free((void *)job->req->bo_handles);
free(job->req);
free(job);
}
drm_public
int apu_queue(struct apu_drm_job *job)
{
struct apu_drm_device *dev = job->dev;
int ret;
pthread_mutex_lock(&dev->queue_lock);
ret = drmCommandWrite(dev->fd, DRM_APU_GEM_QUEUE,
job->req, sizeof(*job->req));
if (ret) {
pthread_mutex_unlock(&dev->queue_lock);
return ret;
}
list_add(&job->node, &dev->queue);
pthread_mutex_unlock(&dev->queue_lock);
return 0;
}
drm_public
int apu_dequeue_result(struct apu_drm_job *job, uint16_t *result,
void *data_out, size_t *size)
{
struct apu_drm_device *dev = job->dev;
int ret;
struct drm_apu_gem_dequeue req = {
.out_sync = job->syncobj,
.data = (uint64_t)data_out,
};
ret = drmCommandWriteRead(dev->fd, DRM_APU_GEM_DEQUEUE, &req,
sizeof(req));
if (ret)
return ret;
*result = req.result;
if (size)
*size = req.size;
pthread_mutex_lock(&dev->queue_lock);
list_del(&job->node);
pthread_mutex_unlock(&dev->queue_lock);
return 0;
}
/*
* Copyright (C) 2021 BayLibre SAS
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __APU_DRM_H__
#define __APU_DRM_H__
#include "drm.h"
#if defined(__cplusplus)
extern "C" {
#endif
/* Please note that modifications to all structs defined here are
* subject to backwards-compatibility constraints.
*/
/* cache modes */
#define APU_BO_CACHED 0x00000000 /* default */
#define APU_BO_WC 0x00000002 /* write-combine */
#define APU_BO_UNCACHED 0x00000004 /* strongly-ordered (uncached) */
struct drm_apu_gem_new {
uint32_t size; /* in */
uint32_t flags; /* in */
uint32_t handle; /* out */
uint64_t offset; /* out */
};
struct drm_apu_gem_queue {
uint32_t device;
uint32_t cmd;
uint32_t out_sync;
uint64_t bo_handles;
uint32_t bo_handle_count;
uint16_t size_in;
uint16_t size_out;
uint64_t data;
};
struct drm_apu_gem_dequeue {
uint32_t out_sync;
uint16_t result;
uint16_t size;
uint64_t data;
};
struct drm_apu_gem_iommu_map {
uint64_t bo_handles;
uint32_t bo_handle_count;
uint64_t bo_device_addresses;
};
struct apu_job_event {
struct drm_event base;
uint32_t out_sync;
};
#define DRM_APU_GEM_NEW 0x00
#define DRM_APU_GEM_QUEUE 0x01
#define DRM_APU_GEM_DEQUEUE 0x02
#define DRM_APU_GEM_IOMMU_MAP 0x03
#define DRM_APU_GEM_IOMMU_UNMAP 0x04
#define DRM_APU_NUM_IOCTLS 0x05
#define DRM_IOCTL_APU_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_APU_GEM_NEW, struct drm_apu_gem_new)
#define DRM_IOCTL_APU_GEM_QUEUE DRM_IOWR(DRM_COMMAND_BASE + DRM_APU_GEM_QUEUE, struct drm_apu_gem_queue)
#define DRM_IOCTL_APU_GEM_DEQUEUE DRM_IOWR(DRM_COMMAND_BASE + DRM_APU_GEM_DEQUEUE, struct drm_apu_gem_dequeue)
#define DRM_IOCTL_APU_GEM_IOMMU_MAP DRM_IOWR(DRM_COMMAND_BASE + DRM_APU_GEM_IOMMU_MAP, struct drm_apu_gem_iommu_map)
#define DRM_IOCTL_APU_GEM_IOMMU_UNMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_APU_GEM_IOMMU_UNMAP, struct drm_apu_gem_iommu_map)
#if defined(__cplusplus)
}
#endif
#endif /* __APU_DRM_H__ */
/*
* Copyright (C) 2021 BayLibre SAS
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef APU_DRMIF_H_
#define APU_DRMIF_H_
#include <xf86drm.h>
#include <stdint.h>
#include <apu_drm.h>
struct apu_drm_device;
/*
* device related functions:
*/
struct apu_drm_device *apu_device_new(int fd, int device_id);
struct apu_drm_device *apu_device_ref(struct apu_drm_device *dev);
int apu_device_del(struct apu_drm_device *dev);
/*
* buffer-object related functions:
*/
struct apu_bo *apu_bo_new(struct apu_drm_device *dev,
uint32_t size, uint32_t flags);
struct apu_bo *apu_cached_bo_new(struct apu_drm_device *dev,
uint32_t size, uint32_t flags);
struct apu_bo *apu_bo_ref(struct apu_bo *bo);
void apu_bo_del(struct apu_bo *bo);
uint32_t apu_bo_handle(struct apu_bo *bo);
int apu_bo_dmabuf(struct apu_bo *bo);
void *apu_bo_map(struct apu_bo *bo);
/*
* job related functions:
*/
struct apu_drm_job *apu_new_job(struct apu_drm_device *dev, size_t size);
int apu_job_init(struct apu_drm_job *job, uint32_t cmd,
struct apu_bo **bos, uint32_t count,
void *data_in, size_t size_in, size_t size_out);
void *apu_job_get_data(struct apu_drm_job *job);
int apu_job_wait(struct apu_drm_job *job);
struct apu_drm_job *apu_job_wait_any(struct apu_drm_device *dev);
void apu_free_job(struct apu_drm_job *job);
int apu_queue(struct apu_drm_job *job);
int apu_dequeue_result(struct apu_drm_job *job, uint16_t *result,
void *data_out, size_t *size);
int apu_bo_iommu_map(struct apu_drm_device *dev,
struct apu_bo **bos, uint64_t *da, uint32_t count);
int apu_bo_iommu_unmap(
struct apu_drm_device *dev,
struct apu_bo **bos, uint32_t count);
#endif /* APU_DRMIF_H_ */
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libdrm_apu
Description: Userspace interface to apu kernel DRM services
Version: 0.1
Libs: -L${libdir} -ldrm_apu
Cflags: -I${includedir} -I${includedir}/libdrm -I${includedir}/apu
Requires.private: libdrm
# Copyright © 2017 Intel Corporation
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
libdrm_apu = shared_library(
'drm_apu',
[files('apu_drm.c'), config_file],
include_directories : [inc_root, inc_drm],
c_args : libdrm_c_args,
link_with : libdrm,
dependencies : [dep_pthread_stubs, dep_atomic_ops],
version : '1.0.0',
install : true,
)
ext_libdrm_apu = declare_dependency(
link_with : [libdrm, libdrm_apu],
include_directories : [inc_drm, include_directories('.')],
)
install_headers('apu_drmif.h', subdir : 'libdrm')
install_headers('apu_drm.h', subdir : 'apu')
pkg.generate(
name : 'libdrm_apu',
libraries : libdrm_apu,
subdirs : ['.', 'libdrm', 'apu'],
version : '0.6',
requires_private : 'libdrm',
description : 'Userspace interface to apu kernel DRM services',
)
test(
'apu-symbols-check',
symbols_check,
args : [
'--lib', libdrm_apu,
'--symbols-file', files('apu-symbols.txt'),
'--nm', prog_nm.path(),
],
)
......@@ -121,6 +121,15 @@ if _omap == 'true'
with_omap = true
endif
with_apu = false
_apu = get_option('apu')
if _apu == 'true'
if not with_atomics
error('libdrm_apu requires atomics.')
endif
with_apu = true
endif
with_freedreno = false
_freedreno = get_option('freedreno')
if _freedreno != 'false'
......@@ -347,6 +356,9 @@ endif
if with_omap
subdir('omap')
endif
if with_apu
subdir('apu')
endif
if with_exynos
subdir('exynos')
endif
......@@ -378,6 +390,7 @@ message(' Radeon API @0@'.format(with_radeon))
message(' AMDGPU API @0@'.format(with_amdgpu))
message(' Nouveau API @0@'.format(with_nouveau))
message(' OMAP API @0@'.format(with_omap))
message(' APU API @0@'.format(with_apu))
message(' EXYNOS API @0@'.format(with_exynos))
message(' Freedreno API @0@ (kgsl: @1@)'.format(with_freedreno, with_freedreno_kgsl))
message(' Tegra API @0@'.format(with_tegra))
......
......@@ -67,6 +67,13 @@ option(
choices : ['true', 'false', 'auto'],
description : '''Enable support for OMAP's experimental KMS API.''',
)
option(
'apu',
type : 'combo',
value : 'false',
choices : ['true', 'false', 'auto'],
description : '''Enable support for APU''',
)
option(
'exynos',
type : 'combo',
......
# Copyright © 2017 Intel Corporation
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
apu_test = executable(
'apu_test',
files('tests.c'),
dependencies : [dep_dl],
include_directories : [inc_root, inc_drm, include_directories('../../apu')],
link_with : [libdrm, libdrm_apu],
c_args : libdrm_c_args,
)
test('apu_test', apu_test)
/*
* Copyright (C) 2021 BayLibre SAS
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "xf86drm.h"
#include "apu_drmif.h"
static const char default_device[] = "/dev/dri/card0";
int memtest(void *ptr, int value, size_t count);
int test_apu_memory(struct apu_drm_device *apu);
int test_apu_job(struct apu_drm_device *apu);
int memtest(void *ptr, int value, size_t count)
{
unsigned int i;
uint8_t *to_test = ptr;
for (i = 0; i < count; i++) {
if (to_test[i] != value)
return 1;
}
return 0;
}
int test_apu_memory(struct apu_drm_device *apu)
{
struct apu_bo *bo;
void *ptr;
int fd;
printf ("Testing memory management\n");
bo = apu_bo_new(apu, 4096, 0);
if (!bo) {
printf ("Failed to allocate bo\n");
return 1;
}
ptr = apu_bo_map(bo);
if (!ptr) {
printf ("Failed to map bo\n");
apu_bo_del(bo);
return 1;
}
memset(ptr, 0x45, 4096);
fd = apu_bo_dmabuf(bo);
if (fd < 0) {
printf ("Failed to dma_buf handle\n");
apu_bo_del(bo);
}
apu_bo_del(bo);
/* test dma_buf */
ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
printf ("Failed to map dma_buf fd\n");
close(fd);
return 1;
}
if (memtest(ptr, 0x45, 4096)) {
printf ("Invalid buffer\n");
close(fd);
return 1;
}
close(fd);
printf ("Testing memory management: OK\n");
return 0;
}
int test_apu_job(struct apu_drm_device *apu)
{
int ret;
struct apu_bo *bo;
struct apu_bo *bos[1];
struct apu_drm_job *job;
uint16_t result;
printf ("Testing job queue\n");
bo = apu_bo_new(apu, 4096, 0);
if (!bo) {
printf ("Failed to allocate bo\n");
return 1;
}
job = apu_new_job(apu, 0);
if (!job) {
apu_bo_del(bo);
return 1;
}
bos[0] = bo;
ret = apu_job_init(job, 1, bos, 1, NULL, 0, 0);
if (ret) {
apu_free_job(job);
apu_bo_del(bo);
return 1;
}
printf ("Submitting a job\n");
ret = apu_queue(job);
if (ret) {
printf ("Failed to queue a job\n");
apu_free_job(job);
apu_bo_del(bo);
return 1;
}
/* TODO: dequeue and free the job */
ret = apu_queue(job);
if (ret) {
printf ("Failed to queue a job\n");
apu_free_job(job);
apu_bo_del(bo);
return 1;
}
ret = apu_job_wait(job);
if (ret) {
printf ("Failed to wait for job completion\n");
apu_free_job(job);
apu_bo_del(bo);
return 1;
}
ret = apu_dequeue_result(job, &result, NULL, NULL);
if (ret) {
printf ("Failed to queue a job\n");
apu_free_job(job);
apu_bo_del(bo);
return 1;
}
/* TODO: check for result */
apu_free_job(job);
apu_bo_del(bo);
printf ("Testing job queue: OK\n");
return 0;
}
int main(int argc, char *argv[])
{
struct apu_drm_device *apu;
drmVersionPtr version;
const char *device;
int err, fd;
if (argc < 2)
device = default_device;
else
device = argv[1];
fd = open(device, O_RDWR);
if (fd < 0)
return 1;
version = drmGetVersion(fd);
if (version) {
printf("Version: %d.%d.%d\n", version->version_major,
version->version_minor, version->version_patchlevel);
printf(" Name: %s\n", version->name);
printf(" Date: %s\n", version->date);
printf(" Description: %s\n", version->desc);
drmFreeVersion(version);
}
apu = apu_device_new(fd, 0);
if (!apu)
return 1;
err = test_apu_memory(apu);
if (!err)
err = test_apu_job(apu);
apu_device_del(apu);
close(fd);
return err;
}
......@@ -47,6 +47,9 @@ endif
if with_nouveau
subdir('nouveau')
endif
if with_apu
subdir('apu')
endif
drmsl = executable(
'drmsl',
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment