Commit | Line | Data |
---|---|---|
aaa36a97 AD |
1 | /* |
2 | * Copyright 2014 Advanced Micro Devices, Inc. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * Permission is hereby granted, free of charge, to any person obtaining a | |
6 | * copy of this software and associated documentation files (the | |
7 | * "Software"), to deal in the Software without restriction, including | |
8 | * without limitation the rights to use, copy, modify, merge, publish, | |
9 | * distribute, sub license, and/or sell copies of the Software, and to | |
10 | * permit persons to whom the Software is furnished to do so, subject to | |
11 | * the following conditions: | |
12 | * | |
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | |
16 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, | |
17 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
19 | * USE OR OTHER DEALINGS IN THE SOFTWARE. | |
20 | * | |
21 | * The above copyright notice and this permission notice (including the | |
22 | * next paragraph) shall be included in all copies or substantial portions | |
23 | * of the Software. | |
24 | * | |
25 | * Authors: Christian König <christian.koenig@amd.com> | |
26 | */ | |
27 | ||
28 | #include <linux/firmware.h> | |
29 | #include <drm/drmP.h> | |
30 | #include "amdgpu.h" | |
31 | #include "amdgpu_vce.h" | |
32 | #include "vid.h" | |
33 | #include "vce/vce_3_0_d.h" | |
34 | #include "vce/vce_3_0_sh_mask.h" | |
be4f38e2 AD |
35 | #include "oss/oss_3_0_d.h" |
36 | #include "oss/oss_3_0_sh_mask.h" | |
5bbc553a | 37 | #include "gca/gfx_8_0_d.h" |
6a585777 AD |
38 | #include "smu/smu_7_1_2_d.h" |
39 | #include "smu/smu_7_1_2_sh_mask.h" | |
5bbc553a LL |
40 | |
41 | #define GRBM_GFX_INDEX__VCE_INSTANCE__SHIFT 0x04 | |
42 | #define GRBM_GFX_INDEX__VCE_INSTANCE_MASK 0x10 | |
3c0ff9f1 LL |
43 | #define mmVCE_LMI_VCPU_CACHE_40BIT_BAR0 0x8616 |
44 | #define mmVCE_LMI_VCPU_CACHE_40BIT_BAR1 0x8617 | |
45 | #define mmVCE_LMI_VCPU_CACHE_40BIT_BAR2 0x8618 | |
aaa36a97 | 46 | |
e9822622 LL |
47 | #define VCE_V3_0_FW_SIZE (384 * 1024) |
48 | #define VCE_V3_0_STACK_SIZE (64 * 1024) | |
49 | #define VCE_V3_0_DATA_SIZE ((16 * 1024 * AMDGPU_MAX_VCE_HANDLES) + (52 * 1024)) | |
50 | ||
5bbc553a | 51 | static void vce_v3_0_mc_resume(struct amdgpu_device *adev, int idx); |
aaa36a97 AD |
52 | static void vce_v3_0_set_ring_funcs(struct amdgpu_device *adev); |
53 | static void vce_v3_0_set_irq_funcs(struct amdgpu_device *adev); | |
54 | ||
55 | /** | |
56 | * vce_v3_0_ring_get_rptr - get read pointer | |
57 | * | |
58 | * @ring: amdgpu_ring pointer | |
59 | * | |
60 | * Returns the current hardware read pointer | |
61 | */ | |
62 | static uint32_t vce_v3_0_ring_get_rptr(struct amdgpu_ring *ring) | |
63 | { | |
64 | struct amdgpu_device *adev = ring->adev; | |
65 | ||
66 | if (ring == &adev->vce.ring[0]) | |
67 | return RREG32(mmVCE_RB_RPTR); | |
68 | else | |
69 | return RREG32(mmVCE_RB_RPTR2); | |
70 | } | |
71 | ||
72 | /** | |
73 | * vce_v3_0_ring_get_wptr - get write pointer | |
74 | * | |
75 | * @ring: amdgpu_ring pointer | |
76 | * | |
77 | * Returns the current hardware write pointer | |
78 | */ | |
79 | static uint32_t vce_v3_0_ring_get_wptr(struct amdgpu_ring *ring) | |
80 | { | |
81 | struct amdgpu_device *adev = ring->adev; | |
82 | ||
83 | if (ring == &adev->vce.ring[0]) | |
84 | return RREG32(mmVCE_RB_WPTR); | |
85 | else | |
86 | return RREG32(mmVCE_RB_WPTR2); | |
87 | } | |
88 | ||
89 | /** | |
90 | * vce_v3_0_ring_set_wptr - set write pointer | |
91 | * | |
92 | * @ring: amdgpu_ring pointer | |
93 | * | |
94 | * Commits the write pointer to the hardware | |
95 | */ | |
96 | static void vce_v3_0_ring_set_wptr(struct amdgpu_ring *ring) | |
97 | { | |
98 | struct amdgpu_device *adev = ring->adev; | |
99 | ||
100 | if (ring == &adev->vce.ring[0]) | |
101 | WREG32(mmVCE_RB_WPTR, ring->wptr); | |
102 | else | |
103 | WREG32(mmVCE_RB_WPTR2, ring->wptr); | |
104 | } | |
105 | ||
0689a570 EH |
106 | static void vce_v3_0_override_vce_clock_gating(struct amdgpu_device *adev, bool override) |
107 | { | |
108 | u32 tmp, data; | |
109 | ||
110 | tmp = data = RREG32(mmVCE_RB_ARB_CTRL); | |
111 | if (override) | |
112 | data |= VCE_RB_ARB_CTRL__VCE_CGTT_OVERRIDE_MASK; | |
113 | else | |
114 | data &= ~VCE_RB_ARB_CTRL__VCE_CGTT_OVERRIDE_MASK; | |
115 | ||
116 | if (tmp != data) | |
117 | WREG32(mmVCE_RB_ARB_CTRL, data); | |
118 | } | |
119 | ||
120 | static void vce_v3_0_set_vce_sw_clock_gating(struct amdgpu_device *adev, | |
121 | bool gated) | |
122 | { | |
123 | u32 tmp, data; | |
124 | /* Set Override to disable Clock Gating */ | |
125 | vce_v3_0_override_vce_clock_gating(adev, true); | |
126 | ||
127 | if (!gated) { | |
128 | /* Force CLOCK ON for VCE_CLOCK_GATING_B, | |
129 | * {*_FORCE_ON, *_FORCE_OFF} = {1, 0} | |
130 | * VREG can be FORCE ON or set to Dynamic, but can't be OFF | |
131 | */ | |
132 | tmp = data = RREG32(mmVCE_CLOCK_GATING_B); | |
133 | data |= 0x1ff; | |
134 | data &= ~0xef0000; | |
135 | if (tmp != data) | |
136 | WREG32(mmVCE_CLOCK_GATING_B, data); | |
137 | ||
138 | /* Force CLOCK ON for VCE_UENC_CLOCK_GATING, | |
139 | * {*_FORCE_ON, *_FORCE_OFF} = {1, 0} | |
140 | */ | |
141 | tmp = data = RREG32(mmVCE_UENC_CLOCK_GATING); | |
142 | data |= 0x3ff000; | |
143 | data &= ~0xffc00000; | |
144 | if (tmp != data) | |
145 | WREG32(mmVCE_UENC_CLOCK_GATING, data); | |
146 | ||
147 | /* set VCE_UENC_CLOCK_GATING_2 */ | |
148 | tmp = data = RREG32(mmVCE_UENC_CLOCK_GATING_2); | |
149 | data |= 0x2; | |
150 | data &= ~0x2; | |
151 | if (tmp != data) | |
152 | WREG32(mmVCE_UENC_CLOCK_GATING_2, data); | |
153 | ||
154 | /* Force CLOCK ON for VCE_UENC_REG_CLOCK_GATING */ | |
155 | tmp = data = RREG32(mmVCE_UENC_REG_CLOCK_GATING); | |
156 | data |= 0x37f; | |
157 | if (tmp != data) | |
158 | WREG32(mmVCE_UENC_REG_CLOCK_GATING, data); | |
159 | ||
160 | /* Force VCE_UENC_DMA_DCLK_CTRL Clock ON */ | |
161 | tmp = data = RREG32(mmVCE_UENC_DMA_DCLK_CTRL); | |
162 | data |= VCE_UENC_DMA_DCLK_CTRL__WRDMCLK_FORCEON_MASK | | |
163 | VCE_UENC_DMA_DCLK_CTRL__RDDMCLK_FORCEON_MASK | | |
164 | VCE_UENC_DMA_DCLK_CTRL__REGCLK_FORCEON_MASK | | |
165 | 0x8; | |
166 | if (tmp != data) | |
167 | WREG32(mmVCE_UENC_DMA_DCLK_CTRL, data); | |
168 | } else { | |
169 | /* Force CLOCK OFF for VCE_CLOCK_GATING_B, | |
170 | * {*, *_FORCE_OFF} = {*, 1} | |
171 | * set VREG to Dynamic, as it can't be OFF | |
172 | */ | |
173 | tmp = data = RREG32(mmVCE_CLOCK_GATING_B); | |
174 | data &= ~0x80010; | |
175 | data |= 0xe70008; | |
176 | if (tmp != data) | |
177 | WREG32(mmVCE_CLOCK_GATING_B, data); | |
178 | /* Force CLOCK OFF for VCE_UENC_CLOCK_GATING, | |
179 | * Force ClOCK OFF takes precedent over Force CLOCK ON setting. | |
180 | * {*_FORCE_ON, *_FORCE_OFF} = {*, 1} | |
181 | */ | |
182 | tmp = data = RREG32(mmVCE_UENC_CLOCK_GATING); | |
183 | data |= 0xffc00000; | |
184 | if (tmp != data) | |
185 | WREG32(mmVCE_UENC_CLOCK_GATING, data); | |
186 | /* Set VCE_UENC_CLOCK_GATING_2 */ | |
187 | tmp = data = RREG32(mmVCE_UENC_CLOCK_GATING_2); | |
188 | data |= 0x10000; | |
189 | if (tmp != data) | |
190 | WREG32(mmVCE_UENC_CLOCK_GATING_2, data); | |
191 | /* Set VCE_UENC_REG_CLOCK_GATING to dynamic */ | |
192 | tmp = data = RREG32(mmVCE_UENC_REG_CLOCK_GATING); | |
193 | data &= ~0xffc00000; | |
194 | if (tmp != data) | |
195 | WREG32(mmVCE_UENC_REG_CLOCK_GATING, data); | |
196 | /* Set VCE_UENC_DMA_DCLK_CTRL CG always in dynamic mode */ | |
197 | tmp = data = RREG32(mmVCE_UENC_DMA_DCLK_CTRL); | |
198 | data &= ~(VCE_UENC_DMA_DCLK_CTRL__WRDMCLK_FORCEON_MASK | | |
199 | VCE_UENC_DMA_DCLK_CTRL__RDDMCLK_FORCEON_MASK | | |
200 | VCE_UENC_DMA_DCLK_CTRL__REGCLK_FORCEON_MASK | | |
201 | 0x8); | |
202 | if (tmp != data) | |
203 | WREG32(mmVCE_UENC_DMA_DCLK_CTRL, data); | |
204 | } | |
205 | vce_v3_0_override_vce_clock_gating(adev, false); | |
206 | } | |
207 | ||
aaa36a97 AD |
208 | /** |
209 | * vce_v3_0_start - start VCE block | |
210 | * | |
211 | * @adev: amdgpu_device pointer | |
212 | * | |
213 | * Setup and start the VCE block | |
214 | */ | |
215 | static int vce_v3_0_start(struct amdgpu_device *adev) | |
216 | { | |
217 | struct amdgpu_ring *ring; | |
5bbc553a LL |
218 | int idx, i, j, r; |
219 | ||
220 | mutex_lock(&adev->grbm_idx_mutex); | |
221 | for (idx = 0; idx < 2; ++idx) { | |
6a585777 AD |
222 | |
223 | if (adev->vce.harvest_config & (1 << idx)) | |
224 | continue; | |
225 | ||
0689a570 | 226 | if (idx == 0) |
5bbc553a LL |
227 | WREG32_P(mmGRBM_GFX_INDEX, 0, |
228 | ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK); | |
229 | else | |
230 | WREG32_P(mmGRBM_GFX_INDEX, | |
231 | GRBM_GFX_INDEX__VCE_INSTANCE_MASK, | |
232 | ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK); | |
233 | ||
234 | vce_v3_0_mc_resume(adev, idx); | |
235 | ||
236 | /* set BUSY flag */ | |
237 | WREG32_P(mmVCE_STATUS, 1, ~1); | |
3c0ff9f1 LL |
238 | if (adev->asic_type >= CHIP_STONEY) |
239 | WREG32_P(mmVCE_VCPU_CNTL, 1, ~0x200001); | |
240 | else | |
241 | WREG32_P(mmVCE_VCPU_CNTL, VCE_VCPU_CNTL__CLK_EN_MASK, | |
242 | ~VCE_VCPU_CNTL__CLK_EN_MASK); | |
5bbc553a LL |
243 | |
244 | WREG32_P(mmVCE_SOFT_RESET, | |
245 | VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK, | |
246 | ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK); | |
247 | ||
248 | mdelay(100); | |
249 | ||
250 | WREG32_P(mmVCE_SOFT_RESET, 0, | |
251 | ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK); | |
252 | ||
253 | for (i = 0; i < 10; ++i) { | |
254 | uint32_t status; | |
255 | for (j = 0; j < 100; ++j) { | |
256 | status = RREG32(mmVCE_STATUS); | |
257 | if (status & 2) | |
258 | break; | |
259 | mdelay(10); | |
260 | } | |
261 | r = 0; | |
262 | if (status & 2) | |
263 | break; | |
264 | ||
265 | DRM_ERROR("VCE not responding, trying to reset the ECPU!!!\n"); | |
266 | WREG32_P(mmVCE_SOFT_RESET, | |
267 | VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK, | |
268 | ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK); | |
269 | mdelay(10); | |
270 | WREG32_P(mmVCE_SOFT_RESET, 0, | |
271 | ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK); | |
272 | mdelay(10); | |
273 | r = -1; | |
274 | } | |
275 | ||
276 | /* clear BUSY flag */ | |
277 | WREG32_P(mmVCE_STATUS, 0, ~1); | |
aaa36a97 | 278 | |
0689a570 EH |
279 | /* Set Clock-Gating off */ |
280 | if (adev->cg_flags & AMDGPU_CG_SUPPORT_VCE_MGCG) | |
281 | vce_v3_0_set_vce_sw_clock_gating(adev, false); | |
282 | ||
5bbc553a LL |
283 | if (r) { |
284 | DRM_ERROR("VCE not responding, giving up!!!\n"); | |
285 | mutex_unlock(&adev->grbm_idx_mutex); | |
286 | return r; | |
287 | } | |
288 | } | |
aaa36a97 | 289 | |
5bbc553a LL |
290 | WREG32_P(mmGRBM_GFX_INDEX, 0, ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK); |
291 | mutex_unlock(&adev->grbm_idx_mutex); | |
aaa36a97 AD |
292 | |
293 | ring = &adev->vce.ring[0]; | |
294 | WREG32(mmVCE_RB_RPTR, ring->wptr); | |
295 | WREG32(mmVCE_RB_WPTR, ring->wptr); | |
296 | WREG32(mmVCE_RB_BASE_LO, ring->gpu_addr); | |
297 | WREG32(mmVCE_RB_BASE_HI, upper_32_bits(ring->gpu_addr)); | |
298 | WREG32(mmVCE_RB_SIZE, ring->ring_size / 4); | |
299 | ||
300 | ring = &adev->vce.ring[1]; | |
301 | WREG32(mmVCE_RB_RPTR2, ring->wptr); | |
302 | WREG32(mmVCE_RB_WPTR2, ring->wptr); | |
303 | WREG32(mmVCE_RB_BASE_LO2, ring->gpu_addr); | |
304 | WREG32(mmVCE_RB_BASE_HI2, upper_32_bits(ring->gpu_addr)); | |
305 | WREG32(mmVCE_RB_SIZE2, ring->ring_size / 4); | |
306 | ||
aaa36a97 AD |
307 | return 0; |
308 | } | |
309 | ||
6a585777 AD |
310 | #define ixVCE_HARVEST_FUSE_MACRO__ADDRESS 0xC0014074 |
311 | #define VCE_HARVEST_FUSE_MACRO__SHIFT 27 | |
312 | #define VCE_HARVEST_FUSE_MACRO__MASK 0x18000000 | |
313 | ||
314 | static unsigned vce_v3_0_get_harvest_config(struct amdgpu_device *adev) | |
315 | { | |
316 | u32 tmp; | |
6a585777 | 317 | |
cfaba566 SL |
318 | /* Fiji, Stoney are single pipe */ |
319 | if ((adev->asic_type == CHIP_FIJI) || | |
1dab5f06 TSD |
320 | (adev->asic_type == CHIP_STONEY)) |
321 | return AMDGPU_VCE_HARVEST_VCE1; | |
188a9bcd AD |
322 | |
323 | /* Tonga and CZ are dual or single pipe */ | |
2f7d10b3 | 324 | if (adev->flags & AMD_IS_APU) |
6a585777 AD |
325 | tmp = (RREG32_SMC(ixVCE_HARVEST_FUSE_MACRO__ADDRESS) & |
326 | VCE_HARVEST_FUSE_MACRO__MASK) >> | |
327 | VCE_HARVEST_FUSE_MACRO__SHIFT; | |
328 | else | |
329 | tmp = (RREG32_SMC(ixCC_HARVEST_FUSES) & | |
330 | CC_HARVEST_FUSES__VCE_DISABLE_MASK) >> | |
331 | CC_HARVEST_FUSES__VCE_DISABLE__SHIFT; | |
332 | ||
333 | switch (tmp) { | |
334 | case 1: | |
1dab5f06 | 335 | return AMDGPU_VCE_HARVEST_VCE0; |
6a585777 | 336 | case 2: |
1dab5f06 | 337 | return AMDGPU_VCE_HARVEST_VCE1; |
6a585777 | 338 | case 3: |
1dab5f06 | 339 | return AMDGPU_VCE_HARVEST_VCE0 | AMDGPU_VCE_HARVEST_VCE1; |
6a585777 | 340 | default: |
1dab5f06 | 341 | return 0; |
6a585777 | 342 | } |
6a585777 AD |
343 | } |
344 | ||
5fc3aeeb | 345 | static int vce_v3_0_early_init(void *handle) |
aaa36a97 | 346 | { |
5fc3aeeb | 347 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
348 | ||
6a585777 AD |
349 | adev->vce.harvest_config = vce_v3_0_get_harvest_config(adev); |
350 | ||
351 | if ((adev->vce.harvest_config & | |
352 | (AMDGPU_VCE_HARVEST_VCE0 | AMDGPU_VCE_HARVEST_VCE1)) == | |
353 | (AMDGPU_VCE_HARVEST_VCE0 | AMDGPU_VCE_HARVEST_VCE1)) | |
354 | return -ENOENT; | |
355 | ||
aaa36a97 AD |
356 | vce_v3_0_set_ring_funcs(adev); |
357 | vce_v3_0_set_irq_funcs(adev); | |
358 | ||
359 | return 0; | |
360 | } | |
361 | ||
5fc3aeeb | 362 | static int vce_v3_0_sw_init(void *handle) |
aaa36a97 | 363 | { |
5fc3aeeb | 364 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
aaa36a97 AD |
365 | struct amdgpu_ring *ring; |
366 | int r; | |
367 | ||
368 | /* VCE */ | |
369 | r = amdgpu_irq_add_id(adev, 167, &adev->vce.irq); | |
370 | if (r) | |
371 | return r; | |
372 | ||
e9822622 LL |
373 | r = amdgpu_vce_sw_init(adev, VCE_V3_0_FW_SIZE + |
374 | (VCE_V3_0_STACK_SIZE + VCE_V3_0_DATA_SIZE) * 2); | |
aaa36a97 AD |
375 | if (r) |
376 | return r; | |
377 | ||
378 | r = amdgpu_vce_resume(adev); | |
379 | if (r) | |
380 | return r; | |
381 | ||
382 | ring = &adev->vce.ring[0]; | |
383 | sprintf(ring->name, "vce0"); | |
384 | r = amdgpu_ring_init(adev, ring, 4096, VCE_CMD_NO_OP, 0xf, | |
385 | &adev->vce.irq, 0, AMDGPU_RING_TYPE_VCE); | |
386 | if (r) | |
387 | return r; | |
388 | ||
389 | ring = &adev->vce.ring[1]; | |
390 | sprintf(ring->name, "vce1"); | |
391 | r = amdgpu_ring_init(adev, ring, 4096, VCE_CMD_NO_OP, 0xf, | |
392 | &adev->vce.irq, 0, AMDGPU_RING_TYPE_VCE); | |
393 | if (r) | |
394 | return r; | |
395 | ||
396 | return r; | |
397 | } | |
398 | ||
5fc3aeeb | 399 | static int vce_v3_0_sw_fini(void *handle) |
aaa36a97 AD |
400 | { |
401 | int r; | |
5fc3aeeb | 402 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
aaa36a97 AD |
403 | |
404 | r = amdgpu_vce_suspend(adev); | |
405 | if (r) | |
406 | return r; | |
407 | ||
408 | r = amdgpu_vce_sw_fini(adev); | |
409 | if (r) | |
410 | return r; | |
411 | ||
412 | return r; | |
413 | } | |
414 | ||
5fc3aeeb | 415 | static int vce_v3_0_hw_init(void *handle) |
aaa36a97 AD |
416 | { |
417 | struct amdgpu_ring *ring; | |
418 | int r; | |
5fc3aeeb | 419 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
aaa36a97 AD |
420 | |
421 | r = vce_v3_0_start(adev); | |
422 | if (r) | |
423 | return r; | |
424 | ||
425 | ring = &adev->vce.ring[0]; | |
426 | ring->ready = true; | |
427 | r = amdgpu_ring_test_ring(ring); | |
428 | if (r) { | |
429 | ring->ready = false; | |
430 | return r; | |
431 | } | |
432 | ||
433 | ring = &adev->vce.ring[1]; | |
434 | ring->ready = true; | |
435 | r = amdgpu_ring_test_ring(ring); | |
436 | if (r) { | |
437 | ring->ready = false; | |
438 | return r; | |
439 | } | |
440 | ||
441 | DRM_INFO("VCE initialized successfully.\n"); | |
442 | ||
443 | return 0; | |
444 | } | |
445 | ||
5fc3aeeb | 446 | static int vce_v3_0_hw_fini(void *handle) |
aaa36a97 | 447 | { |
aaa36a97 AD |
448 | return 0; |
449 | } | |
450 | ||
5fc3aeeb | 451 | static int vce_v3_0_suspend(void *handle) |
aaa36a97 AD |
452 | { |
453 | int r; | |
5fc3aeeb | 454 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
aaa36a97 AD |
455 | |
456 | r = vce_v3_0_hw_fini(adev); | |
457 | if (r) | |
458 | return r; | |
459 | ||
460 | r = amdgpu_vce_suspend(adev); | |
461 | if (r) | |
462 | return r; | |
463 | ||
464 | return r; | |
465 | } | |
466 | ||
5fc3aeeb | 467 | static int vce_v3_0_resume(void *handle) |
aaa36a97 AD |
468 | { |
469 | int r; | |
5fc3aeeb | 470 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
aaa36a97 AD |
471 | |
472 | r = amdgpu_vce_resume(adev); | |
473 | if (r) | |
474 | return r; | |
475 | ||
476 | r = vce_v3_0_hw_init(adev); | |
477 | if (r) | |
478 | return r; | |
479 | ||
480 | return r; | |
481 | } | |
482 | ||
5bbc553a | 483 | static void vce_v3_0_mc_resume(struct amdgpu_device *adev, int idx) |
aaa36a97 AD |
484 | { |
485 | uint32_t offset, size; | |
486 | ||
487 | WREG32_P(mmVCE_CLOCK_GATING_A, 0, ~(1 << 16)); | |
488 | WREG32_P(mmVCE_UENC_CLOCK_GATING, 0x1FF000, ~0xFF9FF000); | |
489 | WREG32_P(mmVCE_UENC_REG_CLOCK_GATING, 0x3F, ~0x3F); | |
490 | WREG32(mmVCE_CLOCK_GATING_B, 0xf7); | |
491 | ||
492 | WREG32(mmVCE_LMI_CTRL, 0x00398000); | |
493 | WREG32_P(mmVCE_LMI_CACHE_CTRL, 0x0, ~0x1); | |
494 | WREG32(mmVCE_LMI_SWAP_CNTL, 0); | |
495 | WREG32(mmVCE_LMI_SWAP_CNTL1, 0); | |
496 | WREG32(mmVCE_LMI_VM_CTRL, 0); | |
3c0ff9f1 LL |
497 | if (adev->asic_type >= CHIP_STONEY) { |
498 | WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR0, (adev->vce.gpu_addr >> 8)); | |
499 | WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR1, (adev->vce.gpu_addr >> 8)); | |
500 | WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR2, (adev->vce.gpu_addr >> 8)); | |
501 | } else | |
502 | WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR, (adev->vce.gpu_addr >> 8)); | |
aaa36a97 | 503 | offset = AMDGPU_VCE_FIRMWARE_OFFSET; |
e9822622 | 504 | size = VCE_V3_0_FW_SIZE; |
aaa36a97 AD |
505 | WREG32(mmVCE_VCPU_CACHE_OFFSET0, offset & 0x7fffffff); |
506 | WREG32(mmVCE_VCPU_CACHE_SIZE0, size); | |
507 | ||
5bbc553a LL |
508 | if (idx == 0) { |
509 | offset += size; | |
510 | size = VCE_V3_0_STACK_SIZE; | |
511 | WREG32(mmVCE_VCPU_CACHE_OFFSET1, offset & 0x7fffffff); | |
512 | WREG32(mmVCE_VCPU_CACHE_SIZE1, size); | |
513 | offset += size; | |
514 | size = VCE_V3_0_DATA_SIZE; | |
515 | WREG32(mmVCE_VCPU_CACHE_OFFSET2, offset & 0x7fffffff); | |
516 | WREG32(mmVCE_VCPU_CACHE_SIZE2, size); | |
517 | } else { | |
518 | offset += size + VCE_V3_0_STACK_SIZE + VCE_V3_0_DATA_SIZE; | |
519 | size = VCE_V3_0_STACK_SIZE; | |
520 | WREG32(mmVCE_VCPU_CACHE_OFFSET1, offset & 0xfffffff); | |
521 | WREG32(mmVCE_VCPU_CACHE_SIZE1, size); | |
522 | offset += size; | |
523 | size = VCE_V3_0_DATA_SIZE; | |
524 | WREG32(mmVCE_VCPU_CACHE_OFFSET2, offset & 0xfffffff); | |
525 | WREG32(mmVCE_VCPU_CACHE_SIZE2, size); | |
526 | } | |
aaa36a97 AD |
527 | |
528 | WREG32_P(mmVCE_LMI_CTRL2, 0x0, ~0x100); | |
529 | ||
530 | WREG32_P(mmVCE_SYS_INT_EN, VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK, | |
531 | ~VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK); | |
532 | } | |
533 | ||
5fc3aeeb | 534 | static bool vce_v3_0_is_idle(void *handle) |
aaa36a97 | 535 | { |
5fc3aeeb | 536 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
be4f38e2 AD |
537 | u32 mask = 0; |
538 | int idx; | |
5fc3aeeb | 539 | |
be4f38e2 AD |
540 | for (idx = 0; idx < 2; ++idx) { |
541 | if (adev->vce.harvest_config & (1 << idx)) | |
542 | continue; | |
543 | ||
544 | if (idx == 0) | |
545 | mask |= SRBM_STATUS2__VCE0_BUSY_MASK; | |
546 | else | |
547 | mask |= SRBM_STATUS2__VCE1_BUSY_MASK; | |
548 | } | |
549 | ||
550 | return !(RREG32(mmSRBM_STATUS2) & mask); | |
aaa36a97 AD |
551 | } |
552 | ||
5fc3aeeb | 553 | static int vce_v3_0_wait_for_idle(void *handle) |
aaa36a97 AD |
554 | { |
555 | unsigned i; | |
5fc3aeeb | 556 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
be4f38e2 AD |
557 | u32 mask = 0; |
558 | int idx; | |
559 | ||
560 | for (idx = 0; idx < 2; ++idx) { | |
561 | if (adev->vce.harvest_config & (1 << idx)) | |
562 | continue; | |
563 | ||
564 | if (idx == 0) | |
565 | mask |= SRBM_STATUS2__VCE0_BUSY_MASK; | |
566 | else | |
567 | mask |= SRBM_STATUS2__VCE1_BUSY_MASK; | |
568 | } | |
aaa36a97 AD |
569 | |
570 | for (i = 0; i < adev->usec_timeout; i++) { | |
be4f38e2 | 571 | if (!(RREG32(mmSRBM_STATUS2) & mask)) |
aaa36a97 AD |
572 | return 0; |
573 | } | |
574 | return -ETIMEDOUT; | |
575 | } | |
576 | ||
5fc3aeeb | 577 | static int vce_v3_0_soft_reset(void *handle) |
aaa36a97 | 578 | { |
5fc3aeeb | 579 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
be4f38e2 AD |
580 | u32 mask = 0; |
581 | int idx; | |
582 | ||
583 | for (idx = 0; idx < 2; ++idx) { | |
584 | if (adev->vce.harvest_config & (1 << idx)) | |
585 | continue; | |
5fc3aeeb | 586 | |
be4f38e2 AD |
587 | if (idx == 0) |
588 | mask |= SRBM_SOFT_RESET__SOFT_RESET_VCE0_MASK; | |
589 | else | |
590 | mask |= SRBM_SOFT_RESET__SOFT_RESET_VCE1_MASK; | |
591 | } | |
592 | WREG32_P(mmSRBM_SOFT_RESET, mask, | |
593 | ~(SRBM_SOFT_RESET__SOFT_RESET_VCE0_MASK | | |
594 | SRBM_SOFT_RESET__SOFT_RESET_VCE1_MASK)); | |
aaa36a97 AD |
595 | mdelay(5); |
596 | ||
597 | return vce_v3_0_start(adev); | |
598 | } | |
599 | ||
5fc3aeeb | 600 | static void vce_v3_0_print_status(void *handle) |
aaa36a97 | 601 | { |
5fc3aeeb | 602 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
603 | ||
aaa36a97 AD |
604 | dev_info(adev->dev, "VCE 3.0 registers\n"); |
605 | dev_info(adev->dev, " VCE_STATUS=0x%08X\n", | |
606 | RREG32(mmVCE_STATUS)); | |
607 | dev_info(adev->dev, " VCE_VCPU_CNTL=0x%08X\n", | |
608 | RREG32(mmVCE_VCPU_CNTL)); | |
609 | dev_info(adev->dev, " VCE_VCPU_CACHE_OFFSET0=0x%08X\n", | |
610 | RREG32(mmVCE_VCPU_CACHE_OFFSET0)); | |
611 | dev_info(adev->dev, " VCE_VCPU_CACHE_SIZE0=0x%08X\n", | |
612 | RREG32(mmVCE_VCPU_CACHE_SIZE0)); | |
613 | dev_info(adev->dev, " VCE_VCPU_CACHE_OFFSET1=0x%08X\n", | |
614 | RREG32(mmVCE_VCPU_CACHE_OFFSET1)); | |
615 | dev_info(adev->dev, " VCE_VCPU_CACHE_SIZE1=0x%08X\n", | |
616 | RREG32(mmVCE_VCPU_CACHE_SIZE1)); | |
617 | dev_info(adev->dev, " VCE_VCPU_CACHE_OFFSET2=0x%08X\n", | |
618 | RREG32(mmVCE_VCPU_CACHE_OFFSET2)); | |
619 | dev_info(adev->dev, " VCE_VCPU_CACHE_SIZE2=0x%08X\n", | |
620 | RREG32(mmVCE_VCPU_CACHE_SIZE2)); | |
621 | dev_info(adev->dev, " VCE_SOFT_RESET=0x%08X\n", | |
622 | RREG32(mmVCE_SOFT_RESET)); | |
623 | dev_info(adev->dev, " VCE_RB_BASE_LO2=0x%08X\n", | |
624 | RREG32(mmVCE_RB_BASE_LO2)); | |
625 | dev_info(adev->dev, " VCE_RB_BASE_HI2=0x%08X\n", | |
626 | RREG32(mmVCE_RB_BASE_HI2)); | |
627 | dev_info(adev->dev, " VCE_RB_SIZE2=0x%08X\n", | |
628 | RREG32(mmVCE_RB_SIZE2)); | |
629 | dev_info(adev->dev, " VCE_RB_RPTR2=0x%08X\n", | |
630 | RREG32(mmVCE_RB_RPTR2)); | |
631 | dev_info(adev->dev, " VCE_RB_WPTR2=0x%08X\n", | |
632 | RREG32(mmVCE_RB_WPTR2)); | |
633 | dev_info(adev->dev, " VCE_RB_BASE_LO=0x%08X\n", | |
634 | RREG32(mmVCE_RB_BASE_LO)); | |
635 | dev_info(adev->dev, " VCE_RB_BASE_HI=0x%08X\n", | |
636 | RREG32(mmVCE_RB_BASE_HI)); | |
637 | dev_info(adev->dev, " VCE_RB_SIZE=0x%08X\n", | |
638 | RREG32(mmVCE_RB_SIZE)); | |
639 | dev_info(adev->dev, " VCE_RB_RPTR=0x%08X\n", | |
640 | RREG32(mmVCE_RB_RPTR)); | |
641 | dev_info(adev->dev, " VCE_RB_WPTR=0x%08X\n", | |
642 | RREG32(mmVCE_RB_WPTR)); | |
643 | dev_info(adev->dev, " VCE_CLOCK_GATING_A=0x%08X\n", | |
644 | RREG32(mmVCE_CLOCK_GATING_A)); | |
645 | dev_info(adev->dev, " VCE_CLOCK_GATING_B=0x%08X\n", | |
646 | RREG32(mmVCE_CLOCK_GATING_B)); | |
647 | dev_info(adev->dev, " VCE_UENC_CLOCK_GATING=0x%08X\n", | |
648 | RREG32(mmVCE_UENC_CLOCK_GATING)); | |
649 | dev_info(adev->dev, " VCE_UENC_REG_CLOCK_GATING=0x%08X\n", | |
650 | RREG32(mmVCE_UENC_REG_CLOCK_GATING)); | |
651 | dev_info(adev->dev, " VCE_SYS_INT_EN=0x%08X\n", | |
652 | RREG32(mmVCE_SYS_INT_EN)); | |
653 | dev_info(adev->dev, " VCE_LMI_CTRL2=0x%08X\n", | |
654 | RREG32(mmVCE_LMI_CTRL2)); | |
655 | dev_info(adev->dev, " VCE_LMI_CTRL=0x%08X\n", | |
656 | RREG32(mmVCE_LMI_CTRL)); | |
657 | dev_info(adev->dev, " VCE_LMI_VM_CTRL=0x%08X\n", | |
658 | RREG32(mmVCE_LMI_VM_CTRL)); | |
659 | dev_info(adev->dev, " VCE_LMI_SWAP_CNTL=0x%08X\n", | |
660 | RREG32(mmVCE_LMI_SWAP_CNTL)); | |
661 | dev_info(adev->dev, " VCE_LMI_SWAP_CNTL1=0x%08X\n", | |
662 | RREG32(mmVCE_LMI_SWAP_CNTL1)); | |
663 | dev_info(adev->dev, " VCE_LMI_CACHE_CTRL=0x%08X\n", | |
664 | RREG32(mmVCE_LMI_CACHE_CTRL)); | |
665 | } | |
666 | ||
667 | static int vce_v3_0_set_interrupt_state(struct amdgpu_device *adev, | |
668 | struct amdgpu_irq_src *source, | |
669 | unsigned type, | |
670 | enum amdgpu_interrupt_state state) | |
671 | { | |
672 | uint32_t val = 0; | |
673 | ||
674 | if (state == AMDGPU_IRQ_STATE_ENABLE) | |
675 | val |= VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK; | |
676 | ||
677 | WREG32_P(mmVCE_SYS_INT_EN, val, ~VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK); | |
678 | return 0; | |
679 | } | |
680 | ||
681 | static int vce_v3_0_process_interrupt(struct amdgpu_device *adev, | |
682 | struct amdgpu_irq_src *source, | |
683 | struct amdgpu_iv_entry *entry) | |
684 | { | |
685 | DRM_DEBUG("IH: VCE\n"); | |
d6c29c30 LL |
686 | |
687 | WREG32_P(mmVCE_SYS_INT_STATUS, | |
688 | VCE_SYS_INT_STATUS__VCE_SYS_INT_TRAP_INTERRUPT_INT_MASK, | |
689 | ~VCE_SYS_INT_STATUS__VCE_SYS_INT_TRAP_INTERRUPT_INT_MASK); | |
690 | ||
aaa36a97 AD |
691 | switch (entry->src_data) { |
692 | case 0: | |
693 | amdgpu_fence_process(&adev->vce.ring[0]); | |
694 | break; | |
695 | case 1: | |
696 | amdgpu_fence_process(&adev->vce.ring[1]); | |
697 | break; | |
698 | default: | |
699 | DRM_ERROR("Unhandled interrupt: %d %d\n", | |
700 | entry->src_id, entry->src_data); | |
701 | break; | |
702 | } | |
703 | ||
704 | return 0; | |
705 | } | |
706 | ||
5fc3aeeb | 707 | static int vce_v3_0_set_clockgating_state(void *handle, |
708 | enum amd_clockgating_state state) | |
aaa36a97 | 709 | { |
0689a570 EH |
710 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
711 | bool enable = (state == AMD_CG_STATE_GATE) ? true : false; | |
712 | int i; | |
713 | ||
714 | if (!(adev->cg_flags & AMDGPU_CG_SUPPORT_VCE_MGCG)) | |
715 | return 0; | |
716 | ||
717 | mutex_lock(&adev->grbm_idx_mutex); | |
718 | for (i = 0; i < 2; i++) { | |
719 | /* Program VCE Instance 0 or 1 if not harvested */ | |
720 | if (adev->vce.harvest_config & (1 << i)) | |
721 | continue; | |
722 | ||
723 | if (i == 0) | |
724 | WREG32_P(mmGRBM_GFX_INDEX, 0, | |
725 | ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK); | |
726 | else | |
727 | WREG32_P(mmGRBM_GFX_INDEX, | |
728 | GRBM_GFX_INDEX__VCE_INSTANCE_MASK, | |
729 | ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK); | |
730 | ||
731 | if (enable) { | |
732 | /* initialize VCE_CLOCK_GATING_A: Clock ON/OFF delay */ | |
733 | uint32_t data = RREG32(mmVCE_CLOCK_GATING_A); | |
734 | data &= ~(0xf | 0xff0); | |
735 | data |= ((0x0 << 0) | (0x04 << 4)); | |
736 | WREG32(mmVCE_CLOCK_GATING_A, data); | |
737 | ||
738 | /* initialize VCE_UENC_CLOCK_GATING: Clock ON/OFF delay */ | |
739 | data = RREG32(mmVCE_UENC_CLOCK_GATING); | |
740 | data &= ~(0xf | 0xff0); | |
741 | data |= ((0x0 << 0) | (0x04 << 4)); | |
742 | WREG32(mmVCE_UENC_CLOCK_GATING, data); | |
743 | } | |
744 | ||
745 | vce_v3_0_set_vce_sw_clock_gating(adev, enable); | |
746 | } | |
747 | ||
748 | WREG32_P(mmGRBM_GFX_INDEX, 0, ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK); | |
749 | mutex_unlock(&adev->grbm_idx_mutex); | |
750 | ||
aaa36a97 AD |
751 | return 0; |
752 | } | |
753 | ||
5fc3aeeb | 754 | static int vce_v3_0_set_powergating_state(void *handle, |
755 | enum amd_powergating_state state) | |
aaa36a97 AD |
756 | { |
757 | /* This doesn't actually powergate the VCE block. | |
758 | * That's done in the dpm code via the SMC. This | |
759 | * just re-inits the block as necessary. The actual | |
760 | * gating still happens in the dpm code. We should | |
761 | * revisit this when there is a cleaner line between | |
762 | * the smc and the hw blocks | |
763 | */ | |
5fc3aeeb | 764 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
765 | ||
766 | if (state == AMD_PG_STATE_GATE) | |
aaa36a97 AD |
767 | /* XXX do we need a vce_v3_0_stop()? */ |
768 | return 0; | |
769 | else | |
770 | return vce_v3_0_start(adev); | |
771 | } | |
772 | ||
5fc3aeeb | 773 | const struct amd_ip_funcs vce_v3_0_ip_funcs = { |
aaa36a97 AD |
774 | .early_init = vce_v3_0_early_init, |
775 | .late_init = NULL, | |
776 | .sw_init = vce_v3_0_sw_init, | |
777 | .sw_fini = vce_v3_0_sw_fini, | |
778 | .hw_init = vce_v3_0_hw_init, | |
779 | .hw_fini = vce_v3_0_hw_fini, | |
780 | .suspend = vce_v3_0_suspend, | |
781 | .resume = vce_v3_0_resume, | |
782 | .is_idle = vce_v3_0_is_idle, | |
783 | .wait_for_idle = vce_v3_0_wait_for_idle, | |
784 | .soft_reset = vce_v3_0_soft_reset, | |
785 | .print_status = vce_v3_0_print_status, | |
786 | .set_clockgating_state = vce_v3_0_set_clockgating_state, | |
787 | .set_powergating_state = vce_v3_0_set_powergating_state, | |
788 | }; | |
789 | ||
790 | static const struct amdgpu_ring_funcs vce_v3_0_ring_funcs = { | |
791 | .get_rptr = vce_v3_0_ring_get_rptr, | |
792 | .get_wptr = vce_v3_0_ring_get_wptr, | |
793 | .set_wptr = vce_v3_0_ring_set_wptr, | |
794 | .parse_cs = amdgpu_vce_ring_parse_cs, | |
795 | .emit_ib = amdgpu_vce_ring_emit_ib, | |
796 | .emit_fence = amdgpu_vce_ring_emit_fence, | |
797 | .emit_semaphore = amdgpu_vce_ring_emit_semaphore, | |
798 | .test_ring = amdgpu_vce_ring_test_ring, | |
799 | .test_ib = amdgpu_vce_ring_test_ib, | |
edff0e28 | 800 | .insert_nop = amdgpu_ring_insert_nop, |
aaa36a97 AD |
801 | }; |
802 | ||
803 | static void vce_v3_0_set_ring_funcs(struct amdgpu_device *adev) | |
804 | { | |
805 | adev->vce.ring[0].funcs = &vce_v3_0_ring_funcs; | |
806 | adev->vce.ring[1].funcs = &vce_v3_0_ring_funcs; | |
807 | } | |
808 | ||
809 | static const struct amdgpu_irq_src_funcs vce_v3_0_irq_funcs = { | |
810 | .set = vce_v3_0_set_interrupt_state, | |
811 | .process = vce_v3_0_process_interrupt, | |
812 | }; | |
813 | ||
814 | static void vce_v3_0_set_irq_funcs(struct amdgpu_device *adev) | |
815 | { | |
816 | adev->vce.irq.num_types = 1; | |
817 | adev->vce.irq.funcs = &vce_v3_0_irq_funcs; | |
818 | }; |