summaryrefslogtreecommitdiff
path: root/amdgpu.c
blob: 35a900df60a28dcdbce1e437371f9cc516eebb2c (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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
/*
 * Copyright 2016 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#ifdef DRV_AMDGPU
#include <amdgpu.h>
#include <amdgpu_drm.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <xf86drm.h>

#include "drv_priv.h"
#include "helpers.h"
#include "util.h"
#include "ac_surface.h"
#include "ac_gpu_info.h"

// clang-format off
#define DRI_PATH "/usr/lib64/dri/radeonsi_dri.so"

typedef void* (*addr_create)(const struct radeon_info*,
                             const struct amdgpu_gpu_info*,
			     uint64_t*);

typedef int (*addr_destroy)(void*);

typedef int (*compute_surface)(void*,
				  const struct radeon_info*,
				  const struct ac_surf_config*,
		                  enum radeon_surf_mode,
				  struct radeon_surf*);

typedef bool (*query_gpu_info)(int, amdgpu_device_handle,
                       struct radeon_info*, struct amdgpu_gpu_info*);

typedef int (*read_write_tiled)(void*, struct ac_surf_config*,
			        struct radeon_surf*, void*,
				uint8_t*, int);

struct dri_functors {
	void			*handle;
	addr_create		fptr_create;
	addr_destroy		fptr_destroy;
	compute_surface		fptr_compute_surface;
	query_gpu_info		fptr_query_gpu_info;
	read_write_tiled	fptr_read_write_tiled;
};

struct drv_priv_ptr {
	struct dri_functors	*dri_handle;
	void			*addrlib_handle;
	enum radeon_surf_mode	surf_mode;
	struct radeon_info	info;
	struct ac_surf_config	surf_config;
	struct radeon_surf	*surf_info;
	uint64_t		base_align;
};

struct bo_priv_ptr {
	void			*untiled_buf_ptr;
	uint32_t		ref_count;
	int			prot;
};

// clang-format on
const static uint32_t render_target_formats[] = { DRM_FORMAT_ABGR8888, DRM_FORMAT_ARGB8888,
						  DRM_FORMAT_RGB565, DRM_FORMAT_XBGR8888,
						  DRM_FORMAT_XRGB8888 };

const static uint32_t texture_source_formats[] = { DRM_FORMAT_GR88, DRM_FORMAT_R8, DRM_FORMAT_NV21,
						   DRM_FORMAT_NV12 };

static int amdgpu_set_metadata(int fd, uint32_t handle, struct amdgpu_bo_metadata *info)
{
	struct drm_amdgpu_gem_metadata args = { 0 };

	if (!info)
		return -EINVAL;

	args.handle = handle;
	args.op = AMDGPU_GEM_METADATA_OP_SET_METADATA;
	args.data.flags = info->flags;
	args.data.tiling_info = info->tiling_info;

	if (info->size_metadata > sizeof(args.data.data))
		return -EINVAL;

	if (info->size_metadata) {
		args.data.data_size_bytes = info->size_metadata;
		memcpy(args.data.data, info->umd_metadata, info->size_metadata);
	}

	return drmCommandWriteRead(fd, DRM_AMDGPU_GEM_METADATA, &args, sizeof(args));
}

static int amdgpu_addrlib_compute(struct drv_priv_ptr *drv_priv, uint32_t width, uint32_t height,
				  uint32_t format, uint64_t use_flags, uint32_t *tiling_flags)
{
	struct radeon_surf *surf_info = drv_priv->surf_info;
	memset(surf_info, 0, sizeof(struct radeon_surf));
	struct ac_surf_config surf_config;

	drv_priv->surf_mode = RADEON_SURF_MODE_2D;

	surf_config.info.width = width;
	surf_config.info.height = height;
	surf_config.info.depth = 1;
	surf_config.info.samples = 1;
	surf_config.info.levels = 1;
	surf_config.info.array_size = 1;
	surf_config.is_3d = 0;
	surf_config.is_cube = 0;

	surf_info->bpe = drv_stride_from_format(format, 1, 0);

	if (use_flags &
	            (BO_USE_CURSOR | BO_USE_LINEAR | BO_USE_SW_READ_OFTEN | BO_USE_SW_WRITE_OFTEN)) {
		drv_priv->surf_mode = RADEON_SURF_MODE_LINEAR_ALIGNED;
	} else if (width <= 16 || height <= 16) {
		drv_priv->surf_mode = RADEON_SURF_MODE_1D;
	}

	if (use_flags & BO_USE_SCANOUT)
		surf_info->flags |= RADEON_SURF_SCANOUT;

	drv_priv->dri_handle->fptr_compute_surface(drv_priv->addrlib_handle, &drv_priv->info,
							&surf_config, drv_priv->surf_mode, surf_info);
	drv_priv->surf_mode = surf_info->u.legacy.level[0].mode;

	drv_priv->surf_config = surf_config;
	*tiling_flags |= AMDGPU_TILING_SET(ARRAY_MODE, 1 << (drv_priv->surf_mode - 1));
	*tiling_flags |= AMDGPU_TILING_SET(MICRO_TILE_MODE, surf_info->micro_tile_mode);

	*tiling_flags |= AMDGPU_TILING_SET(BANK_WIDTH, drv_log_base2(surf_info->u.legacy.bankw));
	*tiling_flags |= AMDGPU_TILING_SET(BANK_HEIGHT, drv_log_base2(surf_info->u.legacy.bankh));
	*tiling_flags |= AMDGPU_TILING_SET(TILE_SPLIT, drv_log_base2(surf_info->u.legacy.tile_split >> 6));
	*tiling_flags |= AMDGPU_TILING_SET(MACRO_TILE_ASPECT, drv_log_base2(surf_info->u.legacy.mtilea));
	*tiling_flags |= AMDGPU_TILING_SET(PIPE_CONFIG, surf_info->u.legacy.pipe_config);
	*tiling_flags |= AMDGPU_TILING_SET(NUM_BANKS, drv_log_base2(surf_info->u.legacy.num_banks >> 1));

	return 0;
}

static void *amdgpu_addrlib_init(int fd, struct dri_functors* dri_handle, struct radeon_info* info, uint64_t *base_align)
{
	int ret;
	struct amdgpu_gpu_info gpu_info = { 0 };
	amdgpu_device_handle dev;
	uint32_t drm_major, drm_minor;
	void *addr_handle = NULL;

	ret = amdgpu_device_initialize(fd, &drm_major, &drm_minor, &dev);
	if (ret) {
		return NULL;
	}
	if (!dri_handle->fptr_query_gpu_info(fd, dev, info, &gpu_info)) {
		return NULL;
	}

	amdgpu_device_deinitialize(dev);
	addr_handle = dri_handle->fptr_create(info, &gpu_info, base_align);
	return addr_handle;
}

static int amdgpu_init(struct driver *drv)
{
	void *addrlib;
	struct format_metadata metadata;
	uint64_t use_flags = BO_USE_RENDER_MASK;
	struct drv_priv_ptr* drv_priv = (struct drv_priv_ptr*) malloc (sizeof(struct drv_priv_ptr));
	drv_priv->surf_info = (struct radeon_surf*) malloc (sizeof(struct radeon_surf));
	drv_priv->dri_handle = (struct dri_functors*) malloc (sizeof(struct dri_functors));
	drv_priv->dri_handle->handle = dlopen(DRI_PATH, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
	if (!drv_priv->dri_handle->handle)
		return -1;

	drv_priv->dri_handle->fptr_create =
		(addr_create)dlsym(drv_priv->dri_handle->handle, "amdgpu_addr_create");
	if (!drv_priv->dri_handle->fptr_create)
		return -1;

	drv_priv->dri_handle->fptr_destroy =
		(addr_destroy)dlsym(drv_priv->dri_handle->handle, "amdgpu_addr_destroy");
	if (!drv_priv->dri_handle->fptr_destroy)
		return -1;

	drv_priv->dri_handle->fptr_compute_surface =
		(compute_surface)dlsym(drv_priv->dri_handle->handle, "ac_compute_surface");
	if (!drv_priv->dri_handle->fptr_compute_surface)
		return -1;

	drv_priv->dri_handle->fptr_query_gpu_info =
		(query_gpu_info)dlsym(drv_priv->dri_handle->handle, "ac_query_gpu_info");
	if (!drv_priv->dri_handle->fptr_query_gpu_info)
		return -1;

	drv_priv->dri_handle->fptr_read_write_tiled =
		(read_write_tiled)dlsym(drv_priv->dri_handle->handle, "ac_read_write_tiled_data");
	if (!drv_priv->dri_handle->fptr_read_write_tiled) {
		return -1;
	}

	addrlib = amdgpu_addrlib_init(drv_get_fd(drv), drv_priv->dri_handle, &drv_priv->info, &drv_priv->base_align);
	if (!addrlib)
		return -1;

	drv_priv->addrlib_handle = addrlib;
	drv->priv = drv_priv;

	drv_add_combinations(drv, texture_source_formats, ARRAY_SIZE(texture_source_formats),
			     &LINEAR_METADATA, BO_USE_TEXTURE_MASK);

	/* YUV format for camera */
	drv_modify_combination(drv, DRM_FORMAT_NV12, &LINEAR_METADATA,
			       BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE);
	/*
	 * R8 format is used for Android's HAL_PIXEL_FORMAT_BLOB and is used for JPEG snapshots
	 * from camera.
	 */
	drv_modify_combination(drv, DRM_FORMAT_R8, &LINEAR_METADATA,
			       BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE);

	drv_modify_combination(drv, DRM_FORMAT_NV21, &LINEAR_METADATA, BO_USE_SCANOUT);
	drv_modify_combination(drv, DRM_FORMAT_NV12, &LINEAR_METADATA, BO_USE_SCANOUT);

	metadata.tiling = RADEON_MICRO_MODE_DISPLAY << 16 | RADEON_SURF_MODE_LINEAR_ALIGNED;
	metadata.priority = 2;
	metadata.modifier = DRM_FORMAT_MOD_LINEAR;

	drv_add_combinations(drv, render_target_formats, ARRAY_SIZE(render_target_formats),
			     &metadata, use_flags);

	drv_modify_combination(drv, DRM_FORMAT_ARGB8888, &metadata, BO_USE_CURSOR | BO_USE_SCANOUT);
	drv_modify_combination(drv, DRM_FORMAT_XRGB8888, &metadata, BO_USE_CURSOR | BO_USE_SCANOUT);
	drv_modify_combination(drv, DRM_FORMAT_XBGR8888, &metadata, BO_USE_SCANOUT);

	metadata.tiling = RADEON_MICRO_MODE_THIN << 16 | RADEON_SURF_MODE_LINEAR_ALIGNED;
	metadata.priority = 3;
	metadata.modifier = DRM_FORMAT_MOD_LINEAR;

	drv_add_combinations(drv, render_target_formats, ARRAY_SIZE(render_target_formats),
			     &metadata, use_flags);

	use_flags &= ~BO_USE_SW_WRITE_OFTEN;
	use_flags &= ~BO_USE_SW_READ_OFTEN;
	use_flags &= ~BO_USE_LINEAR;

	metadata.tiling = RADEON_MICRO_MODE_DISPLAY << 16 | RADEON_SURF_MODE_2D;
	metadata.priority = 4;

	drv_add_combinations(drv, render_target_formats, ARRAY_SIZE(render_target_formats),
			     &metadata, use_flags);

	drv_modify_combination(drv, DRM_FORMAT_ARGB8888, &metadata, BO_USE_SCANOUT);
	drv_modify_combination(drv, DRM_FORMAT_XRGB8888, &metadata, BO_USE_SCANOUT);
	drv_modify_combination(drv, DRM_FORMAT_XBGR8888, &metadata, BO_USE_SCANOUT);

	metadata.tiling = RADEON_MICRO_MODE_THIN << 16 | RADEON_SURF_MODE_2D;
	metadata.priority = 5;

	drv_add_combinations(drv, render_target_formats, ARRAY_SIZE(render_target_formats),
			     &metadata, use_flags);

	return 0;
}

