| /* |
| * Copyright (C) 2014-2015 Etnaviv Project |
| * |
| * 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 (including the next |
| * paragraph) 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. |
| * |
| * Authors: |
| * Christian Gmeiner <christian.gmeiner@gmail.com> |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| #include <assert.h> |
| |
| #include "etnaviv_drmif.h" |
| #include "etnaviv_priv.h" |
| |
| static pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER; |
| |
| static void *grow(void *ptr, uint32_t nr, uint32_t *max, uint32_t sz) |
| { |
| if ((nr + 1) > *max) { |
| if ((*max * 2) < (nr + 1)) |
| *max = nr + 5; |
| else |
| *max = *max * 2; |
| ptr = realloc(ptr, *max * sz); |
| } |
| |
| return ptr; |
| } |
| |
| #define APPEND(x, name) ({ \ |
| (x)->name = grow((x)->name, (x)->nr_ ## name, &(x)->max_ ## name, sizeof((x)->name[0])); \ |
| (x)->nr_ ## name ++; \ |
| }) |
| |
| static inline struct etna_cmd_stream_priv * |
| etna_cmd_stream_priv(struct etna_cmd_stream *stream) |
| { |
| return (struct etna_cmd_stream_priv *)stream; |
| } |
| |
| struct etna_cmd_stream *etna_cmd_stream_new(struct etna_pipe *pipe, uint32_t size, |
| void (*reset_notify)(struct etna_cmd_stream *stream, void *priv), |
| void *priv) |
| { |
| struct etna_cmd_stream_priv *stream = NULL; |
| |
| if (size == 0) { |
| ERROR_MSG("invalid size of 0"); |
| goto fail; |
| } |
| |
| stream = calloc(1, sizeof(*stream)); |
| if (!stream) { |
| ERROR_MSG("allocation failed"); |
| goto fail; |
| } |
| |
| /* allocate even number of 32-bit words */ |
| size = ALIGN(size, 2); |
| |
| stream->base.buffer = malloc(size * sizeof(uint32_t)); |
| if (!stream->base.buffer) { |
| ERROR_MSG("allocation failed"); |
| goto fail; |
| } |
| |
| stream->base.size = size; |
| stream->pipe = pipe; |
| stream->reset_notify = reset_notify; |
| stream->reset_notify_priv = priv; |
| |
| return &stream->base; |
| |
| fail: |
| if (stream) |
| etna_cmd_stream_del(&stream->base); |
| |
| return NULL; |
| } |
| |
| void etna_cmd_stream_del(struct etna_cmd_stream *stream) |
| { |
| struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); |
| |
| free(stream->buffer); |
| free(priv->submit.relocs); |
| free(priv); |
| } |
| |
| static void reset_buffer(struct etna_cmd_stream *stream) |
| { |
| struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); |
| |
| stream->offset = 0; |
| priv->submit.nr_bos = 0; |
| priv->submit.nr_relocs = 0; |
| priv->nr_bos = 0; |
| |
| if (priv->reset_notify) |
| priv->reset_notify(stream, priv->reset_notify_priv); |
| } |
| |
| uint32_t etna_cmd_stream_timestamp(struct etna_cmd_stream *stream) |
| { |
| return etna_cmd_stream_priv(stream)->last_timestamp; |
| } |
| |
| static uint32_t append_bo(struct etna_cmd_stream *stream, struct etna_bo *bo) |
| { |
| struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); |
| uint32_t idx; |
| |
| idx = APPEND(&priv->submit, bos); |
| idx = APPEND(priv, bos); |
| |
| priv->submit.bos[idx].flags = 0; |
| priv->submit.bos[idx].handle = bo->handle; |
| |
| priv->bos[idx] = etna_bo_ref(bo); |
| |
| return idx; |
| } |
| |
| /* add (if needed) bo, return idx: */ |
| static uint32_t bo2idx(struct etna_cmd_stream *stream, struct etna_bo *bo, |
| uint32_t flags) |
| { |
| struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); |
| uint32_t idx; |
| |
| pthread_mutex_lock(&idx_lock); |
| |
| if (!bo->current_stream) { |
| idx = append_bo(stream, bo); |
| bo->current_stream = stream; |
| bo->idx = idx; |
| } else if (bo->current_stream == stream) { |
| idx = bo->idx; |
| } else { |
| /* slow-path: */ |
| for (idx = 0; idx < priv->nr_bos; idx++) |
| if (priv->bos[idx] == bo) |
| break; |
| if (idx == priv->nr_bos) { |
| /* not found */ |
| idx = append_bo(stream, bo); |
| } |
| } |
| pthread_mutex_unlock(&idx_lock); |
| |
| if (flags & ETNA_RELOC_READ) |
| priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_READ; |
| if (flags & ETNA_RELOC_WRITE) |
| priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_WRITE; |
| |
| return idx; |
| } |
| |
| static void flush(struct etna_cmd_stream *stream, int in_fence_fd, |
| int *out_fence_fd) |
| { |
| struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); |
| int ret, id = priv->pipe->id; |
| struct etna_gpu *gpu = priv->pipe->gpu; |
| |
| struct drm_etnaviv_gem_submit req = { |
| .pipe = gpu->core, |
| .exec_state = id, |
| .bos = VOID2U64(priv->submit.bos), |
| .nr_bos = priv->submit.nr_bos, |
| .relocs = VOID2U64(priv->submit.relocs), |
| .nr_relocs = priv->submit.nr_relocs, |
| .stream = VOID2U64(stream->buffer), |
| .stream_size = stream->offset * 4, /* in bytes */ |
| }; |
| |
| if (in_fence_fd != -1) { |
| req.flags |= ETNA_SUBMIT_FENCE_FD_IN | ETNA_SUBMIT_NO_IMPLICIT; |
| req.fence_fd = in_fence_fd; |
| } |
| |
| if (out_fence_fd) |
| req.flags |= ETNA_SUBMIT_FENCE_FD_OUT; |
| |
| ret = drmCommandWriteRead(gpu->dev->fd, DRM_ETNAVIV_GEM_SUBMIT, |
| &req, sizeof(req)); |
| |
| if (ret) |
| ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno)); |
| else |
| priv->last_timestamp = req.fence; |
| |
| for (uint32_t i = 0; i < priv->nr_bos; i++) { |
| struct etna_bo *bo = priv->bos[i]; |
| |
| bo->current_stream = NULL; |
| etna_bo_del(bo); |
| } |
| |
| if (out_fence_fd) |
| *out_fence_fd = req.fence_fd; |
| } |
| |
| void etna_cmd_stream_flush(struct etna_cmd_stream *stream) |
| { |
| flush(stream, -1, NULL); |
| reset_buffer(stream); |
| } |
| |
| void etna_cmd_stream_flush2(struct etna_cmd_stream *stream, int in_fence_fd, |
| int *out_fence_fd) |
| { |
| flush(stream, in_fence_fd, out_fence_fd); |
| reset_buffer(stream); |
| } |
| |
| void etna_cmd_stream_finish(struct etna_cmd_stream *stream) |
| { |
| struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); |
| |
| flush(stream, -1, NULL); |
| etna_pipe_wait(priv->pipe, priv->last_timestamp, 5000); |
| reset_buffer(stream); |
| } |
| |
| void etna_cmd_stream_reloc(struct etna_cmd_stream *stream, const struct etna_reloc *r) |
| { |
| struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); |
| struct drm_etnaviv_gem_submit_reloc *reloc; |
| uint32_t idx = APPEND(&priv->submit, relocs); |
| uint32_t addr = 0; |
| |
| reloc = &priv->submit.relocs[idx]; |
| |
| reloc->reloc_idx = bo2idx(stream, r->bo, r->flags); |
| reloc->reloc_offset = r->offset; |
| reloc->submit_offset = stream->offset * 4; /* in bytes */ |
| reloc->flags = 0; |
| |
| etna_cmd_stream_emit(stream, addr); |
| } |