10ed5401bSmrg/*
20ed5401bSmrg * Copyright © 2012, 2013 Thierry Reding
30ed5401bSmrg * Copyright © 2013 Erik Faye-Lund
40ed5401bSmrg * Copyright © 2014 NVIDIA Corporation
50ed5401bSmrg *
60ed5401bSmrg * Permission is hereby granted, free of charge, to any person obtaining a
70ed5401bSmrg * copy of this software and associated documentation files (the "Software"),
80ed5401bSmrg * to deal in the Software without restriction, including without limitation
90ed5401bSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
100ed5401bSmrg * and/or sell copies of the Software, and to permit persons to whom the
110ed5401bSmrg * Software is furnished to do so, subject to the following conditions:
120ed5401bSmrg *
130ed5401bSmrg * The above copyright notice and this permission notice shall be included in
140ed5401bSmrg * all copies or substantial portions of the Software.
150ed5401bSmrg *
160ed5401bSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
170ed5401bSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
180ed5401bSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
190ed5401bSmrg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
200ed5401bSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
210ed5401bSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
220ed5401bSmrg * OTHER DEALINGS IN THE SOFTWARE.
230ed5401bSmrg */
240ed5401bSmrg
250ed5401bSmrg#ifdef HAVE_CONFIG_H
260ed5401bSmrg#  include "config.h"
270ed5401bSmrg#endif
280ed5401bSmrg
290ed5401bSmrg#include <errno.h>
300ed5401bSmrg#include <stdlib.h>
310ed5401bSmrg#include <string.h>
320ed5401bSmrg#include <time.h>
330ed5401bSmrg#include <unistd.h>
340ed5401bSmrg
350ed5401bSmrg#include <sys/ioctl.h>
36bbff01ceSmrg#include <poll.h>
370ed5401bSmrg
380ed5401bSmrg#include "private.h"
390ed5401bSmrg
400ed5401bSmrgstruct drm_tegra_submit_cmd *
410ed5401bSmrgdrm_tegra_job_add_command(struct drm_tegra_job *job, uint32_t type,
420ed5401bSmrg                          uint32_t flags)
430ed5401bSmrg{
440ed5401bSmrg    struct drm_tegra_submit_cmd *commands, *command;
450ed5401bSmrg    size_t size;
460ed5401bSmrg
470ed5401bSmrg    size = (job->num_commands + 1) * sizeof(*commands);
480ed5401bSmrg
490ed5401bSmrg    commands = realloc(job->commands, size);
500ed5401bSmrg    if (!commands)
510ed5401bSmrg        return NULL;
520ed5401bSmrg
530ed5401bSmrg    command = &commands[job->num_commands];
540ed5401bSmrg    memset(command, 0, sizeof(*command));
550ed5401bSmrg    command->type = type;
560ed5401bSmrg    command->flags = flags;
570ed5401bSmrg
580ed5401bSmrg    job->commands = commands;
590ed5401bSmrg    job->num_commands++;
600ed5401bSmrg
610ed5401bSmrg    return command;
620ed5401bSmrg}
630ed5401bSmrg
640ed5401bSmrgdrm_public int
650ed5401bSmrgdrm_tegra_job_new(struct drm_tegra_channel *channel,
660ed5401bSmrg                  struct drm_tegra_job **jobp)
670ed5401bSmrg{
680ed5401bSmrg    struct drm_tegra_job *job;
690ed5401bSmrg
700ed5401bSmrg    job = calloc(1, sizeof(*job));
710ed5401bSmrg    if (!job)
720ed5401bSmrg        return -ENOMEM;
730ed5401bSmrg
740ed5401bSmrg    job->page_size = sysconf(_SC_PAGESIZE);
750ed5401bSmrg    job->channel = channel;
760ed5401bSmrg
770ed5401bSmrg    *jobp = job;
780ed5401bSmrg
790ed5401bSmrg    return 0;
800ed5401bSmrg}
810ed5401bSmrg
820ed5401bSmrgdrm_public int drm_tegra_job_free(struct drm_tegra_job *job)
830ed5401bSmrg{
840ed5401bSmrg    if (!job)
850ed5401bSmrg        return -EINVAL;
860ed5401bSmrg
870ed5401bSmrg    if (job->pushbuf)
880ed5401bSmrg        drm_tegra_pushbuf_free(job->pushbuf);
890ed5401bSmrg
900ed5401bSmrg    if (job->commands)
910ed5401bSmrg        free(job->commands);
920ed5401bSmrg
930ed5401bSmrg    if (job->buffers)
940ed5401bSmrg        free(job->buffers);
950ed5401bSmrg
960ed5401bSmrg    free(job);
970ed5401bSmrg
980ed5401bSmrg    return 0;
990ed5401bSmrg}
1000ed5401bSmrg
1010ed5401bSmrgdrm_public int
1020ed5401bSmrgdrm_tegra_job_get_pushbuf(struct drm_tegra_job *job,
1030ed5401bSmrg                          struct drm_tegra_pushbuf **pushbufp)
1040ed5401bSmrg{
1050ed5401bSmrg    struct drm_tegra_pushbuf *pushbuf;
1060ed5401bSmrg
1070ed5401bSmrg    if (!job->pushbuf) {
1080ed5401bSmrg        pushbuf = calloc(1, sizeof(*pushbuf));
1090ed5401bSmrg        if (!pushbuf)
1100ed5401bSmrg            return -ENOMEM;
1110ed5401bSmrg
1120ed5401bSmrg        pushbuf->job = job;
1130ed5401bSmrg
1140ed5401bSmrg        pushbuf->start = calloc(1, job->page_size);
1150ed5401bSmrg        if (!pushbuf->start) {
1160ed5401bSmrg            free(pushbuf);
1170ed5401bSmrg            return -ENOMEM;
1180ed5401bSmrg        }
1190ed5401bSmrg
1200ed5401bSmrg        pushbuf->end = pushbuf->start + job->page_size / 4;
1210ed5401bSmrg        pushbuf->ptr = pushbuf->start;
1220ed5401bSmrg
1230ed5401bSmrg        job->pushbuf = pushbuf;
1240ed5401bSmrg    }
1250ed5401bSmrg
1260ed5401bSmrg    *pushbufp = job->pushbuf;
1270ed5401bSmrg
1280ed5401bSmrg    return 0;
1290ed5401bSmrg}
1300ed5401bSmrg
1310ed5401bSmrgdrm_public int
1320ed5401bSmrgdrm_tegra_job_submit(struct drm_tegra_job *job, struct drm_tegra_fence *fence)
1330ed5401bSmrg{
1340ed5401bSmrg    struct drm_tegra_channel *channel = job->channel;
1350ed5401bSmrg    struct drm_tegra *drm = channel->drm;
1360ed5401bSmrg    struct drm_tegra_channel_submit args;
1370ed5401bSmrg    int err;
1380ed5401bSmrg
1390ed5401bSmrg    memset(&args, 0, sizeof(args));
1400ed5401bSmrg    args.context = channel->context;
1410ed5401bSmrg    args.num_bufs = job->num_buffers;
1420ed5401bSmrg    args.num_cmds = job->num_commands;
1430ed5401bSmrg    args.gather_data_words = job->pushbuf->ptr - job->pushbuf->start;
1440ed5401bSmrg    args.syncpt.id = job->syncpt.id;
1450ed5401bSmrg    args.syncpt.increments = job->syncpt.increments;
1460ed5401bSmrg
1470ed5401bSmrg    args.bufs_ptr = (uintptr_t)job->buffers;
1480ed5401bSmrg    args.cmds_ptr = (uintptr_t)job->commands;
1490ed5401bSmrg    args.gather_data_ptr = (uintptr_t)job->pushbuf->start;
1500ed5401bSmrg
1510ed5401bSmrg    err = ioctl(drm->fd, DRM_IOCTL_TEGRA_CHANNEL_SUBMIT, &args);
1520ed5401bSmrg    if (err < 0)
1530ed5401bSmrg        return -errno;
1540ed5401bSmrg
1550ed5401bSmrg    job->syncpt.fence = args.syncpt.value;
1560ed5401bSmrg
1570ed5401bSmrg    if (fence) {
1580ed5401bSmrg        fence->drm = drm;
1590ed5401bSmrg        fence->syncpt = job->syncpt.id;
1600ed5401bSmrg        fence->value = job->syncpt.fence;
1610ed5401bSmrg    }
1620ed5401bSmrg
1630ed5401bSmrg    return 0;
1640ed5401bSmrg}
1650ed5401bSmrg
1660ed5401bSmrgdrm_public int
1670ed5401bSmrgdrm_tegra_job_wait(struct drm_tegra_job *job, unsigned long timeout)
1680ed5401bSmrg{
1690ed5401bSmrg    struct drm_tegra_channel *channel = job->channel;
1700ed5401bSmrg    struct drm_tegra *drm = channel->drm;
1710ed5401bSmrg    struct drm_tegra_syncpoint_wait args;
1720ed5401bSmrg    struct timespec ts;
1730ed5401bSmrg    int err;
1740ed5401bSmrg
1750ed5401bSmrg    clock_gettime(CLOCK_MONOTONIC, &ts);
1760ed5401bSmrg
1770ed5401bSmrg    memset(&args, 0, sizeof(args));
1780ed5401bSmrg    args.timeout_ns = ts.tv_sec * 1000000000 + ts.tv_nsec + timeout;
1790ed5401bSmrg    args.id = job->syncpt.id;
1800ed5401bSmrg    args.threshold = job->syncpt.fence;
1810ed5401bSmrg
1820ed5401bSmrg    err = ioctl(drm->fd, DRM_IOCTL_TEGRA_SYNCPOINT_WAIT, &args);
1830ed5401bSmrg    if (err < 0)
1840ed5401bSmrg        return -errno;
1850ed5401bSmrg
1860ed5401bSmrg    return 0;
1870ed5401bSmrg}
188