static void amdgpu_close(struct driver *drv)
{
	struct drv_priv_ptr* drv_priv = (struct drv_priv_ptr*)drv->priv;
	drv_priv->dri_handle->fptr_destroy(drv_priv->addrlib_handle);
	drv_priv->addrlib_handle = NULL;
	free(drv_priv->surf_info);
	drv_priv->surf_info = NULL;
	dlclose(drv_priv->dri_handle->handle);
	free(drv_priv->dri_handle);
	drv_priv->dri_handle = NULL;
	drv->priv = NULL;
}

static int amdgpu_bo_create(struct bo *bo, uint32_t width, uint32_t height, uint32_t format,
			    uint64_t use_flags)
{
	struct drv_priv_ptr *drv_priv = (struct drv_priv_ptr*)bo->drv->priv;
	struct bo_priv_ptr *bo_priv = (struct bo_priv_ptr*)bo->priv;
	union drm_amdgpu_gem_create gem_create;
	struct amdgpu_bo_metadata metadata = { 0 };
	uint32_t tiling_flags = 0;
	size_t plane;
	int ret;
	struct driver* tmp_drv = bo->drv;

	if (bo->priv == NULL) {
		bo_priv = (struct bo_priv_ptr*) malloc (sizeof(struct bo_priv_ptr));
		bo_priv->ref_count = 0;
		bo_priv->untiled_buf_ptr = NULL;
		bo->priv = bo_priv;
	} else {
		bo_priv = (struct bo_priv_ptr*)bo->priv;
	}

	if (format == DRM_FORMAT_NV12 || format == DRM_FORMAT_NV21) {
		drv_bo_from_format(bo, ALIGN(width, 64), height, format);
	} else {
		if (amdgpu_addrlib_compute(drv_priv, width, height, format, use_flags,
					   &tiling_flags) < 0) {
			return -EINVAL;
		}
		bo->tiling = tiling_flags;
		/* RGB has 1 plane only */
		bo->offsets[0] = drv_priv->surf_info->u.legacy.level[0].offset;
		bo->total_size = bo->sizes[0] = drv_priv->surf_info->surf_size;

		bo->strides[0] = drv_stride_from_format(format,
					drv_priv->surf_info->u.legacy.level[0].nblk_x,0);
	}

	//FIXME:drv pointer gets changed after amdgpu_addrlib_compute call
	//store it in temp pointer
	bo->drv = tmp_drv;

	memset(&gem_create, 0, sizeof(gem_create));

	gem_create.in.bo_size = bo->total_size;
	gem_create.in.alignment = drv_priv->surf_info->surf_alignment;

	/* Set the placement. */
	gem_create.in.domains = AMDGPU_GEM_DOMAIN_VRAM;
	gem_create.in.domain_flags = AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED;
	/* Allocate the buffer with the preferred heap. */

	ret = drmCommandWriteRead(drv_get_fd(bo->drv), DRM_AMDGPU_GEM_CREATE, &gem_create,
				  sizeof(gem_create));

	if (ret < 0) {
		fprintf(stderr, "drmCommandWriteRead failed with %d\n", ret);
		return ret;
	}

	metadata.tiling_info = tiling_flags;

	for (plane = 0; plane < bo->num_planes; plane++)
		bo->handles[plane].u32 = gem_create.out.handle;

	ret = amdgpu_set_metadata(drv_get_fd(bo->drv), bo->handles[0].u32, &metadata);

	return ret;
}

