1/*
2 * Copyright © 2018 Intel Corporation
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
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24#ifndef INTEL_GEM_H
25#define INTEL_GEM_H
26
27#include "drm-uapi/i915_drm.h"
28
29#include <assert.h>
30#include <errno.h>
31#include <stdbool.h>
32#include <stdint.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <sys/ioctl.h>
36
37static inline uint64_t
38intel_canonical_address(uint64_t v)
39{
40   /* From the Broadwell PRM Vol. 2a, MI_LOAD_REGISTER_MEM::MemoryAddress:
41    *
42    *    "This field specifies the address of the memory location where the
43    *    register value specified in the DWord above will read from. The
44    *    address specifies the DWord location of the data. Range =
45    *    GraphicsVirtualAddress[63:2] for a DWord register GraphicsAddress
46    *    [63:48] are ignored by the HW and assumed to be in correct
47    *    canonical form [63:48] == [47]."
48    */
49   const int shift = 63 - 47;
50   return (int64_t)(v << shift) >> shift;
51}
52
53/**
54 * This returns a 48-bit address with the high 16 bits zeroed.
55 *
56 * It's the opposite of intel_canonicalize_address.
57 */
58static inline uint64_t
59intel_48b_address(uint64_t v)
60{
61   const int shift = 63 - 47;
62   return (uint64_t)(v << shift) >> shift;
63}
64
65/**
66 * Call ioctl, restarting if it is interrupted
67 */
68static inline int
69intel_ioctl(int fd, unsigned long request, void *arg)
70{
71    int ret;
72
73    do {
74        ret = ioctl(fd, request, arg);
75    } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
76    return ret;
77}
78
79static inline uint64_t
80intel_read_gpu_timestamp(int fd)
81{
82   struct drm_i915_reg_read reg_read = {};
83   const uint64_t render_ring_timestamp = 0x2358;
84   reg_read.offset = render_ring_timestamp | I915_REG_READ_8B_WA;
85
86   if (intel_ioctl(fd, DRM_IOCTL_I915_REG_READ, &reg_read) < 0)
87      return 0;
88
89   return reg_read.val;
90}
91
92/**
93 * A wrapper around DRM_IOCTL_I915_QUERY
94 *
95 * Unfortunately, the error semantics of this ioctl are rather annoying so
96 * it's better to have a common helper.
97 */
98static inline int
99intel_i915_query_flags(int fd, uint64_t query_id, uint32_t flags,
100                       void *buffer, int32_t *buffer_len)
101{
102   struct drm_i915_query_item item = {
103      .query_id = query_id,
104      .length = *buffer_len,
105      .flags = flags,
106      .data_ptr = (uintptr_t)buffer,
107   };
108
109   struct drm_i915_query args = {
110      .num_items = 1,
111      .flags = 0,
112      .items_ptr = (uintptr_t)&item,
113   };
114
115   int ret = intel_ioctl(fd, DRM_IOCTL_I915_QUERY, &args);
116   if (ret != 0)
117      return -errno;
118   else if (item.length < 0)
119      return item.length;
120
121   *buffer_len = item.length;
122   return 0;
123}
124
125static inline int
126intel_i915_query(int fd, uint64_t query_id, void *buffer,
127                 int32_t *buffer_len)
128{
129   return intel_i915_query_flags(fd, query_id, 0, buffer, buffer_len);
130}
131
132/**
133 * Query for the given data, allocating as needed
134 *
135 * The caller is responsible for freeing the returned pointer.
136 */
137static inline void *
138intel_i915_query_alloc(int fd, uint64_t query_id, int32_t *query_length)
139{
140   if (query_length)
141      *query_length = 0;
142
143   int32_t length = 0;
144   int ret = intel_i915_query(fd, query_id, NULL, &length);
145   if (ret < 0)
146      return NULL;
147
148   void *data = calloc(1, length);
149   assert(data != NULL); /* This shouldn't happen in practice */
150   if (data == NULL)
151      return NULL;
152
153   ret = intel_i915_query(fd, query_id, data, &length);
154   assert(ret == 0); /* We should have caught the error above */
155   if (ret < 0) {
156      free(data);
157      return NULL;
158   }
159
160   if (query_length)
161      *query_length = length;
162
163   return data;
164}
165
166bool intel_gem_supports_syncobj_wait(int fd);
167
168int intel_gem_count_engines(const struct drm_i915_query_engine_info *info,
169                            enum drm_i915_gem_engine_class engine_class);
170int intel_gem_create_context_engines(int fd,
171                                     const struct drm_i915_query_engine_info *info,
172                                     int num_engines, uint16_t *engine_classes);
173
174#endif /* INTEL_GEM_H */
175