summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/xe/xe_guc_hwconfig.c
blob: af2c817d552cad5d04f15ef9b7e3d778a052b766 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// SPDX-License-Identifier: MIT
/*
 * Copyright © 2022 Intel Corporation
 */

#include "xe_guc_hwconfig.h"

#include <drm/drm_managed.h>
#include <drm/drm_print.h>

#include "abi/guc_actions_abi.h"
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_gt.h"
#include "xe_guc.h"
#include "xe_map.h"

static int send_get_hwconfig(struct xe_guc *guc, u64 ggtt_addr, u32 size)
{
	u32 action[] = {
		XE_GUC_ACTION_GET_HWCONFIG,
		lower_32_bits(ggtt_addr),
		upper_32_bits(ggtt_addr),
		size,
	};

	return xe_guc_mmio_send(guc, action, ARRAY_SIZE(action));
}

static int guc_hwconfig_size(struct xe_guc *guc, u32 *size)
{
	int ret = send_get_hwconfig(guc, 0, 0);

	if (ret < 0)
		return ret;

	*size = ret;
	return 0;
}

static int guc_hwconfig_copy(struct xe_guc *guc)
{
	int ret = send_get_hwconfig(guc, xe_bo_ggtt_addr(guc->hwconfig.bo),
				    guc->hwconfig.size);

	if (ret < 0)
		return ret;

	return 0;
}

int xe_guc_hwconfig_init(struct xe_guc *guc)
{
	struct xe_device *xe = guc_to_xe(guc);
	struct xe_gt *gt = guc_to_gt(guc);
	struct xe_tile *tile = gt_to_tile(gt);
	struct xe_bo *bo;
	u32 size;
	int err;

	/* Initialization already done */
	if (guc->hwconfig.bo)
		return 0;

	/*
	 * All hwconfig the same across GTs so only GT0 needs to be configured
	 */
	if (gt->info.id != XE_GT0)
		return 0;

	/* ADL_P, DG2+ supports hwconfig table */
	if (GRAPHICS_VERx100(xe) < 1255 && xe->info.platform != XE_ALDERLAKE_P)
		return 0;

	err = guc_hwconfig_size(guc, &size);
	if (err)
		return err;
	if (!size)
		return -EINVAL;

	bo = xe_managed_bo_create_pin_map(xe, tile, PAGE_ALIGN(size),
					  XE_BO_FLAG_SYSTEM |
					  XE_BO_FLAG_GGTT |
					  XE_BO_FLAG_GGTT_INVALIDATE);
	if (IS_ERR(bo))
		return PTR_ERR(bo);
	guc->hwconfig.bo = bo;
	guc->hwconfig.size = size;

	return guc_hwconfig_copy(guc);
}

u32 xe_guc_hwconfig_size(struct xe_guc *guc)
{
	return !guc->hwconfig.bo ? 0 : guc->hwconfig.size;
}

void xe_guc_hwconfig_copy(struct xe_guc *guc, void *dst)
{
	struct xe_device *xe = guc_to_xe(guc);

	XE_WARN_ON(!guc->hwconfig.bo);

	xe_map_memcpy_from(xe, dst, &guc->hwconfig.bo->vmap, 0,
			   guc->hwconfig.size);
}

void xe_guc_hwconfig_dump(struct xe_guc *guc, struct drm_printer *p)
{
	size_t size = xe_guc_hwconfig_size(guc);
	u32 *hwconfig;
	u64 num_dw;
	u32 extra_bytes;
	int i = 0;

	if (size == 0) {
		drm_printf(p, "No hwconfig available\n");
		return;
	}

	num_dw = div_u64_rem(size, sizeof(u32), &extra_bytes);

	hwconfig = kzalloc(size, GFP_KERNEL);
	if (!hwconfig) {
		drm_printf(p, "Error: could not allocate hwconfig memory\n");
		return;
	}

	xe_guc_hwconfig_copy(guc, hwconfig);

	/* An entry requires at least three dwords for key, length, value */
	while (i + 3 <= num_dw) {
		u32 attribute = hwconfig[i++];
		u32 len_dw = hwconfig[i++];

		if (i + len_dw > num_dw) {
			drm_printf(p, "Error: Attribute %u is %u dwords, but only %llu remain\n",
				   attribute, len_dw, num_dw - i);
			len_dw = num_dw - i;
		}

		/*
		 * If it's a single dword (as most hwconfig attributes are),
		 * then it's probably a number that makes sense to display
		 * in decimal form.  In the rare cases where it's more than
		 * one dword, just print it in hex form and let the user
		 * figure out how to interpret it.
		 */
		if (len_dw == 1)
			drm_printf(p, "[%2u] = %u\n", attribute, hwconfig[i]);
		else
			drm_printf(p, "[%2u] = { %*ph }\n", attribute,
				   (int)(len_dw * sizeof(u32)), &hwconfig[i]);
		i += len_dw;
	}

	if (i < num_dw || extra_bytes)
		drm_printf(p, "Error: %llu extra bytes at end of hwconfig\n",
			   (num_dw - i) * sizeof(u32) + extra_bytes);

	kfree(hwconfig);
}

/*
 * Lookup a specific 32-bit attribute value in the GuC's hwconfig table.
 */
int xe_guc_hwconfig_lookup_u32(struct xe_guc *guc, u32 attribute, u32 *val)
{
	size_t size = xe_guc_hwconfig_size(guc);
	u64 num_dw = div_u64(size, sizeof(u32));
	u32 *hwconfig;
	bool found = false;
	int i = 0;

	if (num_dw == 0)
		return -EINVAL;

	hwconfig = kzalloc(size, GFP_KERNEL);
	if (!hwconfig)
		return -ENOMEM;

	xe_guc_hwconfig_copy(guc, hwconfig);

	/* An entry requires at least three dwords for key, length, value */
	while (i + 3 <= num_dw) {
		u32 key = hwconfig[i++];
		u32 len_dw = hwconfig[i++];

		if (key != attribute) {
			i += len_dw;
			continue;
		}

		*val = hwconfig[i];
		found = true;
		break;
	}

	kfree(hwconfig);

	return found ? 0 : -ENOENT;
}