/* * Copyright © 2010 Jerome Glisse * * This file is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "radeon_device.h" #include "r600d.h" struct r600_block { struct list_head list; u32 idx; u32 last; }; struct r600_inst_name { char safe; char name[64]; }; static struct r600_inst_name sq_cf_inst_name[] = { {1, "NOP"}, {0, "TEX"}, {0, "VTX"}, {0, "VTX_TC"}, {1, "LOOP_START"}, {1, "LOOP_END"}, {1, "LOOP_START_DX10"}, {1, "LOOP_START_NO_AL"}, {1, "LOOP_CONTINUE"}, {1, "LOOP_BREAK"}, {1, "JUMP"}, {1, "PUSH"}, {1, "PUSH_ELSE"}, {1, "ELSE"}, {1, "POP"}, {1, "POP_JUMP"}, {1, "POP_PUSH"}, {1, "POP_PUSH_ELSE"}, {1, "CALL"}, {1, "CALL_FS"}, {1, "RETURN"}, {0, "EMIT_VERTEX"}, {0, "EMIT_CUT_VERTEX"}, {0, "CUT_VERTEX"}, {1, "KILL"}, }; static struct r600_inst_name sq_cf_alu_inst_name[] = { {0, "unknown"}, {0, "unknown"}, {0, "unknown"}, {0, "unknown"}, {0, "unknown"}, {0, "unknown"}, {0, "unknown"}, {0, "unknown"}, {1, "ALU"}, {1, "ALU_PUSH_BEFORE"}, {1, "ALU_POP_AFTER"}, {1, "ALU_POP2_AFTER"}, {0, "unknown"}, {1, "ALU_CONTINUE"}, {1, "ALU_BREAK"}, {1, "ALU_ELSE_AFTER"}, }; static struct r600_inst_name sq_cf_alloc_export_inst_name[] = { {0, "MEM_STREAM0"}, {0, "MEM_STREAM1"}, {0, "MEM_STREAM2"}, {0, "MEM_STREAM3"}, {0, "MEM_SCRATCH"}, {0, "MEM_REDUCTION"}, {0, "MEM_RING"}, {1, "EXPORT"}, {1, "EXPORT_DONE"}, }; void r600_disassemble_sq_cf_inst(u32 *bytecode, u32 ndwords, u32 idx) { u32 addr = G_008DFC_ADDR(bytecode[idx+0]) << 1; u32 count = G_008DFC_COUNT(bytecode[idx+1]); u32 i; printf("0x%08X 0x%08X CF_INST: %s\n", bytecode[idx+0], bytecode[idx+1], sq_cf_inst_name[G_008DFC_CF_INST(bytecode[idx+1])].name); printf(" word0: addr (in dw) %d\n", G_008DFC_ADDR(bytecode[idx+0]) << 1); printf(" word1: pop count %d\n", G_008DFC_POP_COUNT(bytecode[idx+1])); printf(" word1: cf const %d\n", G_008DFC_CF_CONST(bytecode[idx+1])); printf(" word1: cond %d\n", G_008DFC_COND(bytecode[idx+1])); printf(" word1: count %d\n", G_008DFC_COUNT(bytecode[idx+1])); printf(" word1: call count %d\n", G_008DFC_CALL_COUNT(bytecode[idx+1])); printf(" word1: end of program %d\n", G_008DFC_END_OF_PROGRAM(bytecode[idx+1])); printf(" word1: valid pixel mode %d\n", G_008DFC_VALID_PIXEL_MODE(bytecode[idx+1])); printf(" word1: inst 0x%02X (%s)\n", G_008DFC_CF_INST(bytecode[idx+1]), sq_cf_inst_name[G_008DFC_CF_INST(bytecode[idx+1])].name); printf(" word1: whole quad mode %d\n", G_008DFC_WHOLE_QUAD_MODE(bytecode[idx+1])); printf(" word1: barrier %d\n", G_008DFC_BARRIER(bytecode[idx+1])); for (i = 0; i <= count; i++) printf(" 0x%08X 0x%08X 0x%08X 0x%08X\n", bytecode[idx+addr+0+(i*4)], bytecode[idx+addr+1+(i*4)], bytecode[idx+addr+2+(i*4)], bytecode[idx+addr+3+(i*4)]); } void r600_disassemble_sq_cf_alu_inst(u32 *bytecode, u32 ndwords, u32 idx) { printf("0x%08X 0x%08X CF_ALU_INST: %s\n", bytecode[idx+0], bytecode[idx+1], sq_cf_alu_inst_name[G_008DFC_CF_ALU_INST(bytecode[idx+1])].name); printf(" word0: addr (in dw) %d\n", G_008DFC_ALU_ADDR(bytecode[idx+0]) << 1); printf(" word0: kcache bank0 %d\n", G_008DFC_KCACHE_BANK0(bytecode[idx+0])); printf(" word0: kcache bank1 %d\n", G_008DFC_KCACHE_BANK1(bytecode[idx+0])); printf(" word0: kcache mode0 %d\n", G_008DFC_KCACHE_MODE0(bytecode[idx+0])); printf(" word1: kcache mode1 %d\n", G_008DFC_KCACHE_MODE1(bytecode[idx+1])); printf(" word1: kcache addr0 %d\n", G_008DFC_KCACHE_ADDR0(bytecode[idx+1])); printf(" word1: kcache addr1 %d\n", G_008DFC_KCACHE_ADDR1(bytecode[idx+1])); printf(" word1: count %d\n", G_008DFC_ALU_COUNT(bytecode[idx+1])); printf(" word1: use waterfall %d\n", G_008DFC_USES_WATERFALL(bytecode[idx+1])); printf(" word1: inst 0x%02X (%s)\n", G_008DFC_CF_ALU_INST(bytecode[idx+1]), sq_cf_alu_inst_name[G_008DFC_CF_ALU_INST(bytecode[idx+1])].name); printf(" word1: whole quad mode %d\n", G_008DFC_WHOLE_QUAD_MODE(bytecode[idx+1])); printf(" word1: barrier %d\n", G_008DFC_BARRIER(bytecode[idx+1])); } void r600_disassemble_sq_cf_alloc_export_inst(u32 *bytecode, u32 ndwords, u32 idx) { printf("0x%08X 0x%08X CF_ALLOC_EXPORT: %s\n", bytecode[idx+0], bytecode[idx+1], sq_cf_alloc_export_inst_name[G_008DFC_CF_INST(bytecode[idx+1])-0x20].name); printf(" word0: array base %d\n", G_008DFC_ARRAY_BASE(bytecode[idx+0])); printf(" word0: type %d\n", G_008DFC_TYPE(bytecode[idx+0])); printf(" word0: rw gpr %d\n", G_008DFC_RW_GPR(bytecode[idx+0])); printf(" word0: rw rel %d\n", G_008DFC_RW_REL(bytecode[idx+0])); printf(" word0: index gpr %d\n", G_008DFC_INDEX_GPR(bytecode[idx+0])); printf(" word0: elem size %d\n", G_008DFC_ELEM_SIZE(bytecode[idx+0])); printf(" word1: burst count %d\n", G_008DFC_BURST_COUNT(bytecode[idx+1])); printf(" word1: end of program %d\n", G_008DFC_END_OF_PROGRAM(bytecode[idx+1])); printf(" word1: valid pixel mode %d\n", G_008DFC_VALID_PIXEL_MODE(bytecode[idx+1])); printf(" word1: inst 0x%02X (%s)\n", G_008DFC_CF_INST(bytecode[idx+1]), sq_cf_alloc_export_inst_name[G_008DFC_CF_INST(bytecode[idx+1])-0x20].name); printf(" word1: whole quad mode %d\n", G_008DFC_WHOLE_QUAD_MODE(bytecode[idx+1])); printf(" word1: barrier %d\n", G_008DFC_BARRIER(bytecode[idx+1])); } struct r600_block *r600_block_new(u32 *bytecode, u32 ndwords, u32 idx) { struct r600_block *blk; u32 inst; blk = malloc(sizeof(struct r600_block)); if (blk == NULL) return NULL; INIT_LIST_HEAD(&blk->list); blk->idx = idx; blk->last = 0; inst = (bytecode[idx+1] >> 23) & 0x7F; if ((inst & 0x78) >= 0x40) { r600_disassemble_sq_cf_alu_inst(bytecode, ndwords, idx); } else { if (G_008DFC_CF_INST(bytecode[idx+1]) < 0x20) { r600_disassemble_sq_cf_inst(bytecode, ndwords, idx); blk->last = G_008DFC_END_OF_PROGRAM(bytecode[idx+1]); } else { r600_disassemble_sq_cf_alloc_export_inst(bytecode, ndwords, idx); blk->last = G_008DFC_END_OF_PROGRAM(bytecode[idx+1]); } } return blk; } void r600_shader_disassemble(u32 *bytecode, u32 ndwords) { struct r600_block *blk = NULL; u32 idx = 0; do { free(blk); blk = r600_block_new(bytecode, ndwords, idx); if (blk == NULL) return; idx += 2; } while (!blk->last); } int r600_shader_build_fs(struct radeon_device *rdev, u32 *bytecode, u32 *ndwords, struct drm_r600_vs_input *inputs, struct drm_r600_vs_shader *vs) { u32 idx = 0, i, rid, gpr, j; *ndwords = 0; if (!inputs->nelements) { dev_err(rdev->dev, "need at least one input for vertex shader\n"); return -EINVAL; } bytecode[idx++] = 0x00000002; bytecode[idx++] = 0x81000000 | S_008DFC_COUNT(inputs->nelements - 1); bytecode[idx++] = 0x00000000; bytecode[idx++] = 0x8A000000; for (i = 0; i < inputs->nelements; i++) { if (inputs->elements[i].buffer_id >= inputs->nbuffers) { dev_err(rdev->dev, "elements %d referencing invalid buffer %d\n", i, inputs->elements[i].buffer_id); return -EINVAL; } rid = inputs->buffers[inputs->elements[i].buffer_id].resource_id; for (j = 0, gpr = -1; j < vs->ninputs; j++) { if (vs->input_semantic[j] == inputs->elements[i].semantic) { gpr = vs->input_gpr[j]; break; } } /* if vs has no corresponding input skip the elements */ if (gpr == -1) continue; bytecode[idx++] = (inputs->elements[i].sq_vtx_word0 & 0xFC000000) | S_008DFC_BUFFER_ID(rid); bytecode[idx++] = (inputs->elements[i].sq_vtx_word1 & 0xFFFFFC00) | S_008DFC_DST_GPR(gpr); bytecode[idx++] = inputs->elements[i].sq_vtx_word2; bytecode[idx++] = 0xCAFEDEAD; } *ndwords = idx; // r600_shader_disassemble(bytecode, idx); return 0; }