summaryrefslogtreecommitdiff
path: root/radeon/bof.c
blob: 0598cc6bc0fa0e9fba0664e6e077333fef447276 (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
/*
 * Copyright 2010 Jerome Glisse <glisse@freedesktop.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * on the rights to use, copy, modify, merge, publish, distribute, sub
 * license, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *      Jerome Glisse
 */
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "bof.h"

/*
 * helpers
 */
static int bof_entry_grow(bof_t *bof)
{
	bof_t **array;

	if (bof->array_size < bof->nentry)
		return 0;
	array = realloc(bof->array, (bof->nentry + 16) * sizeof(void*));
	if (array == NULL)
		return -ENOMEM;
	bof->array = array;
	bof->nentry += 16;
	return 0;
}

/*
 * object 
 */
bof_t *bof_object(void)
{
	bof_t *object;

	object = calloc(1, sizeof(bof_t));
	if (object == NULL)
		return NULL;
	object->refcount = 1;
	object->type = BOF_TYPE_OBJECT;
	object->size = 12;
	return object;
}

bof_t *bof_object_get(bof_t *object, const char *keyname)
{
	unsigned i;

	for (i = 0; i < object->array_size; i += 2) {
		if (!strcmp(object->array[i]->value, keyname)) {
			return object->array[i + 1];
		}
	}
	return NULL;
}

int bof_object_set(bof_t *object, const char *keyname, bof_t *value)
{
	bof_t *key;
	int r;

	if (object->type != BOF_TYPE_OBJECT)
		return -EINVAL;
	r = bof_entry_grow(object);
	if (r)
		return r;
	key = bof_string(keyname);
	if (key == NULL)
		return -ENOMEM;
	object->array[object->array_size++] = key;
	object->array[object->array_size++] = value;
	object->size += value->size;
	object->size += key->size;
	bof_incref(value);
	return 0;
}

/*
 * array
 */
bof_t *bof_array(void)
{
	bof_t *array = bof_object();

	if (array == NULL)
		return NULL;
	array->type = BOF_TYPE_ARRAY;
	array->size = 12;
	return array;
}

int bof_array_append(bof_t *array, bof_t *value)
{
	int r;
	if (array->type != BOF_TYPE_ARRAY)
		return -EINVAL;
	r = bof_entry_grow(array);
	if (r)
		return r;
	array->array[array->array_size++] = value;
	array->size += value->size;
	bof_incref(value);
	return 0;
}

bof_t *bof_array_get(bof_t *bof, unsigned i)
{
	if (!bof_is_array(bof) || i >= bof->array_size)
		return NULL;
	return bof->array[i];
}

unsigned bof_array_size(bof_t *bof)
{
	if (!bof_is_array(bof))
		return 0;
	return bof->array_size;
}

/*
 * blob
 */
bof_t *bof_blob(unsigned size, void *value)
{
	bof_t *blob = bof_object();

	if (blob == NULL)
		return NULL;
	blob->type = BOF_TYPE_BLOB;
	blob->value = calloc(1, size);
	if (blob->value == NULL) {
		bof_decref(blob);
		return NULL;
	}
	blob->size = size;
	memcpy(blob->value, value, size);
	blob->size += 12;
	return blob;
}

unsigned bof_blob_size(bof_t *bof)
{
	if (!bof_is_blob(bof))
		return 0;
	return bof->size - 12;
}

void *bof_blob_value(bof_t *bof)
{
	if (!bof_is_blob(bof))
		return NULL;
	return bof->value;
}

/*
 * string
 */
bof_t *bof_string(const char *value)
{
	bof_t *string = bof_object();

	if (string == NULL)
		return NULL;
	string->type = BOF_TYPE_STRING;
	string->size = strlen(value) + 1;
	string->value = calloc(1, string->size);
	if (string->value == NULL) {
		bof_decref(string);
		return NULL;
	}
	strcpy(string->value, value);
	string->size += 12;
	return string;
}

/*
 *  int32
 */
bof_t *bof_int32(int32_t value)
{
	bof_t *int32 = bof_object();

	if (int32 == NULL)
		return NULL;
	int32->type = BOF_TYPE_INT32;
	int32->size = 4;
	int32->value = calloc(1, int32->size);
	if (int32->value == NULL) {
		bof_decref(int32);
		return NULL;
	}
	memcpy(int32->value, &value, 4);
	int32->size += 12;
	return int32;
}

int32_t bof_int32_value(bof_t *bof)
{
	return *((uint32_t*)bof->value);
}

/*
 *  common
 */
static void bof_indent(int level)
{
	int i;

	for (i = 0; i < level; i++)
		fprintf(stderr, " ");
}

static void bof_print_bof(bof_t *bof, int level, int entry)
{
	bof_indent(level);
	if (bof == NULL) {
		fprintf(stderr, "--NULL-- for entry %d\n", entry);
		return;
	}
	switch (bof->type) {
	case BOF_TYPE_STRING:
		fprintf(stderr, "%p string [%s %d]\n", bof, (char*)bof->value, bof->size);
		break;
	case BOF_TYPE_INT32:
		fprintf(stderr, "%p int32 [%d %d]\n", bof, *(int*)bof->value, bof->size);
		break;
	case BOF_TYPE_BLOB:
		fprintf(stderr, "%p blob [%d]\n", bof, bof->size);
		break;
	case BOF_TYPE_NULL:
		fprintf(stderr, "%p null [%d]\n", bof, bof->size);
		break;
	case BOF_TYPE_OBJECT:
		fprintf(stderr, "%p object [%d %d]\n", bof, bof->array_size / 2, bof->size);
		break;
	case BOF_TYPE_ARRAY:
		fprintf(stderr, "%p array [%d %d]\n", bof, bof->array_size, bof->size);
		break;
	default:
		fprintf(stderr, "%p unknown [%d]\n", bof, bof->type);
		return;
	}
}

static void bof_print_rec(bof_t *bof, int level, int entry)
{
	unsigned i;

	bof_print_bof(bof, level, entry);
	for (i = 0; i < bof->array_size; i++) {
		bof_print_rec(bof->array[i], level + 2, i);
	}
}

void bof_print(bof_t *bof)
{
	bof_print_rec(bof, 0, 0);
}

static int bof_read(bof_t *root, FILE *file, long end, int level)
{
	bof_t *bof = NULL;
	int r;

	if (ftell(file) >= end) {
		return 0;
	}
	r = bof_entry_grow(root);
	if (r)
		return r;
	bof = bof_object();
	if (bof == NULL)
		return -ENOMEM;
	bof->offset = ftell(file);
	r = fread(&bof->type, 4, 1, file);
	if (r != 1)
		goto out_err;
	r = fread(&bof->size, 4, 1, file);
	if (r != 1)
		goto out_err;
	r = fread(&bof->array_size, 4, 1, file);
	if (r != 1)
		goto out_err;
	switch (bof->type) {
	case BOF_TYPE_STRING:
	case BOF_TYPE_INT32:
	case BOF_TYPE_BLOB:
		bof->value = calloc(1, bof->size - 12);
		if (bof->value == NULL) {
			goto out_err;
		}
		r = fread(bof->value, bof->size - 12, 1, file);
		if (r != 1) {
			fprintf(stderr, "error reading %d\n", bof->size - 12);
			goto out_err;
		}
		break;
	case BOF_TYPE_NULL:
		return 0;
	case BOF_TYPE_OBJECT:
	case BOF_TYPE_ARRAY:
		r = bof_read(bof, file, bof->offset + bof->size, level + 2);
		if (r)
			goto out_err;
		break;
	default:
		fprintf(stderr, "invalid type %d\n", bof->type);
		goto out_err;
	}
	root->array[root->centry++] = bof;
	return bof_read(root, file, end, level);
out_err:
	bof_decref(bof);
	return -EINVAL;
}

bof_t *bof_load_file(const char *filename)
{
	bof_t *root = bof_object();
	int r;

	if (root == NULL) {
		fprintf(stderr, "%s failed to create root object\n", __func__);
		return NULL;
	}
	root->file = fopen(filename, "r");
	if (root->file == NULL)
		goto out_err;
	r = fseek(root->file, 0L, SEEK_SET);
	if (r) {
		fprintf(stderr, "%s failed to seek into file %s\n", __func__, filename);
		goto out_err;
	}
	root->offset = ftell(root->file);
	r = fread(&root->type, 4, 1, root->file);
	if (r != 1)
		goto out_err;
	r = fread(&root->size, 4, 1, root->file);
	if (r != 1)
		goto out_err;
	r = fread(&root->array_size, 4, 1, root->file);
	if (r != 1)
		goto out_err;
	r = bof_read(root, root->file, root->offset + root->size, 2);
	if (r)
		goto out_err;
	return root;
out_err:
	bof_decref(root);
	return NULL;
}

void bof_incref(bof_t *bof)
{
	bof->refcount++;
}

void bof_decref(bof_t *bof)
{
	unsigned i;

	if (bof == NULL)
		return;
	if (--bof->refcount > 0)
		return;
	for (i = 0; i < bof->array_size; i++) {
		bof_decref(bof->array[i]);
		bof->array[i] = NULL;
	}
	bof->array_size = 0;
	if (bof->file) {
		fclose(bof->file);
		bof->file = NULL;
	}
	free(bof->array);
	free(bof->value);
	free(bof);
}

static int bof_file_write(bof_t *bof, FILE *file)
{
	unsigned i;
	int r;

	r = fwrite(&bof->type, 4, 1, file);
	if (r != 1)
		return -EINVAL;
	r = fwrite(&bof->size, 4, 1, file);
	if (r != 1)
		return -EINVAL;
	r = fwrite(&bof->array_size, 4, 1, file);
	if (r != 1)
		return -EINVAL;
	switch (bof->type) {
	case BOF_TYPE_NULL:
		if (bof->size)
			return -EINVAL;
		break;
	case BOF_TYPE_STRING:
	case BOF_TYPE_INT32:
	case BOF_TYPE_BLOB:
		r = fwrite(bof->value, bof->size - 12, 1, file);
		if (r != 1)
			return -EINVAL;
		break;
	case BOF_TYPE_OBJECT:
	case BOF_TYPE_ARRAY:
		for (i = 0; i < bof->array_size; i++) {
			r = bof_file_write(bof->array[i], file);
			if (r)
				return r;
		}
		break;
	default:
		return -EINVAL;
	}
	return 0;
}

int bof_dump_file(bof_t *bof, const char *filename)
{
	unsigned i;
	int r = 0;

	if (bof->file) {
		fclose(bof->file);
		bof->file = NULL;
	}
	bof->file = fopen(filename, "w");
	if (bof->file == NULL) {
		fprintf(stderr, "%s failed to open file %s\n", __func__, filename);
		r = -EINVAL;
		goto out_err;
	}
	r = fseek(bof->file, 0L, SEEK_SET);
	if (r) {
		fprintf(stderr, "%s failed to seek into file %s\n", __func__, filename);
		goto out_err;
	}
	r = fwrite(&bof->type, 4, 1, bof->file);
	if (r != 1)
		goto out_err;
	r = fwrite(&bof->size, 4, 1, bof->file);
	if (r != 1)
		goto out_err;
	r = fwrite(&bof->array_size, 4, 1, bof->file);
	if (r != 1)
		goto out_err;
	for (i = 0; i < bof->array_size; i++) {
		r = bof_file_write(bof->array[i], bof->file);
		if (r)
			return r;
	}
out_err:
	fclose(bof->file);
	bof->file = NULL;
	return r;
}
an>pgpuobj : NULL); if (!dev_priv || !pgpuobj || !(*pgpuobj)) return -EINVAL; gpuobj = *pgpuobj; if (gpuobj->refcount != 0) { DRM_ERROR("gpuobj refcount is %d\n", gpuobj->refcount); return -EINVAL; } engine->instmem.clear(dev, gpuobj); if (gpuobj->im_pramin) { if (gpuobj->flags & NVOBJ_FLAG_FAKE) drm_free(gpuobj->im_pramin, sizeof(*gpuobj->im_pramin), DRM_MEM_DRIVER); else nouveau_mem_free_block(gpuobj->im_pramin); } if (gpuobj->next) gpuobj->next->prev = gpuobj->prev; if (gpuobj->prev) gpuobj->prev->next = gpuobj->next; else dev_priv->gpuobj_all = gpuobj->next; *pgpuobj = NULL; drm_free(gpuobj, sizeof(*gpuobj), DRM_MEM_DRIVER); return 0; } static int nouveau_gpuobj_instance_get(struct drm_device *dev, int channel, struct nouveau_gpuobj *gpuobj, uint32_t *inst) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *cpramin; /* <NV50 use PRAMIN address everywhere */ if (dev_priv->card_type < NV_50) { *inst = gpuobj->im_pramin->start; return 0; } if ((channel > 0) && gpuobj->im_channel != channel) { DRM_ERROR("Channel mismatch: obj %d, ref %d\n", gpuobj->im_channel, channel); return -EINVAL; } /* NV50 channel-local instance */ if (channel > 0) { cpramin = dev_priv->fifos[channel]->ramin->gpuobj; *inst = gpuobj->im_pramin->start - cpramin->im_pramin->start; return 0; } /* NV50 global (VRAM) instance */ if (gpuobj->im_channel < 0) { /* ...from global heap */ if (!gpuobj->im_backing) { DRM_ERROR("AII, no VRAM backing gpuobj\n"); return -EINVAL; } *inst = gpuobj->im_backing->start; return 0; } else { /* ...from local heap */ cpramin = dev_priv->fifos[gpuobj->im_channel]->ramin->gpuobj; *inst = cpramin->im_backing->start + (gpuobj->im_pramin->start - cpramin->im_pramin->start); return 0; } return -EINVAL; } int nouveau_gpuobj_ref_add(struct drm_device *dev, int channel, uint32_t handle, struct nouveau_gpuobj *gpuobj, struct nouveau_gpuobj_ref **ref_ret) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_fifo *chan = NULL; struct nouveau_gpuobj_ref *ref; uint32_t instance; int ret; DRM_DEBUG("ch%d h=0x%08x gpuobj=%p\n", channel, handle, gpuobj); if (!dev_priv || !gpuobj || (ref_ret && *ref_ret != NULL)) return -EINVAL; if (channel >= 0) { if (channel > nouveau_fifo_number(dev)) return -EINVAL; chan = dev_priv->fifos[channel]; } else if (!ref_ret) return -EINVAL; ret = nouveau_gpuobj_instance_get(dev, channel, gpuobj, &instance); if (ret) return ret; ref = drm_calloc(1, sizeof(*ref), DRM_MEM_DRIVER); if (!ref) return -ENOMEM; ref->gpuobj = gpuobj; ref->channel = channel; ref->instance = instance; if (!ref_ret) { ref->handle = handle; ret = nouveau_ramht_insert(dev, ref); if (ret) { drm_free(ref, sizeof(*ref), DRM_MEM_DRIVER); return ret; } ref->next = chan->ramht_refs; chan->ramht_refs = ref; } else { ref->handle = ~0; *ref_ret = ref; } ref->gpuobj->refcount++; return 0; } int nouveau_gpuobj_ref_del(struct drm_device *dev, struct nouveau_gpuobj_ref **pref) { struct nouveau_gpuobj_ref *ref; DRM_DEBUG("ref %p\n", pref ? *pref : NULL); if (!dev || !pref || *pref == NULL) return -EINVAL; ref = *pref; if (ref->handle != ~0) nouveau_ramht_remove(dev, ref); if (ref->gpuobj) { ref->gpuobj->refcount--; if (ref->gpuobj->refcount == 0) { if (!(ref->gpuobj->flags & NVOBJ_FLAG_ALLOW_NO_REFS)) nouveau_gpuobj_del(dev, &ref->gpuobj); } } *pref = NULL; drm_free(ref, sizeof(ref), DRM_MEM_DRIVER); return 0; } int nouveau_gpuobj_new_ref(struct drm_device *dev, int oc, int rc, uint32_t handle, int size, int align, uint32_t flags, struct nouveau_gpuobj_ref **ref) { struct nouveau_gpuobj *gpuobj = NULL; int ret; if ((ret = nouveau_gpuobj_new(dev, oc, size, align, flags, &gpuobj))) return ret; if ((ret = nouveau_gpuobj_ref_add(dev, rc, handle, gpuobj, ref))) { nouveau_gpuobj_del(dev, &gpuobj); return ret; } return 0; } static int nouveau_gpuobj_ref_find(struct drm_device *dev, int channel, uint32_t handle, struct nouveau_gpuobj_ref **ref_ret) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_fifo *chan = dev_priv->fifos[channel]; struct nouveau_gpuobj_ref *ref = chan->ramht_refs; while (ref) { if (ref->handle == handle) { if (ref_ret) *ref_ret = ref; return 0; } ref = ref->next; } return -EINVAL; } int nouveau_gpuobj_new_fake(struct drm_device *dev, uint32_t offset, uint32_t size, uint32_t flags, struct nouveau_gpuobj **pgpuobj, struct nouveau_gpuobj_ref **pref) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *gpuobj = NULL; int i; DRM_DEBUG("offset=0x%08x size=0x%08x flags=0x%08x\n", offset, size, flags); gpuobj = drm_calloc(1, sizeof(*gpuobj), DRM_MEM_DRIVER); if (!gpuobj) return -ENOMEM; DRM_DEBUG("gpuobj %p\n", gpuobj); gpuobj->im_channel = -1; gpuobj->flags = flags | NVOBJ_FLAG_FAKE; gpuobj->im_pramin = drm_calloc(1, sizeof(struct mem_block), DRM_MEM_DRIVER); if (!gpuobj->im_pramin) { nouveau_gpuobj_del(dev, &gpuobj); return -ENOMEM; } gpuobj->im_pramin->start = offset; gpuobj->im_pramin->size = size; if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) { for (i = 0; i < gpuobj->im_pramin->size; i += 4) INSTANCE_WR(gpuobj, i/4, 0); } if (pref) { if ((i = nouveau_gpuobj_ref_add(dev, -1, 0, gpuobj, pref))) { nouveau_gpuobj_del(dev, &gpuobj); return i; } } if (pgpuobj) *pgpuobj = gpuobj; return 0; } static int nouveau_gpuobj_class_instmem_size(struct drm_device *dev, int class) { struct drm_nouveau_private *dev_priv = dev->dev_private; /*XXX: dodgy hack for now */ if (dev_priv->card_type >= NV_50) return 24; if (dev_priv->card_type >= NV_40) return 32; return 16; } /* DMA objects are used to reference a piece of memory in the framebuffer, PCI or AGP address space. Each object is 16 bytes big and looks as follows: entry[0] 11:0 class (seems like I can always use 0 here) 12 page table present? 13 page entry linear? 15:14 access: 0 rw, 1 ro, 2 wo 17:16 target: 0 NV memory, 1 NV memory tiled, 2 PCI, 3 AGP 31:20 dma adjust (bits 0-11 of the address) entry[1] dma limit (size of transfer) entry[X] 1 0 readonly, 1 readwrite 31:12 dma frame address of the page (bits 12-31 of the address) entry[N] page table terminator, same value as the first pte, as does nvidia rivatv uses 0xffffffff Non linear page tables need a list of frame addresses afterwards, the rivatv project has some info on this. The method below creates a DMA object in instance RAM and returns a handle to it that can be used to set up context objects. */ int nouveau_gpuobj_dma_new(struct drm_device *dev, int channel, int class, uint64_t offset, uint64_t size, int access, int target, struct nouveau_gpuobj **gpuobj) { struct drm_nouveau_private *dev_priv = dev->dev_private; int ret; uint32_t is_scatter_gather = 0; /* Total number of pages covered by the request. */ const unsigned int page_count = (size + PAGE_SIZE - 1) / PAGE_SIZE; DRM_DEBUG("ch%d class=0x%04x offset=0x%llx size=0x%llx\n", channel, class, offset, size); DRM_DEBUG("access=%d target=%d\n", access, target); switch (target) { case NV_DMA_TARGET_AGP: offset += dev_priv->gart_info.aper_base; break; case NV_DMA_TARGET_PCI_NONLINEAR: /*assume the "offset" is a virtual memory address*/ is_scatter_gather = 1; /*put back the right value*/ target = NV_DMA_TARGET_PCI; break; default: break; } ret = nouveau_gpuobj_new(dev, channel, is_scatter_gather ? ((page_count << 2) + 12) : nouveau_gpuobj_class_instmem_size(dev, class), 16, NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ZERO_FREE, gpuobj); if (ret) { DRM_ERROR("Error creating gpuobj: %d\n", ret); return ret; } if (dev_priv->card_type < NV_50) { uint32_t frame, adjust, pte_flags = 0; adjust = offset & 0x00000fff; if (access != NV_DMA_ACCESS_RO) pte_flags |= (1<<1); if ( ! is_scatter_gather ) { frame = offset & ~0x00000fff; INSTANCE_WR(*gpuobj, 0, ((1<<12) | (1<<13) | (adjust << 20) | (access << 14) | (target << 16) | class)); INSTANCE_WR(*gpuobj, 1, size - 1); INSTANCE_WR(*gpuobj, 2, frame | pte_flags); INSTANCE_WR(*gpuobj, 3, frame | pte_flags); } else { /* Intial page entry in the scatter-gather area that * corresponds to the base offset */ unsigned int idx = offset / PAGE_SIZE; uint32_t instance_offset; unsigned int i; if ((idx + page_count) > dev->sg->pages) { DRM_ERROR("Requested page range exceedes " "allocated scatter-gather range!"); return -E2BIG; } DRM_DEBUG("Creating PCI DMA object using virtual zone starting at %#llx, size %d\n", offset, (uint32_t)size); INSTANCE_WR(*gpuobj, 0, ((1<<12) | (0<<13) | (adjust << 20) | (access << 14) | (target << 16) | class)); INSTANCE_WR(*gpuobj, 1, (uint32_t) size-1); /*write starting at the third dword*/ instance_offset = 2; /*for each PAGE, get its bus address, fill in the page table entry, and advance*/ for (i = 0; i < page_count; i++) { if (dev->sg->busaddr[idx] == 0) { dev->sg->busaddr[idx] = pci_map_page(dev->pdev, dev->sg->pagelist[idx], 0, PAGE_SIZE, DMA_BIDIRECTIONAL); if (dma_mapping_error(dev->sg->busaddr[idx])) { return -ENOMEM; } } frame = (uint32_t) dev->sg->busaddr[idx]; INSTANCE_WR(*gpuobj, instance_offset, frame | pte_flags); idx++; instance_offset ++; } } } else { uint32_t flags0, flags5; if (target == NV_DMA_TARGET_VIDMEM) { flags0 = 0x00190000; flags5 = 0x00010000; } else { flags0 = 0x7fc00000; flags5 = 0x00080000; } INSTANCE_WR(*gpuobj, 0, flags0 | class); INSTANCE_WR(*gpuobj, 1, offset + size - 1); INSTANCE_WR(*gpuobj, 2, offset); INSTANCE_WR(*gpuobj, 5, flags5); } (*gpuobj)->engine = NVOBJ_ENGINE_SW; (*gpuobj)->class = class; return 0; } int nouveau_gpuobj_gart_dma_new(struct drm_device *dev, int channel, uint64_t offset, uint64_t size, int access, struct nouveau_gpuobj **gpuobj, uint32_t *o_ret) { struct drm_nouveau_private *dev_priv = dev->dev_private; int ret; if (dev_priv->gart_info.type == NOUVEAU_GART_AGP || (dev_priv->card_type >= NV_50 && dev_priv->gart_info.type == NOUVEAU_GART_SGDMA)) { ret = nouveau_gpuobj_dma_new(dev, channel, NV_CLASS_DMA_IN_MEMORY, offset, size, access, NV_DMA_TARGET_AGP, gpuobj); if (o_ret) *o_ret = 0; } else if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA) { *gpuobj = dev_priv->gart_info.sg_ctxdma; if (offset & ~0xffffffffULL) { DRM_ERROR("obj offset exceeds 32-bits\n"); return -EINVAL; } if (o_ret) *o_ret = (uint32_t)offset; ret = (*gpuobj != NULL) ? 0 : -EINVAL; } else { DRM_ERROR("Invalid GART type %d\n", dev_priv->gart_info.type); return -EINVAL; } return ret; } /* Context objects in the instance RAM have the following structure. * On NV40 they are 32 byte long, on NV30 and smaller 16 bytes. NV4 - NV30: entry[0] 11:0 class 12 chroma key enable 13 user clip enable 14 swizzle enable 17:15 patch config: scrcopy_and, rop_and, blend_and, scrcopy, srccopy_pre, blend_pre 18 synchronize enable 19 endian: 1 big, 0 little 21:20 dither mode 23 single step enable 24 patch status: 0 invalid, 1 valid 25 context_surface 0: 1 valid 26 context surface 1: 1 valid 27 context pattern: 1 valid 28 context rop: 1 valid 29,30 context beta, beta4 entry[1] 7:0 mono format 15:8 color format 31:16 notify instance address entry[2] 15:0 dma 0 instance address 31:16 dma 1 instance address entry[3] dma method traps NV40: No idea what the exact format is. Here's what can be deducted: entry[0]: 11:0 class (maybe uses more bits here?) 17 user clip enable 21:19 patch config 25 patch status valid ? entry[1]: 15:0 DMA notifier (maybe 20:0) entry[2]: 15:0 DMA 0 instance (maybe 20:0) 24 big endian entry[3]: 15:0 DMA 1 instance (maybe 20:0) entry[4]: entry[5]: set to 0? */ int nouveau_gpuobj_gr_new(struct drm_device *dev, int channel, int class, struct nouveau_gpuobj **gpuobj) { struct drm_nouveau_private *dev_priv = dev->dev_private; int ret; DRM_DEBUG("ch%d class=0x%04x\n", channel, class); ret = nouveau_gpuobj_new(dev, channel, nouveau_gpuobj_class_instmem_size(dev, class), 16, NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ZERO_FREE, gpuobj); if (ret) { DRM_ERROR("Error creating gpuobj: %d\n", ret); return ret; } if (dev_priv->card_type >= NV_50) { INSTANCE_WR(*gpuobj, 0, class); INSTANCE_WR(*gpuobj, 5, 0x00010000); } else { switch (class) { case NV_CLASS_NULL: INSTANCE_WR(*gpuobj, 0, 0x00001030); INSTANCE_WR(*gpuobj, 1, 0xFFFFFFFF); break; default: if (dev_priv->card_type >= NV_40) { INSTANCE_WR(*gpuobj, 0, class); #ifdef __BIG_ENDIAN INSTANCE_WR(*gpuobj, 2, 0x01000000); #endif } else { #ifdef __BIG_ENDIAN INSTANCE_WR(*gpuobj, 0, class | 0x00080000); #else INSTANCE_WR(*gpuobj, 0, class); #endif } } } (*gpuobj)->engine = NVOBJ_ENGINE_GR; (*gpuobj)->class = class; return 0; } static int nouveau_gpuobj_channel_init_pramin(struct drm_device *dev, int channel) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_fifo *chan = dev_priv->fifos[channel]; struct nouveau_gpuobj *pramin = NULL; int size, base, ret; DRM_DEBUG("ch%d\n", channel); /* Base amount for object storage (4KiB enough?) */ size = 0x1000; base = 0; /* PGRAPH context */ if (dev_priv->card_type == NV_50) { /* Various fixed table thingos */ size += 0x1400; /* mostly unknown stuff */ size += 0x4000; /* vm pd */ base = 0x6000; /* RAMHT, not sure about setting size yet, 32KiB to be safe */ size += 0x8000; /* RAMFC */ size += 0x1000; /* PGRAPH context */ size += 0x60000; } DRM_DEBUG("ch%d PRAMIN size: 0x%08x bytes, base alloc=0x%08x\n", channel, size, base); ret = nouveau_gpuobj_new_ref(dev, -1, -1, 0, size, 0x1000, 0, &chan->ramin); if (ret) { DRM_ERROR("Error allocating channel PRAMIN: %d\n", ret); return ret; } pramin = chan->ramin->gpuobj; ret = nouveau_mem_init_heap(&chan->ramin_heap, pramin->im_pramin->start + base, size); if (ret) { DRM_ERROR("Error creating PRAMIN heap: %d\n", ret); nouveau_gpuobj_ref_del(dev, &chan->ramin); return ret; } return 0; } int nouveau_gpuobj_channel_init(struct drm_device *dev, int channel, uint32_t vram_h, uint32_t tt_h) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_fifo *chan = dev_priv->fifos[channel]; struct nouveau_gpuobj *vram = NULL, *tt = NULL; int ret, i; DRM_DEBUG("ch%d vram=0x%08x tt=0x%08x\n", channel, vram_h, tt_h); /* Reserve a block of PRAMIN for the channel *XXX: maybe on <NV50 too at some point */ if (0 || dev_priv->card_type == NV_50) { ret = nouveau_gpuobj_channel_init_pramin(dev, channel); if (ret) return ret; } /* NV50 VM, point offset 0-512MiB at shared PCIEGART table */ if (dev_priv->card_type >= NV_50) { uint32_t vm_offset; vm_offset = (dev_priv->chipset & 0xf0) == 0x50 ? 0x1400 : 0x200; vm_offset += chan->ramin->gpuobj->im_pramin->start; if ((ret = nouveau_gpuobj_new_fake(dev, vm_offset, 0x4000, 0, &chan->vm_pd, NULL))) return ret; for (i=0; i<0x4000; i+=8) { INSTANCE_WR(chan->vm_pd, (i+0)/4, 0x00000000); INSTANCE_WR(chan->vm_pd, (i+4)/4, 0xdeadcafe); } if ((ret = nouveau_gpuobj_ref_add(dev, -1, 0, dev_priv->gart_info.sg_ctxdma, &chan->vm_gart_pt))) return ret; INSTANCE_WR(chan->vm_pd, (0+0)/4, chan->vm_gart_pt->instance | 0x03); INSTANCE_WR(chan->vm_pd, (0+4)/4, 0x00000000); } /* RAMHT */ if (dev_priv->card_type < NV_50) { ret = nouveau_gpuobj_ref_add(dev, -1, 0, dev_priv->ramht, &chan->ramht); if (ret) return ret; } else { ret = nouveau_gpuobj_new_ref(dev, channel, channel, 0, 0x8000, 16, NVOBJ_FLAG_ZERO_ALLOC, &chan->ramht); if (ret) return ret; } /* VRAM ctxdma */ if ((ret = nouveau_gpuobj_dma_new(dev, channel, NV_CLASS_DMA_IN_MEMORY, 0, dev_priv->fb_available_size, NV_DMA_ACCESS_RW, NV_DMA_TARGET_VIDMEM, &vram))) { DRM_ERROR("Error creating VRAM ctxdma: %d\n", ret); return ret; } if ((ret = nouveau_gpuobj_ref_add(dev, channel, vram_h, vram, NULL))) { DRM_ERROR("Error referencing VRAM ctxdma: %d\n", ret); return ret; } /* TT memory ctxdma */ if (dev_priv->gart_info.type != NOUVEAU_GART_NONE) { ret = nouveau_gpuobj_gart_dma_new(dev, channel, 0, dev_priv->gart_info.aper_size, NV_DMA_ACCESS_RW, &tt, NULL); } else if (dev_priv->pci_heap) { ret = nouveau_gpuobj_dma_new(dev, channel, NV_CLASS_DMA_IN_MEMORY, 0, dev->sg->pages * PAGE_SIZE, NV_DMA_ACCESS_RW, NV_DMA_TARGET_PCI_NONLINEAR, &tt); } else { DRM_ERROR("Invalid GART type %d\n", dev_priv->gart_info.type); ret = -EINVAL; } if (ret) { DRM_ERROR("Error creating TT ctxdma: %d\n", ret); return ret; } ret = nouveau_gpuobj_ref_add(dev, channel, tt_h, tt, NULL); if (ret) { DRM_ERROR("Error referencing TT ctxdma: %d\n", ret); return ret; } return 0; } void nouveau_gpuobj_channel_takedown(struct drm_device *dev, int channel) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_fifo *chan = dev_priv->fifos[channel]; struct nouveau_gpuobj_ref *ref; DRM_DEBUG("ch%d\n", channel); while ((ref = chan->ramht_refs)) { chan->ramht_refs = ref->next; nouveau_gpuobj_ref_del(dev, &ref); } nouveau_gpuobj_ref_del(dev, &chan->ramht); nouveau_gpuobj_del(dev, &chan->vm_pd); nouveau_gpuobj_ref_del(dev, &chan->vm_gart_pt); if (chan->ramin_heap) nouveau_mem_takedown(&chan->ramin_heap); if (chan->ramin) nouveau_gpuobj_ref_del(dev, &chan->ramin); } int nouveau_ioctl_grobj_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_nouveau_grobj_alloc *init = data; struct nouveau_gpuobj *gr = NULL; int ret; if (!nouveau_fifo_owner(dev, file_priv, init->channel)) { DRM_ERROR("pid %d doesn't own channel %d\n", DRM_CURRENTPID, init->channel); return -EINVAL; } //FIXME: check args, only allow trusted objects to be created if (init->handle == ~0) return -EINVAL; if (nouveau_gpuobj_ref_find(dev, init->channel, init->handle, NULL) == 0) return -EEXIST; ret = nouveau_gpuobj_gr_new(dev, init->channel, init->class, &gr); if (ret) { DRM_ERROR("Error creating gr object: %d (%d/0x%08x)\n", ret, init->channel, init->handle); return ret; } if ((ret = nouveau_gpuobj_ref_add(dev, init->channel, init->handle, gr, NULL))) { DRM_ERROR("Error referencing gr object: %d (%d/0x%08x\n)", ret, init->channel, init->handle); nouveau_gpuobj_del(dev, &gr); return ret; } return 0; }