diff --git a/freedreno/Makefile.am b/freedreno/Makefile.am index 45074c9b..ec7e3595 100644 --- a/freedreno/Makefile.am +++ b/freedreno/Makefile.am @@ -23,6 +23,12 @@ libdrm_freedreno_la_SOURCES = \ kgsl/kgsl_priv.h \ kgsl/kgsl_ringbuffer.c \ kgsl/msm_kgsl.h \ + msm/msm_bo.c \ + msm/msm_device.c \ + msm/msm_drm.h \ + msm/msm_pipe.c \ + msm/msm_priv.h \ + msm/msm_ringbuffer.c \ list.h libdrm_freedrenocommonincludedir = ${includedir}/freedreno diff --git a/freedreno/freedreno_device.c b/freedreno/freedreno_device.c index 03553b9f..1e3d9df2 100644 --- a/freedreno/freedreno_device.c +++ b/freedreno/freedreno_device.c @@ -37,6 +37,7 @@ static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER; static void * dev_table; struct fd_device * kgsl_device_new(int fd); +struct fd_device * msm_device_new(int fd); static struct fd_device * fd_device_new_impl(int fd) { @@ -53,6 +54,9 @@ static struct fd_device * fd_device_new_impl(int fd) if (!strcmp(version->name, "kgsl")) { DEBUG_MSG("kgsl DRM device"); dev = kgsl_device_new(fd); + } else if (!strcmp(version->name, "msm")) { + DEBUG_MSG("msm DRM device"); + dev = msm_device_new(fd); } else { ERROR_MSG("unknown device: %s", version->name); dev = NULL; diff --git a/freedreno/freedreno_priv.h b/freedreno/freedreno_priv.h index cbf41d71..b10ac90b 100644 --- a/freedreno/freedreno_priv.h +++ b/freedreno/freedreno_priv.h @@ -128,7 +128,7 @@ struct fd_bo *fd_bo_from_handle(struct fd_device *dev, #define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1)) #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) -#define enable_debug 1 /* TODO make dynamic */ +#define enable_debug 0 /* TODO make dynamic */ #define INFO_MSG(fmt, ...) \ do { drmMsg("[I] "fmt " (%s:%d)\n", \ diff --git a/freedreno/msm/msm_bo.c b/freedreno/msm/msm_bo.c new file mode 100644 index 00000000..57d87256 --- /dev/null +++ b/freedreno/msm/msm_bo.c @@ -0,0 +1,141 @@ +/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */ + +/* + * Copyright (C) 2013 Rob Clark + * + * 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: + * Rob Clark + */ + +#include "msm_priv.h" + +static int bo_allocate(struct msm_bo *msm_bo) +{ + struct fd_bo *bo = &msm_bo->base; + if (!msm_bo->offset) { + struct drm_msm_gem_info req = { + .handle = bo->handle, + }; + int ret; + + /* if the buffer is already backed by pages then this + * doesn't actually do anything (other than giving us + * the offset) + */ + ret = drmCommandWriteRead(bo->dev->fd, DRM_MSM_GEM_INFO, + &req, sizeof(req)); + if (ret) { + ERROR_MSG("alloc failed: %s", strerror(errno)); + return ret; + } + + msm_bo->offset = req.offset; + } + + return 0; +} + +static int msm_bo_offset(struct fd_bo *bo, uint64_t *offset) +{ + struct msm_bo *msm_bo = to_msm_bo(bo); + int ret = bo_allocate(msm_bo); + if (ret) + return ret; + *offset = msm_bo->offset; + return 0; +} + +static int msm_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op) +{ + struct drm_msm_gem_cpu_prep req = { + .handle = bo->handle, + .op = op, + }; + + get_abs_timeout(&req.timeout, 5000); + + return drmCommandWrite(bo->dev->fd, DRM_MSM_GEM_CPU_PREP, &req, sizeof(req)); +} + +static void msm_bo_cpu_fini(struct fd_bo *bo) +{ + struct drm_msm_gem_cpu_fini req = { + .handle = bo->handle, + }; + + drmCommandWrite(bo->dev->fd, DRM_MSM_GEM_CPU_FINI, &req, sizeof(req)); +} + +static void msm_bo_destroy(struct fd_bo *bo) +{ + struct msm_bo *msm_bo = to_msm_bo(bo); + free(msm_bo); + +} + +static struct fd_bo_funcs funcs = { + .offset = msm_bo_offset, + .cpu_prep = msm_bo_cpu_prep, + .cpu_fini = msm_bo_cpu_fini, + .destroy = msm_bo_destroy, +}; + +/* allocate a buffer handle: */ +int msm_bo_new_handle(struct fd_device *dev, + uint32_t size, uint32_t flags, uint32_t *handle) +{ + struct drm_msm_gem_new req = { + .size = size, + .flags = MSM_BO_WC, // TODO figure out proper flags.. + }; + int ret; + + ret = drmCommandWriteRead(dev->fd, DRM_MSM_GEM_NEW, + &req, sizeof(req)); + if (ret) + return ret; + + *handle = req.handle; + + return 0; +} + +/* allocate a new buffer object */ +struct fd_bo * msm_bo_from_handle(struct fd_device *dev, + uint32_t size, uint32_t handle) +{ + struct msm_bo *msm_bo; + struct fd_bo *bo; + unsigned i; + + msm_bo = calloc(1, sizeof(*msm_bo)); + if (!msm_bo) + return NULL; + + bo = &msm_bo->base; + bo->funcs = &funcs; + + for (i = 0; i < ARRAY_SIZE(msm_bo->list); i++) + list_inithead(&msm_bo->list[i]); + + return bo; +} diff --git a/freedreno/msm/msm_device.c b/freedreno/msm/msm_device.c new file mode 100644 index 00000000..cadcc854 --- /dev/null +++ b/freedreno/msm/msm_device.c @@ -0,0 +1,61 @@ +/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */ + +/* + * Copyright (C) 2013 Rob Clark + * + * 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: + * Rob Clark + */ + +#include +#include +#include + +#include "msm_priv.h" + +static void msm_device_destroy(struct fd_device *dev) +{ + struct msm_device *msm_dev = to_msm_device(dev); + free(msm_dev); +} + +static struct fd_device_funcs funcs = { + .bo_new_handle = msm_bo_new_handle, + .bo_from_handle = msm_bo_from_handle, + .pipe_new = msm_pipe_new, + .destroy = msm_device_destroy, +}; + +struct fd_device * msm_device_new(int fd) +{ + struct msm_device *msm_dev; + struct fd_device *dev; + + msm_dev = calloc(1, sizeof(*msm_dev)); + if (!msm_dev) + return NULL; + + dev = &msm_dev->base; + dev->funcs = &funcs; + + return dev; +} diff --git a/freedreno/msm/msm_drm.h b/freedreno/msm/msm_drm.h new file mode 100644 index 00000000..d3c62074 --- /dev/null +++ b/freedreno/msm/msm_drm.h @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef __MSM_DRM_H__ +#define __MSM_DRM_H__ + +#include +#include + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints: + * 1) Do not use pointers, use uint64_t instead for 32 bit / 64 bit + * user/kernel compatibility + * 2) Keep fields aligned to their size + * 3) Because of how drm_ioctl() works, we can add new fields at + * the end of an ioctl if some care is taken: drm_ioctl() will + * zero out the new fields at the tail of the ioctl, so a zero + * value should have a backwards compatible meaning. And for + * output params, userspace won't see the newly added output + * fields.. so that has to be somehow ok. + */ + +#define MSM_PIPE_NONE 0x00 +#define MSM_PIPE_2D0 0x01 +#define MSM_PIPE_2D1 0x02 +#define MSM_PIPE_3D0 0x10 + +/* timeouts are specified in clock-monotonic absolute times (to simplify + * restarting interrupted ioctls). The following struct is logically the + * same as 'struct timespec' but 32/64b ABI safe. + */ +struct drm_msm_timespec { + int64_t tv_sec; /* seconds */ + int64_t tv_nsec; /* nanoseconds */ +}; + +#define MSM_PARAM_GPU_ID 0x01 +#define MSM_PARAM_GMEM_SIZE 0x02 + +struct drm_msm_param { + uint32_t pipe; /* in, MSM_PIPE_x */ + uint32_t param; /* in, MSM_PARAM_x */ + uint64_t value; /* out (get_param) or in (set_param) */ +}; + +/* + * GEM buffers: + */ + +#define MSM_BO_SCANOUT 0x00000001 /* scanout capable */ +#define MSM_BO_GPU_READONLY 0x00000002 +#define MSM_BO_CACHE_MASK 0x000f0000 +/* cache modes */ +#define MSM_BO_CACHED 0x00010000 +#define MSM_BO_WC 0x00020000 +#define MSM_BO_UNCACHED 0x00040000 + +struct drm_msm_gem_new { + uint64_t size; /* in */ + uint32_t flags; /* in, mask of MSM_BO_x */ + uint32_t handle; /* out */ +}; + +struct drm_msm_gem_info { + uint32_t handle; /* in */ + uint32_t pad; + uint64_t offset; /* out, offset to pass to mmap() */ +}; + +#define MSM_PREP_READ 0x01 +#define MSM_PREP_WRITE 0x02 +#define MSM_PREP_NOSYNC 0x04 + +struct drm_msm_gem_cpu_prep { + uint32_t handle; /* in */ + uint32_t op; /* in, mask of MSM_PREP_x */ + struct drm_msm_timespec timeout; /* in */ +}; + +struct drm_msm_gem_cpu_fini { + uint32_t handle; /* in */ +}; + +/* + * Cmdstream Submission: + */ + +/* The value written into the cmdstream is logically: + * + * ((relocbuf->gpuaddr + reloc_offset) << shift) | or + * + * When we have GPU's w/ >32bit ptrs, it should be possible to deal + * with this by emit'ing two reloc entries with appropriate shift + * values. Or a new MSM_SUBMIT_CMD_x type would also be an option. + * + * NOTE that reloc's must be sorted by order of increasing submit_offset, + * otherwise EINVAL. + */ +struct drm_msm_gem_submit_reloc { + uint32_t submit_offset; /* in, offset from submit_bo */ + uint32_t or; /* in, value OR'd with result */ + int32_t shift; /* in, amount of left shift (can be negative) */ + uint32_t reloc_idx; /* in, index of reloc_bo buffer */ + uint64_t reloc_offset; /* in, offset from start of reloc_bo */ +}; + +/* submit-types: + * BUF - this cmd buffer is executed normally. + * IB_TARGET_BUF - this cmd buffer is an IB target. Reloc's are + * processed normally, but the kernel does not setup an IB to + * this buffer in the first-level ringbuffer + * CTX_RESTORE_BUF - only executed if there has been a GPU context + * switch since the last SUBMIT ioctl + */ +#define MSM_SUBMIT_CMD_BUF 0x0001 +#define MSM_SUBMIT_CMD_IB_TARGET_BUF 0x0002 +#define MSM_SUBMIT_CMD_CTX_RESTORE_BUF 0x0003 +struct drm_msm_gem_submit_cmd { + uint32_t type; /* in, one of MSM_SUBMIT_CMD_x */ + uint32_t submit_idx; /* in, index of submit_bo cmdstream buffer */ + uint32_t submit_offset; /* in, offset into submit_bo */ + uint32_t size; /* in, cmdstream size */ + uint32_t pad; + uint32_t nr_relocs; /* in, number of submit_reloc's */ + uint64_t __user relocs; /* in, ptr to array of submit_reloc's */ +}; + +/* Each buffer referenced elsewhere in the cmdstream submit (ie. the + * cmdstream buffer(s) themselves or reloc entries) has one (and only + * one) entry in the submit->bos[] table. + * + * As a optimization, the current buffer (gpu virtual address) can be + * passed back through the 'presumed' field. If on a subsequent reloc, + * userspace passes back a 'presumed' address that is still valid, + * then patching the cmdstream for this entry is skipped. This can + * avoid kernel needing to map/access the cmdstream bo in the common + * case. + */ +#define MSM_SUBMIT_BO_READ 0x0001 +#define MSM_SUBMIT_BO_WRITE 0x0002 +struct drm_msm_gem_submit_bo { + uint32_t flags; /* in, mask of MSM_SUBMIT_BO_x */ + uint32_t handle; /* in, GEM handle */ + uint64_t presumed; /* in/out, presumed buffer address */ +}; + +/* Each cmdstream submit consists of a table of buffers involved, and + * one or more cmdstream buffers. This allows for conditional execution + * (context-restore), and IB buffers needed for per tile/bin draw cmds. + */ +struct drm_msm_gem_submit { + uint32_t pipe; /* in, MSM_PIPE_x */ + uint32_t fence; /* out */ + uint32_t nr_bos; /* in, number of submit_bo's */ + uint32_t nr_cmds; /* in, number of submit_cmd's */ + uint64_t __user bos; /* in, ptr to array of submit_bo's */ + uint64_t __user cmds; /* in, ptr to array of submit_cmd's */ +}; + +/* The normal way to synchronize with the GPU is just to CPU_PREP on + * a buffer if you need to access it from the CPU (other cmdstream + * submission from same or other contexts, PAGE_FLIP ioctl, etc, all + * handle the required synchronization under the hood). This ioctl + * mainly just exists as a way to implement the gallium pipe_fence + * APIs without requiring a dummy bo to synchronize on. + */ +struct drm_msm_wait_fence { + uint32_t fence; /* in */ + uint32_t pad; + struct drm_msm_timespec timeout; /* in */ +}; + +#define DRM_MSM_GET_PARAM 0x00 +/* placeholder: +#define DRM_MSM_SET_PARAM 0x01 + */ +#define DRM_MSM_GEM_NEW 0x02 +#define DRM_MSM_GEM_INFO 0x03 +#define DRM_MSM_GEM_CPU_PREP 0x04 +#define DRM_MSM_GEM_CPU_FINI 0x05 +#define DRM_MSM_GEM_SUBMIT 0x06 +#define DRM_MSM_WAIT_FENCE 0x07 +#define DRM_MSM_NUM_IOCTLS 0x08 + +#define DRM_IOCTL_MSM_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GET_PARAM, struct drm_msm_param) +#define DRM_IOCTL_MSM_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_NEW, struct drm_msm_gem_new) +#define DRM_IOCTL_MSM_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_INFO, struct drm_msm_gem_info) +#define DRM_IOCTL_MSM_GEM_CPU_PREP DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_GEM_CPU_PREP, struct drm_msm_gem_cpu_prep) +#define DRM_IOCTL_MSM_GEM_CPU_FINI DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_GEM_CPU_FINI, struct drm_msm_gem_cpu_fini) +#define DRM_IOCTL_MSM_GEM_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_SUBMIT, struct drm_msm_gem_submit) +#define DRM_IOCTL_MSM_WAIT_FENCE DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_WAIT_FENCE, struct drm_msm_wait_fence) + +#endif /* __MSM_DRM_H__ */ diff --git a/freedreno/msm/msm_pipe.c b/freedreno/msm/msm_pipe.c new file mode 100644 index 00000000..ece4de5a --- /dev/null +++ b/freedreno/msm/msm_pipe.c @@ -0,0 +1,133 @@ +/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */ + +/* + * Copyright (C) 2013 Rob Clark + * + * 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: + * Rob Clark + */ + +#include "msm_priv.h" + + +static int msm_pipe_get_param(struct fd_pipe *pipe, + enum fd_param_id param, uint64_t *value) +{ + struct msm_pipe *msm_pipe = to_msm_pipe(pipe); + switch(param) { + case FD_DEVICE_ID: // XXX probably get rid of this.. + case FD_GPU_ID: + *value = msm_pipe->gpu_id; + return 0; + case FD_GMEM_SIZE: + *value = msm_pipe->gmem; + return 0; + default: + ERROR_MSG("invalid param id: %d", param); + return -1; + } +} + +static int msm_pipe_wait(struct fd_pipe *pipe, uint32_t timestamp) +{ + struct fd_device *dev = pipe->dev; + struct drm_msm_wait_fence req = { + .fence = timestamp, + }; + int ret; + + get_abs_timeout(&req.timeout, 5000); + + ret = drmCommandWrite(dev->fd, DRM_MSM_WAIT_FENCE, &req, sizeof(req)); + if (ret) { + ERROR_MSG("wait-fence failed! %d (%s)", ret, strerror(errno)); + return ret; + } + + return 0; +} + +static void msm_pipe_destroy(struct fd_pipe *pipe) +{ + struct msm_pipe *msm_pipe = to_msm_pipe(pipe); + free(msm_pipe); +} + +static struct fd_pipe_funcs funcs = { + .ringbuffer_new = msm_ringbuffer_new, + .get_param = msm_pipe_get_param, + .wait = msm_pipe_wait, + .destroy = msm_pipe_destroy, +}; + +static uint64_t get_param(struct fd_device *dev, uint32_t pipe, uint32_t param) +{ + struct drm_msm_param req = { + .pipe = pipe, + .param = param, + }; + int ret; + + ret = drmCommandWriteRead(dev->fd, DRM_MSM_GET_PARAM, &req, sizeof(req)); + if (ret) { + ERROR_MSG("get-param failed! %d (%s)", ret, strerror(errno)); + return 0; + } + + return req.value; +} + +struct fd_pipe * msm_pipe_new(struct fd_device *dev, enum fd_pipe_id id) +{ + static const uint32_t pipe_id[] = { + [FD_PIPE_3D] = MSM_PIPE_3D0, + [FD_PIPE_2D] = MSM_PIPE_2D0, + }; + struct msm_pipe *msm_pipe = NULL; + struct fd_pipe *pipe = NULL; + + msm_pipe = calloc(1, sizeof(*msm_pipe)); + if (!msm_pipe) { + ERROR_MSG("allocation failed"); + goto fail; + } + + pipe = &msm_pipe->base; + pipe->funcs = &funcs; + + msm_pipe->pipe = pipe_id[id]; + msm_pipe->gpu_id = get_param(dev, pipe_id[id], MSM_PARAM_GPU_ID); + msm_pipe->gmem = get_param(dev, pipe_id[id], MSM_PARAM_GMEM_SIZE); + + if (! msm_pipe->gpu_id) + goto fail; + + INFO_MSG("Pipe Info:"); + INFO_MSG(" GPU-id: %d", msm_pipe->gpu_id); + INFO_MSG(" GMEM size: 0x%08x", msm_pipe->gmem); + + return pipe; +fail: + if (pipe) + fd_pipe_del(pipe); + return NULL; +} diff --git a/freedreno/msm/msm_priv.h b/freedreno/msm/msm_priv.h new file mode 100644 index 00000000..4c5623a9 --- /dev/null +++ b/freedreno/msm/msm_priv.h @@ -0,0 +1,95 @@ +/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */ + +/* + * Copyright (C) 2013 Rob Clark + * + * 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: + * Rob Clark + */ + +#ifndef MSM_PRIV_H_ +#define MSM_PRIV_H_ + +#include "freedreno_priv.h" + +#ifndef __user +# define __user +#endif + +#include "msm_drm.h" + +struct msm_device { + struct fd_device base; +}; + +static inline struct msm_device * to_msm_device(struct fd_device *x) +{ + return (struct msm_device *)x; +} + +struct fd_device * msm_device_new(int fd); + +struct msm_pipe { + struct fd_pipe base; + uint32_t pipe; + uint32_t gpu_id; + uint32_t gmem; +}; + +static inline struct msm_pipe * to_msm_pipe(struct fd_pipe *x) +{ + return (struct msm_pipe *)x; +} + +struct fd_pipe * msm_pipe_new(struct fd_device *dev, enum fd_pipe_id id); + +struct fd_ringbuffer * msm_ringbuffer_new(struct fd_pipe *pipe, + uint32_t size); + +struct msm_bo { + struct fd_bo base; + uint64_t offset; + uint64_t presumed; + uint32_t indexp1[FD_PIPE_MAX]; /* index plus 1 */ + struct list_head list[FD_PIPE_MAX]; +}; + +static inline struct msm_bo * to_msm_bo(struct fd_bo *x) +{ + return (struct msm_bo *)x; +} + +int msm_bo_new_handle(struct fd_device *dev, + uint32_t size, uint32_t flags, uint32_t *handle); +struct fd_bo * msm_bo_from_handle(struct fd_device *dev, + uint32_t size, uint32_t handle); + +static inline void get_abs_timeout(struct drm_msm_timespec *tv, uint32_t ms) +{ + struct timespec t; + uint32_t s = ms / 1000; + clock_gettime(CLOCK_MONOTONIC, &t); + tv->tv_sec = t.tv_sec + s; + tv->tv_nsec = t.tv_nsec + ((ms - (s * 1000)) * 1000000); +} + +#endif /* MSM_PRIV_H_ */ diff --git a/freedreno/msm/msm_ringbuffer.c b/freedreno/msm/msm_ringbuffer.c new file mode 100644 index 00000000..fc5193ae --- /dev/null +++ b/freedreno/msm/msm_ringbuffer.c @@ -0,0 +1,303 @@ +/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */ + +/* + * Copyright (C) 2013 Rob Clark + * + * 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: + * Rob Clark + */ + +#include + +#include "freedreno_ringbuffer.h" +#include "msm_priv.h" + +struct msm_ringbuffer { + struct fd_ringbuffer base; + struct fd_bo *ring_bo; + + struct list_head submit_list; + + /* bo's table: */ + struct drm_msm_gem_submit_bo *bos; + uint32_t nr_bos, max_bos; + + /* cmd's table: */ + struct drm_msm_gem_submit_cmd *cmds; + uint32_t nr_cmds, max_cmds; + + /* reloc's table: */ + struct drm_msm_gem_submit_reloc *relocs; + uint32_t nr_relocs, max_relocs; +}; + +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 msm_ringbuffer * to_msm_ringbuffer(struct fd_ringbuffer *x) +{ + return (struct msm_ringbuffer *)x; +} + +/* add (if needed) bo, return idx: */ +static uint32_t bo2idx(struct fd_ringbuffer *ring, struct fd_bo *bo, uint32_t flags) +{ + struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); + struct msm_bo *msm_bo = to_msm_bo(bo); + int id = ring->pipe->id; + uint32_t idx; + if (!msm_bo->indexp1[id]) { + struct list_head *list = &msm_bo->list[id]; + idx = APPEND(msm_ring, bos); + msm_ring->bos[idx].flags = 0; + msm_ring->bos[idx].handle = bo->handle; + msm_ring->bos[idx].presumed = msm_bo->presumed; + msm_bo->indexp1[id] = idx + 1; + + assert(LIST_IS_EMPTY(list)); + fd_bo_ref(bo); + list_addtail(list, &msm_ring->submit_list); + } else { + idx = msm_bo->indexp1[id] - 1; + } + if (flags & FD_RELOC_READ) + msm_ring->bos[idx].flags |= MSM_SUBMIT_BO_READ; + if (flags & FD_RELOC_WRITE) + msm_ring->bos[idx].flags |= MSM_SUBMIT_BO_WRITE; + return idx; +} + +static uint32_t offset_bytes(void *end, void *start) +{ + return ((char *)end) - ((char *)start); +} + +static struct drm_msm_gem_submit_cmd * get_cmd(struct fd_ringbuffer *ring, + struct fd_bo *ring_bo, uint32_t submit_offset, uint32_t size, uint32_t type) +{ + struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); + struct drm_msm_gem_submit_cmd *cmd = NULL; + uint32_t i; + + /* figure out if we already have a cmd buf: */ + for (i = 0; i < msm_ring->nr_cmds; i++) { + cmd = &msm_ring->cmds[i]; + if ((cmd->submit_offset == submit_offset) && + (cmd->size == size) && + (cmd->type == type)) + break; + cmd = NULL; + } + + /* create cmd buf if not: */ + if (!cmd) { + uint32_t idx = APPEND(msm_ring, cmds); + cmd = &msm_ring->cmds[idx]; + cmd->type = type; + cmd->submit_idx = bo2idx(ring, ring_bo, FD_RELOC_READ); + cmd->submit_offset = submit_offset; + cmd->size = size; + } + + return cmd; +} + +static void * msm_ringbuffer_hostptr(struct fd_ringbuffer *ring) +{ + struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); + return fd_bo_map(msm_ring->ring_bo); +} + +static uint32_t find_next_reloc_idx(struct msm_ringbuffer *msm_ring, + uint32_t start, uint32_t offset) +{ + uint32_t i; + + /* a binary search would be more clever.. */ + for (i = start; i < msm_ring->nr_relocs; i++) { + struct drm_msm_gem_submit_reloc *reloc = &msm_ring->relocs[i]; + if (reloc->submit_offset >= offset) + return i; + } + + return i; +} + +static int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start) +{ + struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); + struct fd_bo *ring_bo = msm_ring->ring_bo; + struct drm_msm_gem_submit req = { + .pipe = to_msm_pipe(ring->pipe)->pipe, + }; + struct msm_bo *msm_bo = NULL, *tmp; + uint32_t i, submit_offset, size; + int ret, id = ring->pipe->id; + + submit_offset = offset_bytes(last_start, ring->start); + size = offset_bytes(ring->cur, last_start); + + get_cmd(ring, ring_bo, submit_offset, size, MSM_SUBMIT_CMD_BUF); + + /* needs to be after get_cmd() as that could create bos/cmds table: */ + req.bos = VOID2U64(msm_ring->bos), + req.nr_bos = msm_ring->nr_bos; + req.cmds = VOID2U64(msm_ring->cmds), + req.nr_cmds = msm_ring->nr_cmds; + + /* for each of the cmd's fix up their reloc's: */ + for (i = 0; i < msm_ring->nr_cmds; i++) { + struct drm_msm_gem_submit_cmd *cmd = &msm_ring->cmds[i]; + uint32_t a = find_next_reloc_idx(msm_ring, 0, cmd->submit_offset); + uint32_t b = find_next_reloc_idx(msm_ring, a, cmd->submit_offset + cmd->size); + cmd->relocs = VOID2U64(&msm_ring->relocs[a]); + cmd->nr_relocs = (b > a) ? b - a : 0; + } + + DEBUG_MSG("nr_cmds=%u, nr_bos=%u\n", req.nr_cmds, req.nr_bos); + + ret = drmCommandWriteRead(ring->pipe->dev->fd, DRM_MSM_GEM_SUBMIT, + &req, sizeof(req)); + if (ret) + ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno)); + + LIST_FOR_EACH_ENTRY_SAFE(msm_bo, tmp, &msm_ring->submit_list, list[id]) { + struct list_head *list = &msm_bo->list[id]; + list_delinit(list); + msm_bo->indexp1[id] = 0; + fd_bo_del(&msm_bo->base); + } + + msm_ring->nr_cmds = 0; + msm_ring->nr_relocs = 0; + msm_ring->nr_bos = 0; + + return ret; +} + +static void msm_ringbuffer_emit_reloc(struct fd_ringbuffer *ring, + const struct fd_reloc *r) +{ + struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); + struct msm_bo *msm_bo = to_msm_bo(r->bo); + struct drm_msm_gem_submit_reloc *reloc; + uint32_t idx = APPEND(msm_ring, relocs); + uint32_t addr; + + reloc = &msm_ring->relocs[idx]; + + reloc->reloc_idx = bo2idx(ring, r->bo, r->flags); + reloc->reloc_offset = r->offset; + reloc->or = r->or; + reloc->shift = r->shift; + reloc->submit_offset = offset_bytes(ring->cur, ring->start); + + addr = msm_bo->presumed; + if (r->shift < 0) + addr >>= -r->shift; + else + addr <<= r->shift; + (*ring->cur++) = addr | r->or; +} + +static void msm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring, + struct fd_ringmarker *target, struct fd_ringmarker *end) +{ + struct fd_bo *ring_bo = to_msm_ringbuffer(target->ring)->ring_bo; + struct drm_msm_gem_submit_cmd *cmd; + uint32_t submit_offset, size; + + submit_offset = offset_bytes(target->cur, target->ring->start); + size = offset_bytes(end->cur, target->cur); + + assert(target->ring == ring); // TODO + + cmd = get_cmd(ring, ring_bo, submit_offset, size, + MSM_SUBMIT_CMD_IB_TARGET_BUF); + assert(cmd); + + msm_ringbuffer_emit_reloc(ring, &(struct fd_reloc){ + .bo = ring_bo, + .flags = FD_RELOC_READ, + .offset = submit_offset, + }); +} + +static void msm_ringbuffer_destroy(struct fd_ringbuffer *ring) +{ + struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); + if (msm_ring->ring_bo) + fd_bo_del(msm_ring->ring_bo); + free(msm_ring); +} + +static struct fd_ringbuffer_funcs funcs = { + .hostptr = msm_ringbuffer_hostptr, + .flush = msm_ringbuffer_flush, + .emit_reloc = msm_ringbuffer_emit_reloc, + .emit_reloc_ring = msm_ringbuffer_emit_reloc_ring, + .destroy = msm_ringbuffer_destroy, +}; + +struct fd_ringbuffer * msm_ringbuffer_new(struct fd_pipe *pipe, + uint32_t size) +{ + struct msm_ringbuffer *msm_ring; + struct fd_ringbuffer *ring = NULL; + + msm_ring = calloc(1, sizeof(*msm_ring)); + if (!msm_ring) { + ERROR_MSG("allocation failed"); + goto fail; + } + + ring = &msm_ring->base; + ring->funcs = &funcs; + + list_inithead(&msm_ring->submit_list); + + msm_ring->ring_bo = fd_bo_new(pipe->dev, size, 0); + if (!msm_ring->ring_bo) { + ERROR_MSG("ringbuffer allocation failed"); + goto fail; + } + + return ring; +fail: + if (ring) + fd_ringbuffer_del(ring); + return NULL; +}