diff options
| -rw-r--r-- | drivers/gpu/drm/radeon/evergreen_cs.c | 603 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/r100.c | 23 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/r600_cs.c | 285 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon.h | 19 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_asic.c | 26 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_asic.h | 3 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_combios.c | 6 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_cp.c | 14 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_cs.c | 13 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_cursor.c | 17 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_device.c | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_display.c | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_drv.c | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_drv.h | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_object.c | 4 | ||||
| -rw-r--r-- | include/uapi/drm/radeon_drm.h | 1 | 
16 files changed, 897 insertions, 127 deletions
diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c index 62c22710478..74c6b42d259 100644 --- a/drivers/gpu/drm/radeon/evergreen_cs.c +++ b/drivers/gpu/drm/radeon/evergreen_cs.c @@ -34,6 +34,8 @@  #define MAX(a,b)                   (((a)>(b))?(a):(b))  #define MIN(a,b)                   (((a)<(b))?(a):(b)) +int r600_dma_cs_next_reloc(struct radeon_cs_parser *p, +			   struct radeon_cs_reloc **cs_reloc);  static int evergreen_cs_packet_next_reloc(struct radeon_cs_parser *p,  					  struct radeon_cs_reloc **cs_reloc); @@ -507,20 +509,28 @@ static int evergreen_cs_track_validate_htile(struct radeon_cs_parser *p,  		/* height is npipes htiles aligned == npipes * 8 pixel aligned */  		nby = round_up(nby, track->npipes * 8);  	} else { +		/* always assume 8x8 htile */ +		/* align is htile align * 8, htile align vary according to +		 * number of pipe and tile width and nby +		 */  		switch (track->npipes) {  		case 8: +			/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/  			nbx = round_up(nbx, 64 * 8);  			nby = round_up(nby, 64 * 8);  			break;  		case 4: +			/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/  			nbx = round_up(nbx, 64 * 8);  			nby = round_up(nby, 32 * 8);  			break;  		case 2: +			/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/  			nbx = round_up(nbx, 32 * 8);  			nby = round_up(nby, 32 * 8);  			break;  		case 1: +			/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/  			nbx = round_up(nbx, 32 * 8);  			nby = round_up(nby, 16 * 8);  			break; @@ -531,9 +541,10 @@ static int evergreen_cs_track_validate_htile(struct radeon_cs_parser *p,  		}  	}  	/* compute number of htile */ -	nbx = nbx / 8; -	nby = nby / 8; -	size = nbx * nby * 4; +	nbx = nbx >> 3; +	nby = nby >> 3; +	/* size must be aligned on npipes * 2K boundary */ +	size = roundup(nbx * nby * 4, track->npipes * (2 << 10));  	size += track->htile_offset;  	if (size > radeon_bo_size(track->htile_bo)) { @@ -1790,6 +1801,8 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)  	case DB_HTILE_SURFACE:  		/* 8x8 only */  		track->htile_surface = radeon_get_ib_value(p, idx); +		/* force 8x8 htile width and height */ +		ib[idx] |= 3;  		track->db_dirty = true;  		break;  	case CB_IMMED0_BASE: @@ -2243,6 +2256,18 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,  		command = radeon_get_ib_value(p, idx+4);  		size = command & 0x1fffff;  		info = radeon_get_ib_value(p, idx+1); +		if ((((info & 0x60000000) >> 29) != 0) || /* src = GDS or DATA */ +		    (((info & 0x00300000) >> 20) != 0) || /* dst = GDS */ +		    ((((info & 0x00300000) >> 20) == 0) && +		     (command & PACKET3_CP_DMA_CMD_DAS)) || /* dst = register */ +		    ((((info & 0x60000000) >> 29) == 0) && +		     (command & PACKET3_CP_DMA_CMD_SAS))) { /* src = register */ +			/* non mem to mem copies requires dw aligned count */ +			if (size % 4) { +				DRM_ERROR("CP DMA command requires dw count alignment\n"); +				return -EINVAL; +			} +		}  		if (command & PACKET3_CP_DMA_CMD_SAS) {  			/* src address space is register */  			/* GDS is ok */ @@ -2804,6 +2829,455 @@ int evergreen_cs_parse(struct radeon_cs_parser *p)  	return 0;  } +/* + *  DMA + */ + +#define GET_DMA_CMD(h) (((h) & 0xf0000000) >> 28) +#define GET_DMA_COUNT(h) ((h) & 0x000fffff) +#define GET_DMA_T(h) (((h) & 0x00800000) >> 23) +#define GET_DMA_NEW(h) (((h) & 0x04000000) >> 26) +#define GET_DMA_MISC(h) (((h) & 0x0700000) >> 20) + +/** + * evergreen_dma_cs_parse() - parse the DMA IB + * @p:		parser structure holding parsing context. + * + * Parses the DMA IB from the CS ioctl and updates + * the GPU addresses based on the reloc information and + * checks for errors. (Evergreen-Cayman) + * Returns 0 for success and an error on failure. + **/ +int evergreen_dma_cs_parse(struct radeon_cs_parser *p) +{ +	struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx]; +	struct radeon_cs_reloc *src_reloc, *dst_reloc, *dst2_reloc; +	u32 header, cmd, count, tiled, new_cmd, misc; +	volatile u32 *ib = p->ib.ptr; +	u32 idx, idx_value; +	u64 src_offset, dst_offset, dst2_offset; +	int r; + +	do { +		if (p->idx >= ib_chunk->length_dw) { +			DRM_ERROR("Can not parse packet at %d after CS end %d !\n", +				  p->idx, ib_chunk->length_dw); +			return -EINVAL; +		} +		idx = p->idx; +		header = radeon_get_ib_value(p, idx); +		cmd = GET_DMA_CMD(header); +		count = GET_DMA_COUNT(header); +		tiled = GET_DMA_T(header); +		new_cmd = GET_DMA_NEW(header); +		misc = GET_DMA_MISC(header); + +		switch (cmd) { +		case DMA_PACKET_WRITE: +			r = r600_dma_cs_next_reloc(p, &dst_reloc); +			if (r) { +				DRM_ERROR("bad DMA_PACKET_WRITE\n"); +				return -EINVAL; +			} +			if (tiled) { +				dst_offset = ib[idx+1]; +				dst_offset <<= 8; + +				ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); +				p->idx += count + 7; +			} else { +				dst_offset = ib[idx+1]; +				dst_offset |= ((u64)(ib[idx+2] & 0xff)) << 32; + +				ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); +				ib[idx+2] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; +				p->idx += count + 3; +			} +			if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { +				dev_warn(p->dev, "DMA write buffer too small (%llu %lu)\n", +					 dst_offset, radeon_bo_size(dst_reloc->robj)); +				return -EINVAL; +			} +			break; +		case DMA_PACKET_COPY: +			r = r600_dma_cs_next_reloc(p, &src_reloc); +			if (r) { +				DRM_ERROR("bad DMA_PACKET_COPY\n"); +				return -EINVAL; +			} +			r = r600_dma_cs_next_reloc(p, &dst_reloc); +			if (r) { +				DRM_ERROR("bad DMA_PACKET_COPY\n"); +				return -EINVAL; +			} +			if (tiled) { +				idx_value = radeon_get_ib_value(p, idx + 2); +				if (new_cmd) { +					switch (misc) { +					case 0: +						/* L2T, frame to fields */ +						if (idx_value & (1 << 31)) { +							DRM_ERROR("bad L2T, frame to fields DMA_PACKET_COPY\n"); +							return -EINVAL; +						} +						r = r600_dma_cs_next_reloc(p, &dst2_reloc); +						if (r) { +							DRM_ERROR("bad L2T, frame to fields DMA_PACKET_COPY\n"); +							return -EINVAL; +						} +						dst_offset = ib[idx+1]; +						dst_offset <<= 8; +						dst2_offset = ib[idx+2]; +						dst2_offset <<= 8; +						src_offset = ib[idx+8]; +						src_offset |= ((u64)(ib[idx+9] & 0xff)) << 32; +						if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { +							dev_warn(p->dev, "DMA L2T, frame to fields src buffer too small (%llu %lu)\n", +								 src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); +							return -EINVAL; +						} +						if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { +							dev_warn(p->dev, "DMA L2T, frame to fields buffer too small (%llu %lu)\n", +								 dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); +							return -EINVAL; +						} +						if ((dst2_offset + (count * 4)) > radeon_bo_size(dst2_reloc->robj)) { +							dev_warn(p->dev, "DMA L2T, frame to fields buffer too small (%llu %lu)\n", +								 dst2_offset + (count * 4), radeon_bo_size(dst2_reloc->robj)); +							return -EINVAL; +						} +						ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); +						ib[idx+2] += (u32)(dst2_reloc->lobj.gpu_offset >> 8); +						ib[idx+8] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); +						ib[idx+9] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; +						p->idx += 10; +						break; +					case 1: +						/* L2T, T2L partial */ +						if (p->family < CHIP_CAYMAN) { +							DRM_ERROR("L2T, T2L Partial is cayman only !\n"); +							return -EINVAL; +						} +						/* detile bit */ +						if (idx_value & (1 << 31)) { +							/* tiled src, linear dst */ +							ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); + +							ib[idx+7] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); +							ib[idx+8] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; +						} else { +							/* linear src, tiled dst */ +							ib[idx+7] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); +							ib[idx+8] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + +							ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); +						} +						p->idx += 12; +						break; +					case 3: +						/* L2T, broadcast */ +						if (idx_value & (1 << 31)) { +							DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); +							return -EINVAL; +						} +						r = r600_dma_cs_next_reloc(p, &dst2_reloc); +						if (r) { +							DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); +							return -EINVAL; +						} +						dst_offset = ib[idx+1]; +						dst_offset <<= 8; +						dst2_offset = ib[idx+2]; +						dst2_offset <<= 8; +						src_offset = ib[idx+8]; +						src_offset |= ((u64)(ib[idx+9] & 0xff)) << 32; +						if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { +							dev_warn(p->dev, "DMA L2T, broadcast src buffer too small (%llu %lu)\n", +								 src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); +							return -EINVAL; +						} +						if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { +							dev_warn(p->dev, "DMA L2T, broadcast dst buffer too small (%llu %lu)\n", +								 dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); +							return -EINVAL; +						} +						if ((dst2_offset + (count * 4)) > radeon_bo_size(dst2_reloc->robj)) { +							dev_warn(p->dev, "DMA L2T, broadcast dst2 buffer too small (%llu %lu)\n", +								 dst2_offset + (count * 4), radeon_bo_size(dst2_reloc->robj)); +							return -EINVAL; +						} +						ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); +						ib[idx+2] += (u32)(dst2_reloc->lobj.gpu_offset >> 8); +						ib[idx+8] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); +						ib[idx+9] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; +						p->idx += 10; +						break; +					case 4: +						/* L2T, T2L */ +						/* detile bit */ +						if (idx_value & (1 << 31)) { +							/* tiled src, linear dst */ +							src_offset = ib[idx+1]; +							src_offset <<= 8; +							ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); + +							dst_offset = ib[idx+7]; +							dst_offset |= ((u64)(ib[idx+8] & 0xff)) << 32; +							ib[idx+7] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); +							ib[idx+8] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; +						} else { +							/* linear src, tiled dst */ +							src_offset = ib[idx+7]; +							src_offset |= ((u64)(ib[idx+8] & 0xff)) << 32; +							ib[idx+7] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); +							ib[idx+8] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + +							dst_offset = ib[idx+1]; +							dst_offset <<= 8; +							ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); +						} +						if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { +							dev_warn(p->dev, "DMA L2T, T2L src buffer too small (%llu %lu)\n", +								 src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); +							return -EINVAL; +						} +						if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { +							dev_warn(p->dev, "DMA L2T, T2L dst buffer too small (%llu %lu)\n", +								 dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); +							return -EINVAL; +						} +						p->idx += 9; +						break; +					case 5: +						/* T2T partial */ +						if (p->family < CHIP_CAYMAN) { +							DRM_ERROR("L2T, T2L Partial is cayman only !\n"); +							return -EINVAL; +						} +						ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); +						ib[idx+4] += (u32)(dst_reloc->lobj.gpu_offset >> 8); +						p->idx += 13; +						break; +					case 7: +						/* L2T, broadcast */ +						if (idx_value & (1 << 31)) { +							DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); +							return -EINVAL; +						} +						r = r600_dma_cs_next_reloc(p, &dst2_reloc); +						if (r) { +							DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); +							return -EINVAL; +						} +						dst_offset = ib[idx+1]; +						dst_offset <<= 8; +						dst2_offset = ib[idx+2]; +						dst2_offset <<= 8; +						src_offset = ib[idx+8]; +						src_offset |= ((u64)(ib[idx+9] & 0xff)) << 32; +						if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { +							dev_warn(p->dev, "DMA L2T, broadcast src buffer too small (%llu %lu)\n", +								 src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); +							return -EINVAL; +						} +						if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { +							dev_warn(p->dev, "DMA L2T, broadcast dst buffer too small (%llu %lu)\n", +								 dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); +							return -EINVAL; +						} +						if ((dst2_offset + (count * 4)) > radeon_bo_size(dst2_reloc->robj)) { +							dev_warn(p->dev, "DMA L2T, broadcast dst2 buffer too small (%llu %lu)\n", +								 dst2_offset + (count * 4), radeon_bo_size(dst2_reloc->robj)); +							return -EINVAL; +						} +						ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); +						ib[idx+2] += (u32)(dst2_reloc->lobj.gpu_offset >> 8); +						ib[idx+8] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); +						ib[idx+9] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; +						p->idx += 10; +						break; +					default: +						DRM_ERROR("bad DMA_PACKET_COPY misc %u\n", misc); +						return -EINVAL; +					} +				} else { +					switch (misc) { +					case 0: +						/* detile bit */ +						if (idx_value & (1 << 31)) { +							/* tiled src, linear dst */ +							src_offset = ib[idx+1]; +							src_offset <<= 8; +							ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); + +							dst_offset = ib[idx+7]; +							dst_offset |= ((u64)(ib[idx+8] & 0xff)) << 32; +							ib[idx+7] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); +							ib[idx+8] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; +						} else { +							/* linear src, tiled dst */ +							src_offset = ib[idx+7]; +							src_offset |= ((u64)(ib[idx+8] & 0xff)) << 32; +							ib[idx+7] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); +							ib[idx+8] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + +							dst_offset = ib[idx+1]; +							dst_offset <<= 8; +							ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); +						} +						if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { +							dev_warn(p->dev, "DMA L2T, broadcast src buffer too small (%llu %lu)\n", +								 src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); +							return -EINVAL; +						} +						if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { +							dev_warn(p->dev, "DMA L2T, broadcast dst buffer too small (%llu %lu)\n", +								 dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); +							return -EINVAL; +						} +						p->idx += 9; +						break; +					default: +						DRM_ERROR("bad DMA_PACKET_COPY misc %u\n", misc); +						return -EINVAL; +					} +				} +			} else { +				if (new_cmd) { +					switch (misc) { +					case 0: +						/* L2L, byte */ +						src_offset = ib[idx+2]; +						src_offset |= ((u64)(ib[idx+4] & 0xff)) << 32; +						dst_offset = ib[idx+1]; +						dst_offset |= ((u64)(ib[idx+3] & 0xff)) << 32; +						if ((src_offset + count) > radeon_bo_size(src_reloc->robj)) { +							dev_warn(p->dev, "DMA L2L, byte src buffer too small (%llu %lu)\n", +								 src_offset + count, radeon_bo_size(src_reloc->robj)); +							return -EINVAL; +						} +						if ((dst_offset + count) > radeon_bo_size(dst_reloc->robj)) { +							dev_warn(p->dev, "DMA L2L, byte dst buffer too small (%llu %lu)\n", +								 dst_offset + count, radeon_bo_size(dst_reloc->robj)); +							return -EINVAL; +						} +						ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xffffffff); +						ib[idx+2] += (u32)(src_reloc->lobj.gpu_offset & 0xffffffff); +						ib[idx+3] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; +						ib[idx+4] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; +						p->idx += 5; +						break; +					case 1: +						/* L2L, partial */ +						if (p->family < CHIP_CAYMAN) { +							DRM_ERROR("L2L Partial is cayman only !\n"); +							return -EINVAL; +						} +						ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset & 0xffffffff); +						ib[idx+2] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; +						ib[idx+4] += (u32)(dst_reloc->lobj.gpu_offset & 0xffffffff); +						ib[idx+5] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; + +						p->idx += 9; +						break; +					case 4: +						/* L2L, dw, broadcast */ +						r = r600_dma_cs_next_reloc(p, &dst2_reloc); +						if (r) { +							DRM_ERROR("bad L2L, dw, broadcast DMA_PACKET_COPY\n"); +							return -EINVAL; +						} +						dst_offset = ib[idx+1]; +						dst_offset |= ((u64)(ib[idx+4] & 0xff)) << 32; +						dst2_offset = ib[idx+2]; +						dst2_offset |= ((u64)(ib[idx+5] & 0xff)) << 32; +						src_offset = ib[idx+3]; +						src_offset |= ((u64)(ib[idx+6] & 0xff)) << 32; +						if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { +							dev_warn(p->dev, "DMA L2L, dw, broadcast src buffer too small (%llu %lu)\n", +								 src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); +							return -EINVAL; +						} +						if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { +							dev_warn(p->dev, "DMA L2L, dw, broadcast dst buffer too small (%llu %lu)\n", +								 dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); +							return -EINVAL; +						} +						if ((dst2_offset + (count * 4)) > radeon_bo_size(dst2_reloc->robj)) { +							dev_warn(p->dev, "DMA L2L, dw, broadcast dst2 buffer too small (%llu %lu)\n", +								 dst2_offset + (count * 4), radeon_bo_size(dst2_reloc->robj)); +							return -EINVAL; +						} +						ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); +						ib[idx+2] += (u32)(dst2_reloc->lobj.gpu_offset & 0xfffffffc); +						ib[idx+3] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); +						ib[idx+4] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; +						ib[idx+5] += upper_32_bits(dst2_reloc->lobj.gpu_offset) & 0xff; +						ib[idx+6] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; +						p->idx += 7; +						break; +					default: +						DRM_ERROR("bad DMA_PACKET_COPY misc %u\n", misc); +						return -EINVAL; +					} +				} else { +					/* L2L, dw */ +					src_offset = ib[idx+2]; +					src_offset |= ((u64)(ib[idx+4] & 0xff)) << 32; +					dst_offset = ib[idx+1]; +					dst_offset |= ((u64)(ib[idx+3] & 0xff)) << 32; +					if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { +						dev_warn(p->dev, "DMA L2L, dw src buffer too small (%llu %lu)\n", +							 src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); +						return -EINVAL; +					} +					if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { +						dev_warn(p->dev, "DMA L2L, dw dst buffer too small (%llu %lu)\n", +							 dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); +						return -EINVAL; +					} +					ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); +					ib[idx+2] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); +					ib[idx+3] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; +					ib[idx+4] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; +					p->idx += 5; +				} +			} +			break; +		case DMA_PACKET_CONSTANT_FILL: +			r = r600_dma_cs_next_reloc(p, &dst_reloc); +			if (r) { +				DRM_ERROR("bad DMA_PACKET_CONSTANT_FILL\n"); +				return -EINVAL; +			} +			dst_offset = ib[idx+1]; +			dst_offset |= ((u64)(ib[idx+3] & 0x00ff0000)) << 16; +			if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { +				dev_warn(p->dev, "DMA constant fill buffer too small (%llu %lu)\n", +					 dst_offset, radeon_bo_size(dst_reloc->robj)); +				return -EINVAL; +			} +			ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); +			ib[idx+3] += (upper_32_bits(dst_reloc->lobj.gpu_offset) << 16) & 0x00ff0000; +			p->idx += 4; +			break; +		case DMA_PACKET_NOP: +			p->idx += 1; +			break; +		default: +			DRM_ERROR("Unknown packet type %d at %d !\n", cmd, idx); +			return -EINVAL; +		} +	} while (p->idx < p->chunks[p->chunk_ib_idx].length_dw); +#if 0 +	for (r = 0; r < p->ib->length_dw; r++) { +		printk(KERN_INFO "%05d  0x%08X\n", r, p->ib.ptr[r]); +		mdelay(1); +	} +#endif +	return 0; +} +  /* vm parser */  static bool evergreen_vm_reg_valid(u32 reg)  { @@ -3010,6 +3484,18 @@ static int evergreen_vm_packet3_check(struct radeon_device *rdev,  	case PACKET3_CP_DMA:  		command = ib[idx + 4];  		info = ib[idx + 1]; +		if ((((info & 0x60000000) >> 29) != 0) || /* src = GDS or DATA */ +		    (((info & 0x00300000) >> 20) != 0) || /* dst = GDS */ +		    ((((info & 0x00300000) >> 20) == 0) && +		     (command & PACKET3_CP_DMA_CMD_DAS)) || /* dst = register */ +		    ((((info & 0x60000000) >> 29) == 0) && +		     (command & PACKET3_CP_DMA_CMD_SAS))) { /* src = register */ +			/* non mem to mem copies requires dw aligned count */ +			if ((command & 0x1fffff) % 4) { +				DRM_ERROR("CP DMA command requires dw count alignment\n"); +				return -EINVAL; +			} +		}  		if (command & PACKET3_CP_DMA_CMD_SAS) {  			/* src address space is register */  			if (((info & 0x60000000) >> 29) == 0) { @@ -3094,3 +3580,114 @@ int evergreen_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib)  	return ret;  } + +/** + * evergreen_dma_ib_parse() - parse the DMA IB for VM + * @rdev: radeon_device pointer + * @ib:	radeon_ib pointer + * + * Parses the DMA IB from the VM CS ioctl + * checks for errors. (Cayman-SI) + * Returns 0 for success and an error on failure. + **/ +int evergreen_dma_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib) +{ +	u32 idx = 0; +	u32 header, cmd, count, tiled, new_cmd, misc; + +	do { +		header = ib->ptr[idx]; +		cmd = GET_DMA_CMD(header); +		count = GET_DMA_COUNT(header); +		tiled = GET_DMA_T(header); +		new_cmd = GET_DMA_NEW(header); +		misc = GET_DMA_MISC(header); + +		switch (cmd) { +		case DMA_PACKET_WRITE: +			if (tiled) +				idx += count + 7; +			else +				idx += count + 3; +			break; +		case DMA_PACKET_COPY: +			if (tiled) { +				if (new_cmd) { +					switch (misc) { +					case 0: +						/* L2T, frame to fields */ +						idx += 10; +						break; +					case 1: +						/* L2T, T2L partial */ +						idx += 12; +						break; +					case 3: +						/* L2T, broadcast */ +						idx += 10; +						break; +					case 4: +						/* L2T, T2L */ +						idx += 9; +						break; +					case 5: +						/* T2T partial */ +						idx += 13; +						break; +					case 7: +						/* L2T, broadcast */ +						idx += 10; +						break; +					default: +						DRM_ERROR("bad DMA_PACKET_COPY misc %u\n", misc); +						return -EINVAL; +					} +				} else { +					switch (misc) { +					case 0: +						idx += 9; +						break; +					default: +						DRM_ERROR("bad DMA_PACKET_COPY misc %u\n", misc); +						return -EINVAL; +					} +				} +			} else { +				if (new_cmd) { +					switch (misc) { +					case 0: +						/* L2L, byte */ +						idx += 5; +						break; +					case 1: +						/* L2L, partial */ +						idx += 9; +						break; +					case 4: +						/* L2L, dw, broadcast */ +						idx += 7; +						break; +					default: +						DRM_ERROR("bad DMA_PACKET_COPY misc %u\n", misc); +						return -EINVAL; +					} +				} else { +					/* L2L, dw */ +					idx += 5; +				} +			} +			break; +		case DMA_PACKET_CONSTANT_FILL: +			idx += 4; +			break; +		case DMA_PACKET_NOP: +			idx += 1; +			break; +		default: +			DRM_ERROR("Unknown packet type %d at %d !\n", cmd, idx); +			return -EINVAL; +		} +	} while (idx < ib->length_dw); + +	return 0; +} diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 376884f1bcd..8ff7cac222d 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -4135,23 +4135,36 @@ int r100_init(struct radeon_device *rdev)  	return 0;  } -uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg) +uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg, +		      bool always_indirect)  { -	if (reg < rdev->rmmio_size) +	if (reg < rdev->rmmio_size && !always_indirect)  		return readl(((void __iomem *)rdev->rmmio) + reg);  	else { +		unsigned long flags; +		uint32_t ret; + +		spin_lock_irqsave(&rdev->mmio_idx_lock, flags);  		writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX); -		return readl(((void __iomem *)rdev->rmmio) + RADEON_MM_DATA); +		ret = readl(((void __iomem *)rdev->rmmio) + RADEON_MM_DATA); +		spin_unlock_irqrestore(&rdev->mmio_idx_lock, flags); + +		return ret;  	}  } -void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) +void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v, +		  bool always_indirect)  { -	if (reg < rdev->rmmio_size) +	if (reg < rdev->rmmio_size && !always_indirect)  		writel(v, ((void __iomem *)rdev->rmmio) + reg);  	else { +		unsigned long flags; + +		spin_lock_irqsave(&rdev->mmio_idx_lock, flags);  		writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);  		writel(v, ((void __iomem *)rdev->rmmio) + RADEON_MM_DATA); +		spin_unlock_irqrestore(&rdev->mmio_idx_lock, flags);  	}  } diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c index 5d6e7f959e7..0be768be530 100644 --- a/drivers/gpu/drm/radeon/r600_cs.c +++ b/drivers/gpu/drm/radeon/r600_cs.c @@ -657,87 +657,30 @@ static int r600_cs_track_validate_db(struct radeon_cs_parser *p)  			/* nby is npipes htiles aligned == npipes * 8 pixel aligned */  			nby = round_up(nby, track->npipes * 8);  		} else { -			/* htile widht & nby (8 or 4) make 2 bits number */ -			tmp = track->htile_surface & 3; +			/* always assume 8x8 htile */  			/* align is htile align * 8, htile align vary according to  			 * number of pipe and tile width and nby  			 */  			switch (track->npipes) {  			case 8: -				switch (tmp) { -				case 3:	/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ -					nbx = round_up(nbx, 64 * 8); -					nby = round_up(nby, 64 * 8); -					break; -				case 2:	/* HTILE_WIDTH = 4 & HTILE_HEIGHT = 8*/ -				case 1:	/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 4*/ -					nbx = round_up(nbx, 64 * 8); -					nby = round_up(nby, 32 * 8); -					break; -				case 0:	/* HTILE_WIDTH = 4 & HTILE_HEIGHT = 4*/ -					nbx = round_up(nbx, 32 * 8); -					nby = round_up(nby, 32 * 8); -					break; -				default: -					return -EINVAL; -				} +				/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ +				nbx = round_up(nbx, 64 * 8); +				nby = round_up(nby, 64 * 8);  				break;  			case 4: -				switch (tmp) { -				case 3:	/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ -					nbx = round_up(nbx, 64 * 8); -					nby = round_up(nby, 32 * 8); -					break; -				case 2:	/* HTILE_WIDTH = 4 & HTILE_HEIGHT = 8*/ -				case 1:	/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 4*/ -					nbx = round_up(nbx, 32 * 8); -					nby = round_up(nby, 32 * 8); -					break; -				case 0:	/* HTILE_WIDTH = 4 & HTILE_HEIGHT = 4*/ -					nbx = round_up(nbx, 32 * 8); -					nby = round_up(nby, 16 * 8); -					break; -				default: -					return -EINVAL; -				} +				/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ +				nbx = round_up(nbx, 64 * 8); +				nby = round_up(nby, 32 * 8);  				break;  			case 2: -				switch (tmp) { -				case 3:	/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ -					nbx = round_up(nbx, 32 * 8); -					nby = round_up(nby, 32 * 8); -					break; -				case 2:	/* HTILE_WIDTH = 4 & HTILE_HEIGHT = 8*/ -				case 1:	/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 4*/ -					nbx = round_up(nbx, 32 * 8); -					nby = round_up(nby, 16 * 8); -					break; -				case 0:	/* HTILE_WIDTH = 4 & HTILE_HEIGHT = 4*/ -					nbx = round_up(nbx, 16 * 8); -					nby = round_up(nby, 16 * 8); -					break; -				default: -					return -EINVAL; -				} +				/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ +				nbx = round_up(nbx, 32 * 8); +				nby = round_up(nby, 32 * 8);  				break;  			case 1: -				switch (tmp) { -				case 3:	/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ -					nbx = round_up(nbx, 32 * 8); -					nby = round_up(nby, 16 * 8); -					break; -				case 2:	/* HTILE_WIDTH = 4 & HTILE_HEIGHT = 8*/ -				case 1:	/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 4*/ -					nbx = round_up(nbx, 16 * 8); -					nby = round_up(nby, 16 * 8); -					break; -				case 0:	/* HTILE_WIDTH = 4 & HTILE_HEIGHT = 4*/ -					nbx = round_up(nbx, 16 * 8); -					nby = round_up(nby, 8 * 8); -					break; -				default: -					return -EINVAL; -				} +				/* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/ +				nbx = round_up(nbx, 32 * 8); +				nby = round_up(nby, 16 * 8);  				break;  			default:  				dev_warn(p->dev, "%s:%d invalid num pipes %d\n", @@ -746,9 +689,10 @@ static int r600_cs_track_validate_db(struct radeon_cs_parser *p)  			}  		}  		/* compute number of htile */ -		nbx = G_028D24_HTILE_WIDTH(track->htile_surface) ? nbx / 8 : nbx / 4; -		nby = G_028D24_HTILE_HEIGHT(track->htile_surface) ? nby / 8 : nby / 4; -		size = nbx * nby * 4; +		nbx = nbx >> 3; +		nby = nby >> 3; +		/* size must be aligned on npipes * 2K boundary */ +		size = roundup(nbx * nby * 4, track->npipes * (2 << 10));  		size += track->htile_offset;  		if (size > radeon_bo_size(track->htile_bo)) { @@ -1492,6 +1436,8 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)  		break;  	case DB_HTILE_SURFACE:  		track->htile_surface = radeon_get_ib_value(p, idx); +		/* force 8x8 htile width and height */ +		ib[idx] |= 3;  		track->db_dirty = true;  		break;  	case SQ_PGM_START_FS: @@ -2568,3 +2514,196 @@ void r600_cs_legacy_init(void)  {  	r600_cs_packet_next_reloc = &r600_cs_packet_next_reloc_nomm;  } + +/* + *  DMA + */ +/** + * r600_dma_cs_next_reloc() - parse next reloc + * @p:		parser structure holding parsing context. + * @cs_reloc:		reloc informations + * + * Return the next reloc, do bo validation and compute + * GPU offset using the provided start. + **/ +int r600_dma_cs_next_reloc(struct radeon_cs_parser *p, +			   struct radeon_cs_reloc **cs_reloc) +{ +	struct radeon_cs_chunk *relocs_chunk; +	unsigned idx; + +	if (p->chunk_relocs_idx == -1) { +		DRM_ERROR("No relocation chunk !\n"); +		return -EINVAL; +	} +	*cs_reloc = NULL; +	relocs_chunk = &p->chunks[p->chunk_relocs_idx]; +	idx = p->dma_reloc_idx; +	if (idx >= relocs_chunk->length_dw) { +		DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", +			  idx, relocs_chunk->length_dw); +		return -EINVAL; +	} +	*cs_reloc = p->relocs_ptr[idx]; +	p->dma_reloc_idx++; +	return 0; +} + +#define GET_DMA_CMD(h) (((h) & 0xf0000000) >> 28) +#define GET_DMA_COUNT(h) ((h) & 0x0000ffff) +#define GET_DMA_T(h) (((h) & 0x00800000) >> 23) + +/** + * r600_dma_cs_parse() - parse the DMA IB + * @p:		parser structure holding parsing context. + * + * Parses the DMA IB from the CS ioctl and updates + * the GPU addresses based on the reloc information and + * checks for errors. (R6xx-R7xx) + * Returns 0 for success and an error on failure. + **/ +int r600_dma_cs_parse(struct radeon_cs_parser *p) +{ +	struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx]; +	struct radeon_cs_reloc *src_reloc, *dst_reloc; +	u32 header, cmd, count, tiled; +	volatile u32 *ib = p->ib.ptr; +	u32 idx, idx_value; +	u64 src_offset, dst_offset; +	int r; + +	do { +		if (p->idx >= ib_chunk->length_dw) { +			DRM_ERROR("Can not parse packet at %d after CS end %d !\n", +				  p->idx, ib_chunk->length_dw); +			return -EINVAL; +		} +		idx = p->idx; +		header = radeon_get_ib_value(p, idx); +		cmd = GET_DMA_CMD(header); +		count = GET_DMA_COUNT(header); +		tiled = GET_DMA_T(header); + +		switch (cmd) { +		case DMA_PACKET_WRITE: +			r = r600_dma_cs_next_reloc(p, &dst_reloc); +			if (r) { +				DRM_ERROR("bad DMA_PACKET_WRITE\n"); +				return -EINVAL; +			} +			if (tiled) { +				dst_offset = ib[idx+1]; +				dst_offset <<= 8; + +				ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); +				p->idx += count + 5; +			} else { +				dst_offset = ib[idx+1]; +				dst_offset |= ((u64)(ib[idx+2] & 0xff)) << 32; + +				ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); +				ib[idx+2] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; +				p->idx += count + 3; +			} +			if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { +				dev_warn(p->dev, "DMA write buffer too small (%llu %lu)\n", +					 dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); +				return -EINVAL; +			} +			break; +		case DMA_PACKET_COPY: +			r = r600_dma_cs_next_reloc(p, &src_reloc); +			if (r) { +				DRM_ERROR("bad DMA_PACKET_COPY\n"); +				return -EINVAL; +			} +			r = r600_dma_cs_next_reloc(p, &dst_reloc); +			if (r) { +				DRM_ERROR("bad DMA_PACKET_COPY\n"); +				return -EINVAL; +			} +			if (tiled) { +				idx_value = radeon_get_ib_value(p, idx + 2); +				/* detile bit */ +				if (idx_value & (1 << 31)) { +					/* tiled src, linear dst */ +					src_offset = ib[idx+1]; +					src_offset <<= 8; +					ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8); + +					dst_offset = ib[idx+5]; +					dst_offset |= ((u64)(ib[idx+6] & 0xff)) << 32; +					ib[idx+5] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); +					ib[idx+6] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; +				} else { +					/* linear src, tiled dst */ +					src_offset = ib[idx+5]; +					src_offset |= ((u64)(ib[idx+6] & 0xff)) << 32; +					ib[idx+5] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); +					ib[idx+6] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; + +					dst_offset = ib[idx+1]; +					dst_offset <<= 8; +					ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); +				} +				p->idx += 7; +			} else { +				src_offset = ib[idx+2]; +				src_offset |= ((u64)(ib[idx+4] & 0xff)) << 32; +				dst_offset = ib[idx+1]; +				dst_offset |= ((u64)(ib[idx+3] & 0xff)) << 32; + +				ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); +				ib[idx+2] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc); +				ib[idx+3] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff; +				ib[idx+4] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; +				p->idx += 5; +			} +			if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) { +				dev_warn(p->dev, "DMA copy src buffer too small (%llu %lu)\n", +					 src_offset + (count * 4), radeon_bo_size(src_reloc->robj)); +				return -EINVAL; +			} +			if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { +				dev_warn(p->dev, "DMA write dst buffer too small (%llu %lu)\n", +					 dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); +				return -EINVAL; +			} +			break; +		case DMA_PACKET_CONSTANT_FILL: +			if (p->family < CHIP_RV770) { +				DRM_ERROR("Constant Fill is 7xx only !\n"); +				return -EINVAL; +			} +			r = r600_dma_cs_next_reloc(p, &dst_reloc); +			if (r) { +				DRM_ERROR("bad DMA_PACKET_WRITE\n"); +				return -EINVAL; +			} +			dst_offset = ib[idx+1]; +			dst_offset |= ((u64)(ib[idx+3] & 0x00ff0000)) << 16; +			if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) { +				dev_warn(p->dev, "DMA constant fill buffer too small (%llu %lu)\n", +					 dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj)); +				return -EINVAL; +			} +			ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc); +			ib[idx+3] += (upper_32_bits(dst_reloc->lobj.gpu_offset) << 16) & 0x00ff0000; +			p->idx += 4; +			break; +		case DMA_PACKET_NOP: +			p->idx += 1; +			break; +		default: +			DRM_ERROR("Unknown packet type %d at %d !\n", cmd, idx); +			return -EINVAL; +		} +	} while (p->idx < p->chunks[p->chunk_ib_idx].length_dw); +#if 0 +	for (r = 0; r < p->ib->length_dw; r++) { +		printk(KERN_INFO "%05d  0x%08X\n", r, p->ib.ptr[r]); +		mdelay(1); +	} +#endif +	return 0; +} diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 1b9120a875e..5dc744d43d1 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -839,6 +839,7 @@ struct radeon_cs_parser {  	struct radeon_cs_reloc	*relocs;  	struct radeon_cs_reloc	**relocs_ptr;  	struct list_head	validated; +	unsigned		dma_reloc_idx;  	/* indices of various chunks */  	int			chunk_ib_idx;  	int			chunk_relocs_idx; @@ -1556,6 +1557,8 @@ struct radeon_device {  	/* Register mmio */  	resource_size_t			rmmio_base;  	resource_size_t			rmmio_size; +	/* protects concurrent MM_INDEX/DATA based register access */ +	spinlock_t mmio_idx_lock;  	void __iomem			*rmmio;  	radeon_rreg_t			mc_rreg;  	radeon_wreg_t			mc_wreg; @@ -1631,8 +1634,10 @@ int radeon_device_init(struct radeon_device *rdev,  void radeon_device_fini(struct radeon_device *rdev);  int radeon_gpu_wait_for_idle(struct radeon_device *rdev); -uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg); -void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); +uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg, +		      bool always_indirect); +void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v, +		  bool always_indirect);  u32 r100_io_rreg(struct radeon_device *rdev, u32 reg);  void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v); @@ -1648,9 +1653,11 @@ void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v);  #define WREG8(reg, v) writeb(v, (rdev->rmmio) + (reg))  #define RREG16(reg) readw((rdev->rmmio) + (reg))  #define WREG16(reg, v) writew(v, (rdev->rmmio) + (reg)) -#define RREG32(reg) r100_mm_rreg(rdev, (reg)) -#define DREG32(reg) printk(KERN_INFO "REGISTER: " #reg " : 0x%08X\n", r100_mm_rreg(rdev, (reg))) -#define WREG32(reg, v) r100_mm_wreg(rdev, (reg), (v)) +#define RREG32(reg) r100_mm_rreg(rdev, (reg), false) +#define RREG32_IDX(reg) r100_mm_rreg(rdev, (reg), true) +#define DREG32(reg) printk(KERN_INFO "REGISTER: " #reg " : 0x%08X\n", r100_mm_rreg(rdev, (reg), false)) +#define WREG32(reg, v) r100_mm_wreg(rdev, (reg), (v), false) +#define WREG32_IDX(reg, v) r100_mm_wreg(rdev, (reg), (v), true)  #define REG_SET(FIELD, v) (((v) << FIELD##_SHIFT) & FIELD##_MASK)  #define REG_GET(FIELD, v) (((v) << FIELD##_SHIFT) & FIELD##_MASK)  #define RREG32_PLL(reg) rdev->pll_rreg(rdev, (reg)) @@ -1675,7 +1682,7 @@ void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v);  		tmp_ |= ((val) & ~(mask));			\  		WREG32_PLL(reg, tmp_);				\  	} while (0) -#define DREG32_SYS(sqf, rdev, reg) seq_printf((sqf), #reg " : 0x%08X\n", r100_mm_rreg((rdev), (reg))) +#define DREG32_SYS(sqf, rdev, reg) seq_printf((sqf), #reg " : 0x%08X\n", r100_mm_rreg((rdev), (reg), false))  #define RREG32_IO(reg) r100_io_rreg(rdev, (reg))  #define WREG32_IO(reg, v) r100_io_wreg(rdev, (reg), (v)) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 3ea0475f9a9..596bcbe80ed 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -952,7 +952,7 @@ static struct radeon_asic r600_asic = {  			.ib_execute = &r600_dma_ring_ib_execute,  			.emit_fence = &r600_dma_fence_ring_emit,  			.emit_semaphore = &r600_dma_semaphore_ring_emit, -			.cs_parse = NULL, +			.cs_parse = &r600_dma_cs_parse,  			.ring_test = &r600_dma_ring_test,  			.ib_test = &r600_dma_ib_test,  			.is_lockup = &r600_dma_is_lockup, @@ -1036,7 +1036,7 @@ static struct radeon_asic rs780_asic = {  			.ib_execute = &r600_dma_ring_ib_execute,  			.emit_fence = &r600_dma_fence_ring_emit,  			.emit_semaphore = &r600_dma_semaphore_ring_emit, -			.cs_parse = NULL, +			.cs_parse = &r600_dma_cs_parse,  			.ring_test = &r600_dma_ring_test,  			.ib_test = &r600_dma_ib_test,  			.is_lockup = &r600_dma_is_lockup, @@ -1120,7 +1120,7 @@ static struct radeon_asic rv770_asic = {  			.ib_execute = &r600_dma_ring_ib_execute,  			.emit_fence = &r600_dma_fence_ring_emit,  			.emit_semaphore = &r600_dma_semaphore_ring_emit, -			.cs_parse = NULL, +			.cs_parse = &r600_dma_cs_parse,  			.ring_test = &r600_dma_ring_test,  			.ib_test = &r600_dma_ib_test,  			.is_lockup = &r600_dma_is_lockup, @@ -1204,7 +1204,7 @@ static struct radeon_asic evergreen_asic = {  			.ib_execute = &evergreen_dma_ring_ib_execute,  			.emit_fence = &evergreen_dma_fence_ring_emit,  			.emit_semaphore = &r600_dma_semaphore_ring_emit, -			.cs_parse = NULL, +			.cs_parse = &evergreen_dma_cs_parse,  			.ring_test = &r600_dma_ring_test,  			.ib_test = &r600_dma_ib_test,  			.is_lockup = &r600_dma_is_lockup, @@ -1288,7 +1288,7 @@ static struct radeon_asic sumo_asic = {  			.ib_execute = &evergreen_dma_ring_ib_execute,  			.emit_fence = &evergreen_dma_fence_ring_emit,  			.emit_semaphore = &r600_dma_semaphore_ring_emit, -			.cs_parse = NULL, +			.cs_parse = &evergreen_dma_cs_parse,  			.ring_test = &r600_dma_ring_test,  			.ib_test = &r600_dma_ib_test,  			.is_lockup = &r600_dma_is_lockup, @@ -1372,7 +1372,7 @@ static struct radeon_asic btc_asic = {  			.ib_execute = &evergreen_dma_ring_ib_execute,  			.emit_fence = &evergreen_dma_fence_ring_emit,  			.emit_semaphore = &r600_dma_semaphore_ring_emit, -			.cs_parse = NULL, +			.cs_parse = &evergreen_dma_cs_parse,  			.ring_test = &r600_dma_ring_test,  			.ib_test = &r600_dma_ib_test,  			.is_lockup = &r600_dma_is_lockup, @@ -1484,9 +1484,10 @@ static struct radeon_asic cayman_asic = {  		},  		[R600_RING_TYPE_DMA_INDEX] = {  			.ib_execute = &cayman_dma_ring_ib_execute, +			.ib_parse = &evergreen_dma_ib_parse,  			.emit_fence = &evergreen_dma_fence_ring_emit,  			.emit_semaphore = &r600_dma_semaphore_ring_emit, -			.cs_parse = NULL, +			.cs_parse = &evergreen_dma_cs_parse,  			.ring_test = &r600_dma_ring_test,  			.ib_test = &r600_dma_ib_test,  			.is_lockup = &cayman_dma_is_lockup, @@ -1494,9 +1495,10 @@ static struct radeon_asic cayman_asic = {  		},  		[CAYMAN_RING_TYPE_DMA1_INDEX] = {  			.ib_execute = &cayman_dma_ring_ib_execute, +			.ib_parse = &evergreen_dma_ib_parse,  			.emit_fence = &evergreen_dma_fence_ring_emit,  			.emit_semaphore = &r600_dma_semaphore_ring_emit, -			.cs_parse = NULL, +			.cs_parse = &evergreen_dma_cs_parse,  			.ring_test = &r600_dma_ring_test,  			.ib_test = &r600_dma_ib_test,  			.is_lockup = &cayman_dma_is_lockup, @@ -1609,9 +1611,10 @@ static struct radeon_asic trinity_asic = {  		},  		[R600_RING_TYPE_DMA_INDEX] = {  			.ib_execute = &cayman_dma_ring_ib_execute, +			.ib_parse = &evergreen_dma_ib_parse,  			.emit_fence = &evergreen_dma_fence_ring_emit,  			.emit_semaphore = &r600_dma_semaphore_ring_emit, -			.cs_parse = NULL, +			.cs_parse = &evergreen_dma_cs_parse,  			.ring_test = &r600_dma_ring_test,  			.ib_test = &r600_dma_ib_test,  			.is_lockup = &cayman_dma_is_lockup, @@ -1619,9 +1622,10 @@ static struct radeon_asic trinity_asic = {  		},  		[CAYMAN_RING_TYPE_DMA1_INDEX] = {  			.ib_execute = &cayman_dma_ring_ib_execute, +			.ib_parse = &evergreen_dma_ib_parse,  			.emit_fence = &evergreen_dma_fence_ring_emit,  			.emit_semaphore = &r600_dma_semaphore_ring_emit, -			.cs_parse = NULL, +			.cs_parse = &evergreen_dma_cs_parse,  			.ring_test = &r600_dma_ring_test,  			.ib_test = &r600_dma_ib_test,  			.is_lockup = &cayman_dma_is_lockup, @@ -1734,6 +1738,7 @@ static struct radeon_asic si_asic = {  		},  		[R600_RING_TYPE_DMA_INDEX] = {  			.ib_execute = &cayman_dma_ring_ib_execute, +			.ib_parse = &evergreen_dma_ib_parse,  			.emit_fence = &evergreen_dma_fence_ring_emit,  			.emit_semaphore = &r600_dma_semaphore_ring_emit,  			.cs_parse = NULL, @@ -1744,6 +1749,7 @@ static struct radeon_asic si_asic = {  		},  		[CAYMAN_RING_TYPE_DMA1_INDEX] = {  			.ib_execute = &cayman_dma_ring_ib_execute, +			.ib_parse = &evergreen_dma_ib_parse,  			.emit_fence = &evergreen_dma_fence_ring_emit,  			.emit_semaphore = &r600_dma_semaphore_ring_emit,  			.cs_parse = NULL, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index c338931190a..5f4882cc215 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -304,6 +304,7 @@ void r600_pcie_gart_tlb_flush(struct radeon_device *rdev);  uint32_t r600_pciep_rreg(struct radeon_device *rdev, uint32_t reg);  void r600_pciep_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);  int r600_cs_parse(struct radeon_cs_parser *p); +int r600_dma_cs_parse(struct radeon_cs_parser *p);  void r600_fence_ring_emit(struct radeon_device *rdev,  			  struct radeon_fence *fence);  void r600_semaphore_ring_emit(struct radeon_device *rdev, @@ -430,6 +431,7 @@ u32 evergreen_get_vblank_counter(struct radeon_device *rdev, int crtc);  int evergreen_irq_set(struct radeon_device *rdev);  int evergreen_irq_process(struct radeon_device *rdev);  extern int evergreen_cs_parse(struct radeon_cs_parser *p); +extern int evergreen_dma_cs_parse(struct radeon_cs_parser *p);  extern void evergreen_pm_misc(struct radeon_device *rdev);  extern void evergreen_pm_prepare(struct radeon_device *rdev);  extern void evergreen_pm_finish(struct radeon_device *rdev); @@ -471,6 +473,7 @@ void cayman_vm_set_page(struct radeon_device *rdev, uint64_t pe,  			uint64_t addr, unsigned count,  			uint32_t incr, uint32_t flags);  int evergreen_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib); +int evergreen_dma_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib);  void cayman_dma_ring_ib_execute(struct radeon_device *rdev,  				struct radeon_ib *ib);  bool cayman_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring); diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index 45b660b27cf..4af89126e22 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -3246,11 +3246,9 @@ static uint32_t combios_detect_ram(struct drm_device *dev, int ram,  	while (ram--) {  		addr = ram * 1024 * 1024;  		/* write to each page */ -		WREG32(RADEON_MM_INDEX, (addr) | RADEON_MM_APER); -		WREG32(RADEON_MM_DATA, 0xdeadbeef); +		WREG32_IDX((addr) | RADEON_MM_APER, 0xdeadbeef);  		/* read back and verify */ -		WREG32(RADEON_MM_INDEX, (addr) | RADEON_MM_APER); -		if (RREG32(RADEON_MM_DATA) != 0xdeadbeef) +		if (RREG32_IDX((addr) | RADEON_MM_APER) != 0xdeadbeef)  			return 0;  	} diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c index 8b2797dc7b6..9143fc45e35 100644 --- a/drivers/gpu/drm/radeon/radeon_cp.c +++ b/drivers/gpu/drm/radeon/radeon_cp.c @@ -116,20 +116,6 @@ u32 radeon_get_scratch(drm_radeon_private_t *dev_priv, int index)  	}  } -u32 RADEON_READ_MM(drm_radeon_private_t *dev_priv, int addr) -{ -	u32 ret; - -	if (addr < 0x10000) -		ret = DRM_READ32(dev_priv->mmio, addr); -	else { -		DRM_WRITE32(dev_priv->mmio, RADEON_MM_INDEX, addr); -		ret = DRM_READ32(dev_priv->mmio, RADEON_MM_DATA); -	} - -	return ret; -} -  static u32 R500_READ_MCIND(drm_radeon_private_t *dev_priv, int addr)  {  	u32 ret; diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 41672cc563f..396baba0141 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -43,6 +43,7 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)  		return 0;  	}  	chunk = &p->chunks[p->chunk_relocs_idx]; +	p->dma_reloc_idx = 0;  	/* FIXME: we assume that each relocs use 4 dwords */  	p->nrelocs = chunk->length_dw / 4;  	p->relocs_ptr = kcalloc(p->nrelocs, sizeof(void *), GFP_KERNEL); @@ -111,6 +112,18 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority  		} else  			p->ring = RADEON_RING_TYPE_GFX_INDEX;  		break; +	case RADEON_CS_RING_DMA: +		if (p->rdev->family >= CHIP_CAYMAN) { +			if (p->priority > 0) +				p->ring = R600_RING_TYPE_DMA_INDEX; +			else +				p->ring = CAYMAN_RING_TYPE_DMA1_INDEX; +		} else if (p->rdev->family >= CHIP_R600) { +			p->ring = R600_RING_TYPE_DMA_INDEX; +		} else { +			return -EINVAL; +		} +		break;  	}  	return 0;  } diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c index 0fe56c9f64b..ad6df625e8b 100644 --- a/drivers/gpu/drm/radeon/radeon_cursor.c +++ b/drivers/gpu/drm/radeon/radeon_cursor.c @@ -66,24 +66,25 @@ static void radeon_hide_cursor(struct drm_crtc *crtc)  	struct radeon_device *rdev = crtc->dev->dev_private;  	if (ASIC_IS_DCE4(rdev)) { -		WREG32(RADEON_MM_INDEX, EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset); -		WREG32(RADEON_MM_DATA, EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT) | -		       EVERGREEN_CURSOR_URGENT_CONTROL(EVERGREEN_CURSOR_URGENT_1_2)); +		WREG32_IDX(EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset, +			   EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT) | +			   EVERGREEN_CURSOR_URGENT_CONTROL(EVERGREEN_CURSOR_URGENT_1_2));  	} else if (ASIC_IS_AVIVO(rdev)) { -		WREG32(RADEON_MM_INDEX, AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset); -		WREG32(RADEON_MM_DATA, (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT)); +		WREG32_IDX(AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset, +			   (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT));  	} else { +		u32 reg;  		switch (radeon_crtc->crtc_id) {  		case 0: -			WREG32(RADEON_MM_INDEX, RADEON_CRTC_GEN_CNTL); +			reg = RADEON_CRTC_GEN_CNTL;  			break;  		case 1: -			WREG32(RADEON_MM_INDEX, RADEON_CRTC2_GEN_CNTL); +			reg = RADEON_CRTC2_GEN_CNTL;  			break;  		default:  			return;  		} -		WREG32_P(RADEON_MM_DATA, 0, ~RADEON_CRTC_CUR_EN); +		WREG32_IDX(reg, RREG32_IDX(reg) & ~RADEON_CRTC_CUR_EN);  	}  } diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index e2f5f888c37..49b06590001 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1059,6 +1059,7 @@ int radeon_device_init(struct radeon_device *rdev,  	/* Registers mapping */  	/* TODO: block userspace mapping of io register */ +	spin_lock_init(&rdev->mmio_idx_lock);  	rdev->rmmio_base = pci_resource_start(rdev->pdev, 2);  	rdev->rmmio_size = pci_resource_len(rdev->pdev, 2);  	rdev->rmmio = ioremap(rdev->rmmio_base, rdev->rmmio_size); diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index bfa2a601572..310c0e5254b 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -378,8 +378,12 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,  	work->old_rbo = rbo;  	obj = new_radeon_fb->obj;  	rbo = gem_to_radeon_bo(obj); + +	spin_lock(&rbo->tbo.bdev->fence_lock);  	if (rbo->tbo.sync_obj)  		work->fence = radeon_fence_ref(rbo->tbo.sync_obj); +	spin_unlock(&rbo->tbo.bdev->fence_lock); +  	INIT_WORK(&work->work, radeon_unpin_work_func);  	/* We borrow the event spin lock for protecting unpin_work */ diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 91b64278c4f..9b1a727d3c9 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -66,9 +66,11 @@   *   2.23.0 - allow STRMOUT_BASE_UPDATE on RS780 and RS880   *   2.24.0 - eg only: allow MIP_ADDRESS=0 for MSAA textures   *   2.25.0 - eg+: new info request for num SE and num SH + *   2.26.0 - r600-eg: fix htile size computation + *   2.27.0 - r600-SI: Add CS ioctl support for async DMA   */  #define KMS_DRIVER_MAJOR	2 -#define KMS_DRIVER_MINOR	25 +#define KMS_DRIVER_MINOR	27  #define KMS_DRIVER_PATCHLEVEL	0  int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);  int radeon_driver_unload_kms(struct drm_device *dev); diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h index a1b59ca96d0..e7fdf163a8c 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.h +++ b/drivers/gpu/drm/radeon/radeon_drv.h @@ -366,7 +366,6 @@ extern int radeon_cp_buffers(struct drm_device *dev, void *data, struct drm_file  extern u32 radeon_read_fb_location(drm_radeon_private_t *dev_priv);  extern void radeon_write_agp_location(drm_radeon_private_t *dev_priv, u32 agp_loc);  extern void radeon_write_agp_base(drm_radeon_private_t *dev_priv, u64 agp_base); -extern u32 RADEON_READ_MM(drm_radeon_private_t *dev_priv, int addr);  extern void radeon_freelist_reset(struct drm_device * dev);  extern struct drm_buf *radeon_freelist_get(struct drm_device * dev); diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 93d3445477b..883c95d8d90 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -96,9 +96,9 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain)  	}  	if (domain & RADEON_GEM_DOMAIN_CPU) {  		if (rbo->rdev->flags & RADEON_IS_AGP) { -			rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_TT; +			rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_SYSTEM;  		} else { -			rbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_TT; +			rbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM;  		}  	}  	if (!c) diff --git a/include/uapi/drm/radeon_drm.h b/include/uapi/drm/radeon_drm.h index 5645a878fae..eeda91774c8 100644 --- a/include/uapi/drm/radeon_drm.h +++ b/include/uapi/drm/radeon_drm.h @@ -917,6 +917,7 @@ struct drm_radeon_gem_va {  /* The second dword of RADEON_CHUNK_ID_FLAGS is a uint32 that sets the ring type */  #define RADEON_CS_RING_GFX          0  #define RADEON_CS_RING_COMPUTE      1 +#define RADEON_CS_RING_DMA          2  /* The third dword of RADEON_CHUNK_ID_FLAGS is a sint32 that sets the priority */  /* 0 = normal, + = higher priority, - = lower priority */  |