aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-12-15 18:52:01 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-12-15 18:52:01 -0500
commit988adfdffdd43cfd841df734664727993076d7cb (patch)
tree6794f7bba8f595500c2b7d33376ad6614adcfaf2 /drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c
parent26178ec11ef3c6c814bf16a0a2b9c2f7242e3c64 (diff)
parent4e0cd68115620bc3236ff4e58e4c073948629b41 (diff)
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull drm updates from Dave Airlie: "Highlights: - AMD KFD driver merge This is the AMD HSA interface for exposing a lowlevel interface for GPGPU use. They have an open source userspace built on top of this interface, and the code looks as good as it was going to get out of tree. - Initial atomic modesetting work The need for an atomic modesetting interface to allow userspace to try and send a complete set of modesetting state to the driver has arisen, and been suffering from neglect this past year. No more, the start of the common code and changes for msm driver to use it are in this tree. Ongoing work to get the userspace ioctl finished and the code clean will probably wait until next kernel. - DisplayID 1.3 and tiled monitor exposed to userspace. Tiled monitor property is now exposed for userspace to make use of. - Rockchip drm driver merged. - imx gpu driver moved out of staging Other stuff: - core: panel - MIPI DSI + new panels. expose suggested x/y properties for virtual GPUs - i915: Initial Skylake (SKL) support gen3/4 reset work start of dri1/ums removal infoframe tracking fixes for lots of things. - nouveau: tegra k1 voltage support GM204 modesetting support GT21x memory reclocking work - radeon: CI dpm fixes GPUVM improvements Initial DPM fan control - rcar-du: HDMI support added removed some support for old boards slave encoder driver for Analog Devices adv7511 - exynos: Exynos4415 SoC support - msm: a4xx gpu support atomic helper conversion - tegra: iommu support universal plane support ganged-mode DSI support - sti: HDMI i2c improvements - vmwgfx: some late fixes. - qxl: use suggested x/y properties" * 'drm-next' of git://people.freedesktop.org/~airlied/linux: (969 commits) drm: sti: fix module compilation issue drm/i915: save/restore GMBUS freq across suspend/resume on gen4 drm: sti: correctly cleanup CRTC and planes drm: sti: add HQVDP plane drm: sti: add cursor plane drm: sti: enable auxiliary CRTC drm: sti: fix delay in VTG programming drm: sti: prepare sti_tvout to support auxiliary crtc drm: sti: use drm_crtc_vblank_{on/off} instead of drm_vblank_{on/off} drm: sti: fix hdmi avi infoframe drm: sti: remove event lock while disabling vblank drm: sti: simplify gdp code drm: sti: clear all mixer control drm: sti: remove gpio for HDMI hot plug detection drm: sti: allow to change hdmi ddc i2c adapter drm/doc: Document drm_add_modes_noedid() usage drm/i915: Remove '& 0xffff' from the mask given to WA_REG() drm/i915: Invert the mask and val arguments in wa_add() and WA_REG() drm: Zero out DRM object memory upon cleanup drm/i915/bdw: Fix the write setting up the WIZ hashing mode ...
Diffstat (limited to 'drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c')
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c353
1 files changed, 353 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c
new file mode 100644
index 000000000000..935071410724
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c
@@ -0,0 +1,353 @@
1/*
2 * Copyright 2014 Advanced Micro Devices, Inc.
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 shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 */
23
24#include <linux/types.h>
25#include <linux/mutex.h>
26#include <linux/slab.h>
27#include <linux/printk.h>
28#include <linux/sched.h>
29#include "kfd_kernel_queue.h"
30#include "kfd_priv.h"
31#include "kfd_device_queue_manager.h"
32#include "kfd_pm4_headers.h"
33#include "kfd_pm4_opcodes.h"
34
35#define PM4_COUNT_ZERO (((1 << 15) - 1) << 16)
36
37static bool initialize(struct kernel_queue *kq, struct kfd_dev *dev,
38 enum kfd_queue_type type, unsigned int queue_size)
39{
40 struct queue_properties prop;
41 int retval;
42 union PM4_MES_TYPE_3_HEADER nop;
43
44 BUG_ON(!kq || !dev);
45 BUG_ON(type != KFD_QUEUE_TYPE_DIQ && type != KFD_QUEUE_TYPE_HIQ);
46
47 pr_debug("kfd: In func %s initializing queue type %d size %d\n",
48 __func__, KFD_QUEUE_TYPE_HIQ, queue_size);
49
50 nop.opcode = IT_NOP;
51 nop.type = PM4_TYPE_3;
52 nop.u32all |= PM4_COUNT_ZERO;
53
54 kq->dev = dev;
55 kq->nop_packet = nop.u32all;
56 switch (type) {
57 case KFD_QUEUE_TYPE_DIQ:
58 case KFD_QUEUE_TYPE_HIQ:
59 kq->mqd = dev->dqm->get_mqd_manager(dev->dqm,
60 KFD_MQD_TYPE_CIK_HIQ);
61 break;
62 default:
63 BUG();
64 break;
65 }
66
67 if (kq->mqd == NULL)
68 return false;
69
70 prop.doorbell_ptr = kfd_get_kernel_doorbell(dev, &prop.doorbell_off);
71
72 if (prop.doorbell_ptr == NULL)
73 goto err_get_kernel_doorbell;
74
75 retval = kfd2kgd->allocate_mem(dev->kgd,
76 queue_size,
77 PAGE_SIZE,
78 KFD_MEMPOOL_SYSTEM_WRITECOMBINE,
79 (struct kgd_mem **) &kq->pq);
80
81 if (retval != 0)
82 goto err_pq_allocate_vidmem;
83
84 kq->pq_kernel_addr = kq->pq->cpu_ptr;
85 kq->pq_gpu_addr = kq->pq->gpu_addr;
86
87 retval = kfd2kgd->allocate_mem(dev->kgd,
88 sizeof(*kq->rptr_kernel),
89 32,
90 KFD_MEMPOOL_SYSTEM_WRITECOMBINE,
91 (struct kgd_mem **) &kq->rptr_mem);
92
93 if (retval != 0)
94 goto err_rptr_allocate_vidmem;
95
96 kq->rptr_kernel = kq->rptr_mem->cpu_ptr;
97 kq->rptr_gpu_addr = kq->rptr_mem->gpu_addr;
98
99 retval = kfd2kgd->allocate_mem(dev->kgd,
100 sizeof(*kq->wptr_kernel),
101 32,
102 KFD_MEMPOOL_SYSTEM_WRITECOMBINE,
103 (struct kgd_mem **) &kq->wptr_mem);
104
105 if (retval != 0)
106 goto err_wptr_allocate_vidmem;
107
108 kq->wptr_kernel = kq->wptr_mem->cpu_ptr;
109 kq->wptr_gpu_addr = kq->wptr_mem->gpu_addr;
110
111 memset(kq->pq_kernel_addr, 0, queue_size);
112 memset(kq->rptr_kernel, 0, sizeof(*kq->rptr_kernel));
113 memset(kq->wptr_kernel, 0, sizeof(*kq->wptr_kernel));
114
115 prop.queue_size = queue_size;
116 prop.is_interop = false;
117 prop.priority = 1;
118 prop.queue_percent = 100;
119 prop.type = type;
120 prop.vmid = 0;
121 prop.queue_address = kq->pq_gpu_addr;
122 prop.read_ptr = (uint32_t *) kq->rptr_gpu_addr;
123 prop.write_ptr = (uint32_t *) kq->wptr_gpu_addr;
124
125 if (init_queue(&kq->queue, prop) != 0)
126 goto err_init_queue;
127
128 kq->queue->device = dev;
129 kq->queue->process = kfd_get_process(current);
130
131 retval = kq->mqd->init_mqd(kq->mqd, &kq->queue->mqd,
132 &kq->queue->mqd_mem_obj,
133 &kq->queue->gart_mqd_addr,
134 &kq->queue->properties);
135 if (retval != 0)
136 goto err_init_mqd;
137
138 /* assign HIQ to HQD */
139 if (type == KFD_QUEUE_TYPE_HIQ) {
140 pr_debug("assigning hiq to hqd\n");
141 kq->queue->pipe = KFD_CIK_HIQ_PIPE;
142 kq->queue->queue = KFD_CIK_HIQ_QUEUE;
143 kq->mqd->load_mqd(kq->mqd, kq->queue->mqd, kq->queue->pipe,
144 kq->queue->queue, NULL);
145 } else {
146 /* allocate fence for DIQ */
147
148 retval = kfd2kgd->allocate_mem(dev->kgd,
149 sizeof(uint32_t),
150 32,
151 KFD_MEMPOOL_SYSTEM_WRITECOMBINE,
152 (struct kgd_mem **) &kq->fence_mem_obj);
153
154 if (retval != 0)
155 goto err_alloc_fence;
156
157 kq->fence_kernel_address = kq->fence_mem_obj->cpu_ptr;
158 kq->fence_gpu_addr = kq->fence_mem_obj->gpu_addr;
159 }
160
161 print_queue(kq->queue);
162
163 return true;
164err_alloc_fence:
165err_init_mqd:
166 uninit_queue(kq->queue);
167err_init_queue:
168 kfd2kgd->free_mem(dev->kgd, (struct kgd_mem *) kq->wptr_mem);
169err_wptr_allocate_vidmem:
170 kfd2kgd->free_mem(dev->kgd, (struct kgd_mem *) kq->rptr_mem);
171err_rptr_allocate_vidmem:
172 kfd2kgd->free_mem(dev->kgd, (struct kgd_mem *) kq->pq);
173err_pq_allocate_vidmem:
174 pr_err("kfd: error init pq\n");
175 kfd_release_kernel_doorbell(dev, prop.doorbell_ptr);
176err_get_kernel_doorbell:
177 pr_err("kfd: error init doorbell");
178 return false;
179
180}
181
182static void uninitialize(struct kernel_queue *kq)
183{
184 BUG_ON(!kq);
185
186 if (kq->queue->properties.type == KFD_QUEUE_TYPE_HIQ)
187 kq->mqd->destroy_mqd(kq->mqd,
188 NULL,
189 false,
190 QUEUE_PREEMPT_DEFAULT_TIMEOUT_MS,
191 kq->queue->pipe,
192 kq->queue->queue);
193
194 kfd2kgd->free_mem(kq->dev->kgd, (struct kgd_mem *) kq->rptr_mem);
195 kfd2kgd->free_mem(kq->dev->kgd, (struct kgd_mem *) kq->wptr_mem);
196 kfd2kgd->free_mem(kq->dev->kgd, (struct kgd_mem *) kq->pq);
197 kfd_release_kernel_doorbell(kq->dev,
198 kq->queue->properties.doorbell_ptr);
199 uninit_queue(kq->queue);
200}
201
202static int acquire_packet_buffer(struct kernel_queue *kq,
203 size_t packet_size_in_dwords, unsigned int **buffer_ptr)
204{
205 size_t available_size;
206 size_t queue_size_dwords;
207 uint32_t wptr, rptr;
208 unsigned int *queue_address;
209
210 BUG_ON(!kq || !buffer_ptr);
211
212 rptr = *kq->rptr_kernel;
213 wptr = *kq->wptr_kernel;
214 queue_address = (unsigned int *)kq->pq_kernel_addr;
215 queue_size_dwords = kq->queue->properties.queue_size / sizeof(uint32_t);
216
217 pr_debug("kfd: In func %s\nrptr: %d\nwptr: %d\nqueue_address 0x%p\n",
218 __func__, rptr, wptr, queue_address);
219
220 available_size = (rptr - 1 - wptr + queue_size_dwords) %
221 queue_size_dwords;
222
223 if (packet_size_in_dwords >= queue_size_dwords ||
224 packet_size_in_dwords >= available_size) {
225 /*
226 * make sure calling functions know
227 * acquire_packet_buffer() failed
228 */
229 *buffer_ptr = NULL;
230 return -ENOMEM;
231 }
232
233 if (wptr + packet_size_in_dwords >= queue_size_dwords) {
234 while (wptr > 0) {
235 queue_address[wptr] = kq->nop_packet;
236 wptr = (wptr + 1) % queue_size_dwords;
237 }
238 }
239
240 *buffer_ptr = &queue_address[wptr];
241 kq->pending_wptr = wptr + packet_size_in_dwords;
242
243 return 0;
244}
245
246static void submit_packet(struct kernel_queue *kq)
247{
248#ifdef DEBUG
249 int i;
250#endif
251
252 BUG_ON(!kq);
253
254#ifdef DEBUG
255 for (i = *kq->wptr_kernel; i < kq->pending_wptr; i++) {
256 pr_debug("0x%2X ", kq->pq_kernel_addr[i]);
257 if (i % 15 == 0)
258 pr_debug("\n");
259 }
260 pr_debug("\n");
261#endif
262
263 *kq->wptr_kernel = kq->pending_wptr;
264 write_kernel_doorbell(kq->queue->properties.doorbell_ptr,
265 kq->pending_wptr);
266}
267
268static int sync_with_hw(struct kernel_queue *kq, unsigned long timeout_ms)
269{
270 unsigned long org_timeout_ms;
271
272 BUG_ON(!kq);
273
274 org_timeout_ms = timeout_ms;
275 timeout_ms += jiffies * 1000 / HZ;
276 while (*kq->wptr_kernel != *kq->rptr_kernel) {
277 if (time_after(jiffies * 1000 / HZ, timeout_ms)) {
278 pr_err("kfd: kernel_queue %s timeout expired %lu\n",
279 __func__, org_timeout_ms);
280 pr_err("kfd: wptr: %d rptr: %d\n",
281 *kq->wptr_kernel, *kq->rptr_kernel);
282 return -ETIME;
283 }
284 schedule();
285 }
286
287 return 0;
288}
289
290static void rollback_packet(struct kernel_queue *kq)
291{
292 BUG_ON(!kq);
293 kq->pending_wptr = *kq->queue->properties.write_ptr;
294}
295
296struct kernel_queue *kernel_queue_init(struct kfd_dev *dev,
297 enum kfd_queue_type type)
298{
299 struct kernel_queue *kq;
300
301 BUG_ON(!dev);
302
303 kq = kzalloc(sizeof(struct kernel_queue), GFP_KERNEL);
304 if (!kq)
305 return NULL;
306
307 kq->initialize = initialize;
308 kq->uninitialize = uninitialize;
309 kq->acquire_packet_buffer = acquire_packet_buffer;
310 kq->submit_packet = submit_packet;
311 kq->sync_with_hw = sync_with_hw;
312 kq->rollback_packet = rollback_packet;
313
314 if (kq->initialize(kq, dev, type, KFD_KERNEL_QUEUE_SIZE) == false) {
315 pr_err("kfd: failed to init kernel queue\n");
316 kfree(kq);
317 return NULL;
318 }
319 return kq;
320}
321
322void kernel_queue_uninit(struct kernel_queue *kq)
323{
324 BUG_ON(!kq);
325
326 kq->uninitialize(kq);
327 kfree(kq);
328}
329
330static __attribute__((unused)) void test_kq(struct kfd_dev *dev)
331{
332 struct kernel_queue *kq;
333 uint32_t *buffer, i;
334 int retval;
335
336 BUG_ON(!dev);
337
338 pr_debug("kfd: starting kernel queue test\n");
339
340 kq = kernel_queue_init(dev, KFD_QUEUE_TYPE_HIQ);
341 BUG_ON(!kq);
342
343 retval = kq->acquire_packet_buffer(kq, 5, &buffer);
344 BUG_ON(retval != 0);
345 for (i = 0; i < 5; i++)
346 buffer[i] = kq->nop_packet;
347 kq->submit_packet(kq);
348 kq->sync_with_hw(kq, 1000);
349
350 pr_debug("kfd: ending kernel queue test\n");
351}
352
353