1 | /* |
2 | * Copyright 2007-8 Advanced Micro Devices, Inc. |
3 | * Copyright 2008 Red Hat Inc. |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining a |
6 | * copy of this software and associated documentation files (the "Software"), |
7 | * to deal in the Software without restriction, including without limitation |
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
9 | * and/or sell copies of the Software, and to permit persons to whom the |
10 | * Software is furnished to do so, subject to the following conditions: |
11 | * |
12 | * The above copyright notice and this permission notice shall be included in |
13 | * all copies or substantial portions of the Software. |
14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
21 | * OTHER DEALINGS IN THE SOFTWARE. |
22 | * |
23 | * Authors: Dave Airlie |
24 | * Alex Deucher |
25 | */ |
26 | |
27 | #include <drm/amdgpu_drm.h> |
28 | #include "amdgpu.h" |
29 | #include "amdgpu_i2c.h" |
30 | #include "atom.h" |
31 | #include "amdgpu_connectors.h" |
32 | #include "amdgpu_display.h" |
33 | #include "soc15_common.h" |
34 | #include "gc/gc_11_0_0_offset.h" |
35 | #include "gc/gc_11_0_0_sh_mask.h" |
36 | #include <asm/div64.h> |
37 | |
38 | #include <linux/pci.h> |
39 | #include <linux/pm_runtime.h> |
40 | #include <drm/drm_crtc_helper.h> |
41 | #include <drm/drm_damage_helper.h> |
42 | #include <drm/drm_drv.h> |
43 | #include <drm/drm_edid.h> |
44 | #include <drm/drm_fb_helper.h> |
45 | #include <drm/drm_gem_framebuffer_helper.h> |
46 | #include <drm/drm_fourcc.h> |
47 | #include <drm/drm_modeset_helper.h> |
48 | #include <drm/drm_vblank.h> |
49 | |
50 | /** |
51 | * amdgpu_display_hotplug_work_func - work handler for display hotplug event |
52 | * |
53 | * @work: work struct pointer |
54 | * |
55 | * This is the hotplug event work handler (all ASICs). |
56 | * The work gets scheduled from the IRQ handler if there |
57 | * was a hotplug interrupt. It walks through the connector table |
58 | * and calls hotplug handler for each connector. After this, it sends |
59 | * a DRM hotplug event to alert userspace. |
60 | * |
61 | * This design approach is required in order to defer hotplug event handling |
62 | * from the IRQ handler to a work handler because hotplug handler has to use |
63 | * mutexes which cannot be locked in an IRQ handler (since &mutex_lock may |
64 | * sleep). |
65 | */ |
66 | void amdgpu_display_hotplug_work_func(struct work_struct *work) |
67 | { |
68 | struct amdgpu_device *adev = container_of(work, struct amdgpu_device, |
69 | hotplug_work.work); |
70 | struct drm_device *dev = adev_to_drm(adev); |
71 | struct drm_mode_config *mode_config = &dev->mode_config; |
72 | struct drm_connector *connector; |
73 | struct drm_connector_list_iter iter; |
74 | |
75 | mutex_lock(&mode_config->mutex); |
76 | drm_connector_list_iter_begin(dev, iter: &iter); |
77 | drm_for_each_connector_iter(connector, &iter) |
78 | amdgpu_connector_hotplug(connector); |
79 | drm_connector_list_iter_end(iter: &iter); |
80 | mutex_unlock(lock: &mode_config->mutex); |
81 | /* Just fire off a uevent and let userspace tell us what to do */ |
82 | drm_helper_hpd_irq_event(dev); |
83 | } |
84 | |
85 | static int amdgpu_display_framebuffer_init(struct drm_device *dev, |
86 | struct amdgpu_framebuffer *rfb, |
87 | const struct drm_mode_fb_cmd2 *mode_cmd, |
88 | struct drm_gem_object *obj); |
89 | |
90 | static void amdgpu_display_flip_callback(struct dma_fence *f, |
91 | struct dma_fence_cb *cb) |
92 | { |
93 | struct amdgpu_flip_work *work = |
94 | container_of(cb, struct amdgpu_flip_work, cb); |
95 | |
96 | dma_fence_put(fence: f); |
97 | schedule_work(work: &work->flip_work.work); |
98 | } |
99 | |
100 | static bool amdgpu_display_flip_handle_fence(struct amdgpu_flip_work *work, |
101 | struct dma_fence **f) |
102 | { |
103 | struct dma_fence *fence = *f; |
104 | |
105 | if (fence == NULL) |
106 | return false; |
107 | |
108 | *f = NULL; |
109 | |
110 | if (!dma_fence_add_callback(fence, cb: &work->cb, |
111 | func: amdgpu_display_flip_callback)) |
112 | return true; |
113 | |
114 | dma_fence_put(fence); |
115 | return false; |
116 | } |
117 | |
118 | static void amdgpu_display_flip_work_func(struct work_struct *__work) |
119 | { |
120 | struct delayed_work *delayed_work = |
121 | container_of(__work, struct delayed_work, work); |
122 | struct amdgpu_flip_work *work = |
123 | container_of(delayed_work, struct amdgpu_flip_work, flip_work); |
124 | struct amdgpu_device *adev = work->adev; |
125 | struct amdgpu_crtc *amdgpu_crtc = adev->mode_info.crtcs[work->crtc_id]; |
126 | |
127 | struct drm_crtc *crtc = &amdgpu_crtc->base; |
128 | unsigned long flags; |
129 | unsigned int i; |
130 | int vpos, hpos; |
131 | |
132 | for (i = 0; i < work->shared_count; ++i) |
133 | if (amdgpu_display_flip_handle_fence(work, f: &work->shared[i])) |
134 | return; |
135 | |
136 | /* Wait until we're out of the vertical blank period before the one |
137 | * targeted by the flip |
138 | */ |
139 | if (amdgpu_crtc->enabled && |
140 | (amdgpu_display_get_crtc_scanoutpos(dev: adev_to_drm(adev), pipe: work->crtc_id, flags: 0, |
141 | vpos: &vpos, hpos: &hpos, NULL, NULL, |
142 | mode: &crtc->hwmode) |
143 | & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) == |
144 | (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) && |
145 | (int)(work->target_vblank - |
146 | amdgpu_get_vblank_counter_kms(crtc)) > 0) { |
147 | schedule_delayed_work(dwork: &work->flip_work, delay: usecs_to_jiffies(u: 1000)); |
148 | return; |
149 | } |
150 | |
151 | /* We borrow the event spin lock for protecting flip_status */ |
152 | spin_lock_irqsave(&crtc->dev->event_lock, flags); |
153 | |
154 | /* Do the flip (mmio) */ |
155 | adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base, work->async); |
156 | |
157 | /* Set the flip status */ |
158 | amdgpu_crtc->pflip_status = AMDGPU_FLIP_SUBMITTED; |
159 | spin_unlock_irqrestore(lock: &crtc->dev->event_lock, flags); |
160 | |
161 | |
162 | drm_dbg_vbl(adev_to_drm(adev), |
163 | "crtc:%d[%p], pflip_stat:AMDGPU_FLIP_SUBMITTED, work: %p,\n" , |
164 | amdgpu_crtc->crtc_id, amdgpu_crtc, work); |
165 | |
166 | } |
167 | |
168 | /* |
169 | * Handle unpin events outside the interrupt handler proper. |
170 | */ |
171 | static void amdgpu_display_unpin_work_func(struct work_struct *__work) |
172 | { |
173 | struct amdgpu_flip_work *work = |
174 | container_of(__work, struct amdgpu_flip_work, unpin_work); |
175 | int r; |
176 | |
177 | /* unpin of the old buffer */ |
178 | r = amdgpu_bo_reserve(bo: work->old_abo, no_intr: true); |
179 | if (likely(r == 0)) { |
180 | amdgpu_bo_unpin(bo: work->old_abo); |
181 | amdgpu_bo_unreserve(bo: work->old_abo); |
182 | } else |
183 | DRM_ERROR("failed to reserve buffer after flip\n" ); |
184 | |
185 | amdgpu_bo_unref(bo: &work->old_abo); |
186 | kfree(objp: work->shared); |
187 | kfree(objp: work); |
188 | } |
189 | |
190 | int amdgpu_display_crtc_page_flip_target(struct drm_crtc *crtc, |
191 | struct drm_framebuffer *fb, |
192 | struct drm_pending_vblank_event *event, |
193 | uint32_t page_flip_flags, uint32_t target, |
194 | struct drm_modeset_acquire_ctx *ctx) |
195 | { |
196 | struct drm_device *dev = crtc->dev; |
197 | struct amdgpu_device *adev = drm_to_adev(ddev: dev); |
198 | struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); |
199 | struct drm_gem_object *obj; |
200 | struct amdgpu_flip_work *work; |
201 | struct amdgpu_bo *new_abo; |
202 | unsigned long flags; |
203 | u64 tiling_flags; |
204 | int i, r; |
205 | |
206 | work = kzalloc(size: sizeof(*work), GFP_KERNEL); |
207 | if (work == NULL) |
208 | return -ENOMEM; |
209 | |
210 | INIT_DELAYED_WORK(&work->flip_work, amdgpu_display_flip_work_func); |
211 | INIT_WORK(&work->unpin_work, amdgpu_display_unpin_work_func); |
212 | |
213 | work->event = event; |
214 | work->adev = adev; |
215 | work->crtc_id = amdgpu_crtc->crtc_id; |
216 | work->async = (page_flip_flags & DRM_MODE_PAGE_FLIP_ASYNC) != 0; |
217 | |
218 | /* schedule unpin of the old buffer */ |
219 | obj = crtc->primary->fb->obj[0]; |
220 | |
221 | /* take a reference to the old object */ |
222 | work->old_abo = gem_to_amdgpu_bo(obj); |
223 | amdgpu_bo_ref(bo: work->old_abo); |
224 | |
225 | obj = fb->obj[0]; |
226 | new_abo = gem_to_amdgpu_bo(obj); |
227 | |
228 | /* pin the new buffer */ |
229 | r = amdgpu_bo_reserve(bo: new_abo, no_intr: false); |
230 | if (unlikely(r != 0)) { |
231 | DRM_ERROR("failed to reserve new abo buffer before flip\n" ); |
232 | goto cleanup; |
233 | } |
234 | |
235 | if (!adev->enable_virtual_display) { |
236 | r = amdgpu_bo_pin(bo: new_abo, |
237 | domain: amdgpu_display_supported_domains(adev, bo_flags: new_abo->flags)); |
238 | if (unlikely(r != 0)) { |
239 | DRM_ERROR("failed to pin new abo buffer before flip\n" ); |
240 | goto unreserve; |
241 | } |
242 | } |
243 | |
244 | r = amdgpu_ttm_alloc_gart(bo: &new_abo->tbo); |
245 | if (unlikely(r != 0)) { |
246 | DRM_ERROR("%p bind failed\n" , new_abo); |
247 | goto unpin; |
248 | } |
249 | |
250 | r = dma_resv_get_fences(obj: new_abo->tbo.base.resv, usage: DMA_RESV_USAGE_WRITE, |
251 | num_fences: &work->shared_count, |
252 | fences: &work->shared); |
253 | if (unlikely(r != 0)) { |
254 | DRM_ERROR("failed to get fences for buffer\n" ); |
255 | goto unpin; |
256 | } |
257 | |
258 | amdgpu_bo_get_tiling_flags(bo: new_abo, tiling_flags: &tiling_flags); |
259 | amdgpu_bo_unreserve(bo: new_abo); |
260 | |
261 | if (!adev->enable_virtual_display) |
262 | work->base = amdgpu_bo_gpu_offset(bo: new_abo); |
263 | work->target_vblank = target - (uint32_t)drm_crtc_vblank_count(crtc) + |
264 | amdgpu_get_vblank_counter_kms(crtc); |
265 | |
266 | /* we borrow the event spin lock for protecting flip_wrok */ |
267 | spin_lock_irqsave(&crtc->dev->event_lock, flags); |
268 | if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_NONE) { |
269 | DRM_DEBUG_DRIVER("flip queue: crtc already busy\n" ); |
270 | spin_unlock_irqrestore(lock: &crtc->dev->event_lock, flags); |
271 | r = -EBUSY; |
272 | goto pflip_cleanup; |
273 | } |
274 | |
275 | amdgpu_crtc->pflip_status = AMDGPU_FLIP_PENDING; |
276 | amdgpu_crtc->pflip_works = work; |
277 | |
278 | |
279 | DRM_DEBUG_DRIVER("crtc:%d[%p], pflip_stat:AMDGPU_FLIP_PENDING, work: %p,\n" , |
280 | amdgpu_crtc->crtc_id, amdgpu_crtc, work); |
281 | /* update crtc fb */ |
282 | crtc->primary->fb = fb; |
283 | spin_unlock_irqrestore(lock: &crtc->dev->event_lock, flags); |
284 | amdgpu_display_flip_work_func(work: &work->flip_work.work); |
285 | return 0; |
286 | |
287 | pflip_cleanup: |
288 | if (unlikely(amdgpu_bo_reserve(new_abo, false) != 0)) { |
289 | DRM_ERROR("failed to reserve new abo in error path\n" ); |
290 | goto cleanup; |
291 | } |
292 | unpin: |
293 | if (!adev->enable_virtual_display) |
294 | amdgpu_bo_unpin(bo: new_abo); |
295 | |
296 | unreserve: |
297 | amdgpu_bo_unreserve(bo: new_abo); |
298 | |
299 | cleanup: |
300 | amdgpu_bo_unref(bo: &work->old_abo); |
301 | for (i = 0; i < work->shared_count; ++i) |
302 | dma_fence_put(fence: work->shared[i]); |
303 | kfree(objp: work->shared); |
304 | kfree(objp: work); |
305 | |
306 | return r; |
307 | } |
308 | |
309 | int amdgpu_display_crtc_set_config(struct drm_mode_set *set, |
310 | struct drm_modeset_acquire_ctx *ctx) |
311 | { |
312 | struct drm_device *dev; |
313 | struct amdgpu_device *adev; |
314 | struct drm_crtc *crtc; |
315 | bool active = false; |
316 | int ret; |
317 | |
318 | if (!set || !set->crtc) |
319 | return -EINVAL; |
320 | |
321 | dev = set->crtc->dev; |
322 | |
323 | ret = pm_runtime_get_sync(dev: dev->dev); |
324 | if (ret < 0) |
325 | goto out; |
326 | |
327 | ret = drm_crtc_helper_set_config(set, ctx); |
328 | |
329 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) |
330 | if (crtc->enabled) |
331 | active = true; |
332 | |
333 | pm_runtime_mark_last_busy(dev: dev->dev); |
334 | |
335 | adev = drm_to_adev(ddev: dev); |
336 | /* if we have active crtcs and we don't have a power ref, |
337 | * take the current one |
338 | */ |
339 | if (active && !adev->have_disp_power_ref) { |
340 | adev->have_disp_power_ref = true; |
341 | return ret; |
342 | } |
343 | /* if we have no active crtcs, then go to |
344 | * drop the power ref we got before |
345 | */ |
346 | if (!active && adev->have_disp_power_ref) |
347 | adev->have_disp_power_ref = false; |
348 | out: |
349 | /* drop the power reference we got coming in here */ |
350 | pm_runtime_put_autosuspend(dev: dev->dev); |
351 | return ret; |
352 | } |
353 | |
354 | static const char *encoder_names[41] = { |
355 | "NONE" , |
356 | "INTERNAL_LVDS" , |
357 | "INTERNAL_TMDS1" , |
358 | "INTERNAL_TMDS2" , |
359 | "INTERNAL_DAC1" , |
360 | "INTERNAL_DAC2" , |
361 | "INTERNAL_SDVOA" , |
362 | "INTERNAL_SDVOB" , |
363 | "SI170B" , |
364 | "CH7303" , |
365 | "CH7301" , |
366 | "INTERNAL_DVO1" , |
367 | "EXTERNAL_SDVOA" , |
368 | "EXTERNAL_SDVOB" , |
369 | "TITFP513" , |
370 | "INTERNAL_LVTM1" , |
371 | "VT1623" , |
372 | "HDMI_SI1930" , |
373 | "HDMI_INTERNAL" , |
374 | "INTERNAL_KLDSCP_TMDS1" , |
375 | "INTERNAL_KLDSCP_DVO1" , |
376 | "INTERNAL_KLDSCP_DAC1" , |
377 | "INTERNAL_KLDSCP_DAC2" , |
378 | "SI178" , |
379 | "MVPU_FPGA" , |
380 | "INTERNAL_DDI" , |
381 | "VT1625" , |
382 | "HDMI_SI1932" , |
383 | "DP_AN9801" , |
384 | "DP_DP501" , |
385 | "INTERNAL_UNIPHY" , |
386 | "INTERNAL_KLDSCP_LVTMA" , |
387 | "INTERNAL_UNIPHY1" , |
388 | "INTERNAL_UNIPHY2" , |
389 | "NUTMEG" , |
390 | "TRAVIS" , |
391 | "INTERNAL_VCE" , |
392 | "INTERNAL_UNIPHY3" , |
393 | "HDMI_ANX9805" , |
394 | "INTERNAL_AMCLK" , |
395 | "VIRTUAL" , |
396 | }; |
397 | |
398 | static const char *hpd_names[6] = { |
399 | "HPD1" , |
400 | "HPD2" , |
401 | "HPD3" , |
402 | "HPD4" , |
403 | "HPD5" , |
404 | "HPD6" , |
405 | }; |
406 | |
407 | void amdgpu_display_print_display_setup(struct drm_device *dev) |
408 | { |
409 | struct drm_connector *connector; |
410 | struct amdgpu_connector *amdgpu_connector; |
411 | struct drm_encoder *encoder; |
412 | struct amdgpu_encoder *amdgpu_encoder; |
413 | struct drm_connector_list_iter iter; |
414 | uint32_t devices; |
415 | int i = 0; |
416 | |
417 | drm_connector_list_iter_begin(dev, iter: &iter); |
418 | DRM_INFO("AMDGPU Display Connectors\n" ); |
419 | drm_for_each_connector_iter(connector, &iter) { |
420 | amdgpu_connector = to_amdgpu_connector(connector); |
421 | DRM_INFO("Connector %d:\n" , i); |
422 | DRM_INFO(" %s\n" , connector->name); |
423 | if (amdgpu_connector->hpd.hpd != AMDGPU_HPD_NONE) |
424 | DRM_INFO(" %s\n" , hpd_names[amdgpu_connector->hpd.hpd]); |
425 | if (amdgpu_connector->ddc_bus) { |
426 | DRM_INFO(" DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n" , |
427 | amdgpu_connector->ddc_bus->rec.mask_clk_reg, |
428 | amdgpu_connector->ddc_bus->rec.mask_data_reg, |
429 | amdgpu_connector->ddc_bus->rec.a_clk_reg, |
430 | amdgpu_connector->ddc_bus->rec.a_data_reg, |
431 | amdgpu_connector->ddc_bus->rec.en_clk_reg, |
432 | amdgpu_connector->ddc_bus->rec.en_data_reg, |
433 | amdgpu_connector->ddc_bus->rec.y_clk_reg, |
434 | amdgpu_connector->ddc_bus->rec.y_data_reg); |
435 | if (amdgpu_connector->router.ddc_valid) |
436 | DRM_INFO(" DDC Router 0x%x/0x%x\n" , |
437 | amdgpu_connector->router.ddc_mux_control_pin, |
438 | amdgpu_connector->router.ddc_mux_state); |
439 | if (amdgpu_connector->router.cd_valid) |
440 | DRM_INFO(" Clock/Data Router 0x%x/0x%x\n" , |
441 | amdgpu_connector->router.cd_mux_control_pin, |
442 | amdgpu_connector->router.cd_mux_state); |
443 | } else { |
444 | if (connector->connector_type == DRM_MODE_CONNECTOR_VGA || |
445 | connector->connector_type == DRM_MODE_CONNECTOR_DVII || |
446 | connector->connector_type == DRM_MODE_CONNECTOR_DVID || |
447 | connector->connector_type == DRM_MODE_CONNECTOR_DVIA || |
448 | connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || |
449 | connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) |
450 | DRM_INFO(" DDC: no ddc bus - possible BIOS bug - please report to xorg-driver-ati@lists.x.org\n" ); |
451 | } |
452 | DRM_INFO(" Encoders:\n" ); |
453 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
454 | amdgpu_encoder = to_amdgpu_encoder(encoder); |
455 | devices = amdgpu_encoder->devices & amdgpu_connector->devices; |
456 | if (devices) { |
457 | if (devices & ATOM_DEVICE_CRT1_SUPPORT) |
458 | DRM_INFO(" CRT1: %s\n" , encoder_names[amdgpu_encoder->encoder_id]); |
459 | if (devices & ATOM_DEVICE_CRT2_SUPPORT) |
460 | DRM_INFO(" CRT2: %s\n" , encoder_names[amdgpu_encoder->encoder_id]); |
461 | if (devices & ATOM_DEVICE_LCD1_SUPPORT) |
462 | DRM_INFO(" LCD1: %s\n" , encoder_names[amdgpu_encoder->encoder_id]); |
463 | if (devices & ATOM_DEVICE_DFP1_SUPPORT) |
464 | DRM_INFO(" DFP1: %s\n" , encoder_names[amdgpu_encoder->encoder_id]); |
465 | if (devices & ATOM_DEVICE_DFP2_SUPPORT) |
466 | DRM_INFO(" DFP2: %s\n" , encoder_names[amdgpu_encoder->encoder_id]); |
467 | if (devices & ATOM_DEVICE_DFP3_SUPPORT) |
468 | DRM_INFO(" DFP3: %s\n" , encoder_names[amdgpu_encoder->encoder_id]); |
469 | if (devices & ATOM_DEVICE_DFP4_SUPPORT) |
470 | DRM_INFO(" DFP4: %s\n" , encoder_names[amdgpu_encoder->encoder_id]); |
471 | if (devices & ATOM_DEVICE_DFP5_SUPPORT) |
472 | DRM_INFO(" DFP5: %s\n" , encoder_names[amdgpu_encoder->encoder_id]); |
473 | if (devices & ATOM_DEVICE_DFP6_SUPPORT) |
474 | DRM_INFO(" DFP6: %s\n" , encoder_names[amdgpu_encoder->encoder_id]); |
475 | if (devices & ATOM_DEVICE_TV1_SUPPORT) |
476 | DRM_INFO(" TV1: %s\n" , encoder_names[amdgpu_encoder->encoder_id]); |
477 | if (devices & ATOM_DEVICE_CV_SUPPORT) |
478 | DRM_INFO(" CV: %s\n" , encoder_names[amdgpu_encoder->encoder_id]); |
479 | } |
480 | } |
481 | i++; |
482 | } |
483 | drm_connector_list_iter_end(iter: &iter); |
484 | } |
485 | |
486 | bool amdgpu_display_ddc_probe(struct amdgpu_connector *amdgpu_connector, |
487 | bool use_aux) |
488 | { |
489 | u8 out = 0x0; |
490 | u8 buf[8]; |
491 | int ret; |
492 | struct i2c_msg msgs[] = { |
493 | { |
494 | .addr = DDC_ADDR, |
495 | .flags = 0, |
496 | .len = 1, |
497 | .buf = &out, |
498 | }, |
499 | { |
500 | .addr = DDC_ADDR, |
501 | .flags = I2C_M_RD, |
502 | .len = 8, |
503 | .buf = buf, |
504 | } |
505 | }; |
506 | |
507 | /* on hw with routers, select right port */ |
508 | if (amdgpu_connector->router.ddc_valid) |
509 | amdgpu_i2c_router_select_ddc_port(connector: amdgpu_connector); |
510 | |
511 | if (use_aux) |
512 | ret = i2c_transfer(adap: &amdgpu_connector->ddc_bus->aux.ddc, msgs, num: 2); |
513 | else |
514 | ret = i2c_transfer(adap: &amdgpu_connector->ddc_bus->adapter, msgs, num: 2); |
515 | |
516 | if (ret != 2) |
517 | /* Couldn't find an accessible DDC on this connector */ |
518 | return false; |
519 | /* Probe also for valid EDID header |
520 | * EDID header starts with: |
521 | * 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00. |
522 | * Only the first 6 bytes must be valid as |
523 | * drm_edid_block_valid() can fix the last 2 bytes |
524 | */ |
525 | if (drm_edid_header_is_valid(edid: buf) < 6) { |
526 | /* Couldn't find an accessible EDID on this |
527 | * connector |
528 | */ |
529 | return false; |
530 | } |
531 | return true; |
532 | } |
533 | |
534 | static int amdgpu_dirtyfb(struct drm_framebuffer *fb, struct drm_file *file, |
535 | unsigned int flags, unsigned int color, |
536 | struct drm_clip_rect *clips, unsigned int num_clips) |
537 | { |
538 | |
539 | if (file) |
540 | return -ENOSYS; |
541 | |
542 | return drm_atomic_helper_dirtyfb(fb, file_priv: file, flags, color, clips, |
543 | num_clips); |
544 | } |
545 | |
546 | static const struct drm_framebuffer_funcs amdgpu_fb_funcs = { |
547 | .destroy = drm_gem_fb_destroy, |
548 | .create_handle = drm_gem_fb_create_handle, |
549 | }; |
550 | |
551 | static const struct drm_framebuffer_funcs amdgpu_fb_funcs_atomic = { |
552 | .destroy = drm_gem_fb_destroy, |
553 | .create_handle = drm_gem_fb_create_handle, |
554 | .dirty = amdgpu_dirtyfb |
555 | }; |
556 | |
557 | uint32_t amdgpu_display_supported_domains(struct amdgpu_device *adev, |
558 | uint64_t bo_flags) |
559 | { |
560 | uint32_t domain = AMDGPU_GEM_DOMAIN_VRAM; |
561 | |
562 | #if defined(CONFIG_DRM_AMD_DC) |
563 | /* |
564 | * if amdgpu_bo_support_uswc returns false it means that USWC mappings |
565 | * is not supported for this board. But this mapping is required |
566 | * to avoid hang caused by placement of scanout BO in GTT on certain |
567 | * APUs. So force the BO placement to VRAM in case this architecture |
568 | * will not allow USWC mappings. |
569 | * Also, don't allow GTT domain if the BO doesn't have USWC flag set. |
570 | */ |
571 | if ((bo_flags & AMDGPU_GEM_CREATE_CPU_GTT_USWC) && |
572 | amdgpu_bo_support_uswc(bo_flags) && |
573 | adev->dc_enabled && |
574 | adev->mode_info.gpu_vm_support) |
575 | domain |= AMDGPU_GEM_DOMAIN_GTT; |
576 | #endif |
577 | |
578 | return domain; |
579 | } |
580 | |
581 | static const struct drm_format_info dcc_formats[] = { |
582 | { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 2, |
583 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, |
584 | { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 2, |
585 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, |
586 | { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 2, |
587 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, |
588 | .has_alpha = true, }, |
589 | { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 2, |
590 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, |
591 | .has_alpha = true, }, |
592 | { .format = DRM_FORMAT_BGRA8888, .depth = 32, .num_planes = 2, |
593 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, |
594 | .has_alpha = true, }, |
595 | { .format = DRM_FORMAT_XRGB2101010, .depth = 30, .num_planes = 2, |
596 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, |
597 | { .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 2, |
598 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, |
599 | { .format = DRM_FORMAT_ARGB2101010, .depth = 30, .num_planes = 2, |
600 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, |
601 | .has_alpha = true, }, |
602 | { .format = DRM_FORMAT_ABGR2101010, .depth = 30, .num_planes = 2, |
603 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, |
604 | .has_alpha = true, }, |
605 | { .format = DRM_FORMAT_RGB565, .depth = 16, .num_planes = 2, |
606 | .cpp = { 2, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, |
607 | }; |
608 | |
609 | static const struct drm_format_info dcc_retile_formats[] = { |
610 | { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 3, |
611 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, |
612 | { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 3, |
613 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, |
614 | { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 3, |
615 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, |
616 | .has_alpha = true, }, |
617 | { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 3, |
618 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, |
619 | .has_alpha = true, }, |
620 | { .format = DRM_FORMAT_BGRA8888, .depth = 32, .num_planes = 3, |
621 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, |
622 | .has_alpha = true, }, |
623 | { .format = DRM_FORMAT_XRGB2101010, .depth = 30, .num_planes = 3, |
624 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, |
625 | { .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 3, |
626 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, |
627 | { .format = DRM_FORMAT_ARGB2101010, .depth = 30, .num_planes = 3, |
628 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, |
629 | .has_alpha = true, }, |
630 | { .format = DRM_FORMAT_ABGR2101010, .depth = 30, .num_planes = 3, |
631 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, |
632 | .has_alpha = true, }, |
633 | { .format = DRM_FORMAT_RGB565, .depth = 16, .num_planes = 3, |
634 | .cpp = { 2, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, |
635 | }; |
636 | |
637 | static const struct drm_format_info * |
638 | lookup_format_info(const struct drm_format_info formats[], |
639 | int num_formats, u32 format) |
640 | { |
641 | int i; |
642 | |
643 | for (i = 0; i < num_formats; i++) { |
644 | if (formats[i].format == format) |
645 | return &formats[i]; |
646 | } |
647 | |
648 | return NULL; |
649 | } |
650 | |
651 | const struct drm_format_info * |
652 | amdgpu_lookup_format_info(u32 format, uint64_t modifier) |
653 | { |
654 | if (!IS_AMD_FMT_MOD(modifier)) |
655 | return NULL; |
656 | |
657 | if (AMD_FMT_MOD_GET(DCC_RETILE, modifier)) |
658 | return lookup_format_info(formats: dcc_retile_formats, |
659 | ARRAY_SIZE(dcc_retile_formats), |
660 | format); |
661 | |
662 | if (AMD_FMT_MOD_GET(DCC, modifier)) |
663 | return lookup_format_info(formats: dcc_formats, ARRAY_SIZE(dcc_formats), |
664 | format); |
665 | |
666 | /* returning NULL will cause the default format structs to be used. */ |
667 | return NULL; |
668 | } |
669 | |
670 | |
671 | /* |
672 | * Tries to extract the renderable DCC offset from the opaque metadata attached |
673 | * to the buffer. |
674 | */ |
675 | static int |
676 | (struct amdgpu_device *adev, |
677 | struct drm_gem_object *obj, |
678 | uint64_t *offset) |
679 | { |
680 | struct amdgpu_bo *rbo; |
681 | int r = 0; |
682 | uint32_t metadata[10]; /* Something that fits a descriptor + header. */ |
683 | uint32_t size; |
684 | |
685 | rbo = gem_to_amdgpu_bo(obj); |
686 | r = amdgpu_bo_reserve(bo: rbo, no_intr: false); |
687 | |
688 | if (unlikely(r)) { |
689 | /* Don't show error message when returning -ERESTARTSYS */ |
690 | if (r != -ERESTARTSYS) |
691 | DRM_ERROR("Unable to reserve buffer: %d\n" , r); |
692 | return r; |
693 | } |
694 | |
695 | r = amdgpu_bo_get_metadata(bo: rbo, buffer: metadata, buffer_size: sizeof(metadata), metadata_size: &size, NULL); |
696 | amdgpu_bo_unreserve(bo: rbo); |
697 | |
698 | if (r) |
699 | return r; |
700 | |
701 | /* |
702 | * The first word is the metadata version, and we need space for at least |
703 | * the version + pci vendor+device id + 8 words for a descriptor. |
704 | */ |
705 | if (size < 40 || metadata[0] != 1) |
706 | return -EINVAL; |
707 | |
708 | if (adev->family >= AMDGPU_FAMILY_NV) { |
709 | /* resource word 6/7 META_DATA_ADDRESS{_LO} */ |
710 | *offset = ((u64)metadata[9] << 16u) | |
711 | ((metadata[8] & 0xFF000000u) >> 16); |
712 | } else { |
713 | /* resource word 5/7 META_DATA_ADDRESS */ |
714 | *offset = ((u64)metadata[9] << 8u) | |
715 | ((u64)(metadata[7] & 0x1FE0000u) << 23); |
716 | } |
717 | |
718 | return 0; |
719 | } |
720 | |
721 | static int convert_tiling_flags_to_modifier(struct amdgpu_framebuffer *afb) |
722 | { |
723 | struct amdgpu_device *adev = drm_to_adev(ddev: afb->base.dev); |
724 | uint64_t modifier = 0; |
725 | int num_pipes = 0; |
726 | int num_pkrs = 0; |
727 | |
728 | num_pkrs = adev->gfx.config.gb_addr_config_fields.num_pkrs; |
729 | num_pipes = adev->gfx.config.gb_addr_config_fields.num_pipes; |
730 | |
731 | if (!afb->tiling_flags || !AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE)) { |
732 | modifier = DRM_FORMAT_MOD_LINEAR; |
733 | } else { |
734 | int swizzle = AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE); |
735 | bool has_xor = swizzle >= 16; |
736 | int block_size_bits; |
737 | int version; |
738 | int pipe_xor_bits = 0; |
739 | int bank_xor_bits = 0; |
740 | int packers = 0; |
741 | int rb = 0; |
742 | int pipes = ilog2(num_pipes); |
743 | uint32_t dcc_offset = AMDGPU_TILING_GET(afb->tiling_flags, DCC_OFFSET_256B); |
744 | |
745 | switch (swizzle >> 2) { |
746 | case 0: /* 256B */ |
747 | block_size_bits = 8; |
748 | break; |
749 | case 1: /* 4KiB */ |
750 | case 5: /* 4KiB _X */ |
751 | block_size_bits = 12; |
752 | break; |
753 | case 2: /* 64KiB */ |
754 | case 4: /* 64 KiB _T */ |
755 | case 6: /* 64 KiB _X */ |
756 | block_size_bits = 16; |
757 | break; |
758 | case 7: /* 256 KiB */ |
759 | block_size_bits = 18; |
760 | break; |
761 | default: |
762 | /* RESERVED or VAR */ |
763 | return -EINVAL; |
764 | } |
765 | |
766 | if (amdgpu_ip_version(adev, ip: GC_HWIP, inst: 0) >= IP_VERSION(11, 0, 0)) |
767 | version = AMD_FMT_MOD_TILE_VER_GFX11; |
768 | else if (amdgpu_ip_version(adev, ip: GC_HWIP, inst: 0) >= |
769 | IP_VERSION(10, 3, 0)) |
770 | version = AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS; |
771 | else if (amdgpu_ip_version(adev, ip: GC_HWIP, inst: 0) >= |
772 | IP_VERSION(10, 0, 0)) |
773 | version = AMD_FMT_MOD_TILE_VER_GFX10; |
774 | else |
775 | version = AMD_FMT_MOD_TILE_VER_GFX9; |
776 | |
777 | switch (swizzle & 3) { |
778 | case 0: /* Z microtiling */ |
779 | return -EINVAL; |
780 | case 1: /* S microtiling */ |
781 | if (amdgpu_ip_version(adev, ip: GC_HWIP, inst: 0) < |
782 | IP_VERSION(11, 0, 0)) { |
783 | if (!has_xor) |
784 | version = AMD_FMT_MOD_TILE_VER_GFX9; |
785 | } |
786 | break; |
787 | case 2: |
788 | if (amdgpu_ip_version(adev, ip: GC_HWIP, inst: 0) < |
789 | IP_VERSION(11, 0, 0)) { |
790 | if (!has_xor && afb->base.format->cpp[0] != 4) |
791 | version = AMD_FMT_MOD_TILE_VER_GFX9; |
792 | } |
793 | break; |
794 | case 3: |
795 | break; |
796 | } |
797 | |
798 | if (has_xor) { |
799 | if (num_pipes == num_pkrs && num_pkrs == 0) { |
800 | DRM_ERROR("invalid number of pipes and packers\n" ); |
801 | return -EINVAL; |
802 | } |
803 | |
804 | switch (version) { |
805 | case AMD_FMT_MOD_TILE_VER_GFX11: |
806 | pipe_xor_bits = min(block_size_bits - 8, pipes); |
807 | packers = ilog2(adev->gfx.config.gb_addr_config_fields.num_pkrs); |
808 | break; |
809 | case AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS: |
810 | pipe_xor_bits = min(block_size_bits - 8, pipes); |
811 | packers = min(block_size_bits - 8 - pipe_xor_bits, |
812 | ilog2(adev->gfx.config.gb_addr_config_fields.num_pkrs)); |
813 | break; |
814 | case AMD_FMT_MOD_TILE_VER_GFX10: |
815 | pipe_xor_bits = min(block_size_bits - 8, pipes); |
816 | break; |
817 | case AMD_FMT_MOD_TILE_VER_GFX9: |
818 | rb = ilog2(adev->gfx.config.gb_addr_config_fields.num_se) + |
819 | ilog2(adev->gfx.config.gb_addr_config_fields.num_rb_per_se); |
820 | pipe_xor_bits = min(block_size_bits - 8, pipes + |
821 | ilog2(adev->gfx.config.gb_addr_config_fields.num_se)); |
822 | bank_xor_bits = min(block_size_bits - 8 - pipe_xor_bits, |
823 | ilog2(adev->gfx.config.gb_addr_config_fields.num_banks)); |
824 | break; |
825 | } |
826 | } |
827 | |
828 | modifier = AMD_FMT_MOD | |
829 | AMD_FMT_MOD_SET(TILE, AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE)) | |
830 | AMD_FMT_MOD_SET(TILE_VERSION, version) | |
831 | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | |
832 | AMD_FMT_MOD_SET(BANK_XOR_BITS, bank_xor_bits) | |
833 | AMD_FMT_MOD_SET(PACKERS, packers); |
834 | |
835 | if (dcc_offset != 0) { |
836 | bool dcc_i64b = AMDGPU_TILING_GET(afb->tiling_flags, DCC_INDEPENDENT_64B) != 0; |
837 | bool dcc_i128b = version >= AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS; |
838 | const struct drm_format_info *format_info; |
839 | u64 render_dcc_offset; |
840 | |
841 | /* Enable constant encode on RAVEN2 and later. */ |
842 | bool dcc_constant_encode = |
843 | (adev->asic_type > CHIP_RAVEN || |
844 | (adev->asic_type == CHIP_RAVEN && |
845 | adev->external_rev_id >= 0x81)) && |
846 | amdgpu_ip_version(adev, ip: GC_HWIP, inst: 0) < |
847 | IP_VERSION(11, 0, 0); |
848 | |
849 | int max_cblock_size = dcc_i64b ? AMD_FMT_MOD_DCC_BLOCK_64B : |
850 | dcc_i128b ? AMD_FMT_MOD_DCC_BLOCK_128B : |
851 | AMD_FMT_MOD_DCC_BLOCK_256B; |
852 | |
853 | modifier |= AMD_FMT_MOD_SET(DCC, 1) | |
854 | AMD_FMT_MOD_SET(DCC_CONSTANT_ENCODE, dcc_constant_encode) | |
855 | AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, dcc_i64b) | |
856 | AMD_FMT_MOD_SET(DCC_INDEPENDENT_128B, dcc_i128b) | |
857 | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, max_cblock_size); |
858 | |
859 | afb->base.offsets[1] = dcc_offset * 256 + afb->base.offsets[0]; |
860 | afb->base.pitches[1] = |
861 | AMDGPU_TILING_GET(afb->tiling_flags, DCC_PITCH_MAX) + 1; |
862 | |
863 | /* |
864 | * If the userspace driver uses retiling the tiling flags do not contain |
865 | * info on the renderable DCC buffer. Luckily the opaque metadata contains |
866 | * the info so we can try to extract it. The kernel does not use this info |
867 | * but we should convert it to a modifier plane for getfb2, so the |
868 | * userspace driver that gets it doesn't have to juggle around another DCC |
869 | * plane internally. |
870 | */ |
871 | if (extract_render_dcc_offset(adev, obj: afb->base.obj[0], |
872 | offset: &render_dcc_offset) == 0 && |
873 | render_dcc_offset != 0 && |
874 | render_dcc_offset != afb->base.offsets[1] && |
875 | render_dcc_offset < UINT_MAX) { |
876 | uint32_t dcc_block_bits; /* of base surface data */ |
877 | |
878 | modifier |= AMD_FMT_MOD_SET(DCC_RETILE, 1); |
879 | afb->base.offsets[2] = render_dcc_offset; |
880 | |
881 | if (adev->family >= AMDGPU_FAMILY_NV) { |
882 | int = 0; |
883 | |
884 | if ((amdgpu_ip_version(adev, ip: GC_HWIP, |
885 | inst: 0) >= |
886 | IP_VERSION(10, 3, 0)) && |
887 | pipes == packers && pipes > 1) |
888 | extra_pipe = 1; |
889 | |
890 | dcc_block_bits = max(20, 16 + pipes + extra_pipe); |
891 | } else { |
892 | modifier |= AMD_FMT_MOD_SET(RB, rb) | |
893 | AMD_FMT_MOD_SET(PIPE, pipes); |
894 | dcc_block_bits = max(20, 18 + rb); |
895 | } |
896 | |
897 | dcc_block_bits -= ilog2(afb->base.format->cpp[0]); |
898 | afb->base.pitches[2] = ALIGN(afb->base.width, |
899 | 1u << ((dcc_block_bits + 1) / 2)); |
900 | } |
901 | format_info = amdgpu_lookup_format_info(format: afb->base.format->format, |
902 | modifier); |
903 | if (!format_info) |
904 | return -EINVAL; |
905 | |
906 | afb->base.format = format_info; |
907 | } |
908 | } |
909 | |
910 | afb->base.modifier = modifier; |
911 | afb->base.flags |= DRM_MODE_FB_MODIFIERS; |
912 | return 0; |
913 | } |
914 | |
915 | /* Mirrors the is_displayable check in radeonsi's gfx6_compute_surface */ |
916 | static int check_tiling_flags_gfx6(struct amdgpu_framebuffer *afb) |
917 | { |
918 | u64 micro_tile_mode; |
919 | |
920 | /* Zero swizzle mode means linear */ |
921 | if (AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE) == 0) |
922 | return 0; |
923 | |
924 | micro_tile_mode = AMDGPU_TILING_GET(afb->tiling_flags, MICRO_TILE_MODE); |
925 | switch (micro_tile_mode) { |
926 | case 0: /* DISPLAY */ |
927 | case 3: /* RENDER */ |
928 | return 0; |
929 | default: |
930 | drm_dbg_kms(afb->base.dev, |
931 | "Micro tile mode %llu not supported for scanout\n" , |
932 | micro_tile_mode); |
933 | return -EINVAL; |
934 | } |
935 | } |
936 | |
937 | static void get_block_dimensions(unsigned int block_log2, unsigned int cpp, |
938 | unsigned int *width, unsigned int *height) |
939 | { |
940 | unsigned int cpp_log2 = ilog2(cpp); |
941 | unsigned int pixel_log2 = block_log2 - cpp_log2; |
942 | unsigned int width_log2 = (pixel_log2 + 1) / 2; |
943 | unsigned int height_log2 = pixel_log2 - width_log2; |
944 | |
945 | *width = 1 << width_log2; |
946 | *height = 1 << height_log2; |
947 | } |
948 | |
949 | static unsigned int get_dcc_block_size(uint64_t modifier, bool rb_aligned, |
950 | bool pipe_aligned) |
951 | { |
952 | unsigned int ver = AMD_FMT_MOD_GET(TILE_VERSION, modifier); |
953 | |
954 | switch (ver) { |
955 | case AMD_FMT_MOD_TILE_VER_GFX9: { |
956 | /* |
957 | * TODO: for pipe aligned we may need to check the alignment of the |
958 | * total size of the surface, which may need to be bigger than the |
959 | * natural alignment due to some HW workarounds |
960 | */ |
961 | return max(10 + (rb_aligned ? (int)AMD_FMT_MOD_GET(RB, modifier) : 0), 12); |
962 | } |
963 | case AMD_FMT_MOD_TILE_VER_GFX10: |
964 | case AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS: |
965 | case AMD_FMT_MOD_TILE_VER_GFX11: { |
966 | int pipes_log2 = AMD_FMT_MOD_GET(PIPE_XOR_BITS, modifier); |
967 | |
968 | if (ver >= AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS && pipes_log2 > 1 && |
969 | AMD_FMT_MOD_GET(PACKERS, modifier) == pipes_log2) |
970 | ++pipes_log2; |
971 | |
972 | return max(8 + (pipe_aligned ? pipes_log2 : 0), 12); |
973 | } |
974 | default: |
975 | return 0; |
976 | } |
977 | } |
978 | |
979 | static int amdgpu_display_verify_plane(struct amdgpu_framebuffer *rfb, int plane, |
980 | const struct drm_format_info *format, |
981 | unsigned int block_width, unsigned int block_height, |
982 | unsigned int block_size_log2) |
983 | { |
984 | unsigned int width = rfb->base.width / |
985 | ((plane && plane < format->num_planes) ? format->hsub : 1); |
986 | unsigned int height = rfb->base.height / |
987 | ((plane && plane < format->num_planes) ? format->vsub : 1); |
988 | unsigned int cpp = plane < format->num_planes ? format->cpp[plane] : 1; |
989 | unsigned int block_pitch = block_width * cpp; |
990 | unsigned int min_pitch = ALIGN(width * cpp, block_pitch); |
991 | unsigned int block_size = 1 << block_size_log2; |
992 | uint64_t size; |
993 | |
994 | if (rfb->base.pitches[plane] % block_pitch) { |
995 | drm_dbg_kms(rfb->base.dev, |
996 | "pitch %d for plane %d is not a multiple of block pitch %d\n" , |
997 | rfb->base.pitches[plane], plane, block_pitch); |
998 | return -EINVAL; |
999 | } |
1000 | if (rfb->base.pitches[plane] < min_pitch) { |
1001 | drm_dbg_kms(rfb->base.dev, |
1002 | "pitch %d for plane %d is less than minimum pitch %d\n" , |
1003 | rfb->base.pitches[plane], plane, min_pitch); |
1004 | return -EINVAL; |
1005 | } |
1006 | |
1007 | /* Force at least natural alignment. */ |
1008 | if (rfb->base.offsets[plane] % block_size) { |
1009 | drm_dbg_kms(rfb->base.dev, |
1010 | "offset 0x%x for plane %d is not a multiple of block pitch 0x%x\n" , |
1011 | rfb->base.offsets[plane], plane, block_size); |
1012 | return -EINVAL; |
1013 | } |
1014 | |
1015 | size = rfb->base.offsets[plane] + |
1016 | (uint64_t)rfb->base.pitches[plane] / block_pitch * |
1017 | block_size * DIV_ROUND_UP(height, block_height); |
1018 | |
1019 | if (rfb->base.obj[0]->size < size) { |
1020 | drm_dbg_kms(rfb->base.dev, |
1021 | "BO size 0x%zx is less than 0x%llx required for plane %d\n" , |
1022 | rfb->base.obj[0]->size, size, plane); |
1023 | return -EINVAL; |
1024 | } |
1025 | |
1026 | return 0; |
1027 | } |
1028 | |
1029 | |
1030 | static int amdgpu_display_verify_sizes(struct amdgpu_framebuffer *rfb) |
1031 | { |
1032 | const struct drm_format_info *format_info = drm_format_info(format: rfb->base.format->format); |
1033 | uint64_t modifier = rfb->base.modifier; |
1034 | int ret; |
1035 | unsigned int i, block_width, block_height, block_size_log2; |
1036 | |
1037 | if (rfb->base.dev->mode_config.fb_modifiers_not_supported) |
1038 | return 0; |
1039 | |
1040 | for (i = 0; i < format_info->num_planes; ++i) { |
1041 | if (modifier == DRM_FORMAT_MOD_LINEAR) { |
1042 | block_width = 256 / format_info->cpp[i]; |
1043 | block_height = 1; |
1044 | block_size_log2 = 8; |
1045 | } else { |
1046 | int swizzle = AMD_FMT_MOD_GET(TILE, modifier); |
1047 | |
1048 | switch ((swizzle & ~3) + 1) { |
1049 | case DC_SW_256B_S: |
1050 | block_size_log2 = 8; |
1051 | break; |
1052 | case DC_SW_4KB_S: |
1053 | case DC_SW_4KB_S_X: |
1054 | block_size_log2 = 12; |
1055 | break; |
1056 | case DC_SW_64KB_S: |
1057 | case DC_SW_64KB_S_T: |
1058 | case DC_SW_64KB_S_X: |
1059 | block_size_log2 = 16; |
1060 | break; |
1061 | case DC_SW_VAR_S_X: |
1062 | block_size_log2 = 18; |
1063 | break; |
1064 | default: |
1065 | drm_dbg_kms(rfb->base.dev, |
1066 | "Swizzle mode with unknown block size: %d\n" , swizzle); |
1067 | return -EINVAL; |
1068 | } |
1069 | |
1070 | get_block_dimensions(block_log2: block_size_log2, cpp: format_info->cpp[i], |
1071 | width: &block_width, height: &block_height); |
1072 | } |
1073 | |
1074 | ret = amdgpu_display_verify_plane(rfb, plane: i, format: format_info, |
1075 | block_width, block_height, block_size_log2); |
1076 | if (ret) |
1077 | return ret; |
1078 | } |
1079 | |
1080 | if (AMD_FMT_MOD_GET(DCC, modifier)) { |
1081 | if (AMD_FMT_MOD_GET(DCC_RETILE, modifier)) { |
1082 | block_size_log2 = get_dcc_block_size(modifier, rb_aligned: false, pipe_aligned: false); |
1083 | get_block_dimensions(block_log2: block_size_log2 + 8, cpp: format_info->cpp[0], |
1084 | width: &block_width, height: &block_height); |
1085 | ret = amdgpu_display_verify_plane(rfb, plane: i, format: format_info, |
1086 | block_width, block_height, |
1087 | block_size_log2); |
1088 | if (ret) |
1089 | return ret; |
1090 | |
1091 | ++i; |
1092 | block_size_log2 = get_dcc_block_size(modifier, rb_aligned: true, pipe_aligned: true); |
1093 | } else { |
1094 | bool pipe_aligned = AMD_FMT_MOD_GET(DCC_PIPE_ALIGN, modifier); |
1095 | |
1096 | block_size_log2 = get_dcc_block_size(modifier, rb_aligned: true, pipe_aligned); |
1097 | } |
1098 | get_block_dimensions(block_log2: block_size_log2 + 8, cpp: format_info->cpp[0], |
1099 | width: &block_width, height: &block_height); |
1100 | ret = amdgpu_display_verify_plane(rfb, plane: i, format: format_info, |
1101 | block_width, block_height, block_size_log2); |
1102 | if (ret) |
1103 | return ret; |
1104 | } |
1105 | |
1106 | return 0; |
1107 | } |
1108 | |
1109 | static int amdgpu_display_get_fb_info(const struct amdgpu_framebuffer *amdgpu_fb, |
1110 | uint64_t *tiling_flags, bool *tmz_surface) |
1111 | { |
1112 | struct amdgpu_bo *rbo; |
1113 | int r; |
1114 | |
1115 | if (!amdgpu_fb) { |
1116 | *tiling_flags = 0; |
1117 | *tmz_surface = false; |
1118 | return 0; |
1119 | } |
1120 | |
1121 | rbo = gem_to_amdgpu_bo(amdgpu_fb->base.obj[0]); |
1122 | r = amdgpu_bo_reserve(bo: rbo, no_intr: false); |
1123 | |
1124 | if (unlikely(r)) { |
1125 | /* Don't show error message when returning -ERESTARTSYS */ |
1126 | if (r != -ERESTARTSYS) |
1127 | DRM_ERROR("Unable to reserve buffer: %d\n" , r); |
1128 | return r; |
1129 | } |
1130 | |
1131 | if (tiling_flags) |
1132 | amdgpu_bo_get_tiling_flags(bo: rbo, tiling_flags); |
1133 | |
1134 | if (tmz_surface) |
1135 | *tmz_surface = amdgpu_bo_encrypted(bo: rbo); |
1136 | |
1137 | amdgpu_bo_unreserve(bo: rbo); |
1138 | |
1139 | return r; |
1140 | } |
1141 | |
1142 | static int amdgpu_display_gem_fb_verify_and_init(struct drm_device *dev, |
1143 | struct amdgpu_framebuffer *rfb, |
1144 | struct drm_file *file_priv, |
1145 | const struct drm_mode_fb_cmd2 *mode_cmd, |
1146 | struct drm_gem_object *obj) |
1147 | { |
1148 | int ret; |
1149 | |
1150 | rfb->base.obj[0] = obj; |
1151 | drm_helper_mode_fill_fb_struct(dev, fb: &rfb->base, mode_cmd); |
1152 | /* Verify that the modifier is supported. */ |
1153 | if (!drm_any_plane_has_format(dev, format: mode_cmd->pixel_format, |
1154 | modifier: mode_cmd->modifier[0])) { |
1155 | drm_dbg_kms(dev, |
1156 | "unsupported pixel format %p4cc / modifier 0x%llx\n" , |
1157 | &mode_cmd->pixel_format, mode_cmd->modifier[0]); |
1158 | |
1159 | ret = -EINVAL; |
1160 | goto err; |
1161 | } |
1162 | |
1163 | ret = amdgpu_display_framebuffer_init(dev, rfb, mode_cmd, obj); |
1164 | if (ret) |
1165 | goto err; |
1166 | |
1167 | if (drm_drv_uses_atomic_modeset(dev)) |
1168 | ret = drm_framebuffer_init(dev, fb: &rfb->base, |
1169 | funcs: &amdgpu_fb_funcs_atomic); |
1170 | else |
1171 | ret = drm_framebuffer_init(dev, fb: &rfb->base, funcs: &amdgpu_fb_funcs); |
1172 | |
1173 | if (ret) |
1174 | goto err; |
1175 | |
1176 | return 0; |
1177 | err: |
1178 | drm_dbg_kms(dev, "Failed to verify and init gem fb: %d\n" , ret); |
1179 | rfb->base.obj[0] = NULL; |
1180 | return ret; |
1181 | } |
1182 | |
1183 | static int amdgpu_display_framebuffer_init(struct drm_device *dev, |
1184 | struct amdgpu_framebuffer *rfb, |
1185 | const struct drm_mode_fb_cmd2 *mode_cmd, |
1186 | struct drm_gem_object *obj) |
1187 | { |
1188 | struct amdgpu_device *adev = drm_to_adev(ddev: dev); |
1189 | int ret, i; |
1190 | |
1191 | /* |
1192 | * This needs to happen before modifier conversion as that might change |
1193 | * the number of planes. |
1194 | */ |
1195 | for (i = 1; i < rfb->base.format->num_planes; ++i) { |
1196 | if (mode_cmd->handles[i] != mode_cmd->handles[0]) { |
1197 | drm_dbg_kms(dev, "Plane 0 and %d have different BOs: %u vs. %u\n" , |
1198 | i, mode_cmd->handles[0], mode_cmd->handles[i]); |
1199 | ret = -EINVAL; |
1200 | return ret; |
1201 | } |
1202 | } |
1203 | |
1204 | ret = amdgpu_display_get_fb_info(amdgpu_fb: rfb, tiling_flags: &rfb->tiling_flags, tmz_surface: &rfb->tmz_surface); |
1205 | if (ret) |
1206 | return ret; |
1207 | |
1208 | if (dev->mode_config.fb_modifiers_not_supported && !adev->enable_virtual_display) { |
1209 | drm_WARN_ONCE(dev, adev->family >= AMDGPU_FAMILY_AI, |
1210 | "GFX9+ requires FB check based on format modifier\n" ); |
1211 | ret = check_tiling_flags_gfx6(afb: rfb); |
1212 | if (ret) |
1213 | return ret; |
1214 | } |
1215 | |
1216 | if (!dev->mode_config.fb_modifiers_not_supported && |
1217 | !(rfb->base.flags & DRM_MODE_FB_MODIFIERS)) { |
1218 | ret = convert_tiling_flags_to_modifier(afb: rfb); |
1219 | if (ret) { |
1220 | drm_dbg_kms(dev, "Failed to convert tiling flags 0x%llX to a modifier" , |
1221 | rfb->tiling_flags); |
1222 | return ret; |
1223 | } |
1224 | } |
1225 | |
1226 | ret = amdgpu_display_verify_sizes(rfb); |
1227 | if (ret) |
1228 | return ret; |
1229 | |
1230 | for (i = 0; i < rfb->base.format->num_planes; ++i) { |
1231 | drm_gem_object_get(obj: rfb->base.obj[0]); |
1232 | rfb->base.obj[i] = rfb->base.obj[0]; |
1233 | } |
1234 | |
1235 | return 0; |
1236 | } |
1237 | |
1238 | struct drm_framebuffer * |
1239 | amdgpu_display_user_framebuffer_create(struct drm_device *dev, |
1240 | struct drm_file *file_priv, |
1241 | const struct drm_mode_fb_cmd2 *mode_cmd) |
1242 | { |
1243 | struct amdgpu_framebuffer *amdgpu_fb; |
1244 | struct drm_gem_object *obj; |
1245 | struct amdgpu_bo *bo; |
1246 | uint32_t domains; |
1247 | int ret; |
1248 | |
1249 | obj = drm_gem_object_lookup(filp: file_priv, handle: mode_cmd->handles[0]); |
1250 | if (obj == NULL) { |
1251 | drm_dbg_kms(dev, |
1252 | "No GEM object associated to handle 0x%08X, can't create framebuffer\n" , |
1253 | mode_cmd->handles[0]); |
1254 | |
1255 | return ERR_PTR(error: -ENOENT); |
1256 | } |
1257 | |
1258 | /* Handle is imported dma-buf, so cannot be migrated to VRAM for scanout */ |
1259 | bo = gem_to_amdgpu_bo(obj); |
1260 | domains = amdgpu_display_supported_domains(adev: drm_to_adev(ddev: dev), bo_flags: bo->flags); |
1261 | if (obj->import_attach && !(domains & AMDGPU_GEM_DOMAIN_GTT)) { |
1262 | drm_dbg_kms(dev, "Cannot create framebuffer from imported dma_buf\n" ); |
1263 | drm_gem_object_put(obj); |
1264 | return ERR_PTR(error: -EINVAL); |
1265 | } |
1266 | |
1267 | amdgpu_fb = kzalloc(size: sizeof(*amdgpu_fb), GFP_KERNEL); |
1268 | if (amdgpu_fb == NULL) { |
1269 | drm_gem_object_put(obj); |
1270 | return ERR_PTR(error: -ENOMEM); |
1271 | } |
1272 | |
1273 | ret = amdgpu_display_gem_fb_verify_and_init(dev, rfb: amdgpu_fb, file_priv, |
1274 | mode_cmd, obj); |
1275 | if (ret) { |
1276 | kfree(objp: amdgpu_fb); |
1277 | drm_gem_object_put(obj); |
1278 | return ERR_PTR(error: ret); |
1279 | } |
1280 | |
1281 | drm_gem_object_put(obj); |
1282 | return &amdgpu_fb->base; |
1283 | } |
1284 | |
1285 | const struct drm_mode_config_funcs amdgpu_mode_funcs = { |
1286 | .fb_create = amdgpu_display_user_framebuffer_create, |
1287 | }; |
1288 | |
1289 | static const struct drm_prop_enum_list amdgpu_underscan_enum_list[] = { |
1290 | { UNDERSCAN_OFF, "off" }, |
1291 | { UNDERSCAN_ON, "on" }, |
1292 | { UNDERSCAN_AUTO, "auto" }, |
1293 | }; |
1294 | |
1295 | static const struct drm_prop_enum_list amdgpu_audio_enum_list[] = { |
1296 | { AMDGPU_AUDIO_DISABLE, "off" }, |
1297 | { AMDGPU_AUDIO_ENABLE, "on" }, |
1298 | { AMDGPU_AUDIO_AUTO, "auto" }, |
1299 | }; |
1300 | |
1301 | /* XXX support different dither options? spatial, temporal, both, etc. */ |
1302 | static const struct drm_prop_enum_list amdgpu_dither_enum_list[] = { |
1303 | { AMDGPU_FMT_DITHER_DISABLE, "off" }, |
1304 | { AMDGPU_FMT_DITHER_ENABLE, "on" }, |
1305 | }; |
1306 | |
1307 | int amdgpu_display_modeset_create_props(struct amdgpu_device *adev) |
1308 | { |
1309 | int sz; |
1310 | |
1311 | adev->mode_info.coherent_mode_property = |
1312 | drm_property_create_range(dev: adev_to_drm(adev), flags: 0, name: "coherent" , min: 0, max: 1); |
1313 | if (!adev->mode_info.coherent_mode_property) |
1314 | return -ENOMEM; |
1315 | |
1316 | adev->mode_info.load_detect_property = |
1317 | drm_property_create_range(dev: adev_to_drm(adev), flags: 0, name: "load detection" , min: 0, max: 1); |
1318 | if (!adev->mode_info.load_detect_property) |
1319 | return -ENOMEM; |
1320 | |
1321 | drm_mode_create_scaling_mode_property(dev: adev_to_drm(adev)); |
1322 | |
1323 | sz = ARRAY_SIZE(amdgpu_underscan_enum_list); |
1324 | adev->mode_info.underscan_property = |
1325 | drm_property_create_enum(dev: adev_to_drm(adev), flags: 0, |
1326 | name: "underscan" , |
1327 | props: amdgpu_underscan_enum_list, num_values: sz); |
1328 | |
1329 | adev->mode_info.underscan_hborder_property = |
1330 | drm_property_create_range(dev: adev_to_drm(adev), flags: 0, |
1331 | name: "underscan hborder" , min: 0, max: 128); |
1332 | if (!adev->mode_info.underscan_hborder_property) |
1333 | return -ENOMEM; |
1334 | |
1335 | adev->mode_info.underscan_vborder_property = |
1336 | drm_property_create_range(dev: adev_to_drm(adev), flags: 0, |
1337 | name: "underscan vborder" , min: 0, max: 128); |
1338 | if (!adev->mode_info.underscan_vborder_property) |
1339 | return -ENOMEM; |
1340 | |
1341 | sz = ARRAY_SIZE(amdgpu_audio_enum_list); |
1342 | adev->mode_info.audio_property = |
1343 | drm_property_create_enum(dev: adev_to_drm(adev), flags: 0, |
1344 | name: "audio" , |
1345 | props: amdgpu_audio_enum_list, num_values: sz); |
1346 | |
1347 | sz = ARRAY_SIZE(amdgpu_dither_enum_list); |
1348 | adev->mode_info.dither_property = |
1349 | drm_property_create_enum(dev: adev_to_drm(adev), flags: 0, |
1350 | name: "dither" , |
1351 | props: amdgpu_dither_enum_list, num_values: sz); |
1352 | |
1353 | return 0; |
1354 | } |
1355 | |
1356 | void amdgpu_display_update_priority(struct amdgpu_device *adev) |
1357 | { |
1358 | /* adjustment options for the display watermarks */ |
1359 | if ((amdgpu_disp_priority == 0) || (amdgpu_disp_priority > 2)) |
1360 | adev->mode_info.disp_priority = 0; |
1361 | else |
1362 | adev->mode_info.disp_priority = amdgpu_disp_priority; |
1363 | |
1364 | } |
1365 | |
1366 | static bool amdgpu_display_is_hdtv_mode(const struct drm_display_mode *mode) |
1367 | { |
1368 | /* try and guess if this is a tv or a monitor */ |
1369 | if ((mode->vdisplay == 480 && mode->hdisplay == 720) || /* 480p */ |
1370 | (mode->vdisplay == 576) || /* 576p */ |
1371 | (mode->vdisplay == 720) || /* 720p */ |
1372 | (mode->vdisplay == 1080)) /* 1080p */ |
1373 | return true; |
1374 | else |
1375 | return false; |
1376 | } |
1377 | |
1378 | bool amdgpu_display_crtc_scaling_mode_fixup(struct drm_crtc *crtc, |
1379 | const struct drm_display_mode *mode, |
1380 | struct drm_display_mode *adjusted_mode) |
1381 | { |
1382 | struct drm_device *dev = crtc->dev; |
1383 | struct drm_encoder *encoder; |
1384 | struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); |
1385 | struct amdgpu_encoder *amdgpu_encoder; |
1386 | struct drm_connector *connector; |
1387 | u32 src_v = 1, dst_v = 1; |
1388 | u32 src_h = 1, dst_h = 1; |
1389 | |
1390 | amdgpu_crtc->h_border = 0; |
1391 | amdgpu_crtc->v_border = 0; |
1392 | |
1393 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
1394 | if (encoder->crtc != crtc) |
1395 | continue; |
1396 | amdgpu_encoder = to_amdgpu_encoder(encoder); |
1397 | connector = amdgpu_get_connector_for_encoder(encoder); |
1398 | |
1399 | /* set scaling */ |
1400 | if (amdgpu_encoder->rmx_type == RMX_OFF) |
1401 | amdgpu_crtc->rmx_type = RMX_OFF; |
1402 | else if (mode->hdisplay < amdgpu_encoder->native_mode.hdisplay || |
1403 | mode->vdisplay < amdgpu_encoder->native_mode.vdisplay) |
1404 | amdgpu_crtc->rmx_type = amdgpu_encoder->rmx_type; |
1405 | else |
1406 | amdgpu_crtc->rmx_type = RMX_OFF; |
1407 | /* copy native mode */ |
1408 | memcpy(&amdgpu_crtc->native_mode, |
1409 | &amdgpu_encoder->native_mode, |
1410 | sizeof(struct drm_display_mode)); |
1411 | src_v = crtc->mode.vdisplay; |
1412 | dst_v = amdgpu_crtc->native_mode.vdisplay; |
1413 | src_h = crtc->mode.hdisplay; |
1414 | dst_h = amdgpu_crtc->native_mode.hdisplay; |
1415 | |
1416 | /* fix up for overscan on hdmi */ |
1417 | if ((!(mode->flags & DRM_MODE_FLAG_INTERLACE)) && |
1418 | ((amdgpu_encoder->underscan_type == UNDERSCAN_ON) || |
1419 | ((amdgpu_encoder->underscan_type == UNDERSCAN_AUTO) && |
1420 | connector->display_info.is_hdmi && |
1421 | amdgpu_display_is_hdtv_mode(mode)))) { |
1422 | if (amdgpu_encoder->underscan_hborder != 0) |
1423 | amdgpu_crtc->h_border = amdgpu_encoder->underscan_hborder; |
1424 | else |
1425 | amdgpu_crtc->h_border = (mode->hdisplay >> 5) + 16; |
1426 | if (amdgpu_encoder->underscan_vborder != 0) |
1427 | amdgpu_crtc->v_border = amdgpu_encoder->underscan_vborder; |
1428 | else |
1429 | amdgpu_crtc->v_border = (mode->vdisplay >> 5) + 16; |
1430 | amdgpu_crtc->rmx_type = RMX_FULL; |
1431 | src_v = crtc->mode.vdisplay; |
1432 | dst_v = crtc->mode.vdisplay - (amdgpu_crtc->v_border * 2); |
1433 | src_h = crtc->mode.hdisplay; |
1434 | dst_h = crtc->mode.hdisplay - (amdgpu_crtc->h_border * 2); |
1435 | } |
1436 | } |
1437 | if (amdgpu_crtc->rmx_type != RMX_OFF) { |
1438 | fixed20_12 a, b; |
1439 | |
1440 | a.full = dfixed_const(src_v); |
1441 | b.full = dfixed_const(dst_v); |
1442 | amdgpu_crtc->vsc.full = dfixed_div(A: a, B: b); |
1443 | a.full = dfixed_const(src_h); |
1444 | b.full = dfixed_const(dst_h); |
1445 | amdgpu_crtc->hsc.full = dfixed_div(A: a, B: b); |
1446 | } else { |
1447 | amdgpu_crtc->vsc.full = dfixed_const(1); |
1448 | amdgpu_crtc->hsc.full = dfixed_const(1); |
1449 | } |
1450 | return true; |
1451 | } |
1452 | |
1453 | /* |
1454 | * Retrieve current video scanout position of crtc on a given gpu, and |
1455 | * an optional accurate timestamp of when query happened. |
1456 | * |
1457 | * \param dev Device to query. |
1458 | * \param pipe Crtc to query. |
1459 | * \param flags from caller (DRM_CALLED_FROM_VBLIRQ or 0). |
1460 | * For driver internal use only also supports these flags: |
1461 | * |
1462 | * USE_REAL_VBLANKSTART to use the real start of vblank instead |
1463 | * of a fudged earlier start of vblank. |
1464 | * |
1465 | * GET_DISTANCE_TO_VBLANKSTART to return distance to the |
1466 | * fudged earlier start of vblank in *vpos and the distance |
1467 | * to true start of vblank in *hpos. |
1468 | * |
1469 | * \param *vpos Location where vertical scanout position should be stored. |
1470 | * \param *hpos Location where horizontal scanout position should go. |
1471 | * \param *stime Target location for timestamp taken immediately before |
1472 | * scanout position query. Can be NULL to skip timestamp. |
1473 | * \param *etime Target location for timestamp taken immediately after |
1474 | * scanout position query. Can be NULL to skip timestamp. |
1475 | * |
1476 | * Returns vpos as a positive number while in active scanout area. |
1477 | * Returns vpos as a negative number inside vblank, counting the number |
1478 | * of scanlines to go until end of vblank, e.g., -1 means "one scanline |
1479 | * until start of active scanout / end of vblank." |
1480 | * |
1481 | * \return Flags, or'ed together as follows: |
1482 | * |
1483 | * DRM_SCANOUTPOS_VALID = Query successful. |
1484 | * DRM_SCANOUTPOS_INVBL = Inside vblank. |
1485 | * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of |
1486 | * this flag means that returned position may be offset by a constant but |
1487 | * unknown small number of scanlines wrt. real scanout position. |
1488 | * |
1489 | */ |
1490 | int amdgpu_display_get_crtc_scanoutpos(struct drm_device *dev, |
1491 | unsigned int pipe, unsigned int flags, int *vpos, |
1492 | int *hpos, ktime_t *stime, ktime_t *etime, |
1493 | const struct drm_display_mode *mode) |
1494 | { |
1495 | u32 vbl = 0, position = 0; |
1496 | int vbl_start, vbl_end, vtotal, ret = 0; |
1497 | bool in_vbl = true; |
1498 | |
1499 | struct amdgpu_device *adev = drm_to_adev(ddev: dev); |
1500 | |
1501 | /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */ |
1502 | |
1503 | /* Get optional system timestamp before query. */ |
1504 | if (stime) |
1505 | *stime = ktime_get(); |
1506 | |
1507 | if (amdgpu_display_page_flip_get_scanoutpos(adev, pipe, &vbl, &position) == 0) |
1508 | ret |= DRM_SCANOUTPOS_VALID; |
1509 | |
1510 | /* Get optional system timestamp after query. */ |
1511 | if (etime) |
1512 | *etime = ktime_get(); |
1513 | |
1514 | /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */ |
1515 | |
1516 | /* Decode into vertical and horizontal scanout position. */ |
1517 | *vpos = position & 0x1fff; |
1518 | *hpos = (position >> 16) & 0x1fff; |
1519 | |
1520 | /* Valid vblank area boundaries from gpu retrieved? */ |
1521 | if (vbl > 0) { |
1522 | /* Yes: Decode. */ |
1523 | ret |= DRM_SCANOUTPOS_ACCURATE; |
1524 | vbl_start = vbl & 0x1fff; |
1525 | vbl_end = (vbl >> 16) & 0x1fff; |
1526 | } else { |
1527 | /* No: Fake something reasonable which gives at least ok results. */ |
1528 | vbl_start = mode->crtc_vdisplay; |
1529 | vbl_end = 0; |
1530 | } |
1531 | |
1532 | /* Called from driver internal vblank counter query code? */ |
1533 | if (flags & GET_DISTANCE_TO_VBLANKSTART) { |
1534 | /* Caller wants distance from real vbl_start in *hpos */ |
1535 | *hpos = *vpos - vbl_start; |
1536 | } |
1537 | |
1538 | /* Fudge vblank to start a few scanlines earlier to handle the |
1539 | * problem that vblank irqs fire a few scanlines before start |
1540 | * of vblank. Some driver internal callers need the true vblank |
1541 | * start to be used and signal this via the USE_REAL_VBLANKSTART flag. |
1542 | * |
1543 | * The cause of the "early" vblank irq is that the irq is triggered |
1544 | * by the line buffer logic when the line buffer read position enters |
1545 | * the vblank, whereas our crtc scanout position naturally lags the |
1546 | * line buffer read position. |
1547 | */ |
1548 | if (!(flags & USE_REAL_VBLANKSTART)) |
1549 | vbl_start -= adev->mode_info.crtcs[pipe]->lb_vblank_lead_lines; |
1550 | |
1551 | /* Test scanout position against vblank region. */ |
1552 | if ((*vpos < vbl_start) && (*vpos >= vbl_end)) |
1553 | in_vbl = false; |
1554 | |
1555 | /* In vblank? */ |
1556 | if (in_vbl) |
1557 | ret |= DRM_SCANOUTPOS_IN_VBLANK; |
1558 | |
1559 | /* Called from driver internal vblank counter query code? */ |
1560 | if (flags & GET_DISTANCE_TO_VBLANKSTART) { |
1561 | /* Caller wants distance from fudged earlier vbl_start */ |
1562 | *vpos -= vbl_start; |
1563 | return ret; |
1564 | } |
1565 | |
1566 | /* Check if inside vblank area and apply corrective offsets: |
1567 | * vpos will then be >=0 in video scanout area, but negative |
1568 | * within vblank area, counting down the number of lines until |
1569 | * start of scanout. |
1570 | */ |
1571 | |
1572 | /* Inside "upper part" of vblank area? Apply corrective offset if so: */ |
1573 | if (in_vbl && (*vpos >= vbl_start)) { |
1574 | vtotal = mode->crtc_vtotal; |
1575 | |
1576 | /* With variable refresh rate displays the vpos can exceed |
1577 | * the vtotal value. Clamp to 0 to return -vbl_end instead |
1578 | * of guessing the remaining number of lines until scanout. |
1579 | */ |
1580 | *vpos = (*vpos < vtotal) ? (*vpos - vtotal) : 0; |
1581 | } |
1582 | |
1583 | /* Correct for shifted end of vbl at vbl_end. */ |
1584 | *vpos = *vpos - vbl_end; |
1585 | |
1586 | return ret; |
1587 | } |
1588 | |
1589 | int amdgpu_display_crtc_idx_to_irq_type(struct amdgpu_device *adev, int crtc) |
1590 | { |
1591 | if (crtc < 0 || crtc >= adev->mode_info.num_crtc) |
1592 | return AMDGPU_CRTC_IRQ_NONE; |
1593 | |
1594 | switch (crtc) { |
1595 | case 0: |
1596 | return AMDGPU_CRTC_IRQ_VBLANK1; |
1597 | case 1: |
1598 | return AMDGPU_CRTC_IRQ_VBLANK2; |
1599 | case 2: |
1600 | return AMDGPU_CRTC_IRQ_VBLANK3; |
1601 | case 3: |
1602 | return AMDGPU_CRTC_IRQ_VBLANK4; |
1603 | case 4: |
1604 | return AMDGPU_CRTC_IRQ_VBLANK5; |
1605 | case 5: |
1606 | return AMDGPU_CRTC_IRQ_VBLANK6; |
1607 | default: |
1608 | return AMDGPU_CRTC_IRQ_NONE; |
1609 | } |
1610 | } |
1611 | |
1612 | bool amdgpu_crtc_get_scanout_position(struct drm_crtc *crtc, |
1613 | bool in_vblank_irq, int *vpos, |
1614 | int *hpos, ktime_t *stime, ktime_t *etime, |
1615 | const struct drm_display_mode *mode) |
1616 | { |
1617 | struct drm_device *dev = crtc->dev; |
1618 | unsigned int pipe = crtc->index; |
1619 | |
1620 | return amdgpu_display_get_crtc_scanoutpos(dev, pipe, flags: 0, vpos, hpos, |
1621 | stime, etime, mode); |
1622 | } |
1623 | |
1624 | static bool |
1625 | amdgpu_display_robj_is_fb(struct amdgpu_device *adev, struct amdgpu_bo *robj) |
1626 | { |
1627 | struct drm_device *dev = adev_to_drm(adev); |
1628 | struct drm_fb_helper *fb_helper = dev->fb_helper; |
1629 | |
1630 | if (!fb_helper || !fb_helper->buffer) |
1631 | return false; |
1632 | |
1633 | if (gem_to_amdgpu_bo(fb_helper->buffer->gem) != robj) |
1634 | return false; |
1635 | |
1636 | return true; |
1637 | } |
1638 | |
1639 | int amdgpu_display_suspend_helper(struct amdgpu_device *adev) |
1640 | { |
1641 | struct drm_device *dev = adev_to_drm(adev); |
1642 | struct drm_crtc *crtc; |
1643 | struct drm_connector *connector; |
1644 | struct drm_connector_list_iter iter; |
1645 | int r; |
1646 | |
1647 | drm_kms_helper_poll_disable(dev); |
1648 | |
1649 | /* turn off display hw */ |
1650 | drm_modeset_lock_all(dev); |
1651 | drm_connector_list_iter_begin(dev, iter: &iter); |
1652 | drm_for_each_connector_iter(connector, &iter) |
1653 | drm_helper_connector_dpms(connector, |
1654 | DRM_MODE_DPMS_OFF); |
1655 | drm_connector_list_iter_end(iter: &iter); |
1656 | drm_modeset_unlock_all(dev); |
1657 | /* unpin the front buffers and cursors */ |
1658 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
1659 | struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); |
1660 | struct drm_framebuffer *fb = crtc->primary->fb; |
1661 | struct amdgpu_bo *robj; |
1662 | |
1663 | if (amdgpu_crtc->cursor_bo && !adev->enable_virtual_display) { |
1664 | struct amdgpu_bo *aobj = gem_to_amdgpu_bo(amdgpu_crtc->cursor_bo); |
1665 | |
1666 | r = amdgpu_bo_reserve(bo: aobj, no_intr: true); |
1667 | if (r == 0) { |
1668 | amdgpu_bo_unpin(bo: aobj); |
1669 | amdgpu_bo_unreserve(bo: aobj); |
1670 | } |
1671 | } |
1672 | |
1673 | if (!fb || !fb->obj[0]) |
1674 | continue; |
1675 | |
1676 | robj = gem_to_amdgpu_bo(fb->obj[0]); |
1677 | if (!amdgpu_display_robj_is_fb(adev, robj)) { |
1678 | r = amdgpu_bo_reserve(bo: robj, no_intr: true); |
1679 | if (r == 0) { |
1680 | amdgpu_bo_unpin(bo: robj); |
1681 | amdgpu_bo_unreserve(bo: robj); |
1682 | } |
1683 | } |
1684 | } |
1685 | return 0; |
1686 | } |
1687 | |
1688 | int amdgpu_display_resume_helper(struct amdgpu_device *adev) |
1689 | { |
1690 | struct drm_device *dev = adev_to_drm(adev); |
1691 | struct drm_connector *connector; |
1692 | struct drm_connector_list_iter iter; |
1693 | struct drm_crtc *crtc; |
1694 | int r; |
1695 | |
1696 | /* pin cursors */ |
1697 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
1698 | struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); |
1699 | |
1700 | if (amdgpu_crtc->cursor_bo && !adev->enable_virtual_display) { |
1701 | struct amdgpu_bo *aobj = gem_to_amdgpu_bo(amdgpu_crtc->cursor_bo); |
1702 | |
1703 | r = amdgpu_bo_reserve(bo: aobj, no_intr: true); |
1704 | if (r == 0) { |
1705 | r = amdgpu_bo_pin(bo: aobj, AMDGPU_GEM_DOMAIN_VRAM); |
1706 | if (r != 0) |
1707 | dev_err(adev->dev, "Failed to pin cursor BO (%d)\n" , r); |
1708 | amdgpu_crtc->cursor_addr = amdgpu_bo_gpu_offset(bo: aobj); |
1709 | amdgpu_bo_unreserve(bo: aobj); |
1710 | } |
1711 | } |
1712 | } |
1713 | |
1714 | drm_helper_resume_force_mode(dev); |
1715 | |
1716 | /* turn on display hw */ |
1717 | drm_modeset_lock_all(dev); |
1718 | |
1719 | drm_connector_list_iter_begin(dev, iter: &iter); |
1720 | drm_for_each_connector_iter(connector, &iter) |
1721 | drm_helper_connector_dpms(connector, |
1722 | DRM_MODE_DPMS_ON); |
1723 | drm_connector_list_iter_end(iter: &iter); |
1724 | |
1725 | drm_modeset_unlock_all(dev); |
1726 | |
1727 | drm_kms_helper_poll_enable(dev); |
1728 | |
1729 | return 0; |
1730 | } |
1731 | |
1732 | |