162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright 2008 Advanced Micro Devices, Inc.
362306a36Sopenharmony_ci * Copyright 2008 Red Hat Inc.
462306a36Sopenharmony_ci * Copyright 2009 Jerome Glisse.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
762306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
862306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
962306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1062306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
1162306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
1462306a36Sopenharmony_ci * all copies or substantial portions of the Software.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1762306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1862306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1962306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
2062306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2162306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2262306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * Authors: Dave Airlie
2562306a36Sopenharmony_ci *          Alex Deucher
2662306a36Sopenharmony_ci *          Jerome Glisse
2762306a36Sopenharmony_ci *          Christian König
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <drm/drm_device.h>
3162306a36Sopenharmony_ci#include <drm/drm_file.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include "radeon.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/*
3662306a36Sopenharmony_ci * Rings
3762306a36Sopenharmony_ci * Most engines on the GPU are fed via ring buffers.  Ring
3862306a36Sopenharmony_ci * buffers are areas of GPU accessible memory that the host
3962306a36Sopenharmony_ci * writes commands into and the GPU reads commands out of.
4062306a36Sopenharmony_ci * There is a rptr (read pointer) that determines where the
4162306a36Sopenharmony_ci * GPU is currently reading, and a wptr (write pointer)
4262306a36Sopenharmony_ci * which determines where the host has written.  When the
4362306a36Sopenharmony_ci * pointers are equal, the ring is idle.  When the host
4462306a36Sopenharmony_ci * writes commands to the ring buffer, it increments the
4562306a36Sopenharmony_ci * wptr.  The GPU then starts fetching commands and executes
4662306a36Sopenharmony_ci * them until the pointers are equal again.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistatic void radeon_debugfs_ring_init(struct radeon_device *rdev, struct radeon_ring *ring);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/**
5162306a36Sopenharmony_ci * radeon_ring_supports_scratch_reg - check if the ring supports
5262306a36Sopenharmony_ci * writing to scratch registers
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * @rdev: radeon_device pointer
5562306a36Sopenharmony_ci * @ring: radeon_ring structure holding ring information
5662306a36Sopenharmony_ci *
5762306a36Sopenharmony_ci * Check if a specific ring supports writing to scratch registers (all asics).
5862306a36Sopenharmony_ci * Returns true if the ring supports writing to scratch regs, false if not.
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_cibool radeon_ring_supports_scratch_reg(struct radeon_device *rdev,
6162306a36Sopenharmony_ci				      struct radeon_ring *ring)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	switch (ring->idx) {
6462306a36Sopenharmony_ci	case RADEON_RING_TYPE_GFX_INDEX:
6562306a36Sopenharmony_ci	case CAYMAN_RING_TYPE_CP1_INDEX:
6662306a36Sopenharmony_ci	case CAYMAN_RING_TYPE_CP2_INDEX:
6762306a36Sopenharmony_ci		return true;
6862306a36Sopenharmony_ci	default:
6962306a36Sopenharmony_ci		return false;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/**
7462306a36Sopenharmony_ci * radeon_ring_free_size - update the free size
7562306a36Sopenharmony_ci *
7662306a36Sopenharmony_ci * @rdev: radeon_device pointer
7762306a36Sopenharmony_ci * @ring: radeon_ring structure holding ring information
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci * Update the free dw slots in the ring buffer (all asics).
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_civoid radeon_ring_free_size(struct radeon_device *rdev, struct radeon_ring *ring)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	uint32_t rptr = radeon_ring_get_rptr(rdev, ring);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/* This works because ring_size is a power of 2 */
8662306a36Sopenharmony_ci	ring->ring_free_dw = rptr + (ring->ring_size / 4);
8762306a36Sopenharmony_ci	ring->ring_free_dw -= ring->wptr;
8862306a36Sopenharmony_ci	ring->ring_free_dw &= ring->ptr_mask;
8962306a36Sopenharmony_ci	if (!ring->ring_free_dw) {
9062306a36Sopenharmony_ci		/* this is an empty ring */
9162306a36Sopenharmony_ci		ring->ring_free_dw = ring->ring_size / 4;
9262306a36Sopenharmony_ci		/*  update lockup info to avoid false positive */
9362306a36Sopenharmony_ci		radeon_ring_lockup_update(rdev, ring);
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/**
9862306a36Sopenharmony_ci * radeon_ring_alloc - allocate space on the ring buffer
9962306a36Sopenharmony_ci *
10062306a36Sopenharmony_ci * @rdev: radeon_device pointer
10162306a36Sopenharmony_ci * @ring: radeon_ring structure holding ring information
10262306a36Sopenharmony_ci * @ndw: number of dwords to allocate in the ring buffer
10362306a36Sopenharmony_ci *
10462306a36Sopenharmony_ci * Allocate @ndw dwords in the ring buffer (all asics).
10562306a36Sopenharmony_ci * Returns 0 on success, error on failure.
10662306a36Sopenharmony_ci */
10762306a36Sopenharmony_ciint radeon_ring_alloc(struct radeon_device *rdev, struct radeon_ring *ring, unsigned ndw)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	int r;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* make sure we aren't trying to allocate more space than there is on the ring */
11262306a36Sopenharmony_ci	if (ndw > (ring->ring_size / 4))
11362306a36Sopenharmony_ci		return -ENOMEM;
11462306a36Sopenharmony_ci	/* Align requested size with padding so unlock_commit can
11562306a36Sopenharmony_ci	 * pad safely */
11662306a36Sopenharmony_ci	radeon_ring_free_size(rdev, ring);
11762306a36Sopenharmony_ci	ndw = (ndw + ring->align_mask) & ~ring->align_mask;
11862306a36Sopenharmony_ci	while (ndw > (ring->ring_free_dw - 1)) {
11962306a36Sopenharmony_ci		radeon_ring_free_size(rdev, ring);
12062306a36Sopenharmony_ci		if (ndw < ring->ring_free_dw) {
12162306a36Sopenharmony_ci			break;
12262306a36Sopenharmony_ci		}
12362306a36Sopenharmony_ci		r = radeon_fence_wait_next(rdev, ring->idx);
12462306a36Sopenharmony_ci		if (r)
12562306a36Sopenharmony_ci			return r;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci	ring->count_dw = ndw;
12862306a36Sopenharmony_ci	ring->wptr_old = ring->wptr;
12962306a36Sopenharmony_ci	return 0;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/**
13362306a36Sopenharmony_ci * radeon_ring_lock - lock the ring and allocate space on it
13462306a36Sopenharmony_ci *
13562306a36Sopenharmony_ci * @rdev: radeon_device pointer
13662306a36Sopenharmony_ci * @ring: radeon_ring structure holding ring information
13762306a36Sopenharmony_ci * @ndw: number of dwords to allocate in the ring buffer
13862306a36Sopenharmony_ci *
13962306a36Sopenharmony_ci * Lock the ring and allocate @ndw dwords in the ring buffer
14062306a36Sopenharmony_ci * (all asics).
14162306a36Sopenharmony_ci * Returns 0 on success, error on failure.
14262306a36Sopenharmony_ci */
14362306a36Sopenharmony_ciint radeon_ring_lock(struct radeon_device *rdev, struct radeon_ring *ring, unsigned ndw)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	int r;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	mutex_lock(&rdev->ring_lock);
14862306a36Sopenharmony_ci	r = radeon_ring_alloc(rdev, ring, ndw);
14962306a36Sopenharmony_ci	if (r) {
15062306a36Sopenharmony_ci		mutex_unlock(&rdev->ring_lock);
15162306a36Sopenharmony_ci		return r;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/**
15762306a36Sopenharmony_ci * radeon_ring_commit - tell the GPU to execute the new
15862306a36Sopenharmony_ci * commands on the ring buffer
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci * @rdev: radeon_device pointer
16162306a36Sopenharmony_ci * @ring: radeon_ring structure holding ring information
16262306a36Sopenharmony_ci * @hdp_flush: Whether or not to perform an HDP cache flush
16362306a36Sopenharmony_ci *
16462306a36Sopenharmony_ci * Update the wptr (write pointer) to tell the GPU to
16562306a36Sopenharmony_ci * execute new commands on the ring buffer (all asics).
16662306a36Sopenharmony_ci */
16762306a36Sopenharmony_civoid radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *ring,
16862306a36Sopenharmony_ci			bool hdp_flush)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	/* If we are emitting the HDP flush via the ring buffer, we need to
17162306a36Sopenharmony_ci	 * do it before padding.
17262306a36Sopenharmony_ci	 */
17362306a36Sopenharmony_ci	if (hdp_flush && rdev->asic->ring[ring->idx]->hdp_flush)
17462306a36Sopenharmony_ci		rdev->asic->ring[ring->idx]->hdp_flush(rdev, ring);
17562306a36Sopenharmony_ci	/* We pad to match fetch size */
17662306a36Sopenharmony_ci	while (ring->wptr & ring->align_mask) {
17762306a36Sopenharmony_ci		radeon_ring_write(ring, ring->nop);
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci	mb();
18062306a36Sopenharmony_ci	/* If we are emitting the HDP flush via MMIO, we need to do it after
18162306a36Sopenharmony_ci	 * all CPU writes to VRAM finished.
18262306a36Sopenharmony_ci	 */
18362306a36Sopenharmony_ci	if (hdp_flush && rdev->asic->mmio_hdp_flush)
18462306a36Sopenharmony_ci		rdev->asic->mmio_hdp_flush(rdev);
18562306a36Sopenharmony_ci	radeon_ring_set_wptr(rdev, ring);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/**
18962306a36Sopenharmony_ci * radeon_ring_unlock_commit - tell the GPU to execute the new
19062306a36Sopenharmony_ci * commands on the ring buffer and unlock it
19162306a36Sopenharmony_ci *
19262306a36Sopenharmony_ci * @rdev: radeon_device pointer
19362306a36Sopenharmony_ci * @ring: radeon_ring structure holding ring information
19462306a36Sopenharmony_ci * @hdp_flush: Whether or not to perform an HDP cache flush
19562306a36Sopenharmony_ci *
19662306a36Sopenharmony_ci * Call radeon_ring_commit() then unlock the ring (all asics).
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_civoid radeon_ring_unlock_commit(struct radeon_device *rdev, struct radeon_ring *ring,
19962306a36Sopenharmony_ci			       bool hdp_flush)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	radeon_ring_commit(rdev, ring, hdp_flush);
20262306a36Sopenharmony_ci	mutex_unlock(&rdev->ring_lock);
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/**
20662306a36Sopenharmony_ci * radeon_ring_undo - reset the wptr
20762306a36Sopenharmony_ci *
20862306a36Sopenharmony_ci * @ring: radeon_ring structure holding ring information
20962306a36Sopenharmony_ci *
21062306a36Sopenharmony_ci * Reset the driver's copy of the wptr (all asics).
21162306a36Sopenharmony_ci */
21262306a36Sopenharmony_civoid radeon_ring_undo(struct radeon_ring *ring)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	ring->wptr = ring->wptr_old;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/**
21862306a36Sopenharmony_ci * radeon_ring_unlock_undo - reset the wptr and unlock the ring
21962306a36Sopenharmony_ci *
22062306a36Sopenharmony_ci * @rdev:       radeon device structure
22162306a36Sopenharmony_ci * @ring: radeon_ring structure holding ring information
22262306a36Sopenharmony_ci *
22362306a36Sopenharmony_ci * Call radeon_ring_undo() then unlock the ring (all asics).
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_civoid radeon_ring_unlock_undo(struct radeon_device *rdev, struct radeon_ring *ring)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	radeon_ring_undo(ring);
22862306a36Sopenharmony_ci	mutex_unlock(&rdev->ring_lock);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci/**
23262306a36Sopenharmony_ci * radeon_ring_lockup_update - update lockup variables
23362306a36Sopenharmony_ci *
23462306a36Sopenharmony_ci * @rdev:       radeon device structure
23562306a36Sopenharmony_ci * @ring: radeon_ring structure holding ring information
23662306a36Sopenharmony_ci *
23762306a36Sopenharmony_ci * Update the last rptr value and timestamp (all asics).
23862306a36Sopenharmony_ci */
23962306a36Sopenharmony_civoid radeon_ring_lockup_update(struct radeon_device *rdev,
24062306a36Sopenharmony_ci			       struct radeon_ring *ring)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	atomic_set(&ring->last_rptr, radeon_ring_get_rptr(rdev, ring));
24362306a36Sopenharmony_ci	atomic64_set(&ring->last_activity, jiffies_64);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci/**
24762306a36Sopenharmony_ci * radeon_ring_test_lockup() - check if ring is lockedup by recording information
24862306a36Sopenharmony_ci * @rdev:       radeon device structure
24962306a36Sopenharmony_ci * @ring:       radeon_ring structure holding ring information
25062306a36Sopenharmony_ci *
25162306a36Sopenharmony_ci */
25262306a36Sopenharmony_cibool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	uint32_t rptr = radeon_ring_get_rptr(rdev, ring);
25562306a36Sopenharmony_ci	uint64_t last = atomic64_read(&ring->last_activity);
25662306a36Sopenharmony_ci	uint64_t elapsed;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (rptr != atomic_read(&ring->last_rptr)) {
25962306a36Sopenharmony_ci		/* ring is still working, no lockup */
26062306a36Sopenharmony_ci		radeon_ring_lockup_update(rdev, ring);
26162306a36Sopenharmony_ci		return false;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	elapsed = jiffies_to_msecs(jiffies_64 - last);
26562306a36Sopenharmony_ci	if (radeon_lockup_timeout && elapsed >= radeon_lockup_timeout) {
26662306a36Sopenharmony_ci		dev_err(rdev->dev, "ring %d stalled for more than %llumsec\n",
26762306a36Sopenharmony_ci			ring->idx, elapsed);
26862306a36Sopenharmony_ci		return true;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci	/* give a chance to the GPU ... */
27162306a36Sopenharmony_ci	return false;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/**
27562306a36Sopenharmony_ci * radeon_ring_backup - Back up the content of a ring
27662306a36Sopenharmony_ci *
27762306a36Sopenharmony_ci * @rdev: radeon_device pointer
27862306a36Sopenharmony_ci * @ring: the ring we want to back up
27962306a36Sopenharmony_ci * @data: placeholder for returned commit data
28062306a36Sopenharmony_ci *
28162306a36Sopenharmony_ci * Saves all unprocessed commits from a ring, returns the number of dwords saved.
28262306a36Sopenharmony_ci */
28362306a36Sopenharmony_ciunsigned radeon_ring_backup(struct radeon_device *rdev, struct radeon_ring *ring,
28462306a36Sopenharmony_ci			    uint32_t **data)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	unsigned size, ptr, i;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* just in case lock the ring */
28962306a36Sopenharmony_ci	mutex_lock(&rdev->ring_lock);
29062306a36Sopenharmony_ci	*data = NULL;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (ring->ring_obj == NULL) {
29362306a36Sopenharmony_ci		mutex_unlock(&rdev->ring_lock);
29462306a36Sopenharmony_ci		return 0;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* it doesn't make sense to save anything if all fences are signaled */
29862306a36Sopenharmony_ci	if (!radeon_fence_count_emitted(rdev, ring->idx)) {
29962306a36Sopenharmony_ci		mutex_unlock(&rdev->ring_lock);
30062306a36Sopenharmony_ci		return 0;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	/* calculate the number of dw on the ring */
30462306a36Sopenharmony_ci	if (ring->rptr_save_reg)
30562306a36Sopenharmony_ci		ptr = RREG32(ring->rptr_save_reg);
30662306a36Sopenharmony_ci	else if (rdev->wb.enabled)
30762306a36Sopenharmony_ci		ptr = le32_to_cpu(*ring->next_rptr_cpu_addr);
30862306a36Sopenharmony_ci	else {
30962306a36Sopenharmony_ci		/* no way to read back the next rptr */
31062306a36Sopenharmony_ci		mutex_unlock(&rdev->ring_lock);
31162306a36Sopenharmony_ci		return 0;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	size = ring->wptr + (ring->ring_size / 4);
31562306a36Sopenharmony_ci	size -= ptr;
31662306a36Sopenharmony_ci	size &= ring->ptr_mask;
31762306a36Sopenharmony_ci	if (size == 0) {
31862306a36Sopenharmony_ci		mutex_unlock(&rdev->ring_lock);
31962306a36Sopenharmony_ci		return 0;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* and then save the content of the ring */
32362306a36Sopenharmony_ci	*data = kvmalloc_array(size, sizeof(uint32_t), GFP_KERNEL);
32462306a36Sopenharmony_ci	if (!*data) {
32562306a36Sopenharmony_ci		mutex_unlock(&rdev->ring_lock);
32662306a36Sopenharmony_ci		return 0;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci	for (i = 0; i < size; ++i) {
32962306a36Sopenharmony_ci		(*data)[i] = ring->ring[ptr++];
33062306a36Sopenharmony_ci		ptr &= ring->ptr_mask;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	mutex_unlock(&rdev->ring_lock);
33462306a36Sopenharmony_ci	return size;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci/**
33862306a36Sopenharmony_ci * radeon_ring_restore - append saved commands to the ring again
33962306a36Sopenharmony_ci *
34062306a36Sopenharmony_ci * @rdev: radeon_device pointer
34162306a36Sopenharmony_ci * @ring: ring to append commands to
34262306a36Sopenharmony_ci * @size: number of dwords we want to write
34362306a36Sopenharmony_ci * @data: saved commands
34462306a36Sopenharmony_ci *
34562306a36Sopenharmony_ci * Allocates space on the ring and restore the previously saved commands.
34662306a36Sopenharmony_ci */
34762306a36Sopenharmony_ciint radeon_ring_restore(struct radeon_device *rdev, struct radeon_ring *ring,
34862306a36Sopenharmony_ci			unsigned size, uint32_t *data)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	int i, r;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (!size || !data)
35362306a36Sopenharmony_ci		return 0;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* restore the saved ring content */
35662306a36Sopenharmony_ci	r = radeon_ring_lock(rdev, ring, size);
35762306a36Sopenharmony_ci	if (r)
35862306a36Sopenharmony_ci		return r;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	for (i = 0; i < size; ++i) {
36162306a36Sopenharmony_ci		radeon_ring_write(ring, data[i]);
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	radeon_ring_unlock_commit(rdev, ring, false);
36562306a36Sopenharmony_ci	kvfree(data);
36662306a36Sopenharmony_ci	return 0;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci/**
37062306a36Sopenharmony_ci * radeon_ring_init - init driver ring struct.
37162306a36Sopenharmony_ci *
37262306a36Sopenharmony_ci * @rdev: radeon_device pointer
37362306a36Sopenharmony_ci * @ring: radeon_ring structure holding ring information
37462306a36Sopenharmony_ci * @ring_size: size of the ring
37562306a36Sopenharmony_ci * @rptr_offs: offset of the rptr writeback location in the WB buffer
37662306a36Sopenharmony_ci * @nop: nop packet for this ring
37762306a36Sopenharmony_ci *
37862306a36Sopenharmony_ci * Initialize the driver information for the selected ring (all asics).
37962306a36Sopenharmony_ci * Returns 0 on success, error on failure.
38062306a36Sopenharmony_ci */
38162306a36Sopenharmony_ciint radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *ring, unsigned ring_size,
38262306a36Sopenharmony_ci		     unsigned rptr_offs, u32 nop)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	int r;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	ring->ring_size = ring_size;
38762306a36Sopenharmony_ci	ring->rptr_offs = rptr_offs;
38862306a36Sopenharmony_ci	ring->nop = nop;
38962306a36Sopenharmony_ci	ring->rdev = rdev;
39062306a36Sopenharmony_ci	/* Allocate ring buffer */
39162306a36Sopenharmony_ci	if (ring->ring_obj == NULL) {
39262306a36Sopenharmony_ci		r = radeon_bo_create(rdev, ring->ring_size, PAGE_SIZE, true,
39362306a36Sopenharmony_ci				     RADEON_GEM_DOMAIN_GTT, 0, NULL,
39462306a36Sopenharmony_ci				     NULL, &ring->ring_obj);
39562306a36Sopenharmony_ci		if (r) {
39662306a36Sopenharmony_ci			dev_err(rdev->dev, "(%d) ring create failed\n", r);
39762306a36Sopenharmony_ci			return r;
39862306a36Sopenharmony_ci		}
39962306a36Sopenharmony_ci		r = radeon_bo_reserve(ring->ring_obj, false);
40062306a36Sopenharmony_ci		if (unlikely(r != 0))
40162306a36Sopenharmony_ci			return r;
40262306a36Sopenharmony_ci		r = radeon_bo_pin(ring->ring_obj, RADEON_GEM_DOMAIN_GTT,
40362306a36Sopenharmony_ci					&ring->gpu_addr);
40462306a36Sopenharmony_ci		if (r) {
40562306a36Sopenharmony_ci			radeon_bo_unreserve(ring->ring_obj);
40662306a36Sopenharmony_ci			dev_err(rdev->dev, "(%d) ring pin failed\n", r);
40762306a36Sopenharmony_ci			return r;
40862306a36Sopenharmony_ci		}
40962306a36Sopenharmony_ci		r = radeon_bo_kmap(ring->ring_obj,
41062306a36Sopenharmony_ci				       (void **)&ring->ring);
41162306a36Sopenharmony_ci		radeon_bo_unreserve(ring->ring_obj);
41262306a36Sopenharmony_ci		if (r) {
41362306a36Sopenharmony_ci			dev_err(rdev->dev, "(%d) ring map failed\n", r);
41462306a36Sopenharmony_ci			return r;
41562306a36Sopenharmony_ci		}
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci	ring->ptr_mask = (ring->ring_size / 4) - 1;
41862306a36Sopenharmony_ci	ring->ring_free_dw = ring->ring_size / 4;
41962306a36Sopenharmony_ci	if (rdev->wb.enabled) {
42062306a36Sopenharmony_ci		u32 index = RADEON_WB_RING0_NEXT_RPTR + (ring->idx * 4);
42162306a36Sopenharmony_ci		ring->next_rptr_gpu_addr = rdev->wb.gpu_addr + index;
42262306a36Sopenharmony_ci		ring->next_rptr_cpu_addr = &rdev->wb.wb[index/4];
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci	radeon_debugfs_ring_init(rdev, ring);
42562306a36Sopenharmony_ci	radeon_ring_lockup_update(rdev, ring);
42662306a36Sopenharmony_ci	return 0;
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci/**
43062306a36Sopenharmony_ci * radeon_ring_fini - tear down the driver ring struct.
43162306a36Sopenharmony_ci *
43262306a36Sopenharmony_ci * @rdev: radeon_device pointer
43362306a36Sopenharmony_ci * @ring: radeon_ring structure holding ring information
43462306a36Sopenharmony_ci *
43562306a36Sopenharmony_ci * Tear down the driver information for the selected ring (all asics).
43662306a36Sopenharmony_ci */
43762306a36Sopenharmony_civoid radeon_ring_fini(struct radeon_device *rdev, struct radeon_ring *ring)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	int r;
44062306a36Sopenharmony_ci	struct radeon_bo *ring_obj;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	mutex_lock(&rdev->ring_lock);
44362306a36Sopenharmony_ci	ring_obj = ring->ring_obj;
44462306a36Sopenharmony_ci	ring->ready = false;
44562306a36Sopenharmony_ci	ring->ring = NULL;
44662306a36Sopenharmony_ci	ring->ring_obj = NULL;
44762306a36Sopenharmony_ci	mutex_unlock(&rdev->ring_lock);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (ring_obj) {
45062306a36Sopenharmony_ci		r = radeon_bo_reserve(ring_obj, false);
45162306a36Sopenharmony_ci		if (likely(r == 0)) {
45262306a36Sopenharmony_ci			radeon_bo_kunmap(ring_obj);
45362306a36Sopenharmony_ci			radeon_bo_unpin(ring_obj);
45462306a36Sopenharmony_ci			radeon_bo_unreserve(ring_obj);
45562306a36Sopenharmony_ci		}
45662306a36Sopenharmony_ci		radeon_bo_unref(&ring_obj);
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci/*
46162306a36Sopenharmony_ci * Debugfs info
46262306a36Sopenharmony_ci */
46362306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_FS)
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic int radeon_debugfs_ring_info_show(struct seq_file *m, void *unused)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct radeon_ring *ring = m->private;
46862306a36Sopenharmony_ci	struct radeon_device *rdev = ring->rdev;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	uint32_t rptr, wptr, rptr_next;
47162306a36Sopenharmony_ci	unsigned count, i, j;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	radeon_ring_free_size(rdev, ring);
47462306a36Sopenharmony_ci	count = (ring->ring_size / 4) - ring->ring_free_dw;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	wptr = radeon_ring_get_wptr(rdev, ring);
47762306a36Sopenharmony_ci	seq_printf(m, "wptr: 0x%08x [%5d]\n",
47862306a36Sopenharmony_ci		   wptr, wptr);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	rptr = radeon_ring_get_rptr(rdev, ring);
48162306a36Sopenharmony_ci	seq_printf(m, "rptr: 0x%08x [%5d]\n",
48262306a36Sopenharmony_ci		   rptr, rptr);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (ring->rptr_save_reg) {
48562306a36Sopenharmony_ci		rptr_next = RREG32(ring->rptr_save_reg);
48662306a36Sopenharmony_ci		seq_printf(m, "rptr next(0x%04x): 0x%08x [%5d]\n",
48762306a36Sopenharmony_ci			   ring->rptr_save_reg, rptr_next, rptr_next);
48862306a36Sopenharmony_ci	} else
48962306a36Sopenharmony_ci		rptr_next = ~0;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	seq_printf(m, "driver's copy of the wptr: 0x%08x [%5d]\n",
49262306a36Sopenharmony_ci		   ring->wptr, ring->wptr);
49362306a36Sopenharmony_ci	seq_printf(m, "last semaphore signal addr : 0x%016llx\n",
49462306a36Sopenharmony_ci		   ring->last_semaphore_signal_addr);
49562306a36Sopenharmony_ci	seq_printf(m, "last semaphore wait addr   : 0x%016llx\n",
49662306a36Sopenharmony_ci		   ring->last_semaphore_wait_addr);
49762306a36Sopenharmony_ci	seq_printf(m, "%u free dwords in ring\n", ring->ring_free_dw);
49862306a36Sopenharmony_ci	seq_printf(m, "%u dwords in ring\n", count);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if (!ring->ring)
50162306a36Sopenharmony_ci		return 0;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	/* print 8 dw before current rptr as often it's the last executed
50462306a36Sopenharmony_ci	 * packet that is the root issue
50562306a36Sopenharmony_ci	 */
50662306a36Sopenharmony_ci	i = (rptr + ring->ptr_mask + 1 - 32) & ring->ptr_mask;
50762306a36Sopenharmony_ci	for (j = 0; j <= (count + 32); j++) {
50862306a36Sopenharmony_ci		seq_printf(m, "r[%5d]=0x%08x", i, ring->ring[i]);
50962306a36Sopenharmony_ci		if (rptr == i)
51062306a36Sopenharmony_ci			seq_puts(m, " *");
51162306a36Sopenharmony_ci		if (rptr_next == i)
51262306a36Sopenharmony_ci			seq_puts(m, " #");
51362306a36Sopenharmony_ci		seq_puts(m, "\n");
51462306a36Sopenharmony_ci		i = (i + 1) & ring->ptr_mask;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci	return 0;
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(radeon_debugfs_ring_info);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic const char *radeon_debugfs_ring_idx_to_name(uint32_t ridx)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	switch (ridx) {
52462306a36Sopenharmony_ci	case RADEON_RING_TYPE_GFX_INDEX:
52562306a36Sopenharmony_ci		return "radeon_ring_gfx";
52662306a36Sopenharmony_ci	case CAYMAN_RING_TYPE_CP1_INDEX:
52762306a36Sopenharmony_ci		return "radeon_ring_cp1";
52862306a36Sopenharmony_ci	case CAYMAN_RING_TYPE_CP2_INDEX:
52962306a36Sopenharmony_ci		return "radeon_ring_cp2";
53062306a36Sopenharmony_ci	case R600_RING_TYPE_DMA_INDEX:
53162306a36Sopenharmony_ci		return "radeon_ring_dma1";
53262306a36Sopenharmony_ci	case CAYMAN_RING_TYPE_DMA1_INDEX:
53362306a36Sopenharmony_ci		return "radeon_ring_dma2";
53462306a36Sopenharmony_ci	case R600_RING_TYPE_UVD_INDEX:
53562306a36Sopenharmony_ci		return "radeon_ring_uvd";
53662306a36Sopenharmony_ci	case TN_RING_TYPE_VCE1_INDEX:
53762306a36Sopenharmony_ci		return "radeon_ring_vce1";
53862306a36Sopenharmony_ci	case TN_RING_TYPE_VCE2_INDEX:
53962306a36Sopenharmony_ci		return "radeon_ring_vce2";
54062306a36Sopenharmony_ci	default:
54162306a36Sopenharmony_ci		return NULL;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci#endif
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic void radeon_debugfs_ring_init(struct radeon_device *rdev, struct radeon_ring *ring)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_FS)
55062306a36Sopenharmony_ci	const char *ring_name = radeon_debugfs_ring_idx_to_name(ring->idx);
55162306a36Sopenharmony_ci	struct dentry *root = rdev->ddev->primary->debugfs_root;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (ring_name)
55462306a36Sopenharmony_ci		debugfs_create_file(ring_name, 0444, root, ring,
55562306a36Sopenharmony_ci				    &radeon_debugfs_ring_info_fops);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci#endif
55862306a36Sopenharmony_ci}
559