/* * Copyright 2009 Advanced Micro Devices, Inc. * Copyright 2012 Red Hat Inc. * * 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: * Alex Deucher * Jerome Glisse */ #include #include #include #include #include "xf86drm.h" #include "radeon_drm.h" #include "radeon_family.h" #include "r6xx.h" #include "r6xxd.h" static int r6xx_next_reloc_offset(struct rati_file *rfile, unsigned idx, unsigned *next_idx, uint64_t *offset) { unsigned type, it, count, ridx; uint32_t *pm4 = rfile->cmd_buffer_ptr[idx]; type = PKTx_TYPE(pm4[*next_idx]); count = PKTx_COUNT(pm4[*next_idx]); it = PKT3_IT(pm4[*next_idx]); if (type != 3 || it != IT_NOP) { /* missing relocation */ return -EINVAL; } ridx = pm4[(*next_idx) + 1] / 4; if (ridx >= rfile->header.ndata_buffers) { /* relocation out of range */ return -EINVAL; } *offset = rfile->data_buffer[ridx].offset; *next_idx += count + 1; return 0; } static int r6xx_reg_clear_offset(struct rati_file *rfile, unsigned idx, unsigned reg, unsigned pm4_idx, unsigned *next_idx) { uint32_t *pm4 = rfile->cmd_buffer_ptr[idx]; uint64_t offset; int r; /* xor the offset, this will keep offset from start of bo intact */ switch (reg) { case VGT_STRMOUT_BUFFER_BASE_0: case VGT_STRMOUT_BUFFER_BASE_1: case VGT_STRMOUT_BUFFER_BASE_2: case VGT_STRMOUT_BUFFER_BASE_3: case CP_COHER_BASE: case CB_COLOR0_FRAG: case CB_COLOR1_FRAG: case CB_COLOR2_FRAG: case CB_COLOR3_FRAG: case CB_COLOR4_FRAG: case CB_COLOR5_FRAG: case CB_COLOR6_FRAG: case CB_COLOR7_FRAG: case CB_COLOR0_TILE: case CB_COLOR1_TILE: case CB_COLOR2_TILE: case CB_COLOR3_TILE: case CB_COLOR4_TILE: case CB_COLOR5_TILE: case CB_COLOR6_TILE: case CB_COLOR7_TILE: case CB_COLOR0_BASE: case CB_COLOR1_BASE: case CB_COLOR2_BASE: case CB_COLOR3_BASE: case CB_COLOR4_BASE: case CB_COLOR5_BASE: case CB_COLOR6_BASE: case CB_COLOR7_BASE: case DB_DEPTH_BASE: case DB_HTILE_DATA_BASE: case SQ_PGM_START_FS: case SQ_PGM_START_ES: case SQ_PGM_START_VS: case SQ_PGM_START_GS: case SQ_PGM_START_PS: case SQ_ALU_CONST_CACHE_GS_0: case SQ_ALU_CONST_CACHE_GS_1: case SQ_ALU_CONST_CACHE_GS_2: case SQ_ALU_CONST_CACHE_GS_3: case SQ_ALU_CONST_CACHE_GS_4: case SQ_ALU_CONST_CACHE_GS_5: case SQ_ALU_CONST_CACHE_GS_6: case SQ_ALU_CONST_CACHE_GS_7: case SQ_ALU_CONST_CACHE_GS_8: case SQ_ALU_CONST_CACHE_GS_9: case SQ_ALU_CONST_CACHE_GS_10: case SQ_ALU_CONST_CACHE_GS_11: case SQ_ALU_CONST_CACHE_GS_12: case SQ_ALU_CONST_CACHE_GS_13: case SQ_ALU_CONST_CACHE_GS_14: case SQ_ALU_CONST_CACHE_GS_15: case SQ_ALU_CONST_CACHE_PS_0: case SQ_ALU_CONST_CACHE_PS_1: case SQ_ALU_CONST_CACHE_PS_2: case SQ_ALU_CONST_CACHE_PS_3: case SQ_ALU_CONST_CACHE_PS_4: case SQ_ALU_CONST_CACHE_PS_5: case SQ_ALU_CONST_CACHE_PS_6: case SQ_ALU_CONST_CACHE_PS_7: case SQ_ALU_CONST_CACHE_PS_8: case SQ_ALU_CONST_CACHE_PS_9: case SQ_ALU_CONST_CACHE_PS_10: case SQ_ALU_CONST_CACHE_PS_11: case SQ_ALU_CONST_CACHE_PS_12: case SQ_ALU_CONST_CACHE_PS_13: case SQ_ALU_CONST_CACHE_PS_14: case SQ_ALU_CONST_CACHE_PS_15: case SQ_ALU_CONST_CACHE_VS_0: case SQ_ALU_CONST_CACHE_VS_1: case SQ_ALU_CONST_CACHE_VS_2: case SQ_ALU_CONST_CACHE_VS_3: case SQ_ALU_CONST_CACHE_VS_4: case SQ_ALU_CONST_CACHE_VS_5: case SQ_ALU_CONST_CACHE_VS_6: case SQ_ALU_CONST_CACHE_VS_7: case SQ_ALU_CONST_CACHE_VS_8: case SQ_ALU_CONST_CACHE_VS_9: case SQ_ALU_CONST_CACHE_VS_10: case SQ_ALU_CONST_CACHE_VS_11: case SQ_ALU_CONST_CACHE_VS_12: case SQ_ALU_CONST_CACHE_VS_13: case SQ_ALU_CONST_CACHE_VS_14: case SQ_ALU_CONST_CACHE_VS_15: case SX_MEMORY_EXPORT_BASE: r = r6xx_next_reloc_offset(rfile, idx, next_idx, &offset); if (r) { return r; } pm4[pm4_idx] ^= ((offset >> 8) & 0xffffffff); break; default: break; } return 0; } static int r6xx_rfile_cmd_buffer_legalize(struct rati_file *rfile, unsigned idx) { return -EINVAL; } static int r6xx_cmd_buffer_clear_offset(struct rati_file *rfile, unsigned idx) { unsigned i, j, count, reg, it, header, next_idx, tmp; uint32_t *pm4 = rfile->cmd_buffer_ptr[idx]; uint64_t offset; int r; for (i = 0; i < rfile->cmd_buffer[idx].ndw;) { header = pm4[i]; count = PKTx_COUNT(header); switch (PKTx_TYPE(header)) { case 0: reg = PKT0_REG(header); next_idx = i + count + 1; for (j = 0, i++; j < count; j++, reg += 4) { r = r6xx_reg_clear_offset(rfile, idx, reg, i++, &next_idx); if (r) { return r; } } break; case 1: case 2: break; case 3: next_idx = i + count + 1; it = PKT3_IT(pm4[i]); switch (it) { case IT_SET_CONFIG_REG: reg = (pm4[++i] << 2) + SET_CONFIG_REG__OFFSET; for (j = 0, i++; j < (count - 1); j++, reg += 4) { r = r6xx_reg_clear_offset(rfile, idx, reg, i++, &next_idx); if (r) { return r; } } break; case IT_SET_CONTEXT_REG: reg = (pm4[++i] << 2) + SET_CONTEXT_REG__OFFSET; for (j = 0, i++; j < (count - 1); j++, reg += 4) { r = r6xx_reg_clear_offset(rfile, idx, reg, i++, &next_idx); if (r) { return r; } } break; case IT_SET_PREDICATION: tmp = (pm4[i + 2] >> 16) & 0x7; if (tmp) { r = r6xx_next_reloc_offset(rfile, idx, &next_idx, &offset); if (r) { return r; } pm4[i + 1] ^= (offset & 0xffffffff); pm4[i + 2] ^= ((offset >> 32ULL) & 0xff); } break; case IT_DRAW_INDEX: r = r6xx_next_reloc_offset(rfile, idx, &next_idx, &offset); if (r) { return r; } pm4[i + 1] ^= (offset & 0xffffffff); pm4[i + 2] ^= ((offset >> 32ULL) & 0xff); break; case IT_WAIT_REG_MEM: if (pm4[i + 1] & 0x10) { r = r6xx_next_reloc_offset(rfile, idx, &next_idx, &offset); if (r) { return r; } pm4[i + 2] ^= (offset & 0xffffffff); pm4[i + 3] ^= ((offset >> 32ULL) & 0xff); } break; case IT_SURFACE_SYNC: if (pm4[i + 2] != 0xffffffff || pm4[i + 3]) { r = r6xx_next_reloc_offset(rfile, idx, &next_idx, &offset); if (r) { return r; } pm4[i + 3] ^= ((offset >> 8ULL) & 0xffffffff); } break; case IT_EVENT_WRITE: case IT_EVENT_WRITE_EOP: if (count > 1) { r = r6xx_next_reloc_offset(rfile, idx, &next_idx, &offset); if (r) { return r; } pm4[i + 2] ^= (offset & 0xfffffffc); pm4[i + 3] ^= ((offset >> 32ULL) & 0xff); } break; case IT_SET_RESOURCE: for (j = 0, i +=2; j < ((count - 1) / 7); j++, i += 7) { tmp = pm4[i + 6]; switch ((tmp >> 30) & 3) { case SQ_TEX_VTX_VALID_TEXTURE: r = r6xx_next_reloc_offset(rfile, idx, &next_idx, &offset); if (r) { return r; } pm4[i + 2] ^= ((offset >> 8ULL) & 0xffffffff); r = r6xx_next_reloc_offset(rfile, idx, &next_idx, &offset); if (r) { return r; } pm4[i + 3] ^= ((offset >> 8ULL) & 0xffffffff); break; case SQ_TEX_VTX_VALID_BUFFER: r = r6xx_next_reloc_offset(rfile, idx, &next_idx, &offset); if (r) { return r; } pm4[i + 0] ^= (offset & 0xffffffffULL); pm4[i + 2] ^= ((offset >> 32ULL) & 0xff); break; default: break; } } break; #if 0 case PACKET3_STRMOUT_BUFFER_UPDATE: /* Updating memory at DST_ADDRESS. */ if (idx_value & 0x1) { u64 offset; r = r600_cs_packet_next_reloc(p, &reloc); if (r) { DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (missing dst reloc)\n"); return -EINVAL; } offset = radeon_get_ib_value(p, idx+1); offset += ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32; if ((offset + 4) > radeon_bo_size(reloc->robj)) { DRM_ERROR("bad STRMOUT_BUFFER_UPDATE dst bo too small: 0x%llx, 0x%lx\n", offset + 4, radeon_bo_size(reloc->robj)); return -EINVAL; } offset += reloc->lobj.gpu_offset; ib[idx+1] = offset; ib[idx+2] = upper_32_bits(offset) & 0xff; } /* Reading data from SRC_ADDRESS. */ if (((idx_value >> 1) & 0x3) == 2) { u64 offset; r = r600_cs_packet_next_reloc(p, &reloc); if (r) { DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (missing src reloc)\n"); return -EINVAL; } offset = radeon_get_ib_value(p, idx+3); offset += ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; if ((offset + 4) > radeon_bo_size(reloc->robj)) { DRM_ERROR("bad STRMOUT_BUFFER_UPDATE src bo too small: 0x%llx, 0x%lx\n", offset + 4, radeon_bo_size(reloc->robj)); return -EINVAL; } offset += reloc->lobj.gpu_offset; ib[idx+3] = offset; ib[idx+4] = upper_32_bits(offset) & 0xff; } break; case PACKET3_COPY_DW: if (idx_value & 0x1) { u64 offset; /* SRC is memory. */ r = r600_cs_packet_next_reloc(p, &reloc); if (r) { DRM_ERROR("bad COPY_DW (missing src reloc)\n"); return -EINVAL; } offset = radeon_get_ib_value(p, idx+1); offset += ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32; if ((offset + 4) > radeon_bo_size(reloc->robj)) { DRM_ERROR("bad COPY_DW src bo too small: 0x%llx, 0x%lx\n", offset + 4, radeon_bo_size(reloc->robj)); return -EINVAL; } offset += reloc->lobj.gpu_offset; ib[idx+1] = offset; ib[idx+2] = upper_32_bits(offset) & 0xff; } if (idx_value & 0x2) { u64 offset; /* DST is memory. */ r = r600_cs_packet_next_reloc(p, &reloc); if (r) { DRM_ERROR("bad COPY_DW (missing dst reloc)\n"); return -EINVAL; } offset = radeon_get_ib_value(p, idx+3); offset += ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32; if ((offset + 4) > radeon_bo_size(reloc->robj)) { DRM_ERROR("bad COPY_DW dst bo too small: 0x%llx, 0x%lx\n", offset + 4, radeon_bo_size(reloc->robj)); return -EINVAL; } offset += reloc->lobj.gpu_offset; ib[idx+3] = offset; ib[idx+4] = upper_32_bits(offset) & 0xff; } break; #endif default: break; } i = next_idx; break; default: return -EINVAL; } } return 0; } int r6xx_rfile_legalize(struct rati_file *rfile) { unsigned i; int r; for (i = 0; i < rfile->header.ncmd_buffers; i++) { r = r6xx_rfile_cmd_buffer_legalize(rfile, i); if (r) { return r; } } return 0; } int r6xx_rfile_clear_offset(struct rati_file *rfile) { unsigned i; int r = 0; for (i = 0; i < rfile->header.ncmd_buffers; i++) { if (rfile->cmd_buffer[i].flags & RATI_CMD_CLEAR_OFFSET) { r = r6xx_cmd_buffer_clear_offset(rfile, i); if (r) { return r; } } } return 0; } /* * tati helpers */ int r6xx_tati_cmd_buffer_write(struct rati_file *rfile, unsigned idx, FILE *file) { unsigned i, j, count, reg, it; uint32_t *pm4 = rfile->cmd_buffer_ptr[idx]; for (i = 0; i < rfile->cmd_buffer[idx].ndw;) { count = PKTx_COUNT(pm4[i]); switch (PKTx_TYPE(pm4[i])) { case 0: reg = PKT0_REG(pm4[i]); if (fprintf(file, " 0x%08x\n", pm4[i++]) < 0) { return -EINVAL; } for (j = 0; j < count; j++, reg += 4) { if (fprintf(file, " 0x%08x // reg 0x%08x\n", pm4[i++], reg) < 0) { return -EINVAL; } } break; case 1: case 2: if (fprintf(file, " 0x%08x\n", pm4[i++]) < 0) { return -EINVAL; } break; case 3: it = PKT3_IT(pm4[i]); if (fprintf(file, " 0x%08x // pkt3 0x%02x\n", pm4[i++], it) < 0) { return -EINVAL; } switch (it) { case IT_SET_CONFIG_REG: reg = (pm4[i] << 2) + SET_CONFIG_REG__OFFSET; if (fprintf(file, " 0x%08x\n", pm4[i++]) < 0) { return -EINVAL; } for (j = 0; j < (count - 1); j++, reg += 4) { if (fprintf(file, " 0x%08x // reg 0x%08x\n", pm4[i++], reg) < 0) { return -EINVAL; } } break; case IT_SET_CONTEXT_REG: reg = (pm4[i] << 2) + SET_CONTEXT_REG__OFFSET; if (fprintf(file, " 0x%08x\n", pm4[i++]) < 0) { return -EINVAL; } for (j = 0; j < (count - 1); j++, reg += 4) { if (fprintf(file, " 0x%08x // reg 0x%08x\n", pm4[i++], reg) < 0) { return -EINVAL; } } break; default: for (j = 0; j < count; j++) { if (fprintf(file, " 0x%08x\n", pm4[i++]) < 0) { return -EINVAL; } } break; } break; default: return -EINVAL; } } return 0; }