nv50_program.c revision 22e0acc466947b203574c88f4964f61ef46ae3fd
1#include "pipe/p_context.h"
2#include "pipe/p_defines.h"
3#include "pipe/p_state.h"
4#include "pipe/p_inlines.h"
5
6#include "pipe/p_shader_tokens.h"
7#include "tgsi/util/tgsi_parse.h"
8#include "tgsi/util/tgsi_util.h"
9
10#include "nv50_context.h"
11#include "nv50_state.h"
12
13#define OP_MOV 0x001
14#define OP_INTERP 0x008
15#define OP_RCP 0x009
16#define OP_ADD 0x00b
17#define OP_MUL 0x00c
18#define OP_MAD 0x00e
19#define NV50_SU_MAX_TEMP 64
20
21struct nv50_reg {
22	enum {
23		P_TEMP,
24		P_ATTR,
25		P_RESULT,
26		P_CONST,
27		P_IMMD
28	} type;
29	int index;
30
31	int hw;
32	int neg;
33};
34
35struct nv50_pc {
36	struct nv50_program *p;
37
38	/* hw resources */
39	struct nv50_reg *r_temp[NV50_SU_MAX_TEMP];
40
41	/* tgsi resources */
42	struct nv50_reg *temp;
43	int temp_nr;
44	struct nv50_reg *attr;
45	int attr_nr;
46	struct nv50_reg *result;
47	int result_nr;
48	struct nv50_reg *param;
49	int param_nr;
50	struct nv50_reg *immd;
51	float *immd_buf;
52	int immd_nr;
53};
54
55static void
56alloc_reg(struct nv50_pc *pc, struct nv50_reg *reg)
57{
58	int i;
59
60	if (reg->type != P_TEMP || reg->hw >= 0)
61		return;
62
63	for (i = 0; i < NV50_SU_MAX_TEMP; i++) {
64		if (!(pc->r_temp[i])) {
65			pc->r_temp[i] = reg;
66			reg->hw = i;
67			if (pc->p->cfg.vp.high_temp < (i + 1))
68				pc->p->cfg.vp.high_temp = i + 1;
69			return;
70		}
71	}
72
73	assert(0);
74}
75
76static struct nv50_reg *
77alloc_temp(struct nv50_pc *pc, struct nv50_reg *dst)
78{
79	struct nv50_reg *r;
80	int i;
81
82	if (dst && dst->type == P_TEMP && dst->hw == -1)
83		return dst;
84
85	for (i = 0; i < NV50_SU_MAX_TEMP; i++) {
86		if (!pc->r_temp[i]) {
87			r = CALLOC_STRUCT(nv50_reg);
88			r->type = P_TEMP;
89			r->index = -1;
90			r->hw = i;
91			pc->r_temp[i] = r;
92			return r;
93		}
94	}
95
96	assert(0);
97	return NULL;
98}
99
100static void
101free_temp(struct nv50_pc *pc, struct nv50_reg *r)
102{
103	if (r->index == -1) {
104		FREE(pc->r_temp[r->hw]);
105		pc->r_temp[r->hw] = NULL;
106	}
107}
108
109#if 0
110static struct nv50_reg *
111constant(struct nv50_pc *pc, int pipe, int c, float v)
112{
113	struct nv50_reg *r = CALLOC_STRUCT(nv50_reg);
114	struct nv50_program *p = pc->p;
115	struct nv50_program_data *pd;
116	int idx;
117
118	if (pipe >= 0) {
119		for (idx = 0; idx < p->nr_consts; idx++) {
120			if (p->consts[idx].index == pipe)
121				return nv40_sr(NV40SR_CONST, idx);
122		}
123	}
124
125	idx = p->nr_consts++;
126	p->consts = realloc(p->consts, sizeof(*pd) * p->nr_consts);
127	pd = &p->consts[idx];
128
129	pd->index = pipe;
130	pd->component = c;
131	pd->value = v;
132	return nv40_sr(NV40SR_CONST, idx);
133}
134#endif
135
136static void
137emit(struct nv50_pc *pc, unsigned op, struct nv50_reg *dst,
138     struct nv50_reg *src0, struct nv50_reg *src1, struct nv50_reg *src2)
139{
140	struct nv50_program *p = pc->p;
141	struct nv50_reg *tmp0 = NULL, *tmp = NULL, *tmp2 = NULL;
142	unsigned inst[2] = { 0, 0 };
143
144	/* Grr.. Fun restrictions on where attribs can be sourced from.. */
145	if (src0 && (src0->type == P_CONST || src0->type == P_IMMD) &&
146	    (op == OP_MUL || op == OP_MAD)) {
147		tmp = src1;
148		src1 = src0;
149		src0 = tmp;
150		tmp = NULL;
151	}
152
153	if (src1 && src1->type == P_ATTR) {
154		tmp = alloc_temp(pc, dst);
155		tmp->neg = src1->neg; src1->neg = 0;
156		emit(pc, 1, tmp, src1, NULL, NULL);
157		src1 = tmp;
158	}
159
160	if (src2 && src2->type == P_ATTR) {
161		tmp2 = alloc_temp(pc, dst);
162		tmp2->neg = src2->neg; src2->neg = 0;
163		emit(pc, 1, tmp2, src2, NULL, NULL);
164		src2 = tmp2;
165	}
166
167	/* Get this out of the way first.  What type of opcode do we
168	 * want/need to build?
169	 */
170	if ((op & 0x3f0) || dst->type == P_RESULT ||
171	    (src0 && src0->type == P_ATTR) || src1 || src2)
172		inst[0] |= 0x00000001;
173
174	if (inst[0] & 0x00000001) {
175		inst[0] |= ((op & 0xf) << 28);
176		inst[1] |= ((op >> 4) << 26);
177
178		alloc_reg(pc, dst);
179		if (dst->type == P_RESULT)
180			inst[1] |= 0x00000008;
181		inst[0] |= (dst->hw << 2);
182
183		if (src0) {
184			if (src0->type == P_ATTR)
185				inst[1] |= 0x00200000;
186			else
187			if (src0->type == P_CONST || src0->type == P_IMMD)
188				assert(0);
189			alloc_reg(pc, src0);
190			inst[0] |= (src0->hw << 9);
191		}
192
193		if (src1) {
194			if (src1->type == P_CONST || src1->type == P_IMMD) {
195				if (src1->type == P_IMMD)
196					inst[1] |= (NV50_CB_PMISC << 22);
197				else
198					inst[1] |= (NV50_CB_PVP << 22);
199				inst[0] |= 0x00800000; /* src1 is const */
200				/*XXX: does src1 come from "src2" now? */
201				alloc_reg(pc, src1);
202				inst[0] |= (src1->hw << 16);
203			} else {
204				alloc_reg(pc, src1);
205				if (op == OP_MUL || op == OP_MAD)
206					inst[0] |= (src1->hw << 16);
207				else
208					inst[1] |= (src1->hw << 14);
209			}
210		} else {
211			inst[1] |= 0x0003c000; /*XXX FIXME */
212		}
213
214		if (src2) {
215			if (src2->type == P_CONST || src2->type == P_IMMD) {
216				if (src2->type == P_IMMD)
217					inst[1] |= (NV50_CB_PMISC << 22);
218				else
219					inst[1] |= (NV50_CB_PVP << 22);
220				inst[0] |= 0x01000000; /* src2 is const */
221				inst[1] |= (src2->hw << 14);
222			} else {
223				alloc_reg(pc, src2);
224				if (inst[0] & 0x00800000 || op == OP_MAD)
225					inst[1] |= (src2->hw << 14);
226				else
227					inst[0] |= (src2->hw << 16);
228			}
229		}
230
231		/*XXX: FIXME */
232		switch (op) {
233		case OP_ADD:
234		case OP_MUL:
235		case OP_RCP:
236		case OP_MAD:
237			/* 0x04000000 negates arg0 */
238			/* 0x08000000 negates arg1 */
239			/*XXX: true for !0xb also ? */
240			if (src0 && src0->neg)
241				inst[1] |= 0x04000000;
242			if (src1 && src1->neg)
243				inst[1] |= 0x08000000;
244			inst[1] |= 0x00000780;
245			break;
246		default:
247			/* 0x04000000 == arg0 32 bit, otherwise 16 bit */
248			inst[1] |= 0x04000780;
249			break;
250		}
251	} else {
252		inst[0] |= ((op & 0xf) << 28);
253
254		alloc_reg(pc, dst);
255		inst[0] |= (dst->hw << 2);
256
257		if (src0) {
258			alloc_reg(pc, src0);
259			inst[0] |= (src0->hw << 9);
260		}
261
262		/*XXX: NFI if this even works - probably not.. */
263		if (src1) {
264			alloc_reg(pc, src1);
265			inst[0] |= (src1->hw << 16);
266		}
267	}
268
269	if (tmp0) free_temp(pc, tmp0);
270	if (tmp) free_temp(pc, tmp);
271	if (tmp2) free_temp(pc, tmp2);
272
273	if (inst[0] & 1) {
274		p->insns_nr += 2;
275		p->insns = realloc(p->insns, sizeof(unsigned) * p->insns_nr);
276		memcpy(p->insns + (p->insns_nr - 2), inst, sizeof(unsigned)*2);
277	} else {
278		p->insns_nr += 1;
279		p->insns = realloc(p->insns, sizeof(unsigned) * p->insns_nr);
280		memcpy(p->insns + (p->insns_nr - 1), inst, sizeof(unsigned));
281	}
282}
283
284static struct nv50_reg *
285tgsi_dst(struct nv50_pc *pc, int c, const struct tgsi_full_dst_register *dst)
286{
287	switch (dst->DstRegister.File) {
288	case TGSI_FILE_TEMPORARY:
289		return &pc->temp[dst->DstRegister.Index * 4 + c];
290	case TGSI_FILE_OUTPUT:
291		return &pc->result[dst->DstRegister.Index * 4 + c];
292	case TGSI_FILE_NULL:
293		return NULL;
294	default:
295		break;
296	}
297
298	return NULL;
299}
300
301static struct nv50_reg *
302tgsi_src(struct nv50_pc *pc, int c, const struct tgsi_full_src_register *src)
303{
304	/* Handle swizzling */
305	switch (c) {
306	case 0: c = src->SrcRegister.SwizzleX; break;
307	case 1: c = src->SrcRegister.SwizzleY; break;
308	case 2: c = src->SrcRegister.SwizzleZ; break;
309	case 3: c = src->SrcRegister.SwizzleW; break;
310	default:
311		assert(0);
312	}
313
314	switch (src->SrcRegister.File) {
315	case TGSI_FILE_INPUT:
316		return &pc->attr[src->SrcRegister.Index * 4 + c];
317	case TGSI_FILE_TEMPORARY:
318		return &pc->temp[src->SrcRegister.Index * 4 + c];
319	case TGSI_FILE_CONSTANT:
320		return &pc->param[src->SrcRegister.Index * 4 + c];
321	case TGSI_FILE_IMMEDIATE:
322		return &pc->immd[src->SrcRegister.Index * 4 + c];
323	default:
324		break;
325	}
326
327	return NULL;
328}
329
330static boolean
331nv50_program_tx_insn(struct nv50_pc *pc, const union tgsi_full_token *tok)
332{
333	const struct tgsi_full_instruction *inst = &tok->FullInstruction;
334	struct nv50_reg *dst[4], *src[3][4], *none = NULL, *tmp;
335	unsigned mask;
336	int i, c;
337
338	NOUVEAU_ERR("insn %p\n", tok);
339
340	mask = inst->FullDstRegisters[0].DstRegister.WriteMask;
341
342	for (c = 0; c < 4; c++) {
343		if (mask & (1 << c))
344			dst[c] = tgsi_dst(pc, c, &inst->FullDstRegisters[0]);
345		else
346			dst[c] = NULL;
347	}
348
349	for (i = 0; i < inst->Instruction.NumSrcRegs; i++) {
350		for (c = 0; c < 4; c++)
351			src[i][c] = tgsi_src(pc, c, &inst->FullSrcRegisters[i]);
352	}
353
354	switch (inst->Instruction.Opcode) {
355	case TGSI_OPCODE_ADD:
356		for (c = 0; c < 4; c++) {
357			if (mask & (1 << c)) {
358				emit(pc, OP_ADD, dst[c],
359				     src[0][c], src[1][c], none);
360			}
361		}
362		break;
363	case TGSI_OPCODE_DP3:
364		tmp = alloc_temp(pc, NULL);
365		emit(pc, OP_MUL, tmp, src[0][0], src[1][0], NULL);
366		emit(pc, OP_MAD, tmp, src[0][1], src[1][1], tmp);
367		emit(pc, OP_MAD, tmp, src[0][2], src[1][2], tmp);
368		for (c = 0; c < 4; c++) {
369			if (mask & (1 << c))
370				emit(pc, OP_MOV, dst[c], tmp, none, none);
371		}
372		free_temp(pc, tmp);
373		break;
374	case TGSI_OPCODE_DP4:
375		tmp = alloc_temp(pc, NULL);
376		emit(pc, OP_MUL, tmp, src[0][0], src[1][0], NULL);
377		emit(pc, OP_MAD, tmp, src[0][1], src[1][1], tmp);
378		emit(pc, OP_MAD, tmp, src[0][2], src[1][2], tmp);
379		emit(pc, OP_MAD, tmp, src[0][3], src[1][3], tmp);
380		for (c = 0; c < 4; c++) {
381			if (mask & (1 << c))
382				emit(pc, OP_MOV, dst[c], tmp, none, none);
383		}
384		free_temp(pc, tmp);
385		break;
386	case TGSI_OPCODE_DPH:
387		tmp = alloc_temp(pc, NULL);
388		emit(pc, OP_MUL, tmp, src[0][0], src[1][0], NULL);
389		emit(pc, OP_MAD, tmp, src[0][1], src[1][1], tmp);
390		emit(pc, OP_MAD, tmp, src[0][2], src[1][2], tmp);
391		emit(pc, OP_ADD, tmp, src[1][3], tmp, NULL);
392		for (c = 0; c < 4; c++) {
393			if (mask & (1 << c))
394				emit(pc, OP_MOV, dst[c], tmp, none, none);
395		}
396		free_temp(pc, tmp);
397		break;
398	case TGSI_OPCODE_MAD:
399		for (c = 0; c < 4; c++) {
400			if (mask & (1 << c))
401				emit(pc, OP_MAD, dst[c],
402				     src[0][c], src[1][c], src[2][c]);
403		}
404		break;
405	case TGSI_OPCODE_MOV:
406		for (c = 0; c < 4; c++) {
407			if (mask & (1 << c))
408				emit(pc, OP_MOV, dst[c], src[0][c], none, none);
409		}
410		break;
411	case TGSI_OPCODE_MUL:
412		for (c = 0; c < 4; c++) {
413			if (mask & (1 << c))
414				emit(pc, OP_MUL, dst[c],
415				     src[0][c], src[1][c], none);
416		}
417		break;
418	case TGSI_OPCODE_RCP:
419		for (c = 0; c < 4; c++) {
420			if (mask & (1 << c))
421				emit(pc, OP_RCP, dst[c],
422				     src[0][c], none, none);
423		}
424		break;
425	case TGSI_OPCODE_SUB:
426		for (c = 0; c < 4; c++) {
427			if (mask & (1 << c)) {
428				src[1][c]->neg = 1;
429				emit(pc, OP_ADD, dst[c],
430				     src[0][c], src[1][c], none);
431				src[1][c]->neg = 0;
432			}
433		}
434		break;
435	case TGSI_OPCODE_END:
436		break;
437	default:
438		NOUVEAU_ERR("invalid opcode %d\n", inst->Instruction.Opcode);
439		return FALSE;
440	}
441
442	return TRUE;
443}
444
445static boolean
446nv50_program_tx_prep(struct nv50_pc *pc)
447{
448	struct tgsi_parse_context p;
449	boolean ret = FALSE;
450	unsigned i, c;
451
452	tgsi_parse_init(&p, pc->p->pipe.tokens);
453	while (!tgsi_parse_end_of_tokens(&p)) {
454		const union tgsi_full_token *tok = &p.FullToken;
455
456		tgsi_parse_token(&p);
457		switch (tok->Token.Type) {
458		case TGSI_TOKEN_TYPE_IMMEDIATE:
459		{
460			const struct tgsi_full_immediate *imm =
461				&p.FullToken.FullImmediate;
462
463			pc->immd_nr++;
464			pc->immd_buf = realloc(pc->immd_buf, 4 * pc->immd_nr *
465							     sizeof(float));
466			pc->immd_buf[4 * (pc->immd_nr - 1) + 0] =
467				imm->u.ImmediateFloat32[0].Float;
468			pc->immd_buf[4 * (pc->immd_nr - 1) + 1] =
469				imm->u.ImmediateFloat32[1].Float;
470			pc->immd_buf[4 * (pc->immd_nr - 1) + 2] =
471				imm->u.ImmediateFloat32[2].Float;
472			pc->immd_buf[4 * (pc->immd_nr - 1) + 3] =
473				imm->u.ImmediateFloat32[3].Float;
474		}
475			break;
476		case TGSI_TOKEN_TYPE_DECLARATION:
477		{
478			const struct tgsi_full_declaration *d;
479			unsigned last;
480
481			d = &p.FullToken.FullDeclaration;
482			last = d->u.DeclarationRange.Last;
483
484			switch (d->Declaration.File) {
485			case TGSI_FILE_TEMPORARY:
486				if (pc->temp_nr < (last + 1))
487					pc->temp_nr = last + 1;
488				break;
489			case TGSI_FILE_OUTPUT:
490				if (pc->result_nr < (last + 1))
491					pc->result_nr = last + 1;
492				break;
493			case TGSI_FILE_INPUT:
494				if (pc->attr_nr < (last + 1))
495					pc->attr_nr = last + 1;
496				break;
497			case TGSI_FILE_CONSTANT:
498				if (pc->param_nr < (last + 1))
499					pc->param_nr = last + 1;
500				break;
501			default:
502				NOUVEAU_ERR("bad decl file %d\n",
503					    d->Declaration.File);
504				goto out_err;
505			}
506		}
507			break;
508		case TGSI_TOKEN_TYPE_INSTRUCTION:
509			break;
510		default:
511			break;
512		}
513	}
514
515	NOUVEAU_ERR("%d temps\n", pc->temp_nr);
516	if (pc->temp_nr) {
517		pc->temp = calloc(pc->temp_nr * 4, sizeof(struct nv50_reg));
518		if (!pc->temp)
519			goto out_err;
520
521		for (i = 0; i < pc->temp_nr; i++) {
522			for (c = 0; c < 4; c++) {
523				pc->temp[i*4+c].type = P_TEMP;
524				pc->temp[i*4+c].hw = -1;
525				pc->temp[i*4+c].index = i;
526			}
527		}
528	}
529
530	NOUVEAU_ERR("%d attrib regs\n", pc->attr_nr);
531	if (pc->attr_nr) {
532		int aid = 0;
533
534		pc->attr = calloc(pc->attr_nr * 4, sizeof(struct nv50_reg));
535		if (!pc->attr)
536			goto out_err;
537
538		for (i = 0; i < pc->attr_nr; i++) {
539			for (c = 0; c < 4; c++) {
540				pc->p->cfg.vp.attr[aid/32] |= (1 << (aid % 32));
541				pc->attr[i*4+c].type = P_ATTR;
542				pc->attr[i*4+c].hw = aid++;
543				pc->attr[i*4+c].index = i;
544			}
545		}
546	}
547
548	NOUVEAU_ERR("%d result regs\n", pc->result_nr);
549	if (pc->result_nr) {
550		int rid = 0;
551
552		pc->result = calloc(pc->result_nr * 4, sizeof(struct nv50_reg));
553		if (!pc->result)
554			goto out_err;
555
556		for (i = 0; i < pc->result_nr; i++) {
557			for (c = 0; c < 4; c++) {
558				pc->result[i*4+c].type = P_RESULT;
559				pc->result[i*4+c].hw = rid++;
560				pc->result[i*4+c].index = i;
561			}
562		}
563	}
564
565	NOUVEAU_ERR("%d param regs\n", pc->param_nr);
566	if (pc->param_nr) {
567		int rid = 0;
568
569		pc->param = calloc(pc->param_nr * 4, sizeof(struct nv50_reg));
570		if (!pc->param)
571			goto out_err;
572
573		for (i = 0; i < pc->param_nr; i++) {
574			for (c = 0; c < 4; c++) {
575				pc->param[i*4+c].type = P_CONST;
576				pc->param[i*4+c].hw = rid++;
577				pc->param[i*4+c].index = i;
578			}
579		}
580	}
581
582	if (pc->immd_nr) {
583		int rid = 0;
584
585		pc->immd = calloc(pc->immd_nr * 4, sizeof(struct nv50_reg));
586		if (!pc->immd)
587			goto out_err;
588
589		for (i = 0; i < pc->immd_nr; i++) {
590			for (c = 0; c < 4; c++) {
591				pc->immd[i*4+c].type = P_IMMD;
592				pc->immd[i*4+c].hw = rid++;
593				pc->immd[i*4+c].index = i;
594			}
595		}
596	}
597
598	ret = TRUE;
599out_err:
600	tgsi_parse_free(&p);
601	return ret;
602}
603
604static boolean
605nv50_program_tx(struct nv50_program *p)
606{
607	struct tgsi_parse_context parse;
608	struct nv50_pc *pc;
609	boolean ret;
610
611	pc = CALLOC_STRUCT(nv50_pc);
612	if (!pc)
613		return FALSE;
614	pc->p = p;
615	pc->p->cfg.vp.high_temp = 4;
616
617	ret = nv50_program_tx_prep(pc);
618	if (ret == FALSE)
619		goto out_cleanup;
620
621	tgsi_parse_init(&parse, pc->p->pipe.tokens);
622	while (!tgsi_parse_end_of_tokens(&parse)) {
623		const union tgsi_full_token *tok = &parse.FullToken;
624
625		tgsi_parse_token(&parse);
626
627		switch (tok->Token.Type) {
628		case TGSI_TOKEN_TYPE_INSTRUCTION:
629			ret = nv50_program_tx_insn(pc, tok);
630			if (ret == FALSE)
631				goto out_err;
632			break;
633		default:
634			break;
635		}
636	}
637
638	p->immd_nr = pc->immd_nr * 4;
639	p->immd = pc->immd_buf;
640
641out_err:
642	tgsi_parse_free(&parse);
643
644out_cleanup:
645	return ret;
646}
647
648static void
649nv50_program_validate(struct nv50_context *nv50, struct nv50_program *p)
650{
651	struct tgsi_parse_context pc;
652
653	tgsi_parse_init(&pc, p->pipe.tokens);
654
655	if (pc.FullHeader.Processor.Processor == TGSI_PROCESSOR_FRAGMENT) {
656		p->insns_nr = 8;
657		p->insns = malloc(p->insns_nr * sizeof(unsigned));
658		p->insns[0] = 0x80000000;
659		p->insns[1] = 0x9000000c;
660		p->insns[2] = 0x82010600;
661		p->insns[3] = 0x82020604;
662		p->insns[4] = 0x80030609;
663		p->insns[5] = 0x00020780;
664		p->insns[6] = 0x8004060d;
665		p->insns[7] = 0x00020781;
666	} else
667	if (pc.FullHeader.Processor.Processor == TGSI_PROCESSOR_VERTEX) {
668		int i;
669
670		if (nv50_program_tx(p) == FALSE)
671			assert(0);
672		p->insns[p->insns_nr - 1] |= 0x00000001;
673
674		for (i = 0; i < p->insns_nr; i++)
675			NOUVEAU_ERR("%d 0x%08x\n", i, p->insns[i]);
676	} else {
677		NOUVEAU_ERR("invalid TGSI processor\n");
678		tgsi_parse_free(&pc);
679		return;
680	}
681
682	tgsi_parse_free(&pc);
683
684	p->translated = TRUE;
685}
686
687static void
688nv50_program_validate_data(struct nv50_context *nv50, struct nv50_program *p)
689{
690	int i;
691
692	for (i = 0; i < p->immd_nr; i++) {
693		BEGIN_RING(tesla, 0x0f00, 2);
694		OUT_RING  ((NV50_CB_PMISC << 16) | (i << 8));
695		OUT_RING  (fui(p->immd[i]));
696	}
697}
698
699static void
700nv50_program_validate_code(struct nv50_context *nv50, struct nv50_program *p)
701{
702	struct pipe_winsys *ws = nv50->pipe.winsys;
703	void *map;
704
705	if (!p->buffer)
706		p->buffer = ws->buffer_create(ws, 0x100, 0, p->insns_nr * 4);
707	map = ws->buffer_map(ws, p->buffer, PIPE_BUFFER_USAGE_CPU_WRITE);
708	memcpy(map, p->insns, p->insns_nr * 4);
709	ws->buffer_unmap(ws, p->buffer);
710}
711
712void
713nv50_vertprog_validate(struct nv50_context *nv50)
714{
715	struct nouveau_grobj *tesla = nv50->screen->tesla;
716	struct nv50_program *p = nv50->vertprog;
717	struct nouveau_stateobj *so;
718
719	if (!p->translated) {
720		nv50_program_validate(nv50, p);
721		if (!p->translated)
722			assert(0);
723	}
724
725	nv50_program_validate_data(nv50, p);
726	nv50_program_validate_code(nv50, p);
727
728	so = so_new(11, 2);
729	so_method(so, tesla, NV50TCL_VP_ADDRESS_HIGH, 2);
730	so_reloc (so, p->buffer, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
731		  NOUVEAU_BO_HIGH, 0, 0);
732	so_reloc (so, p->buffer, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
733		  NOUVEAU_BO_LOW, 0, 0);
734	so_method(so, tesla, 0x1650, 2);
735	so_data  (so, p->cfg.vp.attr[0]);
736	so_data  (so, p->cfg.vp.attr[1]);
737	so_method(so, tesla, 0x16ac, 2);
738	so_data  (so, 8);
739	so_data  (so, p->cfg.vp.high_temp);
740	so_method(so, tesla, 0x140c, 1);
741	so_data  (so, 0); /* program start offset */
742	so_emit(nv50->screen->nvws, so);
743	so_ref(NULL, &so);
744}
745
746void
747nv50_fragprog_validate(struct nv50_context *nv50)
748{
749	struct pipe_winsys *ws = nv50->pipe.winsys;
750	struct nouveau_grobj *tesla = nv50->screen->tesla;
751	struct nv50_program *p = nv50->fragprog;
752	struct nouveau_stateobj *so;
753	void *map;
754
755	if (!p->translated) {
756		nv50_program_validate(nv50, p);
757		if (!p->translated)
758			assert(0);
759	}
760
761	if (!p->buffer)
762		p->buffer = ws->buffer_create(ws, 0x100, 0, p->insns_nr * 4);
763	map = ws->buffer_map(ws, p->buffer, PIPE_BUFFER_USAGE_CPU_WRITE);
764	memcpy(map, p->insns, p->insns_nr * 4);
765	ws->buffer_unmap(ws, p->buffer);
766
767	so = so_new(3, 2);
768	so_method(so, tesla, NV50TCL_FP_ADDRESS_HIGH, 2);
769	so_reloc (so, p->buffer, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
770		  NOUVEAU_BO_HIGH, 0, 0);
771	so_reloc (so, p->buffer, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
772		  NOUVEAU_BO_LOW, 0, 0);
773	so_emit(nv50->screen->nvws, so);
774	so_ref(NULL, &so);
775}
776
777void
778nv50_program_destroy(struct nv50_context *nv50, struct nv50_program *p)
779{
780	struct pipe_winsys *ws = nv50->pipe.winsys;
781
782	if (p->insns_nr) {
783		if (p->insns)
784			FREE(p->insns);
785		p->insns_nr = 0;
786	}
787
788	if (p->buffer)
789		pipe_buffer_reference(ws, &p->buffer, NULL);
790
791	p->translated = 0;
792}
793
794