xref: /linux/drivers/gpu/drm/etnaviv/etnaviv_buffer.c (revision c0d6f52f9b62479d61f8cd4faf9fb2f8bce6e301)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2014-2018 Etnaviv Project
4  */
5 
6 #include <drm/drm_drv.h>
7 #include <drm/drm_print.h>
8 
9 #include "etnaviv_cmdbuf.h"
10 #include "etnaviv_gpu.h"
11 #include "etnaviv_gem.h"
12 #include "etnaviv_mmu.h"
13 #include "etnaviv_buffer.h"
14 
15 #include "common.xml.h"
16 #include "state.xml.h"
17 #include "state_blt.xml.h"
18 #include "state_hi.xml.h"
19 #include "state_3d.xml.h"
20 #include "cmdstream.xml.h"
21 
22 #include "etnaviv_flop_reset.h"
23 
24 static void etnaviv_cmd_select_pipe(struct etnaviv_gpu *gpu,
25 	struct etnaviv_cmdbuf *buffer, u8 pipe)
26 {
27 	u32 flush = 0;
28 
29 	lockdep_assert_held(&gpu->lock);
30 
31 	/*
32 	 * This assumes that if we're switching to 2D, we're switching
33 	 * away from 3D, and vice versa.  Hence, if we're switching to
34 	 * the 2D core, we need to flush the 3D depth and color caches,
35 	 * otherwise we need to flush the 2D pixel engine cache.
36 	 */
37 	if (gpu->exec_state == ETNA_PIPE_2D)
38 		flush = VIVS_GL_FLUSH_CACHE_PE2D;
39 	else if (gpu->exec_state == ETNA_PIPE_3D)
40 		flush = VIVS_GL_FLUSH_CACHE_DEPTH | VIVS_GL_FLUSH_CACHE_COLOR;
41 
42 	CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE, flush);
43 	CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
44 	CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
45 
46 	CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT,
47 		       VIVS_GL_PIPE_SELECT_PIPE(pipe));
48 }
49 
50 static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
51 	struct etnaviv_cmdbuf *buf, u32 off, u32 len)
52 {
53 	u32 size = buf->size;
54 	u32 *ptr = buf->vaddr + off;
55 
56 	dev_info(gpu->dev, "virt %p phys 0x%08x free 0x%08x\n",
57 			ptr, etnaviv_cmdbuf_get_va(buf,
58 			&gpu->mmu_context->cmdbuf_mapping) +
59 			off, size - len * 4 - off);
60 
61 	print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
62 			ptr, len * 4, 0);
63 }
64 
65 /*
66  * Safely replace the WAIT of a waitlink with a new command and argument.
67  * The GPU may be executing this WAIT while we're modifying it, so we have
68  * to write it in a specific order to avoid the GPU branching to somewhere
69  * else.  'wl_offset' is the offset to the first byte of the WAIT command.
70  */
71 static void etnaviv_buffer_replace_wait(struct etnaviv_cmdbuf *buffer,
72 	unsigned int wl_offset, u32 cmd, u32 arg)
73 {
74 	u32 *lw = buffer->vaddr + wl_offset;
75 
76 	lw[1] = arg;
77 	mb();
78 	lw[0] = cmd;
79 	mb();
80 }
81 
82 /*
83  * Ensure that there is space in the command buffer to contiguously write
84  * 'cmd_dwords' 64-bit words into the buffer, wrapping if necessary.
85  */
86 static u32 etnaviv_buffer_reserve(struct etnaviv_gpu *gpu,
87 	struct etnaviv_cmdbuf *buffer, unsigned int cmd_dwords)
88 {
89 	if (buffer->user_size + cmd_dwords * sizeof(u64) > buffer->size)
90 		buffer->user_size = 0;
91 
92 	return etnaviv_cmdbuf_get_va(buffer,
93 				     &gpu->mmu_context->cmdbuf_mapping) +
94 	       buffer->user_size;
95 }
96 
97 u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
98 {
99 	struct etnaviv_cmdbuf *buffer = &gpu->buffer;
100 
101 	lockdep_assert_held(&gpu->lock);
102 
103 	/* initialize buffer */
104 	buffer->user_size = 0;
105 
106 	/* Queue in PPU flop reset */
107 	if (etnaviv_flop_reset_ppu_require(&gpu->identity))
108 		etnaviv_flop_reset_ppu_run(gpu);
109 
110 	CMD_WAIT(buffer, gpu->fe_waitcycles);
111 	CMD_LINK(buffer, 2,
112 		 etnaviv_cmdbuf_get_va(buffer, &gpu->mmu_context->cmdbuf_mapping)
113 		 + buffer->user_size - 4);
114 
115 	return buffer->user_size / 8;
116 }
117 
118 u16 etnaviv_buffer_config_mmuv2(struct etnaviv_gpu *gpu, u32 mtlb_addr, u32 safe_addr)
119 {
120 	struct etnaviv_cmdbuf *buffer = &gpu->buffer;
121 
122 	lockdep_assert_held(&gpu->lock);
123 
124 	buffer->user_size = 0;
125 
126 	if (gpu->identity.features & chipFeatures_PIPE_3D) {
127 		CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT,
128 			       VIVS_GL_PIPE_SELECT_PIPE(ETNA_PIPE_3D));
129 		CMD_LOAD_STATE(buffer, VIVS_MMUv2_CONFIGURATION,
130 			mtlb_addr | VIVS_MMUv2_CONFIGURATION_MODE_MODE4_K);
131 		CMD_LOAD_STATE(buffer, VIVS_MMUv2_SAFE_ADDRESS, safe_addr);
132 		CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
133 		CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
134 	}
135 
136 	if (gpu->identity.features & chipFeatures_PIPE_2D) {
137 		CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT,
138 			       VIVS_GL_PIPE_SELECT_PIPE(ETNA_PIPE_2D));
139 		CMD_LOAD_STATE(buffer, VIVS_MMUv2_CONFIGURATION,
140 			mtlb_addr | VIVS_MMUv2_CONFIGURATION_MODE_MODE4_K);
141 		CMD_LOAD_STATE(buffer, VIVS_MMUv2_SAFE_ADDRESS, safe_addr);
142 		CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
143 		CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
144 	}
145 
146 	CMD_END(buffer);
147 
148 	buffer->user_size = ALIGN(buffer->user_size, 8);
149 
150 	return buffer->user_size / 8;
151 }
152 
153 u16 etnaviv_buffer_config_pta(struct etnaviv_gpu *gpu, unsigned short id)
154 {
155 	struct etnaviv_cmdbuf *buffer = &gpu->buffer;
156 
157 	lockdep_assert_held(&gpu->lock);
158 
159 	buffer->user_size = 0;
160 
161 	CMD_LOAD_STATE(buffer, VIVS_MMUv2_PTA_CONFIG,
162 		       VIVS_MMUv2_PTA_CONFIG_INDEX(id));
163 
164 	CMD_END(buffer);
165 
166 	buffer->user_size = ALIGN(buffer->user_size, 8);
167 
168 	return buffer->user_size / 8;
169 }
170 
171 void etnaviv_buffer_end(struct etnaviv_gpu *gpu)
172 {
173 	struct etnaviv_cmdbuf *buffer = &gpu->buffer;
174 	unsigned int waitlink_offset = buffer->user_size - 16;
175 	u32 link_target, flush = 0;
176 	bool has_blt = !!(gpu->identity.minor_features5 &
177 			  chipMinorFeatures5_BLT_ENGINE);
178 
179 	lockdep_assert_held(&gpu->lock);
180 
181 	if (gpu->exec_state == ETNA_PIPE_2D)
182 		flush = VIVS_GL_FLUSH_CACHE_PE2D;
183 	else if (gpu->exec_state == ETNA_PIPE_3D)
184 		flush = VIVS_GL_FLUSH_CACHE_DEPTH |
185 			VIVS_GL_FLUSH_CACHE_COLOR |
186 			VIVS_GL_FLUSH_CACHE_TEXTURE |
187 			VIVS_GL_FLUSH_CACHE_TEXTUREVS |
188 			VIVS_GL_FLUSH_CACHE_SHADER_L2;
189 
190 	if (flush) {
191 		unsigned int dwords = 7;
192 
193 		if (has_blt)
194 			dwords += 10;
195 
196 		link_target = etnaviv_buffer_reserve(gpu, buffer, dwords);
197 
198 		CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
199 		CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
200 		if (has_blt) {
201 			CMD_LOAD_STATE(buffer, VIVS_BLT_ENABLE, 0x1);
202 			CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_BLT);
203 			CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_BLT);
204 			CMD_LOAD_STATE(buffer, VIVS_BLT_ENABLE, 0x0);
205 		}
206 		CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE, flush);
207 		if (gpu->exec_state == ETNA_PIPE_3D) {
208 			if (has_blt) {
209 				CMD_LOAD_STATE(buffer, VIVS_BLT_ENABLE, 0x1);
210 				CMD_LOAD_STATE(buffer, VIVS_BLT_SET_COMMAND, 0x1);
211 				CMD_LOAD_STATE(buffer, VIVS_BLT_ENABLE, 0x0);
212 			} else {
213 				CMD_LOAD_STATE(buffer, VIVS_TS_FLUSH_CACHE,
214 					       VIVS_TS_FLUSH_CACHE_FLUSH);
215 			}
216 		}
217 		CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
218 		CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
219 		if (has_blt) {
220 			CMD_LOAD_STATE(buffer, VIVS_BLT_ENABLE, 0x1);
221 			CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_BLT);
222 			CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_BLT);
223 			CMD_LOAD_STATE(buffer, VIVS_BLT_ENABLE, 0x0);
224 		}
225 		CMD_END(buffer);
226 
227 		etnaviv_buffer_replace_wait(buffer, waitlink_offset,
228 					    VIV_FE_LINK_HEADER_OP_LINK |
229 					    VIV_FE_LINK_HEADER_PREFETCH(dwords),
230 					    link_target);
231 	} else {
232 		/* Replace the last link-wait with an "END" command */
233 		etnaviv_buffer_replace_wait(buffer, waitlink_offset,
234 					    VIV_FE_END_HEADER_OP_END, 0);
235 	}
236 }
237 
238 /* Append a 'sync point' to the ring buffer. */
239 void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event)
240 {
241 	struct etnaviv_cmdbuf *buffer = &gpu->buffer;
242 	unsigned int waitlink_offset = buffer->user_size - 16;
243 	u32 dwords, target;
244 
245 	lockdep_assert_held(&gpu->lock);
246 
247 	/*
248 	 * We need at most 3 dwords in the return target:
249 	 * 1 event + 1 end + 1 wait + 1 link.
250 	 */
251 	dwords = 4;
252 	target = etnaviv_buffer_reserve(gpu, buffer, dwords);
253 
254 	/* Signal sync point event */
255 	CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) |
256 		       VIVS_GL_EVENT_FROM_PE);
257 
258 	/* Stop the FE to 'pause' the GPU */
259 	CMD_END(buffer);
260 
261 	/* Append waitlink */
262 	CMD_WAIT(buffer, gpu->fe_waitcycles);
263 	CMD_LINK(buffer, 2,
264 		 etnaviv_cmdbuf_get_va(buffer, &gpu->mmu_context->cmdbuf_mapping)
265 		 + buffer->user_size - 4);
266 
267 	/*
268 	 * Kick off the 'sync point' command by replacing the previous
269 	 * WAIT with a link to the address in the ring buffer.
270 	 */
271 	etnaviv_buffer_replace_wait(buffer, waitlink_offset,
272 				    VIV_FE_LINK_HEADER_OP_LINK |
273 				    VIV_FE_LINK_HEADER_PREFETCH(dwords),
274 				    target);
275 }
276 
277 /* Append a command buffer to the ring buffer. */
278 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
279 	struct etnaviv_iommu_context *mmu_context, unsigned int event,
280 	struct etnaviv_cmdbuf *cmdbuf)
281 {
282 	struct etnaviv_cmdbuf *buffer = &gpu->buffer;
283 	unsigned int waitlink_offset = buffer->user_size - 16;
284 	u32 return_target, return_dwords;
285 	u32 link_target, link_dwords;
286 	bool switch_context = gpu->exec_state != exec_state;
287 	bool switch_mmu_context = gpu->mmu_context != mmu_context;
288 	unsigned int new_flush_seq = READ_ONCE(mmu_context->flush_seq);
289 	bool need_flush = switch_mmu_context || gpu->flush_seq != new_flush_seq;
290 	bool has_blt = !!(gpu->identity.minor_features5 &
291 			  chipMinorFeatures5_BLT_ENGINE);
292 
293 	lockdep_assert_held(&gpu->lock);
294 
295 	if (drm_debug_enabled(DRM_UT_DRIVER))
296 		etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
297 
298 	link_target = etnaviv_cmdbuf_get_va(cmdbuf,
299 					    &gpu->mmu_context->cmdbuf_mapping);
300 	link_dwords = cmdbuf->size / 8;
301 
302 	/*
303 	 * If we need maintenance prior to submitting this buffer, we will
304 	 * need to append a mmu flush load state, followed by a new
305 	 * link to this buffer - a total of four additional words.
306 	 */
307 	if (need_flush || switch_context) {
308 		u32 target, extra_dwords;
309 
310 		/* link command */
311 		extra_dwords = 1;
312 
313 		/* flush command */
314 		if (need_flush) {
315 			if (gpu->mmu_context->global->version == ETNAVIV_IOMMU_V1)
316 				extra_dwords += 1;
317 			else
318 				extra_dwords += 3;
319 		}
320 
321 		/* pipe switch commands */
322 		if (switch_context)
323 			extra_dwords += 4;
324 
325 		/* PTA load command */
326 		if (switch_mmu_context && gpu->sec_mode == ETNA_SEC_KERNEL)
327 			extra_dwords += 1;
328 
329 		target = etnaviv_buffer_reserve(gpu, buffer, extra_dwords);
330 		/*
331 		 * Switch MMU context if necessary. Must be done after the
332 		 * link target has been calculated, as the jump forward in the
333 		 * kernel ring still uses the last active MMU context before
334 		 * the switch.
335 		 */
336 		if (switch_mmu_context) {
337 			struct etnaviv_iommu_context *old_context = gpu->mmu_context;
338 
339 			gpu->mmu_context = etnaviv_iommu_context_get(mmu_context);
340 			etnaviv_iommu_context_put(old_context);
341 		}
342 
343 		if (need_flush) {
344 			/* Add the MMU flush */
345 			if (gpu->mmu_context->global->version == ETNAVIV_IOMMU_V1) {
346 				CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU,
347 					       VIVS_GL_FLUSH_MMU_FLUSH_FEMMU |
348 					       VIVS_GL_FLUSH_MMU_FLUSH_UNK1 |
349 					       VIVS_GL_FLUSH_MMU_FLUSH_UNK2 |
350 					       VIVS_GL_FLUSH_MMU_FLUSH_PEMMU |
351 					       VIVS_GL_FLUSH_MMU_FLUSH_UNK4);
352 			} else {
353 				u32 flush = VIVS_MMUv2_CONFIGURATION_MODE_MASK |
354 					    VIVS_MMUv2_CONFIGURATION_FLUSH_FLUSH;
355 
356 				if (switch_mmu_context &&
357 				    gpu->sec_mode == ETNA_SEC_KERNEL) {
358 					unsigned short id =
359 						etnaviv_iommuv2_get_pta_id(gpu->mmu_context);
360 					CMD_LOAD_STATE(buffer,
361 						VIVS_MMUv2_PTA_CONFIG,
362 						VIVS_MMUv2_PTA_CONFIG_INDEX(id));
363 				}
364 
365 				if (gpu->sec_mode == ETNA_SEC_NONE)
366 					flush |= etnaviv_iommuv2_get_mtlb_addr(gpu->mmu_context);
367 
368 				CMD_LOAD_STATE(buffer, VIVS_MMUv2_CONFIGURATION,
369 					       flush);
370 				CMD_SEM(buffer, SYNC_RECIPIENT_FE,
371 					SYNC_RECIPIENT_PE);
372 				CMD_STALL(buffer, SYNC_RECIPIENT_FE,
373 					SYNC_RECIPIENT_PE);
374 			}
375 
376 			gpu->flush_seq = new_flush_seq;
377 		}
378 
379 		if (switch_context) {
380 			etnaviv_cmd_select_pipe(gpu, buffer, exec_state);
381 			gpu->exec_state = exec_state;
382 		}
383 
384 		/* And the link to the submitted buffer */
385 		link_target = etnaviv_cmdbuf_get_va(cmdbuf,
386 					&gpu->mmu_context->cmdbuf_mapping);
387 		CMD_LINK(buffer, link_dwords, link_target);
388 
389 		/* Update the link target to point to above instructions */
390 		link_target = target;
391 		link_dwords = extra_dwords;
392 	}
393 
394 	/*
395 	 * Append a LINK to the submitted command buffer to return to
396 	 * the ring buffer.  return_target is the ring target address.
397 	 * We need at most 7 dwords in the return target: 2 cache flush +
398 	 * 2 semaphore stall + 1 event + 1 wait + 1 link.
399 	 */
400 	return_dwords = 7;
401 
402 	/*
403 	 * When the BLT engine is present we need 6 more dwords in the return
404 	 * target: 3 enable/flush/disable + 4 enable/semaphore stall/disable,
405 	 * but we don't need the normal TS flush state.
406 	 */
407 	if (has_blt)
408 		return_dwords += 6;
409 
410 	return_target = etnaviv_buffer_reserve(gpu, buffer, return_dwords);
411 	CMD_LINK(cmdbuf, return_dwords, return_target);
412 
413 	/*
414 	 * Append a cache flush, stall, event, wait and link pointing back to
415 	 * the wait command to the ring buffer.
416 	 */
417 	if (gpu->exec_state == ETNA_PIPE_2D) {
418 		CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE,
419 				       VIVS_GL_FLUSH_CACHE_PE2D);
420 	} else {
421 		CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE,
422 				       VIVS_GL_FLUSH_CACHE_DEPTH |
423 				       VIVS_GL_FLUSH_CACHE_COLOR |
424 				       VIVS_GL_FLUSH_CACHE_SHADER_L1);
425 		if (has_blt) {
426 			CMD_LOAD_STATE(buffer, VIVS_BLT_ENABLE, 0x1);
427 			CMD_LOAD_STATE(buffer, VIVS_BLT_SET_COMMAND, 0x1);
428 			CMD_LOAD_STATE(buffer, VIVS_BLT_ENABLE, 0x0);
429 		} else {
430 			CMD_LOAD_STATE(buffer, VIVS_TS_FLUSH_CACHE,
431 					       VIVS_TS_FLUSH_CACHE_FLUSH);
432 		}
433 	}
434 	CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
435 	CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
436 
437 	if (has_blt) {
438 		CMD_LOAD_STATE(buffer, VIVS_BLT_ENABLE, 0x1);
439 		CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_BLT);
440 		CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_BLT);
441 		CMD_LOAD_STATE(buffer, VIVS_BLT_ENABLE, 0x0);
442 	}
443 
444 	CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) |
445 		       VIVS_GL_EVENT_FROM_PE);
446 	CMD_WAIT(buffer, gpu->fe_waitcycles);
447 	CMD_LINK(buffer, 2,
448 		 etnaviv_cmdbuf_get_va(buffer, &gpu->mmu_context->cmdbuf_mapping)
449 		 + buffer->user_size - 4);
450 
451 	if (drm_debug_enabled(DRM_UT_DRIVER))
452 		pr_info("stream link to 0x%08x @ 0x%08x %p\n",
453 			return_target,
454 			etnaviv_cmdbuf_get_va(cmdbuf, &gpu->mmu_context->cmdbuf_mapping),
455 			cmdbuf->vaddr);
456 
457 	if (drm_debug_enabled(DRM_UT_DRIVER)) {
458 		print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
459 			       cmdbuf->vaddr, cmdbuf->size, 0);
460 
461 		pr_info("link op: %p\n", buffer->vaddr + waitlink_offset);
462 		pr_info("addr: 0x%08x\n", link_target);
463 		pr_info("back: 0x%08x\n", return_target);
464 		pr_info("event: %d\n", event);
465 	}
466 
467 	/*
468 	 * Kick off the submitted command by replacing the previous
469 	 * WAIT with a link to the address in the ring buffer.
470 	 */
471 	etnaviv_buffer_replace_wait(buffer, waitlink_offset,
472 				    VIV_FE_LINK_HEADER_OP_LINK |
473 				    VIV_FE_LINK_HEADER_PREFETCH(link_dwords),
474 				    link_target);
475 
476 	if (drm_debug_enabled(DRM_UT_DRIVER))
477 		etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
478 }
479