static int amdgpu_gbm_bo_import(struct bo *bo, struct drv_import_fd_data *data)
{
	struct drv_priv_ptr *drv_priv = (struct drv_priv_ptr*)bo->drv->priv;
	struct bo_priv_ptr *bo_priv = NULL;
	uint32_t width = data->width;
	uint32_t height = data->height;
	uint32_t format = data->format;
	uint32_t usage = data->use_flags;
	uint32_t tiling_flags = 0;

	if (bo->priv == NULL) {
		bo_priv = (struct bo_priv_ptr*) malloc (sizeof(struct bo_priv_ptr));
		bo_priv->ref_count = 0;
		bo->priv = bo_priv;
	} else {
		bo_priv = (struct bo_priv_ptr*)bo->priv;
	}

	if (format == DRM_FORMAT_NV12 || format == DRM_FORMAT_NV21) {
		drv_bo_from_format(bo, ALIGN(width, 64), height, format);
	} else {
		if (amdgpu_addrlib_compute(drv_priv, width, height, format, usage,
						&tiling_flags) < 0) {
			return -EINVAL;
		}
	}
	return drv_prime_bo_import(bo, data);
}

static void *amdgpu_bo_map(struct bo *bo, struct vma *vma, size_t plane, uint32_t map_flags)
{
	int ret;
	union drm_amdgpu_gem_mmap gem_map;
	void *addr = NULL;
	int prot = drv_get_prot(map_flags);
	struct bo_priv_ptr *bo_priv = (struct bo_priv_ptr*)bo->priv;
	struct drv_priv_ptr *drv_priv = (struct drv_priv_ptr*)bo->drv->priv;

	memset(&gem_map, 0, sizeof(gem_map));
	gem_map.in.handle = bo->handles[plane].u32;

	ret = drmIoctl(bo->drv->fd, DRM_IOCTL_AMDGPU_GEM_MMAP, &gem_map);
	if (ret) {
		fprintf(stderr, "drv: DRM_IOCTL_AMDGPU_GEM_MMAP failed\n");
		return MAP_FAILED;
	}

	vma->length = bo->total_size;
	addr = mmap(0, bo->total_size, prot, MAP_SHARED, bo->drv->fd, gem_map.out.addr_ptr);
	if (drv_priv->surf_mode >= RADEON_SURF_MODE_2D) {
		if (bo_priv->ref_count == 0) {
			uint32_t buf_size = drv_priv->surf_info->u.legacy.level[0].nblk_x *
			                    drv_priv->surf_config.info.height *
					    (drv_priv->surf_info->bpe);
			bo_priv->untiled_buf_ptr = malloc(buf_size);
			if (bo_priv->untiled_buf_ptr == NULL) {
				munmap(addr, vma->length);
				return NULL;
			}
		}
		bo_priv->prot = prot;
		bo_priv->ref_count++;
		drv_priv->dri_handle->fptr_read_write_tiled(drv_priv->addrlib_handle,
		                                           &drv_priv->surf_config,
							   drv_priv->surf_info, addr,
							   (uint8_t*)bo_priv->untiled_buf_ptr,
							   0);
		return bo_priv->untiled_buf_ptr;
	}
	bo_priv->ref_count++;
	return addr;
}

