diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c')
| -rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c | 222 | 
1 files changed, 135 insertions, 87 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c index 875626a2eccb..7fe8fd884f06 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c @@ -36,7 +36,7 @@  #include "cikd.h"  /* 1 second timeout */ -#define VCE_IDLE_TIMEOUT_MS	1000 +#define VCE_IDLE_TIMEOUT	msecs_to_jiffies(1000)  /* Firmware Names */  #ifdef CONFIG_DRM_AMDGPU_CIK @@ -85,8 +85,6 @@ int amdgpu_vce_sw_init(struct amdgpu_device *adev, unsigned long size)  	unsigned ucode_version, version_major, version_minor, binary_id;  	int i, r; -	INIT_DELAYED_WORK(&adev->vce.idle_work, amdgpu_vce_idle_work_handler); -  	switch (adev->asic_type) {  #ifdef CONFIG_DRM_AMDGPU_CIK  	case CHIP_BONAIRE: @@ -197,6 +195,9 @@ int amdgpu_vce_sw_init(struct amdgpu_device *adev, unsigned long size)  		adev->vce.filp[i] = NULL;  	} +	INIT_DELAYED_WORK(&adev->vce.idle_work, amdgpu_vce_idle_work_handler); +	mutex_init(&adev->vce.idle_mutex); +  	return 0;  } @@ -209,6 +210,8 @@ int amdgpu_vce_sw_init(struct amdgpu_device *adev, unsigned long size)   */  int amdgpu_vce_sw_fini(struct amdgpu_device *adev)  { +	unsigned i; +  	if (adev->vce.vcpu_bo == NULL)  		return 0; @@ -216,10 +219,11 @@ int amdgpu_vce_sw_fini(struct amdgpu_device *adev)  	amdgpu_bo_unref(&adev->vce.vcpu_bo); -	amdgpu_ring_fini(&adev->vce.ring[0]); -	amdgpu_ring_fini(&adev->vce.ring[1]); +	for (i = 0; i < adev->vce.num_rings; i++) +		amdgpu_ring_fini(&adev->vce.ring[i]);  	release_firmware(adev->vce.fw); +	mutex_destroy(&adev->vce.idle_mutex);  	return 0;  } @@ -280,8 +284,8 @@ int amdgpu_vce_resume(struct amdgpu_device *adev)  	hdr = (const struct common_firmware_header *)adev->vce.fw->data;  	offset = le32_to_cpu(hdr->ucode_array_offset_bytes); -	memcpy(cpu_addr, (adev->vce.fw->data) + offset, -		(adev->vce.fw->size) - offset); +	memcpy_toio(cpu_addr, adev->vce.fw->data + offset, +		    adev->vce.fw->size - offset);  	amdgpu_bo_kunmap(adev->vce.vcpu_bo); @@ -301,46 +305,56 @@ static void amdgpu_vce_idle_work_handler(struct work_struct *work)  {  	struct amdgpu_device *adev =  		container_of(work, struct amdgpu_device, vce.idle_work.work); +	unsigned i, count = 0; -	if ((amdgpu_fence_count_emitted(&adev->vce.ring[0]) == 0) && -	    (amdgpu_fence_count_emitted(&adev->vce.ring[1]) == 0)) { +	for (i = 0; i < adev->vce.num_rings; i++) +		count += amdgpu_fence_count_emitted(&adev->vce.ring[i]); + +	if (count == 0) {  		if (adev->pm.dpm_enabled) {  			amdgpu_dpm_enable_vce(adev, false);  		} else {  			amdgpu_asic_set_vce_clocks(adev, 0, 0);  		}  	} else { -		schedule_delayed_work(&adev->vce.idle_work, -				      msecs_to_jiffies(VCE_IDLE_TIMEOUT_MS)); +		schedule_delayed_work(&adev->vce.idle_work, VCE_IDLE_TIMEOUT);  	}  }  /** - * amdgpu_vce_note_usage - power up VCE + * amdgpu_vce_ring_begin_use - power up VCE   * - * @adev: amdgpu_device pointer + * @ring: amdgpu ring   *   * Make sure VCE is powerd up when we want to use it   */ -static void amdgpu_vce_note_usage(struct amdgpu_device *adev) +void amdgpu_vce_ring_begin_use(struct amdgpu_ring *ring)  { -	bool streams_changed = false; -	bool set_clocks = !cancel_delayed_work_sync(&adev->vce.idle_work); -	set_clocks &= schedule_delayed_work(&adev->vce.idle_work, -					    msecs_to_jiffies(VCE_IDLE_TIMEOUT_MS)); - -	if (adev->pm.dpm_enabled) { -		/* XXX figure out if the streams changed */ -		streams_changed = false; -	} +	struct amdgpu_device *adev = ring->adev; +	bool set_clocks; -	if (set_clocks || streams_changed) { +	mutex_lock(&adev->vce.idle_mutex); +	set_clocks = !cancel_delayed_work_sync(&adev->vce.idle_work); +	if (set_clocks) {  		if (adev->pm.dpm_enabled) {  			amdgpu_dpm_enable_vce(adev, true);  		} else {  			amdgpu_asic_set_vce_clocks(adev, 53300, 40000);  		}  	} +	mutex_unlock(&adev->vce.idle_mutex); +} + +/** + * amdgpu_vce_ring_end_use - power VCE down + * + * @ring: amdgpu ring + * + * Schedule work to power VCE down again + */ +void amdgpu_vce_ring_end_use(struct amdgpu_ring *ring) +{ +	schedule_delayed_work(&ring->adev->vce.idle_work, VCE_IDLE_TIMEOUT);  }  /** @@ -357,11 +371,10 @@ void amdgpu_vce_free_handles(struct amdgpu_device *adev, struct drm_file *filp)  	int i, r;  	for (i = 0; i < AMDGPU_MAX_VCE_HANDLES; ++i) {  		uint32_t handle = atomic_read(&adev->vce.handles[i]); +  		if (!handle || adev->vce.filp[i] != filp)  			continue; -		amdgpu_vce_note_usage(adev); -  		r = amdgpu_vce_get_destroy_msg(ring, handle, false, NULL);  		if (r)  			DRM_ERROR("Error destroying VCE handle (%d)!\n", r); @@ -437,7 +450,7 @@ int amdgpu_vce_get_create_msg(struct amdgpu_ring *ring, uint32_t handle,  		ib->ptr[i] = 0x0;  	r = amdgpu_ib_schedule(ring, 1, ib, NULL, NULL, &f); -	job->fence = f; +	job->fence = fence_get(f);  	if (r)  		goto err; @@ -469,7 +482,6 @@ int amdgpu_vce_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle,  	struct amdgpu_job *job;  	struct amdgpu_ib *ib;  	struct fence *f = NULL; -	uint64_t dummy;  	int i, r;  	r = amdgpu_job_alloc_with_ib(ring->adev, ib_size_dw * 4, &job); @@ -477,7 +489,6 @@ int amdgpu_vce_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle,  		return r;  	ib = &job->ibs[0]; -	dummy = ib->gpu_addr + 1024;  	/* stitch together an VCE destroy msg */  	ib->length_dw = 0; @@ -485,11 +496,14 @@ int amdgpu_vce_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle,  	ib->ptr[ib->length_dw++] = 0x00000001; /* session cmd */  	ib->ptr[ib->length_dw++] = handle; -	ib->ptr[ib->length_dw++] = 0x00000014; /* len */ -	ib->ptr[ib->length_dw++] = 0x05000005; /* feedback buffer */ -	ib->ptr[ib->length_dw++] = upper_32_bits(dummy); -	ib->ptr[ib->length_dw++] = dummy; -	ib->ptr[ib->length_dw++] = 0x00000001; +	ib->ptr[ib->length_dw++] = 0x00000020; /* len */ +	ib->ptr[ib->length_dw++] = 0x00000002; /* task info */ +	ib->ptr[ib->length_dw++] = 0xffffffff; /* next task info, set to 0xffffffff if no */ +	ib->ptr[ib->length_dw++] = 0x00000001; /* destroy session */ +	ib->ptr[ib->length_dw++] = 0x00000000; +	ib->ptr[ib->length_dw++] = 0x00000000; +	ib->ptr[ib->length_dw++] = 0xffffffff; /* feedback is not needed, set to 0xffffffff and firmware will not output feedback */ +	ib->ptr[ib->length_dw++] = 0x00000000;  	ib->ptr[ib->length_dw++] = 0x00000008; /* len */  	ib->ptr[ib->length_dw++] = 0x02000001; /* destroy cmd */ @@ -499,7 +513,7 @@ int amdgpu_vce_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle,  	if (direct) {  		r = amdgpu_ib_schedule(ring, 1, ib, NULL, NULL, &f); -		job->fence = f; +		job->fence = fence_get(f);  		if (r)  			goto err; @@ -580,12 +594,10 @@ static int amdgpu_vce_cs_reloc(struct amdgpu_cs_parser *p, uint32_t ib_idx,   * we we don't have another free session index.   */  static int amdgpu_vce_validate_handle(struct amdgpu_cs_parser *p, -				      uint32_t handle, bool *allocated) +				      uint32_t handle, uint32_t *allocated)  {  	unsigned i; -	*allocated = false; -  	/* validate the handle */  	for (i = 0; i < AMDGPU_MAX_VCE_HANDLES; ++i) {  		if (atomic_read(&p->adev->vce.handles[i]) == handle) { @@ -602,7 +614,7 @@ static int amdgpu_vce_validate_handle(struct amdgpu_cs_parser *p,  		if (!atomic_cmpxchg(&p->adev->vce.handles[i], 0, handle)) {  			p->adev->vce.filp[i] = p->filp;  			p->adev->vce.img_size[i] = 0; -			*allocated = true; +			*allocated |= 1 << i;  			return i;  		}  	} @@ -622,14 +634,16 @@ int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, uint32_t ib_idx)  	struct amdgpu_ib *ib = &p->job->ibs[ib_idx];  	unsigned fb_idx = 0, bs_idx = 0;  	int session_idx = -1; -	bool destroyed = false; -	bool created = false; -	bool allocated = false; +	uint32_t destroyed = 0; +	uint32_t created = 0; +	uint32_t allocated = 0;  	uint32_t tmp, handle = 0;  	uint32_t *size = &tmp; -	int i, r = 0, idx = 0; +	int i, r, idx = 0; -	amdgpu_vce_note_usage(p->adev); +	r = amdgpu_cs_sysvm_access_required(p); +	if (r) +		return r;  	while (idx < ib->length_dw) {  		uint32_t len = amdgpu_get_ib_value(p, ib_idx, idx); @@ -641,30 +655,30 @@ int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, uint32_t ib_idx)  			goto out;  		} -		if (destroyed) { -			DRM_ERROR("No other command allowed after destroy!\n"); -			r = -EINVAL; -			goto out; -		} -  		switch (cmd) { -		case 0x00000001: // session +		case 0x00000001: /* session */  			handle = amdgpu_get_ib_value(p, ib_idx, idx + 2);  			session_idx = amdgpu_vce_validate_handle(p, handle,  								 &allocated); -			if (session_idx < 0) -				return session_idx; +			if (session_idx < 0) { +				r = session_idx; +				goto out; +			}  			size = &p->adev->vce.img_size[session_idx];  			break; -		case 0x00000002: // task info +		case 0x00000002: /* task info */  			fb_idx = amdgpu_get_ib_value(p, ib_idx, idx + 6);  			bs_idx = amdgpu_get_ib_value(p, ib_idx, idx + 7);  			break; -		case 0x01000001: // create -			created = true; -			if (!allocated) { +		case 0x01000001: /* create */ +			created |= 1 << session_idx; +			if (destroyed & (1 << session_idx)) { +				destroyed &= ~(1 << session_idx); +				allocated |= 1 << session_idx; + +			} else if (!(allocated & (1 << session_idx))) {  				DRM_ERROR("Handle already in use!\n");  				r = -EINVAL;  				goto out; @@ -675,16 +689,31 @@ int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, uint32_t ib_idx)  				8 * 3 / 2;  			break; -		case 0x04000001: // config extension -		case 0x04000002: // pic control -		case 0x04000005: // rate control -		case 0x04000007: // motion estimation -		case 0x04000008: // rdo -		case 0x04000009: // vui -		case 0x05000002: // auxiliary buffer +		case 0x04000001: /* config extension */ +		case 0x04000002: /* pic control */ +		case 0x04000005: /* rate control */ +		case 0x04000007: /* motion estimation */ +		case 0x04000008: /* rdo */ +		case 0x04000009: /* vui */ +		case 0x05000002: /* auxiliary buffer */ +		case 0x05000009: /* clock table */  			break; -		case 0x03000001: // encode +		case 0x0500000c: /* hw config */ +			switch (p->adev->asic_type) { +#ifdef CONFIG_DRM_AMDGPU_CIK +			case CHIP_KAVERI: +			case CHIP_MULLINS: +#endif +			case CHIP_CARRIZO: +				break; +			default: +				r = -EINVAL; +				goto out; +			} +			break; + +		case 0x03000001: /* encode */  			r = amdgpu_vce_cs_reloc(p, ib_idx, idx + 10, idx + 9,  						*size, 0);  			if (r) @@ -696,18 +725,18 @@ int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, uint32_t ib_idx)  				goto out;  			break; -		case 0x02000001: // destroy -			destroyed = true; +		case 0x02000001: /* destroy */ +			destroyed |= 1 << session_idx;  			break; -		case 0x05000001: // context buffer +		case 0x05000001: /* context buffer */  			r = amdgpu_vce_cs_reloc(p, ib_idx, idx + 3, idx + 2,  						*size * 2, 0);  			if (r)  				goto out;  			break; -		case 0x05000004: // video bitstream buffer +		case 0x05000004: /* video bitstream buffer */  			tmp = amdgpu_get_ib_value(p, ib_idx, idx + 4);  			r = amdgpu_vce_cs_reloc(p, ib_idx, idx + 3, idx + 2,  						tmp, bs_idx); @@ -715,7 +744,7 @@ int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, uint32_t ib_idx)  				goto out;  			break; -		case 0x05000005: // feedback buffer +		case 0x05000005: /* feedback buffer */  			r = amdgpu_vce_cs_reloc(p, ib_idx, idx + 3, idx + 2,  						4096, fb_idx);  			if (r) @@ -737,21 +766,24 @@ int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, uint32_t ib_idx)  		idx += len / 4;  	} -	if (allocated && !created) { +	if (allocated & ~created) {  		DRM_ERROR("New session without create command!\n");  		r = -ENOENT;  	}  out: -	if ((!r && destroyed) || (r && allocated)) { -		/* -		 * IB contains a destroy msg or we have allocated an -		 * handle and got an error, anyway free the handle -		 */ -		for (i = 0; i < AMDGPU_MAX_VCE_HANDLES; ++i) -			atomic_cmpxchg(&p->adev->vce.handles[i], handle, 0); +	if (!r) { +		/* No error, free all destroyed handle slots */ +		tmp = destroyed; +	} else { +		/* Error during parsing, free all allocated handle slots */ +		tmp = allocated;  	} +	for (i = 0; i < AMDGPU_MAX_VCE_HANDLES; ++i) +		if (tmp & (1 << i)) +			atomic_set(&p->adev->vce.handles[i], 0); +  	return r;  } @@ -791,6 +823,18 @@ void amdgpu_vce_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 seq,  	amdgpu_ring_write(ring, VCE_CMD_END);  } +unsigned amdgpu_vce_ring_get_emit_ib_size(struct amdgpu_ring *ring) +{ +	return +		4; /* amdgpu_vce_ring_emit_ib */ +} + +unsigned amdgpu_vce_ring_get_dma_frame_size(struct amdgpu_ring *ring) +{ +	return +		6; /* amdgpu_vce_ring_emit_fence  x1 no user fence */ +} +  /**   * amdgpu_vce_ring_test_ring - test if VCE ring is working   * @@ -837,32 +881,36 @@ int amdgpu_vce_ring_test_ring(struct amdgpu_ring *ring)   * @ring: the engine to test on   *   */ -int amdgpu_vce_ring_test_ib(struct amdgpu_ring *ring) +int amdgpu_vce_ring_test_ib(struct amdgpu_ring *ring, long timeout)  {  	struct fence *fence = NULL; -	int r; +	long r; -	/* skip vce ring1 ib test for now, since it's not reliable */ -	if (ring == &ring->adev->vce.ring[1]) +	/* skip vce ring1/2 ib test for now, since it's not reliable */ +	if (ring != &ring->adev->vce.ring[0])  		return 0;  	r = amdgpu_vce_get_create_msg(ring, 1, NULL);  	if (r) { -		DRM_ERROR("amdgpu: failed to get create msg (%d).\n", r); +		DRM_ERROR("amdgpu: failed to get create msg (%ld).\n", r);  		goto error;  	}  	r = amdgpu_vce_get_destroy_msg(ring, 1, true, &fence);  	if (r) { -		DRM_ERROR("amdgpu: failed to get destroy ib (%d).\n", r); +		DRM_ERROR("amdgpu: failed to get destroy ib (%ld).\n", r);  		goto error;  	} -	r = fence_wait(fence, false); -	if (r) { -		DRM_ERROR("amdgpu: fence wait failed (%d).\n", r); +	r = fence_wait_timeout(fence, false, timeout); +	if (r == 0) { +		DRM_ERROR("amdgpu: IB test timed out.\n"); +		r = -ETIMEDOUT; +	} else if (r < 0) { +		DRM_ERROR("amdgpu: fence wait failed (%ld).\n", r);  	} else {  		DRM_INFO("ib test on ring %d succeeded\n", ring->idx); +		r = 0;  	}  error:  	fence_put(fence);  | 
