diff options
author | Marek Olšák <maraeo@gmail.com> | 2012-01-27 21:17:59 +0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2012-02-13 16:09:11 +0400 |
commit | dd220a00e8bd5ad7f98ecdc3eed699a7cfabdc27 (patch) | |
tree | 8cdedce29665aae1f92ebcccefacda0598d08a1c /drivers/gpu/drm/radeon/r600_cs.c | |
parent | 51a59ac8739b333eaa43a3102b6acaab5037bfa2 (diff) | |
download | linux-dd220a00e8bd5ad7f98ecdc3eed699a7cfabdc27.tar.xz |
drm/radeon/kms: add support for streamout v7
v2: agd5f: add strmout CS checking, copy_dw register checking
v3: agd5f: don't use cs_check_reg() for copy_dw checking as it
will incorrectly patch the command stream for certain regs.
v4: agd5f: add warning if safe reg check fails for copy_dw
v5: agd5f: add stricter checking for 6xx/7xx
v6: agd5f: add range checking for copy_dw on eg+,
add sx_surface_sync to safe reg list for 7xx.
v7: agd5f: add stricter checking for eg+
Signed-off-by: Marek Olšák <maraeo@gmail.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/radeon/r600_cs.c')
-rw-r--r-- | drivers/gpu/drm/radeon/r600_cs.c | 179 |
1 files changed, 175 insertions, 4 deletions
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c index 38ce5d0427e3..9f17571eea62 100644 --- a/drivers/gpu/drm/radeon/r600_cs.c +++ b/drivers/gpu/drm/radeon/r600_cs.c @@ -61,6 +61,10 @@ struct r600_cs_track { u32 cb_color_size[8]; u32 vgt_strmout_en; u32 vgt_strmout_buffer_en; + struct radeon_bo *vgt_strmout_bo[4]; + u64 vgt_strmout_bo_mc[4]; + u32 vgt_strmout_bo_offset[4]; + u32 vgt_strmout_size[4]; u32 db_depth_control; u32 db_depth_info; u32 db_depth_size_idx; @@ -310,6 +314,13 @@ static void r600_cs_track_init(struct r600_cs_track *track) track->db_depth_size = 0xFFFFFFFF; track->db_depth_size_idx = 0; track->db_depth_control = 0xFFFFFFFF; + + for (i = 0; i < 4; i++) { + track->vgt_strmout_size[i] = 0; + track->vgt_strmout_bo[i] = NULL; + track->vgt_strmout_bo_offset[i] = 0xFFFFFFFF; + track->vgt_strmout_bo_mc[i] = 0xFFFFFFFF; + } } static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i) @@ -430,11 +441,28 @@ static int r600_cs_track_check(struct radeon_cs_parser *p) /* on legacy kernel we don't perform advanced check */ if (p->rdev == NULL) return 0; - /* we don't support out buffer yet */ - if (track->vgt_strmout_en || track->vgt_strmout_buffer_en) { - dev_warn(p->dev, "this kernel doesn't support SMX output buffer\n"); - return -EINVAL; + + /* check streamout */ + if (track->vgt_strmout_en) { + for (i = 0; i < 4; i++) { + if (track->vgt_strmout_buffer_en & (1 << i)) { + if (track->vgt_strmout_bo[i]) { + u64 offset = (u64)track->vgt_strmout_bo_offset[i] + + (u64)track->vgt_strmout_size[i]; + if (offset > radeon_bo_size(track->vgt_strmout_bo[i])) { + DRM_ERROR("streamout %d bo too small: 0x%llx, 0x%lx\n", + i, offset, + radeon_bo_size(track->vgt_strmout_bo[i])); + return -EINVAL; + } + } else { + dev_warn(p->dev, "No buffer for streamout %d\n", i); + return -EINVAL; + } + } + } } + /* check that we have a cb for each enabled target, we don't check * shader_mask because it seems mesa isn't always setting it :( */ @@ -975,6 +1003,39 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx) case R_028B20_VGT_STRMOUT_BUFFER_EN: track->vgt_strmout_buffer_en = radeon_get_ib_value(p, idx); break; + case VGT_STRMOUT_BUFFER_BASE_0: + case VGT_STRMOUT_BUFFER_BASE_1: + case VGT_STRMOUT_BUFFER_BASE_2: + case VGT_STRMOUT_BUFFER_BASE_3: + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "bad SET_CONTEXT_REG " + "0x%04X\n", reg); + return -EINVAL; + } + tmp = (reg - VGT_STRMOUT_BUFFER_BASE_0) / 16; + track->vgt_strmout_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8; + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->vgt_strmout_bo[tmp] = reloc->robj; + track->vgt_strmout_bo_mc[tmp] = reloc->lobj.gpu_offset; + break; + case VGT_STRMOUT_BUFFER_SIZE_0: + case VGT_STRMOUT_BUFFER_SIZE_1: + case VGT_STRMOUT_BUFFER_SIZE_2: + case VGT_STRMOUT_BUFFER_SIZE_3: + tmp = (reg - VGT_STRMOUT_BUFFER_SIZE_0) / 16; + /* size in register is DWs, convert to bytes */ + track->vgt_strmout_size[tmp] = radeon_get_ib_value(p, idx) * 4; + break; + case CP_COHER_BASE: + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + dev_warn(p->dev, "missing reloc for CP_COHER_BASE " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + break; case R_028238_CB_TARGET_MASK: track->cb_target_mask = radeon_get_ib_value(p, idx); break; @@ -1397,6 +1458,22 @@ static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx, return 0; } +static bool r600_is_safe_reg(struct radeon_cs_parser *p, u32 reg, u32 idx) +{ + u32 m, i; + + i = (reg >> 7); + if (i >= ARRAY_SIZE(r600_reg_safe_bm)) { + dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); + return false; + } + m = 1 << ((reg >> 2) & 31); + if (!(r600_reg_safe_bm[i] & m)) + return true; + dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); + return false; +} + static int r600_packet3_check(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt) { @@ -1742,6 +1819,100 @@ static int r600_packet3_check(struct radeon_cs_parser *p, return -EINVAL; } break; + case PACKET3_STRMOUT_BUFFER_UPDATE: + if (pkt->count != 4) { + DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (invalid count)\n"); + return -EINVAL; + } + /* 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; + } + ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff); + ib[idx+2] += upper_32_bits(reloc->lobj.gpu_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; + } + ib[idx+3] += (u32)(reloc->lobj.gpu_offset & 0xffffffff); + ib[idx+4] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff; + } + break; + case PACKET3_COPY_DW: + if (pkt->count != 4) { + DRM_ERROR("bad COPY_DW (invalid count)\n"); + return -EINVAL; + } + 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; + } + ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff); + ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff; + } else { + /* SRC is a reg. */ + reg = radeon_get_ib_value(p, idx+1) << 2; + if (!r600_is_safe_reg(p, reg, idx+1)) + return -EINVAL; + } + 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; + } + ib[idx+3] += (u32)(reloc->lobj.gpu_offset & 0xffffffff); + ib[idx+4] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff; + } else { + /* DST is a reg. */ + reg = radeon_get_ib_value(p, idx+3) << 2; + if (!r600_is_safe_reg(p, reg, idx+3)) + return -EINVAL; + } + break; case PACKET3_NOP: break; default: |