aboutsummaryrefslogtreecommitdiffstats
path: root/common/recipes-kernel/linux/linux-amd/0027-drm-radeon-initial-VCE-support-v4.patch
diff options
context:
space:
mode:
Diffstat (limited to 'common/recipes-kernel/linux/linux-amd/0027-drm-radeon-initial-VCE-support-v4.patch')
-rw-r--r--common/recipes-kernel/linux/linux-amd/0027-drm-radeon-initial-VCE-support-v4.patch1434
1 files changed, 1434 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/linux-amd/0027-drm-radeon-initial-VCE-support-v4.patch b/common/recipes-kernel/linux/linux-amd/0027-drm-radeon-initial-VCE-support-v4.patch
new file mode 100644
index 00000000..00996021
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-amd/0027-drm-radeon-initial-VCE-support-v4.patch
@@ -0,0 +1,1434 @@
+From 769bdc09213b2d223c39eb5f8df2c741fca88321 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Christian=20K=C3=B6nig?= <christian.koenig@amd.com>
+Date: Thu, 23 May 2013 12:10:04 +0200
+Subject: [PATCH 27/60] drm/radeon: initial VCE support v4
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Only VCE 2.0 support so far.
+
+v2: squashing multiple patches into this one
+v3: add IRQ support for CIK, major cleanups,
+ basic code documentation
+v4: remove HAINAN from chipset list
+
+Signed-off-by: Christian König <christian.koenig@amd.com>
+---
+ drivers/gpu/drm/radeon/Makefile | 6 +
+ drivers/gpu/drm/radeon/cik.c | 60 ++++
+ drivers/gpu/drm/radeon/cikd.h | 33 ++
+ drivers/gpu/drm/radeon/radeon.h | 56 +++-
+ drivers/gpu/drm/radeon/radeon_asic.c | 17 +
+ drivers/gpu/drm/radeon/radeon_asic.h | 13 +
+ drivers/gpu/drm/radeon/radeon_cs.c | 4 +
+ drivers/gpu/drm/radeon/radeon_kms.c | 1 +
+ drivers/gpu/drm/radeon/radeon_ring.c | 4 +
+ drivers/gpu/drm/radeon/radeon_test.c | 39 ++-
+ drivers/gpu/drm/radeon/radeon_vce.c | 588 +++++++++++++++++++++++++++++++++++
+ drivers/gpu/drm/radeon/sid.h | 47 +++
+ drivers/gpu/drm/radeon/vce_v1_0.c | 187 +++++++++++
+ drivers/gpu/drm/radeon/vce_v2_0.c | 70 +++++
+ include/uapi/drm/radeon_drm.h | 1 +
+ 15 files changed, 1117 insertions(+), 9 deletions(-)
+ create mode 100644 drivers/gpu/drm/radeon/radeon_vce.c
+ create mode 100644 drivers/gpu/drm/radeon/vce_v1_0.c
+ create mode 100644 drivers/gpu/drm/radeon/vce_v2_0.c
+
+diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
+index 306364a..ed60caa 100644
+--- a/drivers/gpu/drm/radeon/Makefile
++++ b/drivers/gpu/drm/radeon/Makefile
+@@ -99,6 +99,12 @@ radeon-y += \
+ uvd_v3_1.o \
+ uvd_v4_2.o
+
++# add VCE block
++radeon-y += \
++ radeon_vce.o \
++ vce_v1_0.o \
++ vce_v2_0.o \
++
+ radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
+ radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o
+ radeon-$(CONFIG_ACPI) += radeon_acpi.o
+diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
+index cef0bbe..e256340 100644
+--- a/drivers/gpu/drm/radeon/cik.c
++++ b/drivers/gpu/drm/radeon/cik.c
+@@ -6835,6 +6835,20 @@ restart_ih:
+ /* reset addr and status */
+ WREG32_P(VM_CONTEXT1_CNTL2, 1, ~1);
+ break;
++ case 167: /* VCE */
++ DRM_DEBUG("IH: VCE int: 0x%08x\n", src_data);
++ switch (src_data) {
++ case 0:
++ radeon_fence_process(rdev, TN_RING_TYPE_VCE1_INDEX);
++ break;
++ case 1:
++ radeon_fence_process(rdev, TN_RING_TYPE_VCE2_INDEX);
++ break;
++ default:
++ DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data);
++ break;
++ }
++ break;
+ case 176: /* GFX RB CP_INT */
+ case 177: /* GFX IB CP_INT */
+ radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX);
+@@ -7134,6 +7148,22 @@ static int cik_startup(struct radeon_device *rdev)
+ if (r)
+ rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_size = 0;
+
++ r = radeon_vce_resume(rdev);
++ if (!r) {
++ r = vce_v2_0_resume(rdev);
++ if (!r)
++ r = radeon_fence_driver_start_ring(rdev,
++ TN_RING_TYPE_VCE1_INDEX);
++ if (!r)
++ r = radeon_fence_driver_start_ring(rdev,
++ TN_RING_TYPE_VCE2_INDEX);
++ }
++ if (r) {
++ dev_err(rdev->dev, "VCE init error (%d).\n", r);
++ rdev->ring[TN_RING_TYPE_VCE1_INDEX].ring_size = 0;
++ rdev->ring[TN_RING_TYPE_VCE2_INDEX].ring_size = 0;
++ }
++
+ /* Enable IRQ */
+ if (!rdev->irq.installed) {
+ r = radeon_irq_kms_init(rdev);
+@@ -7209,6 +7239,23 @@ static int cik_startup(struct radeon_device *rdev)
+ DRM_ERROR("radeon: failed initializing UVD (%d).\n", r);
+ }
+
++ r = -ENOENT;
++
++ ring = &rdev->ring[TN_RING_TYPE_VCE1_INDEX];
++ if (ring->ring_size)
++ r = radeon_ring_init(rdev, ring, ring->ring_size, 0,
++ VCE_CMD_NO_OP);
++
++ ring = &rdev->ring[TN_RING_TYPE_VCE2_INDEX];
++ if (ring->ring_size)
++ r = radeon_ring_init(rdev, ring, ring->ring_size, 0,
++ VCE_CMD_NO_OP);
++
++ if (!r)
++ r = vce_v1_0_init(rdev);
++ else if (r != -ENOENT)
++ DRM_ERROR("radeon: failed initializing VCE (%d).\n", r);
++
+ r = radeon_ib_pool_init(rdev);
+ if (r) {
+ dev_err(rdev->dev, "IB initialization failed (%d).\n", r);
+@@ -7276,6 +7323,7 @@ int cik_suspend(struct radeon_device *rdev)
+ cik_sdma_enable(rdev, false);
+ uvd_v1_0_fini(rdev);
+ radeon_uvd_suspend(rdev);
++ radeon_vce_suspend(rdev);
+ cik_fini_pg(rdev);
+ cik_fini_cg(rdev);
+ cik_irq_suspend(rdev);
+@@ -7405,6 +7453,17 @@ int cik_init(struct radeon_device *rdev)
+ r600_ring_init(rdev, ring, 4096);
+ }
+
++ r = radeon_vce_init(rdev);
++ if (!r) {
++ ring = &rdev->ring[TN_RING_TYPE_VCE1_INDEX];
++ ring->ring_obj = NULL;
++ r600_ring_init(rdev, ring, 4096);
++
++ ring = &rdev->ring[TN_RING_TYPE_VCE2_INDEX];
++ ring->ring_obj = NULL;
++ r600_ring_init(rdev, ring, 4096);
++ }
++
+ rdev->ih.ring_obj = NULL;
+ r600_ih_ring_init(rdev, 64 * 1024);
+
+@@ -7465,6 +7524,7 @@ void cik_fini(struct radeon_device *rdev)
+ radeon_irq_kms_fini(rdev);
+ uvd_v1_0_fini(rdev);
+ radeon_uvd_fini(rdev);
++ radeon_vce_fini(rdev);
+ cik_pcie_gart_fini(rdev);
+ r600_vram_scratch_fini(rdev);
+ radeon_gem_fini(rdev);
+diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h
+index c4738bc..54eb8be 100644
+--- a/drivers/gpu/drm/radeon/cikd.h
++++ b/drivers/gpu/drm/radeon/cikd.h
+@@ -1908,4 +1908,37 @@
+ /* UVD CTX indirect */
+ #define UVD_CGC_MEM_CTRL 0xC0
+
++/* VCE */
++
++#define VCE_VCPU_CACHE_OFFSET0 0x20024
++#define VCE_VCPU_CACHE_SIZE0 0x20028
++#define VCE_VCPU_CACHE_OFFSET1 0x2002c
++#define VCE_VCPU_CACHE_SIZE1 0x20030
++#define VCE_VCPU_CACHE_OFFSET2 0x20034
++#define VCE_VCPU_CACHE_SIZE2 0x20038
++#define VCE_RB_RPTR2 0x20178
++#define VCE_RB_WPTR2 0x2017c
++#define VCE_RB_RPTR 0x2018c
++#define VCE_RB_WPTR 0x20190
++#define VCE_CLOCK_GATING_A 0x202f8
++#define VCE_CLOCK_GATING_B 0x202fc
++#define VCE_UENC_CLOCK_GATING 0x207bc
++#define VCE_UENC_REG_CLOCK_GATING 0x207c0
++#define VCE_SYS_INT_EN 0x21300
++# define VCE_SYS_INT_TRAP_INTERRUPT_EN (1 << 3)
++#define VCE_LMI_CTRL2 0x21474
++#define VCE_LMI_CTRL 0x21498
++#define VCE_LMI_VM_CTRL 0x214a0
++#define VCE_LMI_SWAP_CNTL 0x214b4
++#define VCE_LMI_SWAP_CNTL1 0x214b8
++#define VCE_LMI_CACHE_CTRL 0x214f4
++
++#define VCE_CMD_NO_OP 0x00000000
++#define VCE_CMD_END 0x00000001
++#define VCE_CMD_IB 0x00000002
++#define VCE_CMD_FENCE 0x00000003
++#define VCE_CMD_TRAP 0x00000004
++#define VCE_CMD_IB_AUTO 0x00000005
++#define VCE_CMD_SEMAPHORE 0x00000006
++
+ #endif
+diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
+index aa1ffa9..0abbe5e 100644
+--- a/drivers/gpu/drm/radeon/radeon.h
++++ b/drivers/gpu/drm/radeon/radeon.h
+@@ -111,19 +111,16 @@ extern int radeon_aspm;
+ #define RADEONFB_CONN_LIMIT 4
+ #define RADEON_BIOS_NUM_SCRATCH 8
+
+-/* max number of rings */
+-#define RADEON_NUM_RINGS 6
+-
+ /* fence seq are set to this number when signaled */
+ #define RADEON_FENCE_SIGNALED_SEQ 0LL
+
+ /* internal ring indices */
+ /* r1xx+ has gfx CP ring */
+-#define RADEON_RING_TYPE_GFX_INDEX 0
++#define RADEON_RING_TYPE_GFX_INDEX 0
+
+ /* cayman has 2 compute CP rings */
+-#define CAYMAN_RING_TYPE_CP1_INDEX 1
+-#define CAYMAN_RING_TYPE_CP2_INDEX 2
++#define CAYMAN_RING_TYPE_CP1_INDEX 1
++#define CAYMAN_RING_TYPE_CP2_INDEX 2
+
+ /* R600+ has an async dma ring */
+ #define R600_RING_TYPE_DMA_INDEX 3
+@@ -131,7 +128,14 @@ extern int radeon_aspm;
+ #define CAYMAN_RING_TYPE_DMA1_INDEX 4
+
+ /* R600+ */
+-#define R600_RING_TYPE_UVD_INDEX 5
++#define R600_RING_TYPE_UVD_INDEX 5
++
++/* TN+ */
++#define TN_RING_TYPE_VCE1_INDEX 6
++#define TN_RING_TYPE_VCE2_INDEX 7
++
++/* max number of rings */
++#define RADEON_NUM_RINGS 8
+
+ /* hardcode those limit for now */
+ #define RADEON_VA_IB_OFFSET (1 << 20)
+@@ -1592,6 +1596,42 @@ int radeon_uvd_calc_upll_dividers(struct radeon_device *rdev,
+ int radeon_uvd_send_upll_ctlreq(struct radeon_device *rdev,
+ unsigned cg_upll_func_cntl);
+
++/*
++ * VCE
++ */
++#define RADEON_MAX_VCE_HANDLES 16
++#define RADEON_VCE_STACK_SIZE (1024*1024)
++#define RADEON_VCE_HEAP_SIZE (4*1024*1024)
++
++struct radeon_vce {
++ struct radeon_bo *vcpu_bo;
++ void *cpu_addr;
++ uint64_t gpu_addr;
++ atomic_t handles[RADEON_MAX_VCE_HANDLES];
++ struct drm_file *filp[RADEON_MAX_VCE_HANDLES];
++};
++
++int radeon_vce_init(struct radeon_device *rdev);
++void radeon_vce_fini(struct radeon_device *rdev);
++int radeon_vce_suspend(struct radeon_device *rdev);
++int radeon_vce_resume(struct radeon_device *rdev);
++int radeon_vce_get_create_msg(struct radeon_device *rdev, int ring,
++ uint32_t handle, struct radeon_fence **fence);
++int radeon_vce_get_destroy_msg(struct radeon_device *rdev, int ring,
++ uint32_t handle, struct radeon_fence **fence);
++void radeon_vce_free_handles(struct radeon_device *rdev, struct drm_file *filp);
++int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi);
++int radeon_vce_cs_parse(struct radeon_cs_parser *p);
++bool radeon_vce_semaphore_emit(struct radeon_device *rdev,
++ struct radeon_ring *ring,
++ struct radeon_semaphore *semaphore,
++ bool emit_wait);
++void radeon_vce_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
++void radeon_vce_fence_emit(struct radeon_device *rdev,
++ struct radeon_fence *fence);
++int radeon_vce_ring_test(struct radeon_device *rdev, struct radeon_ring *ring);
++int radeon_vce_ib_test(struct radeon_device *rdev, struct radeon_ring *ring);
++
+ struct r600_audio_pin {
+ int channels;
+ int rate;
+@@ -2186,6 +2226,7 @@ struct radeon_device {
+ struct radeon_gem gem;
+ struct radeon_pm pm;
+ struct radeon_uvd uvd;
++ struct radeon_vce vce;
+ uint32_t bios_scratch[RADEON_BIOS_NUM_SCRATCH];
+ struct radeon_wb wb;
+ struct radeon_dummy_page dummy_page;
+@@ -2204,6 +2245,7 @@ struct radeon_device {
+ const struct firmware *sdma_fw; /* CIK SDMA firmware */
+ const struct firmware *smc_fw; /* SMC firmware */
+ const struct firmware *uvd_fw; /* UVD firmware */
++ const struct firmware *vce_fw; /* VCE firmware */
+ struct r600_vram_scratch vram_scratch;
+ int msi_enabled; /* msi enabled */
+ struct r600_ih ih; /* r6/700 interrupt ring */
+diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
+index a539869..763280b 100644
+--- a/drivers/gpu/drm/radeon/radeon_asic.c
++++ b/drivers/gpu/drm/radeon/radeon_asic.c
+@@ -1978,6 +1978,19 @@ static struct radeon_asic_ring ci_dma_ring = {
+ .set_wptr = &cik_sdma_set_wptr,
+ };
+
++static struct radeon_asic_ring ci_vce_ring = {
++ .ib_execute = &radeon_vce_ib_execute,
++ .emit_fence = &radeon_vce_fence_emit,
++ .emit_semaphore = &radeon_vce_semaphore_emit,
++ .cs_parse = &radeon_vce_cs_parse,
++ .ring_test = &radeon_vce_ring_test,
++ .ib_test = &radeon_vce_ib_test,
++ .is_lockup = &radeon_ring_test_lockup,
++ .get_rptr = &vce_v1_0_get_rptr,
++ .get_wptr = &vce_v1_0_get_wptr,
++ .set_wptr = &vce_v1_0_set_wptr,
++};
++
+ static struct radeon_asic ci_asic = {
+ .init = &cik_init,
+ .fini = &cik_fini,
+@@ -2006,6 +2019,8 @@ static struct radeon_asic ci_asic = {
+ [R600_RING_TYPE_DMA_INDEX] = &ci_dma_ring,
+ [CAYMAN_RING_TYPE_DMA1_INDEX] = &ci_dma_ring,
+ [R600_RING_TYPE_UVD_INDEX] = &cayman_uvd_ring,
++ [TN_RING_TYPE_VCE1_INDEX] = &ci_vce_ring,
++ [TN_RING_TYPE_VCE2_INDEX] = &ci_vce_ring,
+ },
+ .irq = {
+ .set = &cik_irq_set,
+@@ -2107,6 +2122,8 @@ static struct radeon_asic kv_asic = {
+ [R600_RING_TYPE_DMA_INDEX] = &ci_dma_ring,
+ [CAYMAN_RING_TYPE_DMA1_INDEX] = &ci_dma_ring,
+ [R600_RING_TYPE_UVD_INDEX] = &cayman_uvd_ring,
++ [TN_RING_TYPE_VCE1_INDEX] = &ci_vce_ring,
++ [TN_RING_TYPE_VCE2_INDEX] = &ci_vce_ring,
+ },
+ .irq = {
+ .set = &cik_irq_set,
+diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
+index 998042e..a6c3eeb 100644
+--- a/drivers/gpu/drm/radeon/radeon_asic.h
++++ b/drivers/gpu/drm/radeon/radeon_asic.h
+@@ -850,4 +850,17 @@ bool uvd_v3_1_semaphore_emit(struct radeon_device *rdev,
+ /* uvd v4.2 */
+ int uvd_v4_2_resume(struct radeon_device *rdev);
+
++/* vce v1.0 */
++uint32_t vce_v1_0_get_rptr(struct radeon_device *rdev,
++ struct radeon_ring *ring);
++uint32_t vce_v1_0_get_wptr(struct radeon_device *rdev,
++ struct radeon_ring *ring);
++void vce_v1_0_set_wptr(struct radeon_device *rdev,
++ struct radeon_ring *ring);
++int vce_v1_0_init(struct radeon_device *rdev);
++int vce_v1_0_start(struct radeon_device *rdev);
++
++/* vce v2.0 */
++int vce_v2_0_resume(struct radeon_device *rdev);
++
+ #endif
+diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
+index eec1ad3..9d4e25d 100644
+--- a/drivers/gpu/drm/radeon/radeon_cs.c
++++ b/drivers/gpu/drm/radeon/radeon_cs.c
+@@ -153,6 +153,10 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority
+ case RADEON_CS_RING_UVD:
+ p->ring = R600_RING_TYPE_UVD_INDEX;
+ break;
++ case RADEON_CS_RING_VCE:
++ /* TODO: only use the low priority ring for now */
++ p->ring = TN_RING_TYPE_VCE1_INDEX;
++ break;
+ }
+ return 0;
+ }
+diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
+index e90c42b..b35967a 100644
+--- a/drivers/gpu/drm/radeon/radeon_kms.c
++++ b/drivers/gpu/drm/radeon/radeon_kms.c
+@@ -588,6 +588,7 @@ void radeon_driver_preclose_kms(struct drm_device *dev,
+ if (rdev->cmask_filp == file_priv)
+ rdev->cmask_filp = NULL;
+ radeon_uvd_free_handles(rdev, file_priv);
++ radeon_vce_free_handles(rdev, file_priv);
+ }
+
+ /*
+diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
+index 65f1cea..91457f8 100644
+--- a/drivers/gpu/drm/radeon/radeon_ring.c
++++ b/drivers/gpu/drm/radeon/radeon_ring.c
+@@ -814,6 +814,8 @@ static int cayman_cp2_index = CAYMAN_RING_TYPE_CP2_INDEX;
+ static int radeon_dma1_index = R600_RING_TYPE_DMA_INDEX;
+ static int radeon_dma2_index = CAYMAN_RING_TYPE_DMA1_INDEX;
+ static int r600_uvd_index = R600_RING_TYPE_UVD_INDEX;
++static int si_vce1_index = TN_RING_TYPE_VCE1_INDEX;
++static int si_vce2_index = TN_RING_TYPE_VCE2_INDEX;
+
+ static struct drm_info_list radeon_debugfs_ring_info_list[] = {
+ {"radeon_ring_gfx", radeon_debugfs_ring_info, 0, &radeon_gfx_index},
+@@ -822,6 +824,8 @@ static struct drm_info_list radeon_debugfs_ring_info_list[] = {
+ {"radeon_ring_dma1", radeon_debugfs_ring_info, 0, &radeon_dma1_index},
+ {"radeon_ring_dma2", radeon_debugfs_ring_info, 0, &radeon_dma2_index},
+ {"radeon_ring_uvd", radeon_debugfs_ring_info, 0, &r600_uvd_index},
++ {"radeon_ring_vce1", radeon_debugfs_ring_info, 0, &si_vce1_index},
++ {"radeon_ring_vce2", radeon_debugfs_ring_info, 0, &si_vce2_index},
+ };
+
+ static int radeon_debugfs_sa_info(struct seq_file *m, void *data)
+diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c
+index 12e8099..3a13e0d 100644
+--- a/drivers/gpu/drm/radeon/radeon_test.c
++++ b/drivers/gpu/drm/radeon/radeon_test.c
+@@ -257,20 +257,36 @@ static int radeon_test_create_and_emit_fence(struct radeon_device *rdev,
+ struct radeon_ring *ring,
+ struct radeon_fence **fence)
+ {
++ uint32_t handle = ring->idx ^ 0xdeafbeef;
+ int r;
+
+ if (ring->idx == R600_RING_TYPE_UVD_INDEX) {
+- r = radeon_uvd_get_create_msg(rdev, ring->idx, 1, NULL);
++ r = radeon_uvd_get_create_msg(rdev, ring->idx, handle, NULL);
+ if (r) {
+ DRM_ERROR("Failed to get dummy create msg\n");
+ return r;
+ }
+
+- r = radeon_uvd_get_destroy_msg(rdev, ring->idx, 1, fence);
++ r = radeon_uvd_get_destroy_msg(rdev, ring->idx, handle, fence);
+ if (r) {
+ DRM_ERROR("Failed to get dummy destroy msg\n");
+ return r;
+ }
++
++ } else if (ring->idx == TN_RING_TYPE_VCE1_INDEX ||
++ ring->idx == TN_RING_TYPE_VCE2_INDEX) {
++ r = radeon_vce_get_create_msg(rdev, ring->idx, handle, NULL);
++ if (r) {
++ DRM_ERROR("Failed to get dummy create msg\n");
++ return r;
++ }
++
++ r = radeon_vce_get_destroy_msg(rdev, ring->idx, handle, fence);
++ if (r) {
++ DRM_ERROR("Failed to get dummy destroy msg\n");
++ return r;
++ }
++
+ } else {
+ r = radeon_ring_lock(rdev, ring, 64);
+ if (r) {
+@@ -486,6 +502,16 @@ out_cleanup:
+ printk(KERN_WARNING "Error while testing ring sync (%d).\n", r);
+ }
+
++static bool radeon_test_sync_possible(struct radeon_ring *ringA,
++ struct radeon_ring *ringB)
++{
++ if (ringA->idx == TN_RING_TYPE_VCE2_INDEX &&
++ ringB->idx == TN_RING_TYPE_VCE1_INDEX)
++ return false;
++
++ return true;
++}
++
+ void radeon_test_syncing(struct radeon_device *rdev)
+ {
+ int i, j, k;
+@@ -500,6 +526,9 @@ void radeon_test_syncing(struct radeon_device *rdev)
+ if (!ringB->ready)
+ continue;
+
++ if (!radeon_test_sync_possible(ringA, ringB))
++ continue;
++
+ DRM_INFO("Testing syncing between rings %d and %d...\n", i, j);
+ radeon_test_ring_sync(rdev, ringA, ringB);
+
+@@ -511,6 +540,12 @@ void radeon_test_syncing(struct radeon_device *rdev)
+ if (!ringC->ready)
+ continue;
+
++ if (!radeon_test_sync_possible(ringA, ringC))
++ continue;
++
++ if (!radeon_test_sync_possible(ringB, ringC))
++ continue;
++
+ DRM_INFO("Testing syncing between rings %d, %d and %d...\n", i, j, k);
+ radeon_test_ring_sync2(rdev, ringA, ringB, ringC);
+
+diff --git a/drivers/gpu/drm/radeon/radeon_vce.c b/drivers/gpu/drm/radeon/radeon_vce.c
+new file mode 100644
+index 0000000..2547d8e
+--- /dev/null
++++ b/drivers/gpu/drm/radeon/radeon_vce.c
+@@ -0,0 +1,588 @@
++/*
++ * Copyright 2013 Advanced Micro Devices, Inc.
++ * All Rights Reserved.
++ *
++ * 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 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 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 COPYRIGHT HOLDERS, AUTHORS AND/OR ITS 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.
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * Authors: Christian König <christian.koenig@amd.com>
++ */
++
++#include <linux/firmware.h>
++#include <linux/module.h>
++#include <drm/drmP.h>
++#include <drm/drm.h>
++
++#include "radeon.h"
++#include "radeon_asic.h"
++#include "sid.h"
++
++/* Firmware Names */
++#define FIRMWARE_BONAIRE "radeon/BONAIRE_vce.bin"
++
++MODULE_FIRMWARE(FIRMWARE_BONAIRE);
++
++/**
++ * radeon_vce_init - allocate memory, load vce firmware
++ *
++ * @rdev: radeon_device pointer
++ *
++ * First step to get VCE online, allocate memory and load the firmware
++ */
++int radeon_vce_init(struct radeon_device *rdev)
++{
++ unsigned long bo_size;
++ const char *fw_name;
++ int i, r;
++
++ switch (rdev->family) {
++ case CHIP_BONAIRE:
++ case CHIP_KAVERI:
++ case CHIP_KABINI:
++ fw_name = FIRMWARE_BONAIRE;
++ break;
++
++ default:
++ return -EINVAL;
++ }
++
++ r = request_firmware(&rdev->vce_fw, fw_name, rdev->dev);
++ if (r) {
++ dev_err(rdev->dev, "radeon_vce: Can't load firmware \"%s\"\n",
++ fw_name);
++ return r;
++ }
++
++ bo_size = RADEON_GPU_PAGE_ALIGN(rdev->vce_fw->size) +
++ RADEON_VCE_STACK_SIZE + RADEON_VCE_HEAP_SIZE;
++ r = radeon_bo_create(rdev, bo_size, PAGE_SIZE, true,
++ RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->vce.vcpu_bo);
++ if (r) {
++ dev_err(rdev->dev, "(%d) failed to allocate VCE bo\n", r);
++ return r;
++ }
++
++ r = radeon_vce_resume(rdev);
++ if (r)
++ return r;
++
++ memset(rdev->vce.cpu_addr, 0, bo_size);
++ memcpy(rdev->vce.cpu_addr, rdev->vce_fw->data, rdev->vce_fw->size);
++
++ r = radeon_vce_suspend(rdev);
++ if (r)
++ return r;
++
++ for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
++ atomic_set(&rdev->vce.handles[i], 0);
++ rdev->vce.filp[i] = NULL;
++ }
++
++ return 0;
++}
++
++/**
++ * radeon_vce_fini - free memory
++ *
++ * @rdev: radeon_device pointer
++ *
++ * Last step on VCE teardown, free firmware memory
++ */
++void radeon_vce_fini(struct radeon_device *rdev)
++{
++ radeon_vce_suspend(rdev);
++ radeon_bo_unref(&rdev->vce.vcpu_bo);
++}
++
++/**
++ * radeon_vce_suspend - unpin VCE fw memory
++ *
++ * @rdev: radeon_device pointer
++ *
++ * TODO: Test VCE suspend/resume
++ */
++int radeon_vce_suspend(struct radeon_device *rdev)
++{
++ int r;
++
++ if (rdev->vce.vcpu_bo == NULL)
++ return 0;
++
++ r = radeon_bo_reserve(rdev->vce.vcpu_bo, false);
++ if (!r) {
++ radeon_bo_kunmap(rdev->vce.vcpu_bo);
++ radeon_bo_unpin(rdev->vce.vcpu_bo);
++ radeon_bo_unreserve(rdev->vce.vcpu_bo);
++ }
++ return r;
++}
++
++/**
++ * radeon_vce_resume - pin VCE fw memory
++ *
++ * @rdev: radeon_device pointer
++ *
++ * TODO: Test VCE suspend/resume
++ */
++int radeon_vce_resume(struct radeon_device *rdev)
++{
++ int r;
++
++ if (rdev->vce.vcpu_bo == NULL)
++ return -EINVAL;
++
++ r = radeon_bo_reserve(rdev->vce.vcpu_bo, false);
++ if (r) {
++ radeon_bo_unref(&rdev->vce.vcpu_bo);
++ dev_err(rdev->dev, "(%d) failed to reserve VCE bo\n", r);
++ return r;
++ }
++
++ r = radeon_bo_pin(rdev->vce.vcpu_bo, RADEON_GEM_DOMAIN_VRAM,
++ &rdev->vce.gpu_addr);
++ if (r) {
++ radeon_bo_unreserve(rdev->vce.vcpu_bo);
++ radeon_bo_unref(&rdev->vce.vcpu_bo);
++ dev_err(rdev->dev, "(%d) VCE bo pin failed\n", r);
++ return r;
++ }
++
++ r = radeon_bo_kmap(rdev->vce.vcpu_bo, &rdev->vce.cpu_addr);
++ if (r) {
++ dev_err(rdev->dev, "(%d) VCE map failed\n", r);
++ return r;
++ }
++
++ radeon_bo_unreserve(rdev->vce.vcpu_bo);
++
++ return 0;
++}
++
++/**
++ * radeon_vce_free_handles - free still open VCE handles
++ *
++ * @rdev: radeon_device pointer
++ * @filp: drm file pointer
++ *
++ * Close all VCE handles still open by this file pointer
++ */
++void radeon_vce_free_handles(struct radeon_device *rdev, struct drm_file *filp)
++{
++ int i, r;
++ for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
++ uint32_t handle = atomic_read(&rdev->vce.handles[i]);
++ if (!handle || rdev->vce.filp[i] != filp)
++ continue;
++
++ r = radeon_vce_get_destroy_msg(rdev, TN_RING_TYPE_VCE1_INDEX,
++ handle, NULL);
++ if (r)
++ DRM_ERROR("Error destroying VCE handle (%d)!\n", r);
++
++ rdev->vce.filp[i] = NULL;
++ atomic_set(&rdev->vce.handles[i], 0);
++ }
++}
++
++/**
++ * radeon_vce_get_create_msg - generate a VCE create msg
++ *
++ * @rdev: radeon_device pointer
++ * @ring: ring we should submit the msg to
++ * @handle: VCE session handle to use
++ * @fence: optional fence to return
++ *
++ * Open up a stream for HW test
++ */
++int radeon_vce_get_create_msg(struct radeon_device *rdev, int ring,
++ uint32_t handle, struct radeon_fence **fence)
++{
++ const unsigned ib_size_dw = 1024;
++ struct radeon_ib ib;
++ uint64_t dummy;
++ int i, r;
++
++ r = radeon_ib_get(rdev, ring, &ib, NULL, ib_size_dw * 4);
++ if (r) {
++ DRM_ERROR("radeon: failed to get ib (%d).\n", r);
++ return r;
++ }
++
++ dummy = ib.gpu_addr + 1024;
++
++ /* stitch together an VCE create msg */
++ ib.length_dw = 0;
++ ib.ptr[ib.length_dw++] = 0x0000000c; /* len */
++ ib.ptr[ib.length_dw++] = 0x00000001; /* session cmd */
++ ib.ptr[ib.length_dw++] = handle;
++
++ ib.ptr[ib.length_dw++] = 0x00000030; /* len */
++ ib.ptr[ib.length_dw++] = 0x01000001; /* create cmd */
++ ib.ptr[ib.length_dw++] = 0x00000000;
++ ib.ptr[ib.length_dw++] = 0x00000042;
++ ib.ptr[ib.length_dw++] = 0x0000000a;
++ ib.ptr[ib.length_dw++] = 0x00000001;
++ ib.ptr[ib.length_dw++] = 0x00000080;
++ ib.ptr[ib.length_dw++] = 0x00000060;
++ ib.ptr[ib.length_dw++] = 0x00000100;
++ ib.ptr[ib.length_dw++] = 0x00000100;
++ ib.ptr[ib.length_dw++] = 0x0000000c;
++ ib.ptr[ib.length_dw++] = 0x00000000;
++
++ 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;
++
++ for (i = ib.length_dw; i < ib_size_dw; ++i)
++ ib.ptr[i] = 0x0;
++
++ r = radeon_ib_schedule(rdev, &ib, NULL);
++ if (r) {
++ DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
++ }
++
++ if (fence)
++ *fence = radeon_fence_ref(ib.fence);
++
++ radeon_ib_free(rdev, &ib);
++
++ return r;
++}
++
++/**
++ * radeon_vce_get_destroy_msg - generate a VCE destroy msg
++ *
++ * @rdev: radeon_device pointer
++ * @ring: ring we should submit the msg to
++ * @handle: VCE session handle to use
++ * @fence: optional fence to return
++ *
++ * Close up a stream for HW test or if userspace failed to do so
++ */
++int radeon_vce_get_destroy_msg(struct radeon_device *rdev, int ring,
++ uint32_t handle, struct radeon_fence **fence)
++{
++ const unsigned ib_size_dw = 1024;
++ struct radeon_ib ib;
++ uint64_t dummy;
++ int i, r;
++
++ r = radeon_ib_get(rdev, ring, &ib, NULL, ib_size_dw * 4);
++ if (r) {
++ DRM_ERROR("radeon: failed to get ib (%d).\n", r);
++ return r;
++ }
++
++ dummy = ib.gpu_addr + 1024;
++
++ /* stitch together an VCE destroy msg */
++ ib.length_dw = 0;
++ ib.ptr[ib.length_dw++] = 0x0000000c; /* len */
++ 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++] = 0x00000008; /* len */
++ ib.ptr[ib.length_dw++] = 0x02000001; /* destroy cmd */
++
++ for (i = ib.length_dw; i < ib_size_dw; ++i)
++ ib.ptr[i] = 0x0;
++
++ r = radeon_ib_schedule(rdev, &ib, NULL);
++ if (r) {
++ DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
++ }
++
++ if (fence)
++ *fence = radeon_fence_ref(ib.fence);
++
++ radeon_ib_free(rdev, &ib);
++
++ return r;
++}
++
++/**
++ * radeon_vce_cs_reloc - command submission relocation
++ *
++ * @p: parser context
++ * @lo: address of lower dword
++ * @hi: address of higher dword
++ *
++ * Patch relocation inside command stream with real buffer address
++ */
++int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi)
++{
++ struct radeon_cs_chunk *relocs_chunk;
++ uint64_t offset;
++ unsigned idx;
++
++ relocs_chunk = &p->chunks[p->chunk_relocs_idx];
++ offset = radeon_get_ib_value(p, lo);
++ idx = radeon_get_ib_value(p, hi);
++
++ if (idx >= relocs_chunk->length_dw) {
++ DRM_ERROR("Relocs at %d after relocations chunk end %d !\n",
++ idx, relocs_chunk->length_dw);
++ return -EINVAL;
++ }
++
++ offset += p->relocs_ptr[(idx / 4)]->lobj.gpu_offset;
++
++ p->ib.ptr[lo] = offset & 0xFFFFFFFF;
++ p->ib.ptr[hi] = offset >> 32;
++
++ return 0;
++}
++
++/**
++ * radeon_vce_cs_parse - parse and validate the command stream
++ *
++ * @p: parser context
++ *
++ */
++int radeon_vce_cs_parse(struct radeon_cs_parser *p)
++{
++ uint32_t handle = 0;
++ bool destroy = false;
++ int i, r;
++
++ while (p->idx < p->chunks[p->chunk_ib_idx].length_dw) {
++ uint32_t len = radeon_get_ib_value(p, p->idx);
++ uint32_t cmd = radeon_get_ib_value(p, p->idx + 1);
++
++ if ((len < 8) || (len & 3)) {
++ DRM_ERROR("invalid VCE command length (%d)!\n", len);
++ return -EINVAL;
++ }
++
++ switch (cmd) {
++ case 0x00000001: // session
++ handle = radeon_get_ib_value(p, p->idx + 2);
++ break;
++
++ case 0x00000002: // task info
++ case 0x01000001: // create
++ case 0x04000001: // config extension
++ case 0x04000002: // pic control
++ case 0x04000005: // rate control
++ case 0x04000007: // motion estimation
++ case 0x04000008: // rdo
++ break;
++
++ case 0x03000001: // encode
++ r = radeon_vce_cs_reloc(p, p->idx + 10, p->idx + 9);
++ if (r)
++ return r;
++
++ r = radeon_vce_cs_reloc(p, p->idx + 12, p->idx + 11);
++ if (r)
++ return r;
++ break;
++
++ case 0x02000001: // destroy
++ destroy = true;
++ break;
++
++ case 0x05000001: // context buffer
++ case 0x05000004: // video bitstream buffer
++ case 0x05000005: // feedback buffer
++ r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2);
++ if (r)
++ return r;
++ break;
++
++ default:
++ DRM_ERROR("invalid VCE command (0x%x)!\n", cmd);
++ return -EINVAL;
++ }
++
++ p->idx += len / 4;
++ }
++
++ if (destroy) {
++ /* IB contains a destroy msg, free the handle */
++ for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i)
++ atomic_cmpxchg(&p->rdev->vce.handles[i], handle, 0);
++
++ return 0;
++ }
++
++ /* create or encode, validate the handle */
++ for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
++ if (atomic_read(&p->rdev->vce.handles[i]) == handle)
++ return 0;
++ }
++
++ /* handle not found try to alloc a new one */
++ for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
++ if (!atomic_cmpxchg(&p->rdev->vce.handles[i], 0, handle)) {
++ p->rdev->vce.filp[i] = p->filp;
++ return 0;
++ }
++ }
++
++ DRM_ERROR("No more free VCE handles!\n");
++ return -EINVAL;
++}
++
++/**
++ * radeon_vce_semaphore_emit - emit a semaphore command
++ *
++ * @rdev: radeon_device pointer
++ * @ring: engine to use
++ * @semaphore: address of semaphore
++ * @emit_wait: true=emit wait, false=emit signal
++ *
++ */
++bool radeon_vce_semaphore_emit(struct radeon_device *rdev,
++ struct radeon_ring *ring,
++ struct radeon_semaphore *semaphore,
++ bool emit_wait)
++{
++ uint64_t addr = semaphore->gpu_addr;
++
++ radeon_ring_write(ring, VCE_CMD_SEMAPHORE);
++ radeon_ring_write(ring, (addr >> 3) & 0x000FFFFF);
++ radeon_ring_write(ring, (addr >> 23) & 0x000FFFFF);
++ radeon_ring_write(ring, 0x01003000 | (emit_wait ? 1 : 0));
++ if (!emit_wait)
++ radeon_ring_write(ring, VCE_CMD_END);
++
++ return true;
++}
++
++/**
++ * radeon_vce_ib_execute - execute indirect buffer
++ *
++ * @rdev: radeon_device pointer
++ * @ib: the IB to execute
++ *
++ */
++void radeon_vce_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
++{
++ struct radeon_ring *ring = &rdev->ring[ib->ring];
++ radeon_ring_write(ring, VCE_CMD_IB);
++ radeon_ring_write(ring, ib->gpu_addr);
++ radeon_ring_write(ring, upper_32_bits(ib->gpu_addr));
++ radeon_ring_write(ring, ib->length_dw);
++}
++
++/**
++ * radeon_vce_fence_emit - add a fence command to the ring
++ *
++ * @rdev: radeon_device pointer
++ * @fence: the fence
++ *
++ */
++void radeon_vce_fence_emit(struct radeon_device *rdev,
++ struct radeon_fence *fence)
++{
++ struct radeon_ring *ring = &rdev->ring[fence->ring];
++ uint32_t addr = rdev->fence_drv[fence->ring].gpu_addr;
++
++ radeon_ring_write(ring, VCE_CMD_FENCE);
++ radeon_ring_write(ring, addr);
++ radeon_ring_write(ring, upper_32_bits(addr));
++ radeon_ring_write(ring, fence->seq);
++ radeon_ring_write(ring, VCE_CMD_TRAP);
++ radeon_ring_write(ring, VCE_CMD_END);
++}
++
++/**
++ * radeon_vce_ring_test - test if VCE ring is working
++ *
++ * @rdev: radeon_device pointer
++ * @ring: the engine to test on
++ *
++ */
++int radeon_vce_ring_test(struct radeon_device *rdev, struct radeon_ring *ring)
++{
++ uint32_t rptr = vce_v1_0_get_rptr(rdev, ring);
++ unsigned i;
++ int r;
++
++ r = radeon_ring_lock(rdev, ring, 16);
++ if (r) {
++ DRM_ERROR("radeon: vce failed to lock ring %d (%d).\n",
++ ring->idx, r);
++ return r;
++ }
++ radeon_ring_write(ring, VCE_CMD_END);
++ radeon_ring_unlock_commit(rdev, ring);
++
++ for (i = 0; i < rdev->usec_timeout; i++) {
++ if (vce_v1_0_get_rptr(rdev, ring) != rptr)
++ break;
++ DRM_UDELAY(1);
++ }
++
++ if (i < rdev->usec_timeout) {
++ DRM_INFO("ring test on %d succeeded in %d usecs\n",
++ ring->idx, i);
++ } else {
++ DRM_ERROR("radeon: ring %d test failed\n",
++ ring->idx);
++ r = -ETIMEDOUT;
++ }
++
++ return r;
++}
++
++/**
++ * radeon_vce_ib_test - test if VCE IBs are working
++ *
++ * @rdev: radeon_device pointer
++ * @ring: the engine to test on
++ *
++ */
++int radeon_vce_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
++{
++ struct radeon_fence *fence = NULL;
++ int r;
++
++ r = radeon_vce_get_create_msg(rdev, ring->idx, 1, NULL);
++ if (r) {
++ DRM_ERROR("radeon: failed to get create msg (%d).\n", r);
++ goto error;
++ }
++
++ r = radeon_vce_get_destroy_msg(rdev, ring->idx, 1, &fence);
++ if (r) {
++ DRM_ERROR("radeon: failed to get destroy ib (%d).\n", r);
++ goto error;
++ }
++
++ r = radeon_fence_wait(fence, false);
++ if (r) {
++ DRM_ERROR("radeon: fence wait failed (%d).\n", r);
++ } else {
++ DRM_INFO("ib test on ring %d succeeded\n", ring->idx);
++ }
++error:
++ radeon_fence_unref(&fence);
++ return r;
++}
+diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h
+index 940e36b..2b71e67 100644
+--- a/drivers/gpu/drm/radeon/sid.h
++++ b/drivers/gpu/drm/radeon/sid.h
+@@ -1747,4 +1747,51 @@
+ #define DMA_PACKET_CONSTANT_FILL 0xd
+ #define DMA_PACKET_NOP 0xf
+
++#define VCE_STATUS 0x20004
++#define VCE_VCPU_CNTL 0x20014
++#define VCE_CLK_EN (1 << 0)
++#define VCE_VCPU_CACHE_OFFSET0 0x20024
++#define VCE_VCPU_CACHE_SIZE0 0x20028
++#define VCE_VCPU_CACHE_OFFSET1 0x2002c
++#define VCE_VCPU_CACHE_SIZE1 0x20030
++#define VCE_VCPU_CACHE_OFFSET2 0x20034
++#define VCE_VCPU_CACHE_SIZE2 0x20038
++#define VCE_SOFT_RESET 0x20120
++#define VCE_ECPU_SOFT_RESET (1 << 0)
++#define VCE_FME_SOFT_RESET (1 << 2)
++#define VCE_RB_BASE_LO2 0x2016c
++#define VCE_RB_BASE_HI2 0x20170
++#define VCE_RB_SIZE2 0x20174
++#define VCE_RB_RPTR2 0x20178
++#define VCE_RB_WPTR2 0x2017c
++#define VCE_RB_BASE_LO 0x20180
++#define VCE_RB_BASE_HI 0x20184
++#define VCE_RB_SIZE 0x20188
++#define VCE_RB_RPTR 0x2018c
++#define VCE_RB_WPTR 0x20190
++#define VCE_CLOCK_GATING_A 0x202f8
++#define VCE_CLOCK_GATING_B 0x202fc
++#define VCE_UENC_CLOCK_GATING 0x205bc
++#define VCE_UENC_REG_CLOCK_GATING 0x205c0
++#define VCE_FW_REG_STATUS 0x20e10
++# define VCE_FW_REG_STATUS_BUSY (1 << 0)
++# define VCE_FW_REG_STATUS_PASS (1 << 3)
++# define VCE_FW_REG_STATUS_DONE (1 << 11)
++#define VCE_LMI_FW_START_KEYSEL 0x20e18
++#define VCE_LMI_FW_PERIODIC_CTRL 0x20e20
++#define VCE_LMI_CTRL2 0x20e74
++#define VCE_LMI_CTRL 0x20e98
++#define VCE_LMI_VM_CTRL 0x20ea0
++#define VCE_LMI_SWAP_CNTL 0x20eb4
++#define VCE_LMI_SWAP_CNTL1 0x20eb8
++#define VCE_LMI_CACHE_CTRL 0x20ef4
++
++#define VCE_CMD_NO_OP 0x00000000
++#define VCE_CMD_END 0x00000001
++#define VCE_CMD_IB 0x00000002
++#define VCE_CMD_FENCE 0x00000003
++#define VCE_CMD_TRAP 0x00000004
++#define VCE_CMD_IB_AUTO 0x00000005
++#define VCE_CMD_SEMAPHORE 0x00000006
++
+ #endif
+diff --git a/drivers/gpu/drm/radeon/vce_v1_0.c b/drivers/gpu/drm/radeon/vce_v1_0.c
+new file mode 100644
+index 0000000..e0c3534
+--- /dev/null
++++ b/drivers/gpu/drm/radeon/vce_v1_0.c
+@@ -0,0 +1,187 @@
++/*
++ * Copyright 2013 Advanced Micro Devices, Inc.
++ * All Rights Reserved.
++ *
++ * 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 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 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 COPYRIGHT HOLDERS, AUTHORS AND/OR ITS 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.
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * Authors: Christian König <christian.koenig@amd.com>
++ */
++
++#include <linux/firmware.h>
++#include <drm/drmP.h>
++#include "radeon.h"
++#include "radeon_asic.h"
++#include "sid.h"
++
++/**
++ * vce_v1_0_get_rptr - get read pointer
++ *
++ * @rdev: radeon_device pointer
++ * @ring: radeon_ring pointer
++ *
++ * Returns the current hardware read pointer
++ */
++uint32_t vce_v1_0_get_rptr(struct radeon_device *rdev,
++ struct radeon_ring *ring)
++{
++ if (ring->idx == TN_RING_TYPE_VCE1_INDEX)
++ return RREG32(VCE_RB_RPTR);
++ else
++ return RREG32(VCE_RB_RPTR2);
++}
++
++/**
++ * vce_v1_0_get_wptr - get write pointer
++ *
++ * @rdev: radeon_device pointer
++ * @ring: radeon_ring pointer
++ *
++ * Returns the current hardware write pointer
++ */
++uint32_t vce_v1_0_get_wptr(struct radeon_device *rdev,
++ struct radeon_ring *ring)
++{
++ if (ring->idx == TN_RING_TYPE_VCE1_INDEX)
++ return RREG32(VCE_RB_WPTR);
++ else
++ return RREG32(VCE_RB_WPTR2);
++}
++
++/**
++ * vce_v1_0_set_wptr - set write pointer
++ *
++ * @rdev: radeon_device pointer
++ * @ring: radeon_ring pointer
++ *
++ * Commits the write pointer to the hardware
++ */
++void vce_v1_0_set_wptr(struct radeon_device *rdev,
++ struct radeon_ring *ring)
++{
++ if (ring->idx == TN_RING_TYPE_VCE1_INDEX)
++ WREG32(VCE_RB_WPTR, ring->wptr);
++ else
++ WREG32(VCE_RB_WPTR2, ring->wptr);
++}
++
++/**
++ * vce_v1_0_start - start VCE block
++ *
++ * @rdev: radeon_device pointer
++ *
++ * Setup and start the VCE block
++ */
++int vce_v1_0_start(struct radeon_device *rdev)
++{
++ struct radeon_ring *ring;
++ int i, j, r;
++
++ /* set BUSY flag */
++ WREG32_P(VCE_STATUS, 1, ~1);
++
++ ring = &rdev->ring[TN_RING_TYPE_VCE1_INDEX];
++ WREG32(VCE_RB_RPTR, ring->rptr);
++ WREG32(VCE_RB_WPTR, ring->wptr);
++ WREG32(VCE_RB_BASE_LO, ring->gpu_addr);
++ WREG32(VCE_RB_BASE_HI, upper_32_bits(ring->gpu_addr));
++ WREG32(VCE_RB_SIZE, ring->ring_size / 4);
++
++ ring = &rdev->ring[TN_RING_TYPE_VCE2_INDEX];
++ WREG32(VCE_RB_RPTR2, ring->rptr);
++ WREG32(VCE_RB_WPTR2, ring->wptr);
++ WREG32(VCE_RB_BASE_LO2, ring->gpu_addr);
++ WREG32(VCE_RB_BASE_HI2, upper_32_bits(ring->gpu_addr));
++ WREG32(VCE_RB_SIZE2, ring->ring_size / 4);
++
++ WREG32_P(VCE_VCPU_CNTL, VCE_CLK_EN, ~VCE_CLK_EN);
++
++ WREG32_P(VCE_SOFT_RESET,
++ VCE_ECPU_SOFT_RESET |
++ VCE_FME_SOFT_RESET, ~(
++ VCE_ECPU_SOFT_RESET |
++ VCE_FME_SOFT_RESET));
++
++ mdelay(100);
++
++ WREG32_P(VCE_SOFT_RESET, 0, ~(
++ VCE_ECPU_SOFT_RESET |
++ VCE_FME_SOFT_RESET));
++
++ for (i = 0; i < 10; ++i) {
++ uint32_t status;
++ for (j = 0; j < 100; ++j) {
++ status = RREG32(VCE_STATUS);
++ if (status & 2)
++ break;
++ mdelay(10);
++ }
++ r = 0;
++ if (status & 2)
++ break;
++
++ DRM_ERROR("VCE not responding, trying to reset the ECPU!!!\n");
++ WREG32_P(VCE_SOFT_RESET, VCE_ECPU_SOFT_RESET, ~VCE_ECPU_SOFT_RESET);
++ mdelay(10);
++ WREG32_P(VCE_SOFT_RESET, 0, ~VCE_ECPU_SOFT_RESET);
++ mdelay(10);
++ r = -1;
++ }
++
++ /* clear BUSY flag */
++ WREG32_P(VCE_STATUS, 0, ~1);
++
++ if (r) {
++ DRM_ERROR("VCE not responding, giving up!!!\n");
++ return r;
++ }
++
++ return 0;
++}
++
++int vce_v1_0_init(struct radeon_device *rdev)
++{
++ struct radeon_ring *ring;
++ int r;
++
++ r = vce_v1_0_start(rdev);
++ if (r)
++ return r;
++
++ ring = &rdev->ring[TN_RING_TYPE_VCE1_INDEX];
++ ring->ready = true;
++ r = radeon_ring_test(rdev, TN_RING_TYPE_VCE1_INDEX, ring);
++ if (r) {
++ ring->ready = false;
++ return r;
++ }
++
++ ring = &rdev->ring[TN_RING_TYPE_VCE2_INDEX];
++ ring->ready = true;
++ r = radeon_ring_test(rdev, TN_RING_TYPE_VCE2_INDEX, ring);
++ if (r) {
++ ring->ready = false;
++ return r;
++ }
++
++ DRM_INFO("VCE initialized successfully.\n");
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/radeon/vce_v2_0.c b/drivers/gpu/drm/radeon/vce_v2_0.c
+new file mode 100644
+index 0000000..4911d1b
+--- /dev/null
++++ b/drivers/gpu/drm/radeon/vce_v2_0.c
+@@ -0,0 +1,70 @@
++/*
++ * Copyright 2013 Advanced Micro Devices, Inc.
++ * All Rights Reserved.
++ *
++ * 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 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 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 COPYRIGHT HOLDERS, AUTHORS AND/OR ITS 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.
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * Authors: Christian König <christian.koenig@amd.com>
++ */
++
++#include <linux/firmware.h>
++#include <drm/drmP.h>
++#include "radeon.h"
++#include "radeon_asic.h"
++#include "cikd.h"
++
++int vce_v2_0_resume(struct radeon_device *rdev)
++{
++ uint64_t addr = rdev->vce.gpu_addr;
++ uint32_t size;
++
++ WREG32_P(VCE_CLOCK_GATING_A, 0, ~(1 << 16));
++ WREG32_P(VCE_UENC_CLOCK_GATING, 0x1FF000, ~0xFF9FF000);
++ WREG32_P(VCE_UENC_REG_CLOCK_GATING, 0x3F, ~0x3F);
++ WREG32(VCE_CLOCK_GATING_B, 0xf7);
++
++ WREG32(VCE_LMI_CTRL, 0x00398000);
++ WREG32_P(VCE_LMI_CACHE_CTRL, 0x0, ~0x1);
++ WREG32(VCE_LMI_SWAP_CNTL, 0);
++ WREG32(VCE_LMI_SWAP_CNTL1, 0);
++ WREG32(VCE_LMI_VM_CTRL, 0);
++
++ size = RADEON_GPU_PAGE_ALIGN(rdev->vce_fw->size);
++ WREG32(VCE_VCPU_CACHE_OFFSET0, addr & 0x7fffffff);
++ WREG32(VCE_VCPU_CACHE_SIZE0, size);
++
++ addr += size;
++ size = RADEON_VCE_STACK_SIZE;
++ WREG32(VCE_VCPU_CACHE_OFFSET1, addr & 0x7fffffff);
++ WREG32(VCE_VCPU_CACHE_SIZE1, size);
++
++ addr += size;
++ size = RADEON_VCE_HEAP_SIZE;
++ WREG32(VCE_VCPU_CACHE_OFFSET2, addr & 0x7fffffff);
++ WREG32(VCE_VCPU_CACHE_SIZE2, size);
++
++ WREG32_P(VCE_LMI_CTRL2, 0x0, ~0x100);
++
++ WREG32_P(VCE_SYS_INT_EN, VCE_SYS_INT_TRAP_INTERRUPT_EN,
++ ~VCE_SYS_INT_TRAP_INTERRUPT_EN);
++
++ return 0;
++}
+diff --git a/include/uapi/drm/radeon_drm.h b/include/uapi/drm/radeon_drm.h
+index fe421e8a..b93c92a 100644
+--- a/include/uapi/drm/radeon_drm.h
++++ b/include/uapi/drm/radeon_drm.h
+@@ -919,6 +919,7 @@ struct drm_radeon_gem_va {
+ #define RADEON_CS_RING_COMPUTE 1
+ #define RADEON_CS_RING_DMA 2
+ #define RADEON_CS_RING_UVD 3
++#define RADEON_CS_RING_VCE 4
+ /* The third dword of RADEON_CHUNK_ID_FLAGS is a sint32 that sets the priority */
+ /* 0 = normal, + = higher priority, - = lower priority */
+
+--
+1.9.1
+