1 /* 2 * Copyright 2019 Raspberry Pi 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 V3DV_CL_H 25 #define V3DV_CL_H 26 27 #include "broadcom/cle/v3d_packet_helpers.h" 28 29 #include "list.h" 30 31 struct v3dv_bo; 32 struct v3dv_job; 33 struct v3dv_cl; 34 35 void v3dv_job_add_bo(struct v3dv_job *job, struct v3dv_bo *bo); 36 37 /** 38 * Undefined structure, used for typechecking that you're passing the pointers 39 * to these functions correctly. 40 */ 41 struct v3dv_cl_out; 42 43 /** A reference to a BO used in the CL packing functions */ 44 struct v3dv_cl_reloc { 45 struct v3dv_bo *bo; 46 uint32_t offset; 47 }; 48 49 static inline void 50 pack_emit_reloc(void *cl, const void *reloc) {} 51 52 #define __gen_user_data struct v3dv_cl 53 #define __gen_address_type struct v3dv_cl_reloc 54 #define __gen_address_offset(reloc) (((reloc)->bo ? (reloc)->bo->offset : 0) + \ 55 (reloc)->offset) 56 #define __gen_emit_reloc cl_pack_emit_reloc 57 #define __gen_unpack_address(cl, s, e) __unpack_address(cl, s, e) 58 59 struct v3dv_cl { 60 void *base; 61 struct v3dv_job *job; 62 struct v3dv_cl_out *next; 63 struct v3dv_bo *bo; 64 uint32_t size; 65 struct list_head bo_list; 66 }; 67 68 static inline struct v3dv_cl_reloc 69 __unpack_address(const uint8_t *cl, uint32_t s, uint32_t e) 70 { 71 struct v3dv_cl_reloc reloc = 72 { NULL, __gen_unpack_uint(cl, s, e) << (31 - (e - s)) }; 73 return reloc; 74 } 75 76 static inline uint32_t 77 v3dv_cl_offset(struct v3dv_cl *cl) 78 { 79 return (char *)cl->next - (char *)cl->base; 80 } 81 82 static inline struct v3dv_cl_reloc 83 v3dv_cl_address(struct v3dv_bo *bo, uint32_t offset) 84 { 85 struct v3dv_cl_reloc reloc = { 86 .bo = bo, 87 .offset = offset, 88 }; 89 return reloc; 90 } 91 92 static inline struct v3dv_cl_reloc 93 v3dv_cl_get_address(struct v3dv_cl *cl) 94 { 95 return (struct v3dv_cl_reloc){ .bo = cl->bo, .offset = v3dv_cl_offset(cl) }; 96 } 97 98 void v3dv_cl_init(struct v3dv_job *job, struct v3dv_cl *cl); 99 void v3dv_cl_destroy(struct v3dv_cl *cl); 100 101 static inline struct v3dv_cl_out * 102 cl_start(struct v3dv_cl *cl) 103 { 104 return cl->next; 105 } 106 107 static inline void 108 cl_end(struct v3dv_cl *cl, struct v3dv_cl_out *next) 109 { 110 cl->next = next; 111 assert(v3dv_cl_offset(cl) <= cl->size); 112 } 113 114 static inline void 115 cl_advance(struct v3dv_cl_out **cl, uint32_t n) 116 { 117 (*cl) = (struct v3dv_cl_out *)((char *)(*cl) + n); 118 } 119 120 static inline void 121 cl_aligned_u32(struct v3dv_cl_out **cl, uint32_t n) 122 { 123 *(uint32_t *)(*cl) = n; 124 cl_advance(cl, 4); 125 } 126 127 static inline void 128 cl_aligned_f(struct v3dv_cl_out **cl, float f) 129 { 130 cl_aligned_u32(cl, fui(f)); 131 } 132 133 static inline void 134 cl_aligned_reloc(struct v3dv_cl *cl, 135 struct v3dv_cl_out **cl_out, 136 struct v3dv_bo *bo, 137 uint32_t offset) 138 { 139 cl_aligned_u32(cl_out, bo->offset + offset); 140 v3dv_job_add_bo(cl->job, bo); 141 } 142 143 uint32_t v3dv_cl_ensure_space(struct v3dv_cl *cl, uint32_t space, uint32_t alignment); 144 void v3dv_cl_ensure_space_with_branch(struct v3dv_cl *cl, uint32_t space); 145 146 /* We redefine ALIGN as a macro as we want to use cl_aligned_packet_length for 147 * struct fields 148 */ 149 #define ALIGN(value, alignment) \ 150 (((value) + (alignment) - 1) & ~((alignment) - 1)) 151 152 #define cl_packet_header(packet) V3DX(packet ## _header) 153 #define cl_packet_length(packet) V3DX(packet ## _length) 154 #define cl_aligned_packet_length(packet, alignment) ALIGN(cl_packet_length(packet), alignment) 155 #define cl_packet_pack(packet) V3DX(packet ## _pack) 156 #define cl_packet_struct(packet) V3DX(packet) 157 158 /* Macro for setting up an emit of a CL struct. A temporary unpacked struct 159 * is created, which you get to set fields in of the form: 160 * 161 * cl_emit(bcl, FLAT_SHADE_FLAGS, flags) { 162 * .flags.flat_shade_flags = 1 << 2, 163 * } 164 * 165 * or default values only can be emitted with just: 166 * 167 * cl_emit(bcl, FLAT_SHADE_FLAGS, flags); 168 * 169 * The trick here is that we make a for loop that will execute the body 170 * (either the block or the ';' after the macro invocation) exactly once. 171 */ 172 #define cl_emit(cl, packet, name) \ 173 for (struct cl_packet_struct(packet) name = { \ 174 cl_packet_header(packet) \ 175 }, \ 176 *_loop_terminate = &name; \ 177 __builtin_expect(_loop_terminate != NULL, 1); \ 178 ({ \ 179 struct v3dv_cl_out *cl_out = cl_start(cl); \ 180 cl_packet_pack(packet)(cl, (uint8_t *)cl_out, &name); \ 181 cl_advance(&cl_out, cl_packet_length(packet)); \ 182 cl_end(cl, cl_out); \ 183 _loop_terminate = NULL; \ 184 })) \ 185 186 #define cl_emit_with_prepacked(cl, packet, prepacked, name) \ 187 for (struct cl_packet_struct(packet) name = { \ 188 cl_packet_header(packet) \ 189 }, \ 190 *_loop_terminate = &name; \ 191 __builtin_expect(_loop_terminate != NULL, 1); \ 192 ({ \ 193 struct v3dv_cl_out *cl_out = cl_start(cl); \ 194 uint8_t packed[cl_packet_length(packet)]; \ 195 cl_packet_pack(packet)(cl, packed, &name); \ 196 for (int _i = 0; _i < cl_packet_length(packet); _i++) \ 197 ((uint8_t *)cl_out)[_i] = packed[_i] | (prepacked)[_i]; \ 198 cl_advance(&cl_out, cl_packet_length(packet)); \ 199 cl_end(cl, cl_out); \ 200 _loop_terminate = NULL; \ 201 })) \ 202 203 /** 204 * Helper function called by the XML-generated pack functions for filling in 205 * an address field in shader records. 206 * 207 * Since we have a private address space as of V3D, our BOs can have lifelong 208 * offsets, and all the kernel needs to know is which BOs need to be paged in 209 * for this exec. 210 */ 211 static inline void 212 cl_pack_emit_reloc(struct v3dv_cl *cl, const struct v3dv_cl_reloc *reloc) 213 { 214 if (reloc->bo) 215 v3dv_job_add_bo(cl->job, reloc->bo); 216 } 217 218 #define cl_emit_prepacked_sized(cl, packet, size) do { \ 219 memcpy((cl)->next, packet, size); \ 220 cl_advance(&(cl)->next, size); \ 221 } while (0) 222 223 #define cl_emit_prepacked(cl, packet) \ 224 cl_emit_prepacked_sized(cl, packet, sizeof(*(packet))) 225 226 #define v3dvx_pack(packed, packet, name) \ 227 for (struct cl_packet_struct(packet) name = { \ 228 cl_packet_header(packet) \ 229 }, \ 230 *_loop_terminate = &name; \ 231 __builtin_expect(_loop_terminate != NULL, 1); \ 232 ({ \ 233 cl_packet_pack(packet)(NULL, (uint8_t *)packed, &name); \ 234 VG(VALGRIND_CHECK_MEM_IS_DEFINED((uint8_t *)packed, \ 235 cl_packet_length(packet))); \ 236 _loop_terminate = NULL; \ 237 })) \ 238 239 #endif /* V3DV_CL_H */ 240