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 31struct v3dv_bo; 32struct v3dv_job; 33struct v3dv_cl; 34 35void 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 */ 41struct v3dv_cl_out; 42 43/** A reference to a BO used in the CL packing functions */ 44struct v3dv_cl_reloc { 45 struct v3dv_bo *bo; 46 uint32_t offset; 47}; 48 49static inline void 50pack_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 59struct 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 68static 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 76static inline uint32_t 77v3dv_cl_offset(struct v3dv_cl *cl) 78{ 79 return (char *)cl->next - (char *)cl->base; 80} 81 82static inline struct v3dv_cl_reloc 83v3dv_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 92static inline struct v3dv_cl_reloc 93v3dv_cl_get_address(struct v3dv_cl *cl) 94{ 95 return (struct v3dv_cl_reloc){ .bo = cl->bo, .offset = v3dv_cl_offset(cl) }; 96} 97 98void v3dv_cl_init(struct v3dv_job *job, struct v3dv_cl *cl); 99void v3dv_cl_destroy(struct v3dv_cl *cl); 100 101static inline struct v3dv_cl_out * 102cl_start(struct v3dv_cl *cl) 103{ 104 return cl->next; 105} 106 107static inline void 108cl_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 114static inline void 115cl_advance(struct v3dv_cl_out **cl, uint32_t n) 116{ 117 (*cl) = (struct v3dv_cl_out *)((char *)(*cl) + n); 118} 119 120static inline void 121cl_aligned_u32(struct v3dv_cl_out **cl, uint32_t n) 122{ 123 *(uint32_t *)(*cl) = n; 124 cl_advance(cl, 4); 125} 126 127static inline void 128cl_aligned_f(struct v3dv_cl_out **cl, float f) 129{ 130 cl_aligned_u32(cl, fui(f)); 131} 132 133static inline void 134cl_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 143uint32_t v3dv_cl_ensure_space(struct v3dv_cl *cl, uint32_t space, uint32_t alignment); 144void 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 */ 211static inline void 212cl_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