xref: /linux/drivers/gpu/drm/amd/amdgpu/amdgpu_wb.c (revision fbf41e05119f43e9d69307272ced4ed3b82ad371)
1 // SPDX-License-Identifier: GPL-2.0 OR MIT
2 /*
3  * Copyright 2026 Advanced Micro Devices, Inc.
4 
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21  * OTHER DEALINGS IN THE SOFTWARE.
22  */
23 #include <linux/spinlock.h>
24 
25 #include "amdgpu.h"
26 #include "amdgpu_wb.h"
27 
28 /*
29  * amdgpu_wb_*()
30  * Writeback is the method by which the GPU updates special pages in memory
31  * with the status of certain GPU events (fences, ring pointers,etc.).
32  */
33 
34 /**
35  * amdgpu_wb_fini - Disable Writeback and free memory
36  *
37  * @adev: amdgpu_device pointer
38  *
39  * Disables Writeback and frees the Writeback memory (all asics).
40  * Used at driver shutdown.
41  */
42 void amdgpu_wb_fini(struct amdgpu_device *adev)
43 {
44 	if (adev->wb.wb_obj) {
45 		amdgpu_bo_free_kernel(&adev->wb.wb_obj,
46 				      &adev->wb.gpu_addr,
47 				      (void **)&adev->wb.wb);
48 		adev->wb.wb_obj = NULL;
49 	}
50 }
51 
52 /**
53  * amdgpu_wb_init - Init Writeback driver info and allocate memory
54  *
55  * @adev: amdgpu_device pointer
56  *
57  * Initializes writeback and allocates writeback memory (all asics).
58  * Used at driver startup.
59  * Returns 0 on success or an -error on failure.
60  */
61 int amdgpu_wb_init(struct amdgpu_device *adev)
62 {
63 	int r;
64 
65 	if (adev->wb.wb_obj == NULL) {
66 		/* AMDGPU_MAX_WB * sizeof(uint32_t) * 8 = AMDGPU_MAX_WB 256bit slots */
67 		r = amdgpu_bo_create_kernel(adev, AMDGPU_MAX_WB * sizeof(uint32_t) * 8,
68 					    PAGE_SIZE, AMDGPU_GEM_DOMAIN_GTT,
69 					    &adev->wb.wb_obj, &adev->wb.gpu_addr,
70 					    (void **)&adev->wb.wb);
71 		if (r) {
72 			dev_warn(adev->dev, "(%d) create WB bo failed\n", r);
73 			return r;
74 		}
75 
76 		adev->wb.num_wb = AMDGPU_MAX_WB;
77 		memset(&adev->wb.used, 0, sizeof(adev->wb.used));
78 
79 		/* clear wb memory */
80 		memset((char *)adev->wb.wb, 0, AMDGPU_MAX_WB * sizeof(uint32_t) * 8);
81 	}
82 
83 	return 0;
84 }
85 
86 /**
87  * amdgpu_wb_get - Allocate a wb entry
88  *
89  * @adev: amdgpu_device pointer
90  * @wb: wb index
91  *
92  * Allocate a wb slot for use by the driver (all asics).
93  * Returns 0 on success or -EINVAL on failure.
94  */
95 int amdgpu_wb_get(struct amdgpu_device *adev, u32 *wb)
96 {
97 	unsigned long flags, offset;
98 
99 	spin_lock_irqsave(&adev->wb.lock, flags);
100 	offset = find_first_zero_bit(adev->wb.used, adev->wb.num_wb);
101 	if (offset < adev->wb.num_wb) {
102 		__set_bit(offset, adev->wb.used);
103 		spin_unlock_irqrestore(&adev->wb.lock, flags);
104 		*wb = offset << 3; /* convert to dw offset */
105 		return 0;
106 	} else {
107 		spin_unlock_irqrestore(&adev->wb.lock, flags);
108 		return -EINVAL;
109 	}
110 }
111 
112 /**
113  * amdgpu_wb_free - Free a wb entry
114  *
115  * @adev: amdgpu_device pointer
116  * @wb: wb index
117  *
118  * Free a wb slot allocated for use by the driver (all asics)
119  */
120 void amdgpu_wb_free(struct amdgpu_device *adev, u32 wb)
121 {
122 	unsigned long flags;
123 
124 	wb >>= 3;
125 	spin_lock_irqsave(&adev->wb.lock, flags);
126 	if (wb < adev->wb.num_wb)
127 		__clear_bit(wb, adev->wb.used);
128 	spin_unlock_irqrestore(&adev->wb.lock, flags);
129 }
130