1/*
2 * Copyright © 2022 Imagination Technologies Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * 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 THE
18 * 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
24#include <assert.h>
25#include <stdbool.h>
26#include <stdint.h>
27#include <xf86drm.h>
28
29#include "hwdef/rogue_hw_utils.h"
30#include "pvr_csb.h"
31#include "pvr_device_info.h"
32#include "pvr_private.h"
33#include "pvr_srv.h"
34#include "pvr_srv_bo.h"
35#include "pvr_srv_bridge.h"
36#include "pvr_srv_job_compute.h"
37#include "pvr_srv_job_render.h"
38#include "pvr_srv_job_transfer.h"
39#include "pvr_srv_public.h"
40#include "pvr_srv_sync.h"
41#include "pvr_srv_job_null.h"
42#include "pvr_types.h"
43#include "pvr_winsys.h"
44#include "pvr_winsys_helper.h"
45#include "util/log.h"
46#include "util/macros.h"
47#include "util/os_misc.h"
48#include "vk_log.h"
49
50/* Amount of space used to hold sync prim values (in bytes). */
51#define PVR_SRV_SYNC_PRIM_VALUE_SIZE 4U
52
53static VkResult pvr_srv_heap_init(
54   struct pvr_srv_winsys *srv_ws,
55   struct pvr_srv_winsys_heap *srv_heap,
56   uint32_t heap_idx,
57   const struct pvr_winsys_static_data_offsets *const static_data_offsets)
58{
59   pvr_dev_addr_t base_address;
60   uint32_t log2_page_size;
61   uint64_t reserved_size;
62   VkResult result;
63   uint64_t size;
64
65   result = pvr_srv_get_heap_details(srv_ws->render_fd,
66                                     heap_idx,
67                                     0,
68                                     NULL,
69                                     &base_address,
70                                     &size,
71                                     &reserved_size,
72                                     &log2_page_size);
73   if (result != VK_SUCCESS)
74      return result;
75
76   result = pvr_winsys_helper_winsys_heap_init(&srv_ws->base,
77                                               base_address,
78                                               size,
79                                               base_address,
80                                               reserved_size,
81                                               log2_page_size,
82                                               static_data_offsets,
83                                               &srv_heap->base);
84   if (result != VK_SUCCESS)
85      return result;
86
87   assert(srv_heap->base.page_size == srv_ws->base.page_size);
88   assert(srv_heap->base.log2_page_size == srv_ws->base.log2_page_size);
89   assert(srv_heap->base.reserved_size % PVR_SRV_RESERVED_SIZE_GRANULARITY ==
90          0);
91
92   /* Create server-side counterpart of Device Memory heap */
93   result = pvr_srv_int_heap_create(srv_ws->render_fd,
94                                    srv_heap->base.base_addr,
95                                    srv_heap->base.size,
96                                    srv_heap->base.log2_page_size,
97                                    srv_ws->server_memctx,
98                                    &srv_heap->server_heap);
99   if (result != VK_SUCCESS) {
100      pvr_winsys_helper_winsys_heap_finish(&srv_heap->base);
101      return result;
102   }
103
104   return VK_SUCCESS;
105}
106
107static bool pvr_srv_heap_finish(struct pvr_srv_winsys *srv_ws,
108                                struct pvr_srv_winsys_heap *srv_heap)
109{
110   if (!pvr_winsys_helper_winsys_heap_finish(&srv_heap->base))
111      return false;
112
113   pvr_srv_int_heap_destroy(srv_ws->render_fd, srv_heap->server_heap);
114
115   return true;
116}
117
118static VkResult pvr_srv_memctx_init(struct pvr_srv_winsys *srv_ws)
119{
120   const struct pvr_winsys_static_data_offsets
121      general_heap_static_data_offsets = {
122         .yuv_csc = FWIF_GENERAL_HEAP_YUV_CSC_OFFSET_BYTES,
123      };
124   const struct pvr_winsys_static_data_offsets pds_heap_static_data_offsets = {
125      .eot = FWIF_PDS_HEAP_EOT_OFFSET_BYTES,
126      .vdm_sync = FWIF_PDS_HEAP_VDM_SYNC_OFFSET_BYTES,
127   };
128   const struct pvr_winsys_static_data_offsets usc_heap_static_data_offsets = {
129      .vdm_sync = FWIF_USC_HEAP_VDM_SYNC_OFFSET_BYTES,
130   };
131   const struct pvr_winsys_static_data_offsets no_static_data_offsets = { 0 };
132
133   char heap_name[PVR_SRV_DEVMEM_HEAPNAME_MAXLENGTH];
134   int transfer_3d_heap_idx = -1;
135   int vis_test_heap_idx = -1;
136   int general_heap_idx = -1;
137   int rgn_hdr_heap_idx = -1;
138   int pds_heap_idx = -1;
139   int usc_heap_idx = -1;
140   uint32_t heap_count;
141   VkResult result;
142
143   result = pvr_srv_int_ctx_create(srv_ws->render_fd,
144                                   &srv_ws->server_memctx,
145                                   &srv_ws->server_memctx_data);
146   if (result != VK_SUCCESS)
147      return result;
148
149   os_get_page_size(&srv_ws->base.page_size);
150   srv_ws->base.log2_page_size = util_logbase2(srv_ws->base.page_size);
151
152   result = pvr_srv_get_heap_count(srv_ws->render_fd, &heap_count);
153   if (result != VK_SUCCESS)
154      goto err_pvr_srv_int_ctx_destroy;
155
156   assert(heap_count > 0);
157
158   for (uint32_t i = 0; i < heap_count; i++) {
159      result = pvr_srv_get_heap_details(srv_ws->render_fd,
160                                        i,
161                                        sizeof(heap_name),
162                                        heap_name,
163                                        NULL,
164                                        NULL,
165                                        NULL,
166                                        NULL);
167      if (result != VK_SUCCESS)
168         goto err_pvr_srv_int_ctx_destroy;
169
170      if (general_heap_idx == -1 &&
171          strncmp(heap_name,
172                  PVR_SRV_GENERAL_HEAP_IDENT,
173                  sizeof(PVR_SRV_GENERAL_HEAP_IDENT)) == 0) {
174         general_heap_idx = i;
175      } else if (pds_heap_idx == -1 &&
176                 strncmp(heap_name,
177                         PVR_SRV_PDSCODEDATA_HEAP_IDENT,
178                         sizeof(PVR_SRV_PDSCODEDATA_HEAP_IDENT)) == 0) {
179         pds_heap_idx = i;
180      } else if (rgn_hdr_heap_idx == -1 &&
181                 strncmp(heap_name,
182                         PVR_SRV_RGNHDR_BRN_63142_HEAP_IDENT,
183                         sizeof(PVR_SRV_RGNHDR_BRN_63142_HEAP_IDENT)) == 0) {
184         rgn_hdr_heap_idx = i;
185      } else if (transfer_3d_heap_idx == -1 &&
186                 strncmp(heap_name,
187                         PVR_SRV_TRANSFER_3D_HEAP_IDENT,
188                         sizeof(PVR_SRV_TRANSFER_3D_HEAP_IDENT)) == 0) {
189         transfer_3d_heap_idx = i;
190      } else if (usc_heap_idx == -1 &&
191                 strncmp(heap_name,
192                         PVR_SRV_USCCODE_HEAP_IDENT,
193                         sizeof(PVR_SRV_USCCODE_HEAP_IDENT)) == 0) {
194         usc_heap_idx = i;
195      } else if (vis_test_heap_idx == -1 &&
196                 strncmp(heap_name,
197                         PVR_SRV_VISIBILITY_TEST_HEAP_IDENT,
198                         sizeof(PVR_SRV_VISIBILITY_TEST_HEAP_IDENT)) == 0) {
199         vis_test_heap_idx = i;
200      }
201   }
202
203   /* Check for and initialise required heaps. */
204   if (general_heap_idx == -1 || pds_heap_idx == -1 ||
205       transfer_3d_heap_idx == -1 || usc_heap_idx == -1 ||
206       vis_test_heap_idx == -1) {
207      result = vk_error(NULL, VK_ERROR_INITIALIZATION_FAILED);
208      goto err_pvr_srv_int_ctx_destroy;
209   }
210
211   result = pvr_srv_heap_init(srv_ws,
212                              &srv_ws->general_heap,
213                              general_heap_idx,
214                              &general_heap_static_data_offsets);
215   if (result != VK_SUCCESS)
216      goto err_pvr_srv_int_ctx_destroy;
217
218   result = pvr_srv_heap_init(srv_ws,
219                              &srv_ws->pds_heap,
220                              pds_heap_idx,
221                              &pds_heap_static_data_offsets);
222   if (result != VK_SUCCESS)
223      goto err_pvr_srv_heap_finish_general;
224
225   result = pvr_srv_heap_init(srv_ws,
226                              &srv_ws->transfer_3d_heap,
227                              transfer_3d_heap_idx,
228                              &no_static_data_offsets);
229   if (result != VK_SUCCESS)
230      goto err_pvr_srv_heap_finish_pds;
231
232   result = pvr_srv_heap_init(srv_ws,
233                              &srv_ws->usc_heap,
234                              usc_heap_idx,
235                              &usc_heap_static_data_offsets);
236   if (result != VK_SUCCESS)
237      goto err_pvr_srv_heap_finish_transfer_3d;
238
239   result = pvr_srv_heap_init(srv_ws,
240                              &srv_ws->vis_test_heap,
241                              vis_test_heap_idx,
242                              &no_static_data_offsets);
243   if (result != VK_SUCCESS)
244      goto err_pvr_srv_heap_finish_usc;
245
246   /* Check for and set up optional heaps. */
247   if (rgn_hdr_heap_idx != -1) {
248      result = pvr_srv_heap_init(srv_ws,
249                                 &srv_ws->rgn_hdr_heap,
250                                 rgn_hdr_heap_idx,
251                                 &no_static_data_offsets);
252      if (result != VK_SUCCESS)
253         goto err_pvr_srv_heap_finish_vis_test;
254
255      srv_ws->rgn_hdr_heap_present = true;
256   } else {
257      srv_ws->rgn_hdr_heap_present = false;
258   }
259
260   result =
261      pvr_winsys_helper_allocate_static_memory(&srv_ws->base,
262                                               pvr_srv_heap_alloc_reserved,
263                                               &srv_ws->general_heap.base,
264                                               &srv_ws->pds_heap.base,
265                                               &srv_ws->usc_heap.base,
266                                               &srv_ws->general_vma,
267                                               &srv_ws->pds_vma,
268                                               &srv_ws->usc_vma);
269   if (result != VK_SUCCESS)
270      goto err_pvr_srv_heap_finish_rgn_hdr;
271
272   result = pvr_winsys_helper_fill_static_memory(&srv_ws->base,
273                                                 srv_ws->general_vma,
274                                                 srv_ws->pds_vma,
275                                                 srv_ws->usc_vma);
276   if (result != VK_SUCCESS)
277      goto err_pvr_srv_free_static_memory;
278
279   return VK_SUCCESS;
280
281err_pvr_srv_free_static_memory:
282   pvr_winsys_helper_free_static_memory(srv_ws->general_vma,
283                                        srv_ws->pds_vma,
284                                        srv_ws->usc_vma);
285
286err_pvr_srv_heap_finish_rgn_hdr:
287   if (srv_ws->rgn_hdr_heap_present)
288      pvr_srv_heap_finish(srv_ws, &srv_ws->rgn_hdr_heap);
289
290err_pvr_srv_heap_finish_vis_test:
291   pvr_srv_heap_finish(srv_ws, &srv_ws->vis_test_heap);
292
293err_pvr_srv_heap_finish_usc:
294   pvr_srv_heap_finish(srv_ws, &srv_ws->usc_heap);
295
296err_pvr_srv_heap_finish_transfer_3d:
297   pvr_srv_heap_finish(srv_ws, &srv_ws->transfer_3d_heap);
298
299err_pvr_srv_heap_finish_pds:
300   pvr_srv_heap_finish(srv_ws, &srv_ws->pds_heap);
301
302err_pvr_srv_heap_finish_general:
303   pvr_srv_heap_finish(srv_ws, &srv_ws->general_heap);
304
305err_pvr_srv_int_ctx_destroy:
306   pvr_srv_int_ctx_destroy(srv_ws->render_fd, srv_ws->server_memctx);
307
308   return result;
309}
310
311static void pvr_srv_memctx_finish(struct pvr_srv_winsys *srv_ws)
312{
313   pvr_winsys_helper_free_static_memory(srv_ws->general_vma,
314                                        srv_ws->pds_vma,
315                                        srv_ws->usc_vma);
316
317   if (srv_ws->rgn_hdr_heap_present) {
318      if (!pvr_srv_heap_finish(srv_ws, &srv_ws->rgn_hdr_heap)) {
319         vk_errorf(NULL,
320                   VK_ERROR_UNKNOWN,
321                   "Region header heap in use, can not deinit");
322      }
323   }
324
325   if (!pvr_srv_heap_finish(srv_ws, &srv_ws->vis_test_heap)) {
326      vk_errorf(NULL,
327                VK_ERROR_UNKNOWN,
328                "Visibility test heap in use, can not deinit");
329   }
330
331   if (!pvr_srv_heap_finish(srv_ws, &srv_ws->usc_heap))
332      vk_errorf(NULL, VK_ERROR_UNKNOWN, "USC heap in use, can not deinit");
333
334   if (!pvr_srv_heap_finish(srv_ws, &srv_ws->transfer_3d_heap)) {
335      vk_errorf(NULL,
336                VK_ERROR_UNKNOWN,
337                "Transfer 3D heap in use, can not deinit");
338   }
339
340   if (!pvr_srv_heap_finish(srv_ws, &srv_ws->pds_heap))
341      vk_errorf(NULL, VK_ERROR_UNKNOWN, "PDS heap in use, can not deinit");
342
343   if (!pvr_srv_heap_finish(srv_ws, &srv_ws->general_heap)) {
344      vk_errorf(NULL, VK_ERROR_UNKNOWN, "General heap in use, can not deinit");
345   }
346
347   pvr_srv_int_ctx_destroy(srv_ws->render_fd, srv_ws->server_memctx);
348}
349
350static VkResult pvr_srv_sync_prim_block_init(struct pvr_srv_winsys *srv_ws)
351{
352   /* We don't currently make use of this value, but we're required to provide
353    * a valid pointer to pvr_srv_alloc_sync_primitive_block.
354    */
355   void *sync_block_pmr;
356
357   return pvr_srv_alloc_sync_primitive_block(srv_ws->render_fd,
358                                             &srv_ws->sync_block_handle,
359                                             &sync_block_pmr,
360                                             &srv_ws->sync_block_size,
361                                             &srv_ws->sync_block_fw_addr);
362}
363
364static void pvr_srv_sync_prim_block_finish(struct pvr_srv_winsys *srv_ws)
365{
366   pvr_srv_free_sync_primitive_block(srv_ws->render_fd,
367                                     srv_ws->sync_block_handle);
368   srv_ws->sync_block_handle = NULL;
369}
370
371static void pvr_srv_winsys_destroy(struct pvr_winsys *ws)
372{
373   struct pvr_srv_winsys *srv_ws = to_pvr_srv_winsys(ws);
374   int fd = srv_ws->render_fd;
375
376   pvr_srv_sync_prim_block_finish(srv_ws);
377   pvr_srv_memctx_finish(srv_ws);
378   vk_free(srv_ws->alloc, srv_ws);
379   pvr_srv_connection_destroy(fd);
380}
381
382static uint64_t
383pvr_srv_get_min_free_list_size(const struct pvr_device_info *dev_info)
384{
385   uint64_t min_num_pages;
386
387   if (PVR_HAS_FEATURE(dev_info, roguexe)) {
388      if (PVR_HAS_QUIRK(dev_info, 66011))
389         min_num_pages = 40U;
390      else
391         min_num_pages = 25U;
392   } else {
393      min_num_pages = 50U;
394   }
395
396   return min_num_pages << ROGUE_BIF_PM_PHYSICAL_PAGE_SHIFT;
397}
398
399static inline uint64_t
400pvr_srv_get_num_phantoms(const struct pvr_device_info *dev_info)
401{
402   return DIV_ROUND_UP(PVR_GET_FEATURE_VALUE(dev_info, num_clusters, 1U), 4U);
403}
404
405/* Return the total reserved size of partition in dwords. */
406static inline uint64_t pvr_srv_get_total_reserved_partition_size(
407   const struct pvr_device_info *dev_info)
408{
409   uint32_t tile_size_x = PVR_GET_FEATURE_VALUE(dev_info, tile_size_x, 0);
410   uint32_t tile_size_y = PVR_GET_FEATURE_VALUE(dev_info, tile_size_y, 0);
411   uint32_t max_partitions = PVR_GET_FEATURE_VALUE(dev_info, max_partitions, 0);
412
413   if (tile_size_x == 16 && tile_size_y == 16) {
414      return tile_size_x * tile_size_y * max_partitions *
415             PVR_GET_FEATURE_VALUE(dev_info,
416                                   usc_min_output_registers_per_pix,
417                                   0);
418   }
419
420   return max_partitions * 1024U;
421}
422
423static inline uint64_t
424pvr_srv_get_reserved_shared_size(const struct pvr_device_info *dev_info)
425{
426   uint32_t common_store_size_in_dwords =
427      PVR_GET_FEATURE_VALUE(dev_info,
428                            common_store_size_in_dwords,
429                            512U * 4U * 4U);
430   uint32_t reserved_shared_size =
431      common_store_size_in_dwords - (256U * 4U) -
432      pvr_srv_get_total_reserved_partition_size(dev_info);
433
434   if (PVR_HAS_QUIRK(dev_info, 44079)) {
435      uint32_t common_store_split_point = (768U * 4U * 4U);
436
437      return MIN2(common_store_split_point - (256U * 4U), reserved_shared_size);
438   }
439
440   return reserved_shared_size;
441}
442
443static inline uint64_t
444pvr_srv_get_max_coeffs(const struct pvr_device_info *dev_info)
445{
446   uint32_t max_coeff_additional_portion = ROGUE_MAX_VERTEX_SHARED_REGISTERS;
447   uint32_t pending_allocation_shared_regs = 2U * 1024U;
448   uint32_t pending_allocation_coeff_regs = 0U;
449   uint32_t num_phantoms = pvr_srv_get_num_phantoms(dev_info);
450   uint32_t tiles_in_flight =
451      PVR_GET_FEATURE_VALUE(dev_info, isp_max_tiles_in_flight, 0);
452   uint32_t max_coeff_pixel_portion =
453      DIV_ROUND_UP(tiles_in_flight, num_phantoms);
454
455   max_coeff_pixel_portion *= ROGUE_MAX_PIXEL_SHARED_REGISTERS;
456
457   /* Compute tasks on cores with BRN48492 and without compute overlap may lock
458    * up without two additional lines of coeffs.
459    */
460   if (PVR_HAS_QUIRK(dev_info, 48492) &&
461       !PVR_HAS_FEATURE(dev_info, compute_overlap)) {
462      pending_allocation_coeff_regs = 2U * 1024U;
463   }
464
465   if (PVR_HAS_ERN(dev_info, 38748))
466      pending_allocation_shared_regs = 0U;
467
468   if (PVR_HAS_ERN(dev_info, 38020)) {
469      max_coeff_additional_portion +=
470         rogue_max_compute_shared_registers(dev_info);
471   }
472
473   return pvr_srv_get_reserved_shared_size(dev_info) +
474          pending_allocation_coeff_regs -
475          (max_coeff_pixel_portion + max_coeff_additional_portion +
476           pending_allocation_shared_regs);
477}
478
479static inline uint64_t
480pvr_srv_get_cdm_max_local_mem_size_regs(const struct pvr_device_info *dev_info)
481{
482   uint32_t available_coeffs_in_dwords = pvr_srv_get_max_coeffs(dev_info);
483
484   if (PVR_HAS_QUIRK(dev_info, 48492) && PVR_HAS_FEATURE(dev_info, roguexe) &&
485       !PVR_HAS_FEATURE(dev_info, compute_overlap)) {
486      /* Driver must not use the 2 reserved lines. */
487      available_coeffs_in_dwords -= ROGUE_CSRM_LINE_SIZE_IN_DWORDS * 2;
488   }
489
490   /* The maximum amount of local memory available to a kernel is the minimum
491    * of the total number of coefficient registers available and the max common
492    * store allocation size which can be made by the CDM.
493    *
494    * If any coeff lines are reserved for tessellation or pixel then we need to
495    * subtract those too.
496    */
497   return MIN2(available_coeffs_in_dwords,
498               ROGUE_MAX_PER_KERNEL_LOCAL_MEM_SIZE_REGS);
499}
500
501static int
502pvr_srv_winsys_device_info_init(struct pvr_winsys *ws,
503                                struct pvr_device_info *dev_info,
504                                struct pvr_device_runtime_info *runtime_info)
505{
506   struct pvr_srv_winsys *srv_ws = to_pvr_srv_winsys(ws);
507   VkResult result;
508   int ret;
509
510   ret = pvr_device_info_init(dev_info, srv_ws->bvnc);
511   if (ret) {
512      mesa_logw("Unsupported BVNC: %u.%u.%u.%u\n",
513                PVR_BVNC_UNPACK_B(srv_ws->bvnc),
514                PVR_BVNC_UNPACK_V(srv_ws->bvnc),
515                PVR_BVNC_UNPACK_N(srv_ws->bvnc),
516                PVR_BVNC_UNPACK_C(srv_ws->bvnc));
517      return ret;
518   }
519
520   runtime_info->min_free_list_size = pvr_srv_get_min_free_list_size(dev_info);
521   runtime_info->reserved_shared_size =
522      pvr_srv_get_reserved_shared_size(dev_info);
523   runtime_info->total_reserved_partition_size =
524      pvr_srv_get_total_reserved_partition_size(dev_info);
525   runtime_info->num_phantoms = pvr_srv_get_num_phantoms(dev_info);
526   runtime_info->max_coeffs = pvr_srv_get_max_coeffs(dev_info);
527   runtime_info->cdm_max_local_mem_size_regs =
528      pvr_srv_get_cdm_max_local_mem_size_regs(dev_info);
529
530   if (PVR_HAS_FEATURE(dev_info, gpu_multicore_support)) {
531      result = pvr_srv_get_multicore_info(srv_ws->render_fd,
532                                          0,
533                                          NULL,
534                                          &runtime_info->core_count);
535      if (result != VK_SUCCESS)
536         return -ENODEV;
537   } else {
538      runtime_info->core_count = 1;
539   }
540
541   return 0;
542}
543
544static void pvr_srv_winsys_get_heaps_info(struct pvr_winsys *ws,
545                                          struct pvr_winsys_heaps *heaps)
546{
547   struct pvr_srv_winsys *srv_ws = to_pvr_srv_winsys(ws);
548
549   heaps->general_heap = &srv_ws->general_heap.base;
550   heaps->pds_heap = &srv_ws->pds_heap.base;
551   heaps->transfer_3d_heap = &srv_ws->transfer_3d_heap.base;
552   heaps->usc_heap = &srv_ws->usc_heap.base;
553   heaps->vis_test_heap = &srv_ws->vis_test_heap.base;
554
555   if (srv_ws->rgn_hdr_heap_present)
556      heaps->rgn_hdr_heap = &srv_ws->rgn_hdr_heap.base;
557   else
558      heaps->rgn_hdr_heap = &srv_ws->general_heap.base;
559}
560
561static const struct pvr_winsys_ops srv_winsys_ops = {
562   .destroy = pvr_srv_winsys_destroy,
563   .device_info_init = pvr_srv_winsys_device_info_init,
564   .get_heaps_info = pvr_srv_winsys_get_heaps_info,
565   .buffer_create = pvr_srv_winsys_buffer_create,
566   .buffer_create_from_fd = pvr_srv_winsys_buffer_create_from_fd,
567   .buffer_destroy = pvr_srv_winsys_buffer_destroy,
568   .buffer_get_fd = pvr_srv_winsys_buffer_get_fd,
569   .buffer_map = pvr_srv_winsys_buffer_map,
570   .buffer_unmap = pvr_srv_winsys_buffer_unmap,
571   .heap_alloc = pvr_srv_winsys_heap_alloc,
572   .heap_free = pvr_srv_winsys_heap_free,
573   .vma_map = pvr_srv_winsys_vma_map,
574   .vma_unmap = pvr_srv_winsys_vma_unmap,
575   .free_list_create = pvr_srv_winsys_free_list_create,
576   .free_list_destroy = pvr_srv_winsys_free_list_destroy,
577   .render_target_dataset_create = pvr_srv_render_target_dataset_create,
578   .render_target_dataset_destroy = pvr_srv_render_target_dataset_destroy,
579   .render_ctx_create = pvr_srv_winsys_render_ctx_create,
580   .render_ctx_destroy = pvr_srv_winsys_render_ctx_destroy,
581   .render_submit = pvr_srv_winsys_render_submit,
582   .compute_ctx_create = pvr_srv_winsys_compute_ctx_create,
583   .compute_ctx_destroy = pvr_srv_winsys_compute_ctx_destroy,
584   .compute_submit = pvr_srv_winsys_compute_submit,
585   .transfer_ctx_create = pvr_srv_winsys_transfer_ctx_create,
586   .transfer_ctx_destroy = pvr_srv_winsys_transfer_ctx_destroy,
587   .transfer_submit = pvr_srv_winsys_transfer_submit,
588   .null_job_submit = pvr_srv_winsys_null_job_submit,
589};
590
591static bool pvr_is_driver_compatible(int render_fd)
592{
593   drmVersionPtr version;
594
595   version = drmGetVersion(render_fd);
596   if (!version)
597      return false;
598
599   assert(strcmp(version->name, "pvr") == 0);
600
601   /* Only the 1.17 driver is supported for now. */
602   if (version->version_major != PVR_SRV_VERSION_MAJ ||
603       version->version_minor != PVR_SRV_VERSION_MIN) {
604      vk_errorf(NULL,
605                VK_ERROR_INCOMPATIBLE_DRIVER,
606                "Unsupported downstream driver version (%u.%u)",
607                version->version_major,
608                version->version_minor);
609      drmFreeVersion(version);
610
611      return false;
612   }
613
614   drmFreeVersion(version);
615
616   return true;
617}
618
619struct pvr_winsys *pvr_srv_winsys_create(int master_fd,
620                                         int render_fd,
621                                         const VkAllocationCallbacks *alloc)
622{
623   struct pvr_srv_winsys *srv_ws;
624   VkResult result;
625   uint64_t bvnc;
626
627   if (!pvr_is_driver_compatible(render_fd))
628      return NULL;
629
630   result = pvr_srv_init_module(render_fd, PVR_SRVKM_MODULE_TYPE_SERVICES);
631   if (result != VK_SUCCESS)
632      return NULL;
633
634   result = pvr_srv_connection_create(render_fd, &bvnc);
635   if (result != VK_SUCCESS)
636      return NULL;
637
638   srv_ws =
639      vk_zalloc(alloc, sizeof(*srv_ws), 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
640   if (!srv_ws) {
641      vk_error(NULL, VK_ERROR_OUT_OF_HOST_MEMORY);
642      goto err_pvr_srv_connection_destroy;
643   }
644
645   srv_ws->base.ops = &srv_winsys_ops;
646   srv_ws->bvnc = bvnc;
647   srv_ws->master_fd = master_fd;
648   srv_ws->render_fd = render_fd;
649   srv_ws->alloc = alloc;
650
651   srv_ws->base.syncobj_type = pvr_srv_sync_type;
652   srv_ws->base.sync_types[0] = &srv_ws->base.syncobj_type;
653   srv_ws->base.sync_types[1] = NULL;
654
655   result = pvr_srv_memctx_init(srv_ws);
656   if (result != VK_SUCCESS)
657      goto err_vk_free_srv_ws;
658
659   result = pvr_srv_sync_prim_block_init(srv_ws);
660   if (result != VK_SUCCESS)
661      goto err_pvr_srv_memctx_finish;
662
663   return &srv_ws->base;
664
665err_pvr_srv_memctx_finish:
666   pvr_srv_memctx_finish(srv_ws);
667
668err_vk_free_srv_ws:
669   vk_free(alloc, srv_ws);
670
671err_pvr_srv_connection_destroy:
672   pvr_srv_connection_destroy(render_fd);
673
674   return NULL;
675}
676
677struct pvr_srv_sync_prim *pvr_srv_sync_prim_alloc(struct pvr_srv_winsys *srv_ws)
678{
679   struct pvr_srv_sync_prim *sync_prim;
680
681   if (p_atomic_read(&srv_ws->sync_block_offset) == srv_ws->sync_block_size) {
682      vk_error(NULL, VK_ERROR_UNKNOWN);
683      return NULL;
684   }
685
686   sync_prim = vk_alloc(srv_ws->alloc,
687                        sizeof(*sync_prim),
688                        8,
689                        VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
690   if (!sync_prim) {
691      vk_error(NULL, VK_ERROR_OUT_OF_HOST_MEMORY);
692      return NULL;
693   }
694
695   /* p_atomic_add_return() returns the new value rather than the old one, so
696    * we have to subtract PVR_SRV_SYNC_PRIM_VALUE_SIZE to get the old value.
697    */
698   sync_prim->offset = p_atomic_add_return(&srv_ws->sync_block_offset,
699                                           PVR_SRV_SYNC_PRIM_VALUE_SIZE);
700   sync_prim->offset -= PVR_SRV_SYNC_PRIM_VALUE_SIZE;
701   if (sync_prim->offset == srv_ws->sync_block_size) {
702      /* FIXME: need to free offset back to srv_ws->sync_block_offset. */
703      vk_free(srv_ws->alloc, sync_prim);
704
705      vk_error(NULL, VK_ERROR_UNKNOWN);
706
707      return NULL;
708   }
709
710   sync_prim->srv_ws = srv_ws;
711
712   return sync_prim;
713}
714
715/* FIXME: Add support for freeing offsets back to the sync block. */
716void pvr_srv_sync_prim_free(struct pvr_srv_sync_prim *sync_prim)
717{
718   if (sync_prim) {
719      struct pvr_srv_winsys *srv_ws = sync_prim->srv_ws;
720
721      vk_free(srv_ws->alloc, sync_prim);
722   }
723}
724