static int amdgpu_bo_unmap(struct bo *bo, struct vma *vma)
{
	struct bo_priv_ptr *bo_priv = (struct bo_priv_ptr *)bo->priv;
	struct drv_priv_ptr *drv_priv = (struct drv_priv_ptr *)bo->drv->priv;
	bo_priv->ref_count--;
	if (drv_priv->surf_mode >= RADEON_SURF_MODE_2D) {
		if (bo_priv->untiled_buf_ptr != NULL) {
			if (bo_priv->prot & PROT_WRITE)
				drv_priv->dri_handle->fptr_read_write_tiled(drv_priv->addrlib_handle,
		                                                           &drv_priv->surf_config,
				                                           drv_priv->surf_info,
									   vma->addr,
				                                           (uint8_t*)bo_priv->untiled_buf_ptr,
				                                           1);
			if (bo_priv->ref_count == 0) {
				free(bo_priv->untiled_buf_ptr);
				bo_priv->untiled_buf_ptr = NULL;
			}
		}
	}
	return munmap(vma->addr, vma->length);
}
 
static uint32_t amdgpu_resolve_format(uint32_t format, uint64_t use_flags)
{
	switch (format) {
	case DRM_FORMAT_FLEX_IMPLEMENTATION_DEFINED:
		/* Camera subsystem requires NV12. */
		if (use_flags & (BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE))
			return DRM_FORMAT_NV12;
		/*HACK: See b/28671744 */
		return DRM_FORMAT_XBGR8888;
	case DRM_FORMAT_FLEX_YCbCr_420_888:
		return DRM_FORMAT_NV12;
	default:
		return format;
	}
}

const struct backend backend_amdgpu = {
	.name = "amdgpu",
	.init = amdgpu_init,
	.close = amdgpu_close,
	.bo_create = amdgpu_bo_create,
	.bo_destroy = drv_gem_bo_destroy,
	.bo_import = amdgpu_gbm_bo_import,
	.bo_map = amdgpu_bo_map,
	.bo_unmap = amdgpu_bo_unmap,
	.resolve_format = amdgpu_resolve_format,
};

#endif