Home | History | Annotate | Download | only in kgsl
      1 /* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
      2 
      3 /*
      4  * Copyright (C) 2013 Rob Clark <robclark (at) freedesktop.org>
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice (including the next
     14  * paragraph) shall be included in all copies or substantial portions of the
     15  * Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     23  * SOFTWARE.
     24  *
     25  * Authors:
     26  *    Rob Clark <robclark (at) freedesktop.org>
     27  */
     28 
     29 #include "kgsl_priv.h"
     30 
     31 #include <linux/fb.h>
     32 
     33 static int set_memtype(struct fd_device *dev, uint32_t handle, uint32_t flags)
     34 {
     35 	struct drm_kgsl_gem_memtype req = {
     36 			.handle = handle,
     37 			.type = flags & DRM_FREEDRENO_GEM_TYPE_MEM_MASK,
     38 	};
     39 
     40 	return drmCommandWrite(dev->fd, DRM_KGSL_GEM_SETMEMTYPE,
     41 			&req, sizeof(req));
     42 }
     43 
     44 static int bo_alloc(struct kgsl_bo *kgsl_bo)
     45 {
     46 	struct fd_bo *bo = &kgsl_bo->base;
     47 	if (!kgsl_bo->offset) {
     48 		struct drm_kgsl_gem_alloc req = {
     49 				.handle = bo->handle,
     50 		};
     51 		int ret;
     52 
     53 		/* if the buffer is already backed by pages then this
     54 		 * doesn't actually do anything (other than giving us
     55 		 * the offset)
     56 		 */
     57 		ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_ALLOC,
     58 				&req, sizeof(req));
     59 		if (ret) {
     60 			ERROR_MSG("alloc failed: %s", strerror(errno));
     61 			return ret;
     62 		}
     63 
     64 		kgsl_bo->offset = req.offset;
     65 	}
     66 
     67 	return 0;
     68 }
     69 
     70 static int kgsl_bo_offset(struct fd_bo *bo, uint64_t *offset)
     71 {
     72 	struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo);
     73 	int ret = bo_alloc(kgsl_bo);
     74 	if (ret)
     75 		return ret;
     76 	*offset = kgsl_bo->offset;
     77 	return 0;
     78 }
     79 
     80 static int kgsl_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
     81 {
     82 	uint32_t timestamp = kgsl_bo_get_timestamp(to_kgsl_bo(bo));
     83 
     84 	if (op & DRM_FREEDRENO_PREP_NOSYNC) {
     85 		uint32_t current;
     86 		int ret;
     87 
     88 		/* special case for is_idle().. we can't really handle that
     89 		 * properly in kgsl (perhaps we need a way to just disable
     90 		 * the bo-cache for kgsl?)
     91 		 */
     92 		if (!pipe)
     93 			return -EBUSY;
     94 
     95 		ret = kgsl_pipe_timestamp(to_kgsl_pipe(pipe), &current);
     96 		if (ret)
     97 			return ret;
     98 
     99 		if (timestamp > current)
    100 			return -EBUSY;
    101 
    102 		return 0;
    103 	}
    104 
    105 	if (timestamp)
    106 		fd_pipe_wait(pipe, timestamp);
    107 
    108 	return 0;
    109 }
    110 
    111 static void kgsl_bo_cpu_fini(struct fd_bo *bo)
    112 {
    113 }
    114 
    115 static int kgsl_bo_madvise(struct fd_bo *bo, int willneed)
    116 {
    117 	return willneed; /* not supported by kgsl */
    118 }
    119 
    120 static void kgsl_bo_destroy(struct fd_bo *bo)
    121 {
    122 	struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo);
    123 	free(kgsl_bo);
    124 
    125 }
    126 
    127 static const struct fd_bo_funcs funcs = {
    128 		.offset = kgsl_bo_offset,
    129 		.cpu_prep = kgsl_bo_cpu_prep,
    130 		.cpu_fini = kgsl_bo_cpu_fini,
    131 		.madvise = kgsl_bo_madvise,
    132 		.destroy = kgsl_bo_destroy,
    133 };
    134 
    135 /* allocate a buffer handle: */
    136 drm_private int kgsl_bo_new_handle(struct fd_device *dev,
    137 		uint32_t size, uint32_t flags, uint32_t *handle)
    138 {
    139 	struct drm_kgsl_gem_create req = {
    140 			.size = size,
    141 	};
    142 	int ret;
    143 
    144 	ret = drmCommandWriteRead(dev->fd, DRM_KGSL_GEM_CREATE,
    145 			&req, sizeof(req));
    146 	if (ret)
    147 		return ret;
    148 
    149 	// TODO make flags match msm driver, since kgsl is legacy..
    150 	// translate flags in kgsl..
    151 
    152 	set_memtype(dev, req.handle, flags);
    153 
    154 	*handle = req.handle;
    155 
    156 	return 0;
    157 }
    158 
    159 /* allocate a new buffer object */
    160 drm_private struct fd_bo * kgsl_bo_from_handle(struct fd_device *dev,
    161 		uint32_t size, uint32_t handle)
    162 {
    163 	struct kgsl_bo *kgsl_bo;
    164 	struct fd_bo *bo;
    165 	unsigned i;
    166 
    167 	kgsl_bo = calloc(1, sizeof(*kgsl_bo));
    168 	if (!kgsl_bo)
    169 		return NULL;
    170 
    171 	bo = &kgsl_bo->base;
    172 	bo->funcs = &funcs;
    173 
    174 	for (i = 0; i < ARRAY_SIZE(kgsl_bo->list); i++)
    175 		list_inithead(&kgsl_bo->list[i]);
    176 
    177 	return bo;
    178 }
    179 
    180 struct fd_bo *
    181 fd_bo_from_fbdev(struct fd_pipe *pipe, int fbfd, uint32_t size)
    182 {
    183 	struct fd_bo *bo;
    184 
    185 	if (!is_kgsl_pipe(pipe))
    186 		return NULL;
    187 
    188 	bo = fd_bo_new(pipe->dev, 1, 0);
    189 
    190 	/* this is fugly, but works around a bug in the kernel..
    191 	 * priv->memdesc.size never gets set, so getbufinfo ioctl
    192 	 * thinks the buffer hasn't be allocate and fails
    193 	 */
    194 	if (bo) {
    195 		void *fbmem = drm_mmap(NULL, size, PROT_READ | PROT_WRITE,
    196 				MAP_SHARED, fbfd, 0);
    197 		struct kgsl_map_user_mem req = {
    198 				.memtype = KGSL_USER_MEM_TYPE_ADDR,
    199 				.len     = size,
    200 				.offset  = 0,
    201 				.hostptr = (unsigned long)fbmem,
    202 		};
    203 		struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo);
    204 		int ret;
    205 
    206 		ret = ioctl(to_kgsl_pipe(pipe)->fd, IOCTL_KGSL_MAP_USER_MEM, &req);
    207 		if (ret) {
    208 			ERROR_MSG("mapping user mem failed: %s",
    209 					strerror(errno));
    210 			goto fail;
    211 		}
    212 		kgsl_bo->gpuaddr = req.gpuaddr;
    213 		bo->map = fbmem;
    214 	}
    215 
    216 	return bo;
    217 fail:
    218 	if (bo)
    219 		fd_bo_del(bo);
    220 	return NULL;
    221 }
    222 
    223 drm_private uint32_t kgsl_bo_gpuaddr(struct kgsl_bo *kgsl_bo, uint32_t offset)
    224 {
    225 	struct fd_bo *bo = &kgsl_bo->base;
    226 	if (!kgsl_bo->gpuaddr) {
    227 		struct drm_kgsl_gem_bufinfo req = {
    228 				.handle = bo->handle,
    229 		};
    230 		int ret;
    231 
    232 		ret = bo_alloc(kgsl_bo);
    233 		if (ret) {
    234 			return ret;
    235 		}
    236 
    237 		ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_GET_BUFINFO,
    238 				&req, sizeof(req));
    239 		if (ret) {
    240 			ERROR_MSG("get bufinfo failed: %s", strerror(errno));
    241 			return 0;
    242 		}
    243 
    244 		kgsl_bo->gpuaddr = req.gpuaddr[0];
    245 	}
    246 	return kgsl_bo->gpuaddr + offset;
    247 }
    248 
    249 /*
    250  * Super-cheezy way to synchronization between mesa and ddx..  the
    251  * SET_ACTIVE ioctl gives us a way to stash a 32b # w/ a GEM bo, and
    252  * GET_BUFINFO gives us a way to retrieve it.  We use this to stash
    253  * the timestamp of the last ISSUEIBCMDS on the buffer.
    254  *
    255  * To avoid an obscene amount of syscalls, we:
    256  *  1) Only set the timestamp for buffers w/ an flink name, ie.
    257  *     only buffers shared across processes.  This is enough to
    258  *     catch the DRI2 buffers.
    259  *  2) Only set the timestamp for buffers submitted to the 3d ring
    260  *     and only check the timestamps on buffers submitted to the
    261  *     2d ring.  This should be enough to handle synchronizing of
    262  *     presentation blit.  We could do synchronization in the other
    263  *     direction too, but that would be problematic if we are using
    264  *     the 3d ring from DDX, since client side wouldn't know this.
    265  *
    266  * The waiting on timestamp happens before flush, and setting of
    267  * timestamp happens after flush.  It is transparent to the user
    268  * of libdrm_freedreno as all the tracking of buffers happens via
    269  * _emit_reloc()..
    270  */
    271 
    272 drm_private void kgsl_bo_set_timestamp(struct kgsl_bo *kgsl_bo,
    273 		uint32_t timestamp)
    274 {
    275 	struct fd_bo *bo = &kgsl_bo->base;
    276 	if (bo->name) {
    277 		struct drm_kgsl_gem_active req = {
    278 				.handle = bo->handle,
    279 				.active = timestamp,
    280 		};
    281 		int ret;
    282 
    283 		ret = drmCommandWrite(bo->dev->fd, DRM_KGSL_GEM_SET_ACTIVE,
    284 				&req, sizeof(req));
    285 		if (ret) {
    286 			ERROR_MSG("set active failed: %s", strerror(errno));
    287 		}
    288 	}
    289 }
    290 
    291 drm_private uint32_t kgsl_bo_get_timestamp(struct kgsl_bo *kgsl_bo)
    292 {
    293 	struct fd_bo *bo = &kgsl_bo->base;
    294 	uint32_t timestamp = 0;
    295 	if (bo->name) {
    296 		struct drm_kgsl_gem_bufinfo req = {
    297 				.handle = bo->handle,
    298 		};
    299 		int ret;
    300 
    301 		ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_GET_BUFINFO,
    302 				&req, sizeof(req));
    303 		if (ret) {
    304 			ERROR_MSG("get bufinfo failed: %s", strerror(errno));
    305 			return 0;
    306 		}
    307 
    308 		timestamp = req.active;
    309 	}
    310 	return timestamp;
    311 }
    312