1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright © 2022 Google, Inc.
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci *
11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci * Software.
14bf215546Sopenharmony_ci *
15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20bf215546Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21bf215546Sopenharmony_ci * SOFTWARE.
22bf215546Sopenharmony_ci */
23bf215546Sopenharmony_ci
24bf215546Sopenharmony_ci#include "util/slab.h"
25bf215546Sopenharmony_ci
26bf215546Sopenharmony_ci#include "freedreno_ringbuffer_sp.h"
27bf215546Sopenharmony_ci#include "virtio_priv.h"
28bf215546Sopenharmony_ci
29bf215546Sopenharmony_cistatic int
30bf215546Sopenharmony_ciquery_param(struct fd_pipe *pipe, uint32_t param, uint64_t *value)
31bf215546Sopenharmony_ci{
32bf215546Sopenharmony_ci   struct virtio_pipe *virtio_pipe = to_virtio_pipe(pipe);
33bf215546Sopenharmony_ci   struct drm_msm_param req = {
34bf215546Sopenharmony_ci      .pipe = virtio_pipe->pipe,
35bf215546Sopenharmony_ci      .param = param,
36bf215546Sopenharmony_ci   };
37bf215546Sopenharmony_ci   int ret;
38bf215546Sopenharmony_ci
39bf215546Sopenharmony_ci   ret = virtio_simple_ioctl(pipe->dev, DRM_IOCTL_MSM_GET_PARAM, &req);
40bf215546Sopenharmony_ci   if (ret)
41bf215546Sopenharmony_ci      return ret;
42bf215546Sopenharmony_ci
43bf215546Sopenharmony_ci   *value = req.value;
44bf215546Sopenharmony_ci
45bf215546Sopenharmony_ci   return 0;
46bf215546Sopenharmony_ci}
47bf215546Sopenharmony_ci
48bf215546Sopenharmony_cistatic int
49bf215546Sopenharmony_ciquery_queue_param(struct fd_pipe *pipe, uint32_t param, uint64_t *value)
50bf215546Sopenharmony_ci{
51bf215546Sopenharmony_ci   struct msm_ccmd_submitqueue_query_req req = {
52bf215546Sopenharmony_ci         .hdr = MSM_CCMD(SUBMITQUEUE_QUERY, sizeof(req)),
53bf215546Sopenharmony_ci         .queue_id = to_virtio_pipe(pipe)->queue_id,
54bf215546Sopenharmony_ci         .param = param,
55bf215546Sopenharmony_ci         .len = sizeof(*value),
56bf215546Sopenharmony_ci   };
57bf215546Sopenharmony_ci   struct msm_ccmd_submitqueue_query_rsp *rsp;
58bf215546Sopenharmony_ci   unsigned rsp_len = sizeof(*rsp) + req.len;
59bf215546Sopenharmony_ci
60bf215546Sopenharmony_ci   rsp = virtio_alloc_rsp(pipe->dev, &req.hdr, rsp_len);
61bf215546Sopenharmony_ci
62bf215546Sopenharmony_ci   int ret = virtio_execbuf(pipe->dev, &req.hdr, true);
63bf215546Sopenharmony_ci   if (ret)
64bf215546Sopenharmony_ci      goto out;
65bf215546Sopenharmony_ci
66bf215546Sopenharmony_ci   memcpy(value, rsp->payload, req.len);
67bf215546Sopenharmony_ci
68bf215546Sopenharmony_ci   ret = rsp->ret;
69bf215546Sopenharmony_ci
70bf215546Sopenharmony_ciout:
71bf215546Sopenharmony_ci   return ret;
72bf215546Sopenharmony_ci}
73bf215546Sopenharmony_ci
74bf215546Sopenharmony_cistatic int
75bf215546Sopenharmony_civirtio_pipe_get_param(struct fd_pipe *pipe, enum fd_param_id param,
76bf215546Sopenharmony_ci                   uint64_t *value)
77bf215546Sopenharmony_ci{
78bf215546Sopenharmony_ci   struct virtio_pipe *virtio_pipe = to_virtio_pipe(pipe);
79bf215546Sopenharmony_ci   struct virtio_device *virtio_dev = to_virtio_device(pipe->dev);
80bf215546Sopenharmony_ci
81bf215546Sopenharmony_ci   switch (param) {
82bf215546Sopenharmony_ci   case FD_DEVICE_ID: // XXX probably get rid of this..
83bf215546Sopenharmony_ci   case FD_GPU_ID:
84bf215546Sopenharmony_ci      *value = virtio_pipe->gpu_id;
85bf215546Sopenharmony_ci      return 0;
86bf215546Sopenharmony_ci   case FD_GMEM_SIZE:
87bf215546Sopenharmony_ci      *value = virtio_pipe->gmem;
88bf215546Sopenharmony_ci      return 0;
89bf215546Sopenharmony_ci   case FD_GMEM_BASE:
90bf215546Sopenharmony_ci      *value = virtio_pipe->gmem_base;
91bf215546Sopenharmony_ci      return 0;
92bf215546Sopenharmony_ci   case FD_CHIP_ID:
93bf215546Sopenharmony_ci      *value = virtio_pipe->chip_id;
94bf215546Sopenharmony_ci      return 0;
95bf215546Sopenharmony_ci   case FD_MAX_FREQ:
96bf215546Sopenharmony_ci      *value = virtio_dev->caps.u.msm.max_freq;
97bf215546Sopenharmony_ci      return 0;
98bf215546Sopenharmony_ci   case FD_TIMESTAMP:
99bf215546Sopenharmony_ci      return query_param(pipe, MSM_PARAM_TIMESTAMP, value);
100bf215546Sopenharmony_ci   case FD_NR_RINGS:
101bf215546Sopenharmony_ci      *value = virtio_dev->caps.u.msm.priorities;
102bf215546Sopenharmony_ci      return 0;
103bf215546Sopenharmony_ci   case FD_CTX_FAULTS:
104bf215546Sopenharmony_ci      return query_queue_param(pipe, MSM_SUBMITQUEUE_PARAM_FAULTS, value);
105bf215546Sopenharmony_ci   case FD_GLOBAL_FAULTS:
106bf215546Sopenharmony_ci      return query_param(pipe, MSM_PARAM_FAULTS, value);
107bf215546Sopenharmony_ci   case FD_SUSPEND_COUNT:
108bf215546Sopenharmony_ci      return query_param(pipe, MSM_PARAM_SUSPENDS, value);
109bf215546Sopenharmony_ci   default:
110bf215546Sopenharmony_ci      ERROR_MSG("invalid param id: %d", param);
111bf215546Sopenharmony_ci      return -1;
112bf215546Sopenharmony_ci   }
113bf215546Sopenharmony_ci}
114bf215546Sopenharmony_ci
115bf215546Sopenharmony_cistatic int
116bf215546Sopenharmony_civirtio_pipe_wait(struct fd_pipe *pipe, const struct fd_fence *fence, uint64_t timeout)
117bf215546Sopenharmony_ci{
118bf215546Sopenharmony_ci   struct msm_ccmd_wait_fence_req req = {
119bf215546Sopenharmony_ci         .hdr = MSM_CCMD(WAIT_FENCE, sizeof(req)),
120bf215546Sopenharmony_ci         .queue_id = to_virtio_pipe(pipe)->queue_id,
121bf215546Sopenharmony_ci         .fence = fence->kfence,
122bf215546Sopenharmony_ci   };
123bf215546Sopenharmony_ci   struct msm_ccmd_submitqueue_query_rsp *rsp;
124bf215546Sopenharmony_ci   int64_t end_time = os_time_get_nano() + timeout;
125bf215546Sopenharmony_ci   int ret;
126bf215546Sopenharmony_ci
127bf215546Sopenharmony_ci   do {
128bf215546Sopenharmony_ci      rsp = virtio_alloc_rsp(pipe->dev, &req.hdr, sizeof(*rsp));
129bf215546Sopenharmony_ci
130bf215546Sopenharmony_ci      ret = virtio_execbuf(pipe->dev, &req.hdr, true);
131bf215546Sopenharmony_ci      if (ret)
132bf215546Sopenharmony_ci         goto out;
133bf215546Sopenharmony_ci
134bf215546Sopenharmony_ci      if ((timeout != PIPE_TIMEOUT_INFINITE) &&
135bf215546Sopenharmony_ci          (os_time_get_nano() >= end_time))
136bf215546Sopenharmony_ci         break;
137bf215546Sopenharmony_ci
138bf215546Sopenharmony_ci      ret = rsp->ret;
139bf215546Sopenharmony_ci   } while (ret == -ETIMEDOUT);
140bf215546Sopenharmony_ci
141bf215546Sopenharmony_ciout:
142bf215546Sopenharmony_ci   return ret;
143bf215546Sopenharmony_ci}
144bf215546Sopenharmony_ci
145bf215546Sopenharmony_cistatic int
146bf215546Sopenharmony_ciopen_submitqueue(struct fd_pipe *pipe, uint32_t prio)
147bf215546Sopenharmony_ci{
148bf215546Sopenharmony_ci   struct virtio_pipe *virtio_pipe = to_virtio_pipe(pipe);
149bf215546Sopenharmony_ci
150bf215546Sopenharmony_ci   struct drm_msm_submitqueue req = {
151bf215546Sopenharmony_ci      .flags = 0,
152bf215546Sopenharmony_ci      .prio = prio,
153bf215546Sopenharmony_ci   };
154bf215546Sopenharmony_ci   uint64_t nr_rings = 1;
155bf215546Sopenharmony_ci   int ret;
156bf215546Sopenharmony_ci
157bf215546Sopenharmony_ci   virtio_pipe_get_param(pipe, FD_NR_RINGS, &nr_rings);
158bf215546Sopenharmony_ci
159bf215546Sopenharmony_ci   req.prio = MIN2(req.prio, MAX2(nr_rings, 1) - 1);
160bf215546Sopenharmony_ci
161bf215546Sopenharmony_ci   ret = virtio_simple_ioctl(pipe->dev, DRM_IOCTL_MSM_SUBMITQUEUE_NEW, &req);
162bf215546Sopenharmony_ci   if (ret) {
163bf215546Sopenharmony_ci      ERROR_MSG("could not create submitqueue! %d (%s)", ret, strerror(errno));
164bf215546Sopenharmony_ci      return ret;
165bf215546Sopenharmony_ci   }
166bf215546Sopenharmony_ci
167bf215546Sopenharmony_ci   virtio_pipe->queue_id = req.id;
168bf215546Sopenharmony_ci   virtio_pipe->ring_idx = req.prio + 1;
169bf215546Sopenharmony_ci
170bf215546Sopenharmony_ci   return 0;
171bf215546Sopenharmony_ci}
172bf215546Sopenharmony_ci
173bf215546Sopenharmony_cistatic void
174bf215546Sopenharmony_ciclose_submitqueue(struct fd_pipe *pipe, uint32_t queue_id)
175bf215546Sopenharmony_ci{
176bf215546Sopenharmony_ci   virtio_simple_ioctl(pipe->dev, DRM_IOCTL_MSM_SUBMITQUEUE_CLOSE, &queue_id);
177bf215546Sopenharmony_ci}
178bf215546Sopenharmony_ci
179bf215546Sopenharmony_cistatic void
180bf215546Sopenharmony_civirtio_pipe_destroy(struct fd_pipe *pipe)
181bf215546Sopenharmony_ci{
182bf215546Sopenharmony_ci   struct virtio_pipe *virtio_pipe = to_virtio_pipe(pipe);
183bf215546Sopenharmony_ci
184bf215546Sopenharmony_ci   if (util_queue_is_initialized(&virtio_pipe->retire_queue))
185bf215546Sopenharmony_ci      util_queue_destroy(&virtio_pipe->retire_queue);
186bf215546Sopenharmony_ci
187bf215546Sopenharmony_ci   close_submitqueue(pipe, virtio_pipe->queue_id);
188bf215546Sopenharmony_ci   fd_pipe_sp_ringpool_fini(pipe);
189bf215546Sopenharmony_ci   free(virtio_pipe);
190bf215546Sopenharmony_ci}
191bf215546Sopenharmony_ci
192bf215546Sopenharmony_cistatic const struct fd_pipe_funcs funcs = {
193bf215546Sopenharmony_ci   .ringbuffer_new_object = fd_ringbuffer_sp_new_object,
194bf215546Sopenharmony_ci   .submit_new = virtio_submit_new,
195bf215546Sopenharmony_ci   .flush = fd_pipe_sp_flush,
196bf215546Sopenharmony_ci   .get_param = virtio_pipe_get_param,
197bf215546Sopenharmony_ci   .wait = virtio_pipe_wait,
198bf215546Sopenharmony_ci   .destroy = virtio_pipe_destroy,
199bf215546Sopenharmony_ci};
200bf215546Sopenharmony_ci
201bf215546Sopenharmony_cistatic void
202bf215546Sopenharmony_ciinit_shmem(struct fd_device *dev)
203bf215546Sopenharmony_ci{
204bf215546Sopenharmony_ci   struct virtio_device *virtio_dev = to_virtio_device(dev);
205bf215546Sopenharmony_ci
206bf215546Sopenharmony_ci   simple_mtx_lock(&virtio_dev->rsp_lock);
207bf215546Sopenharmony_ci
208bf215546Sopenharmony_ci   /* One would like to do this in virtio_device_new(), but we'd
209bf215546Sopenharmony_ci    * have to bypass/reinvent fd_bo_new()..
210bf215546Sopenharmony_ci    */
211bf215546Sopenharmony_ci   if (unlikely(!virtio_dev->shmem)) {
212bf215546Sopenharmony_ci      virtio_dev->shmem_bo = fd_bo_new(dev, 0x4000,
213bf215546Sopenharmony_ci                                       _FD_BO_VIRTIO_SHM, "shmem");
214bf215546Sopenharmony_ci      virtio_dev->shmem = fd_bo_map(virtio_dev->shmem_bo);
215bf215546Sopenharmony_ci      virtio_dev->shmem_bo->bo_reuse = NO_CACHE;
216bf215546Sopenharmony_ci
217bf215546Sopenharmony_ci      uint32_t offset = virtio_dev->shmem->rsp_mem_offset;
218bf215546Sopenharmony_ci      virtio_dev->rsp_mem_len = fd_bo_size(virtio_dev->shmem_bo) - offset;
219bf215546Sopenharmony_ci      virtio_dev->rsp_mem = &((uint8_t *)virtio_dev->shmem)[offset];
220bf215546Sopenharmony_ci   }
221bf215546Sopenharmony_ci
222bf215546Sopenharmony_ci   simple_mtx_unlock(&virtio_dev->rsp_lock);
223bf215546Sopenharmony_ci}
224bf215546Sopenharmony_ci
225bf215546Sopenharmony_cistruct fd_pipe *
226bf215546Sopenharmony_civirtio_pipe_new(struct fd_device *dev, enum fd_pipe_id id, uint32_t prio)
227bf215546Sopenharmony_ci{
228bf215546Sopenharmony_ci   static const uint32_t pipe_id[] = {
229bf215546Sopenharmony_ci      [FD_PIPE_3D] = MSM_PIPE_3D0,
230bf215546Sopenharmony_ci      [FD_PIPE_2D] = MSM_PIPE_2D0,
231bf215546Sopenharmony_ci   };
232bf215546Sopenharmony_ci   struct virtio_device *virtio_dev = to_virtio_device(dev);
233bf215546Sopenharmony_ci   struct virtio_pipe *virtio_pipe = NULL;
234bf215546Sopenharmony_ci   struct fd_pipe *pipe = NULL;
235bf215546Sopenharmony_ci
236bf215546Sopenharmony_ci   init_shmem(dev);
237bf215546Sopenharmony_ci
238bf215546Sopenharmony_ci   virtio_pipe = calloc(1, sizeof(*virtio_pipe));
239bf215546Sopenharmony_ci   if (!virtio_pipe) {
240bf215546Sopenharmony_ci      ERROR_MSG("allocation failed");
241bf215546Sopenharmony_ci      goto fail;
242bf215546Sopenharmony_ci   }
243bf215546Sopenharmony_ci
244bf215546Sopenharmony_ci   pipe = &virtio_pipe->base;
245bf215546Sopenharmony_ci
246bf215546Sopenharmony_ci   pipe->funcs = &funcs;
247bf215546Sopenharmony_ci
248bf215546Sopenharmony_ci   /* initialize before get_param(): */
249bf215546Sopenharmony_ci   pipe->dev = dev;
250bf215546Sopenharmony_ci   virtio_pipe->pipe = pipe_id[id];
251bf215546Sopenharmony_ci
252bf215546Sopenharmony_ci   virtio_pipe->gpu_id = virtio_dev->caps.u.msm.gpu_id;
253bf215546Sopenharmony_ci   virtio_pipe->gmem = virtio_dev->caps.u.msm.gmem_size;
254bf215546Sopenharmony_ci   virtio_pipe->gmem_base = virtio_dev->caps.u.msm.gmem_base;
255bf215546Sopenharmony_ci   virtio_pipe->chip_id = virtio_dev->caps.u.msm.chip_id;
256bf215546Sopenharmony_ci
257bf215546Sopenharmony_ci
258bf215546Sopenharmony_ci   if (!(virtio_pipe->gpu_id || virtio_pipe->chip_id))
259bf215546Sopenharmony_ci      goto fail;
260bf215546Sopenharmony_ci
261bf215546Sopenharmony_ci   util_queue_init(&virtio_pipe->retire_queue, "rq", 8, 1,
262bf215546Sopenharmony_ci                   UTIL_QUEUE_INIT_RESIZE_IF_FULL, NULL);
263bf215546Sopenharmony_ci
264bf215546Sopenharmony_ci   INFO_MSG("Pipe Info:");
265bf215546Sopenharmony_ci   INFO_MSG(" GPU-id:          %d", virtio_pipe->gpu_id);
266bf215546Sopenharmony_ci   INFO_MSG(" Chip-id:         0x%016"PRIx64, virtio_pipe->chip_id);
267bf215546Sopenharmony_ci   INFO_MSG(" GMEM size:       0x%08x", virtio_pipe->gmem);
268bf215546Sopenharmony_ci
269bf215546Sopenharmony_ci   if (open_submitqueue(pipe, prio))
270bf215546Sopenharmony_ci      goto fail;
271bf215546Sopenharmony_ci
272bf215546Sopenharmony_ci   fd_pipe_sp_ringpool_init(pipe);
273bf215546Sopenharmony_ci
274bf215546Sopenharmony_ci   return pipe;
275bf215546Sopenharmony_cifail:
276bf215546Sopenharmony_ci   if (pipe)
277bf215546Sopenharmony_ci      fd_pipe_del(pipe);
278bf215546Sopenharmony_ci   return NULL;
279bf215546Sopenharmony_ci}
280