Home | History | Annotate | Download | only in etnaviv
      1 /*
      2  * Copyright (C) 2014-2015 Etnaviv Project
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     21  * SOFTWARE.
     22  *
     23  * Authors:
     24  *    Christian Gmeiner <christian.gmeiner (at) gmail.com>
     25  */
     26 
     27 #ifdef HAVE_CONFIG_H
     28 # include <config.h>
     29 #endif
     30 
     31 #include <assert.h>
     32 
     33 #include "etnaviv_drmif.h"
     34 #include "etnaviv_priv.h"
     35 
     36 static pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER;
     37 
     38 static void *grow(void *ptr, uint32_t nr, uint32_t *max, uint32_t sz)
     39 {
     40 	if ((nr + 1) > *max) {
     41 		if ((*max * 2) < (nr + 1))
     42 			*max = nr + 5;
     43 		else
     44 			*max = *max * 2;
     45 		ptr = realloc(ptr, *max * sz);
     46 	}
     47 
     48 	return ptr;
     49 }
     50 
     51 #define APPEND(x, name) ({ \
     52 	(x)->name = grow((x)->name, (x)->nr_ ## name, &(x)->max_ ## name, sizeof((x)->name[0])); \
     53 	(x)->nr_ ## name ++; \
     54 })
     55 
     56 static inline struct etna_cmd_stream_priv *
     57 etna_cmd_stream_priv(struct etna_cmd_stream *stream)
     58 {
     59     return (struct etna_cmd_stream_priv *)stream;
     60 }
     61 
     62 struct etna_cmd_stream *etna_cmd_stream_new(struct etna_pipe *pipe, uint32_t size,
     63 		void (*reset_notify)(struct etna_cmd_stream *stream, void *priv),
     64 		void *priv)
     65 {
     66 	struct etna_cmd_stream_priv *stream = NULL;
     67 
     68 	if (size == 0) {
     69 		ERROR_MSG("invalid size of 0");
     70 		goto fail;
     71 	}
     72 
     73 	stream = calloc(1, sizeof(*stream));
     74 	if (!stream) {
     75 		ERROR_MSG("allocation failed");
     76 		goto fail;
     77 	}
     78 
     79 	/* allocate even number of 32-bit words */
     80 	size = ALIGN(size, 2);
     81 
     82 	stream->base.buffer = malloc(size * sizeof(uint32_t));
     83 	if (!stream->base.buffer) {
     84 		ERROR_MSG("allocation failed");
     85 		goto fail;
     86 	}
     87 
     88 	stream->base.size = size;
     89 	stream->pipe = pipe;
     90 	stream->reset_notify = reset_notify;
     91 	stream->reset_notify_priv = priv;
     92 
     93 	return &stream->base;
     94 
     95 fail:
     96 	if (stream)
     97 		etna_cmd_stream_del(&stream->base);
     98 
     99 	return NULL;
    100 }
    101 
    102 void etna_cmd_stream_del(struct etna_cmd_stream *stream)
    103 {
    104 	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
    105 
    106 	free(stream->buffer);
    107 	free(priv->submit.relocs);
    108 	free(priv);
    109 }
    110 
    111 static void reset_buffer(struct etna_cmd_stream *stream)
    112 {
    113 	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
    114 
    115 	stream->offset = 0;
    116 	priv->submit.nr_bos = 0;
    117 	priv->submit.nr_relocs = 0;
    118 	priv->nr_bos = 0;
    119 
    120 	if (priv->reset_notify)
    121 		priv->reset_notify(stream, priv->reset_notify_priv);
    122 }
    123 
    124 uint32_t etna_cmd_stream_timestamp(struct etna_cmd_stream *stream)
    125 {
    126 	return etna_cmd_stream_priv(stream)->last_timestamp;
    127 }
    128 
    129 static uint32_t append_bo(struct etna_cmd_stream *stream, struct etna_bo *bo)
    130 {
    131 	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
    132 	uint32_t idx;
    133 
    134 	idx = APPEND(&priv->submit, bos);
    135 	idx = APPEND(priv, bos);
    136 
    137 	priv->submit.bos[idx].flags = 0;
    138 	priv->submit.bos[idx].handle = bo->handle;
    139 
    140 	priv->bos[idx] = etna_bo_ref(bo);
    141 
    142 	return idx;
    143 }
    144 
    145 /* add (if needed) bo, return idx: */
    146 static uint32_t bo2idx(struct etna_cmd_stream *stream, struct etna_bo *bo,
    147 		uint32_t flags)
    148 {
    149 	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
    150 	uint32_t idx;
    151 
    152 	pthread_mutex_lock(&idx_lock);
    153 
    154 	if (!bo->current_stream) {
    155 		idx = append_bo(stream, bo);
    156 		bo->current_stream = stream;
    157 		bo->idx = idx;
    158 	} else if (bo->current_stream == stream) {
    159 		idx = bo->idx;
    160 	} else {
    161 		/* slow-path: */
    162 		for (idx = 0; idx < priv->nr_bos; idx++)
    163 			if (priv->bos[idx] == bo)
    164 				break;
    165 		if (idx == priv->nr_bos) {
    166 			/* not found */
    167 			idx = append_bo(stream, bo);
    168 		}
    169 	}
    170 	pthread_mutex_unlock(&idx_lock);
    171 
    172 	if (flags & ETNA_RELOC_READ)
    173 		priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_READ;
    174 	if (flags & ETNA_RELOC_WRITE)
    175 		priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_WRITE;
    176 
    177 	return idx;
    178 }
    179 
    180 static void flush(struct etna_cmd_stream *stream)
    181 {
    182 	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
    183 	int ret, id = priv->pipe->id;
    184 	struct etna_gpu *gpu = priv->pipe->gpu;
    185 
    186 	struct drm_etnaviv_gem_submit req = {
    187 		.pipe = gpu->core,
    188 		.exec_state = id,
    189 		.bos = VOID2U64(priv->submit.bos),
    190 		.nr_bos = priv->submit.nr_bos,
    191 		.relocs = VOID2U64(priv->submit.relocs),
    192 		.nr_relocs = priv->submit.nr_relocs,
    193 		.stream = VOID2U64(stream->buffer),
    194 		.stream_size = stream->offset * 4, /* in bytes */
    195 	};
    196 
    197 	ret = drmCommandWriteRead(gpu->dev->fd, DRM_ETNAVIV_GEM_SUBMIT,
    198 			&req, sizeof(req));
    199 
    200 	if (ret)
    201 		ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
    202 	else
    203 		priv->last_timestamp = req.fence;
    204 
    205 	for (uint32_t i = 0; i < priv->nr_bos; i++) {
    206 		struct etna_bo *bo = priv->bos[i];
    207 
    208 		bo->current_stream = NULL;
    209 		etna_bo_del(bo);
    210 	}
    211 }
    212 
    213 void etna_cmd_stream_flush(struct etna_cmd_stream *stream)
    214 {
    215 	flush(stream);
    216 	reset_buffer(stream);
    217 }
    218 
    219 void etna_cmd_stream_finish(struct etna_cmd_stream *stream)
    220 {
    221 	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
    222 
    223 	flush(stream);
    224 	etna_pipe_wait(priv->pipe, priv->last_timestamp, 5000);
    225 	reset_buffer(stream);
    226 }
    227 
    228 void etna_cmd_stream_reloc(struct etna_cmd_stream *stream, const struct etna_reloc *r)
    229 {
    230 	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
    231 	struct drm_etnaviv_gem_submit_reloc *reloc;
    232 	uint32_t idx = APPEND(&priv->submit, relocs);
    233 	uint32_t addr = 0;
    234 
    235 	reloc = &priv->submit.relocs[idx];
    236 
    237 	reloc->reloc_idx = bo2idx(stream, r->bo, r->flags);
    238 	reloc->reloc_offset = r->offset;
    239 	reloc->submit_offset = stream->offset * 4; /* in bytes */
    240 	reloc->flags = 0;
    241 
    242 	etna_cmd_stream_emit(stream, addr);
    243 }
    244