162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2007-8 Advanced Micro Devices, Inc. 362306a36Sopenharmony_ci * Copyright 2008 Red Hat Inc. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 662306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 762306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 862306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 962306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 1062306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1362306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1862306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 1962306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 2062306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2162306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Authors: Dave Airlie 2462306a36Sopenharmony_ci * Alex Deucher 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <drm/amdgpu_drm.h> 2862306a36Sopenharmony_ci#include "amdgpu.h" 2962306a36Sopenharmony_ci#include "amdgpu_i2c.h" 3062306a36Sopenharmony_ci#include "atom.h" 3162306a36Sopenharmony_ci#include "amdgpu_connectors.h" 3262306a36Sopenharmony_ci#include "amdgpu_display.h" 3362306a36Sopenharmony_ci#include "soc15_common.h" 3462306a36Sopenharmony_ci#include "gc/gc_11_0_0_offset.h" 3562306a36Sopenharmony_ci#include "gc/gc_11_0_0_sh_mask.h" 3662306a36Sopenharmony_ci#include <asm/div64.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include <linux/pci.h> 3962306a36Sopenharmony_ci#include <linux/pm_runtime.h> 4062306a36Sopenharmony_ci#include <drm/drm_crtc_helper.h> 4162306a36Sopenharmony_ci#include <drm/drm_damage_helper.h> 4262306a36Sopenharmony_ci#include <drm/drm_drv.h> 4362306a36Sopenharmony_ci#include <drm/drm_edid.h> 4462306a36Sopenharmony_ci#include <drm/drm_fb_helper.h> 4562306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 4662306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 4762306a36Sopenharmony_ci#include <drm/drm_modeset_helper.h> 4862306a36Sopenharmony_ci#include <drm/drm_vblank.h> 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/** 5162306a36Sopenharmony_ci * amdgpu_display_hotplug_work_func - work handler for display hotplug event 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * @work: work struct pointer 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * This is the hotplug event work handler (all ASICs). 5662306a36Sopenharmony_ci * The work gets scheduled from the IRQ handler if there 5762306a36Sopenharmony_ci * was a hotplug interrupt. It walks through the connector table 5862306a36Sopenharmony_ci * and calls hotplug handler for each connector. After this, it sends 5962306a36Sopenharmony_ci * a DRM hotplug event to alert userspace. 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * This design approach is required in order to defer hotplug event handling 6262306a36Sopenharmony_ci * from the IRQ handler to a work handler because hotplug handler has to use 6362306a36Sopenharmony_ci * mutexes which cannot be locked in an IRQ handler (since &mutex_lock may 6462306a36Sopenharmony_ci * sleep). 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_civoid amdgpu_display_hotplug_work_func(struct work_struct *work) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct amdgpu_device *adev = container_of(work, struct amdgpu_device, 6962306a36Sopenharmony_ci hotplug_work.work); 7062306a36Sopenharmony_ci struct drm_device *dev = adev_to_drm(adev); 7162306a36Sopenharmony_ci struct drm_mode_config *mode_config = &dev->mode_config; 7262306a36Sopenharmony_ci struct drm_connector *connector; 7362306a36Sopenharmony_ci struct drm_connector_list_iter iter; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci mutex_lock(&mode_config->mutex); 7662306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &iter); 7762306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &iter) 7862306a36Sopenharmony_ci amdgpu_connector_hotplug(connector); 7962306a36Sopenharmony_ci drm_connector_list_iter_end(&iter); 8062306a36Sopenharmony_ci mutex_unlock(&mode_config->mutex); 8162306a36Sopenharmony_ci /* Just fire off a uevent and let userspace tell us what to do */ 8262306a36Sopenharmony_ci drm_helper_hpd_irq_event(dev); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int amdgpu_display_framebuffer_init(struct drm_device *dev, 8662306a36Sopenharmony_ci struct amdgpu_framebuffer *rfb, 8762306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd, 8862306a36Sopenharmony_ci struct drm_gem_object *obj); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void amdgpu_display_flip_callback(struct dma_fence *f, 9162306a36Sopenharmony_ci struct dma_fence_cb *cb) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct amdgpu_flip_work *work = 9462306a36Sopenharmony_ci container_of(cb, struct amdgpu_flip_work, cb); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci dma_fence_put(f); 9762306a36Sopenharmony_ci schedule_work(&work->flip_work.work); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic bool amdgpu_display_flip_handle_fence(struct amdgpu_flip_work *work, 10162306a36Sopenharmony_ci struct dma_fence **f) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct dma_fence *fence = *f; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (fence == NULL) 10662306a36Sopenharmony_ci return false; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci *f = NULL; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (!dma_fence_add_callback(fence, &work->cb, 11162306a36Sopenharmony_ci amdgpu_display_flip_callback)) 11262306a36Sopenharmony_ci return true; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci dma_fence_put(fence); 11562306a36Sopenharmony_ci return false; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic void amdgpu_display_flip_work_func(struct work_struct *__work) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct delayed_work *delayed_work = 12162306a36Sopenharmony_ci container_of(__work, struct delayed_work, work); 12262306a36Sopenharmony_ci struct amdgpu_flip_work *work = 12362306a36Sopenharmony_ci container_of(delayed_work, struct amdgpu_flip_work, flip_work); 12462306a36Sopenharmony_ci struct amdgpu_device *adev = work->adev; 12562306a36Sopenharmony_ci struct amdgpu_crtc *amdgpu_crtc = adev->mode_info.crtcs[work->crtc_id]; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci struct drm_crtc *crtc = &amdgpu_crtc->base; 12862306a36Sopenharmony_ci unsigned long flags; 12962306a36Sopenharmony_ci unsigned int i; 13062306a36Sopenharmony_ci int vpos, hpos; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci for (i = 0; i < work->shared_count; ++i) 13362306a36Sopenharmony_ci if (amdgpu_display_flip_handle_fence(work, &work->shared[i])) 13462306a36Sopenharmony_ci return; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Wait until we're out of the vertical blank period before the one 13762306a36Sopenharmony_ci * targeted by the flip 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci if (amdgpu_crtc->enabled && 14062306a36Sopenharmony_ci (amdgpu_display_get_crtc_scanoutpos(adev_to_drm(adev), work->crtc_id, 0, 14162306a36Sopenharmony_ci &vpos, &hpos, NULL, NULL, 14262306a36Sopenharmony_ci &crtc->hwmode) 14362306a36Sopenharmony_ci & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) == 14462306a36Sopenharmony_ci (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) && 14562306a36Sopenharmony_ci (int)(work->target_vblank - 14662306a36Sopenharmony_ci amdgpu_get_vblank_counter_kms(crtc)) > 0) { 14762306a36Sopenharmony_ci schedule_delayed_work(&work->flip_work, usecs_to_jiffies(1000)); 14862306a36Sopenharmony_ci return; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* We borrow the event spin lock for protecting flip_status */ 15262306a36Sopenharmony_ci spin_lock_irqsave(&crtc->dev->event_lock, flags); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* Do the flip (mmio) */ 15562306a36Sopenharmony_ci adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base, work->async); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* Set the flip status */ 15862306a36Sopenharmony_ci amdgpu_crtc->pflip_status = AMDGPU_FLIP_SUBMITTED; 15962306a36Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci drm_dbg_vbl(adev_to_drm(adev), 16362306a36Sopenharmony_ci "crtc:%d[%p], pflip_stat:AMDGPU_FLIP_SUBMITTED, work: %p,\n", 16462306a36Sopenharmony_ci amdgpu_crtc->crtc_id, amdgpu_crtc, work); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* 16962306a36Sopenharmony_ci * Handle unpin events outside the interrupt handler proper. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_cistatic void amdgpu_display_unpin_work_func(struct work_struct *__work) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct amdgpu_flip_work *work = 17462306a36Sopenharmony_ci container_of(__work, struct amdgpu_flip_work, unpin_work); 17562306a36Sopenharmony_ci int r; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* unpin of the old buffer */ 17862306a36Sopenharmony_ci r = amdgpu_bo_reserve(work->old_abo, true); 17962306a36Sopenharmony_ci if (likely(r == 0)) { 18062306a36Sopenharmony_ci amdgpu_bo_unpin(work->old_abo); 18162306a36Sopenharmony_ci amdgpu_bo_unreserve(work->old_abo); 18262306a36Sopenharmony_ci } else 18362306a36Sopenharmony_ci DRM_ERROR("failed to reserve buffer after flip\n"); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci amdgpu_bo_unref(&work->old_abo); 18662306a36Sopenharmony_ci kfree(work->shared); 18762306a36Sopenharmony_ci kfree(work); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ciint amdgpu_display_crtc_page_flip_target(struct drm_crtc *crtc, 19162306a36Sopenharmony_ci struct drm_framebuffer *fb, 19262306a36Sopenharmony_ci struct drm_pending_vblank_event *event, 19362306a36Sopenharmony_ci uint32_t page_flip_flags, uint32_t target, 19462306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 19762306a36Sopenharmony_ci struct amdgpu_device *adev = drm_to_adev(dev); 19862306a36Sopenharmony_ci struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); 19962306a36Sopenharmony_ci struct drm_gem_object *obj; 20062306a36Sopenharmony_ci struct amdgpu_flip_work *work; 20162306a36Sopenharmony_ci struct amdgpu_bo *new_abo; 20262306a36Sopenharmony_ci unsigned long flags; 20362306a36Sopenharmony_ci u64 tiling_flags; 20462306a36Sopenharmony_ci int i, r; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci work = kzalloc(sizeof(*work), GFP_KERNEL); 20762306a36Sopenharmony_ci if (work == NULL) 20862306a36Sopenharmony_ci return -ENOMEM; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci INIT_DELAYED_WORK(&work->flip_work, amdgpu_display_flip_work_func); 21162306a36Sopenharmony_ci INIT_WORK(&work->unpin_work, amdgpu_display_unpin_work_func); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci work->event = event; 21462306a36Sopenharmony_ci work->adev = adev; 21562306a36Sopenharmony_ci work->crtc_id = amdgpu_crtc->crtc_id; 21662306a36Sopenharmony_ci work->async = (page_flip_flags & DRM_MODE_PAGE_FLIP_ASYNC) != 0; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* schedule unpin of the old buffer */ 21962306a36Sopenharmony_ci obj = crtc->primary->fb->obj[0]; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* take a reference to the old object */ 22262306a36Sopenharmony_ci work->old_abo = gem_to_amdgpu_bo(obj); 22362306a36Sopenharmony_ci amdgpu_bo_ref(work->old_abo); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci obj = fb->obj[0]; 22662306a36Sopenharmony_ci new_abo = gem_to_amdgpu_bo(obj); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* pin the new buffer */ 22962306a36Sopenharmony_ci r = amdgpu_bo_reserve(new_abo, false); 23062306a36Sopenharmony_ci if (unlikely(r != 0)) { 23162306a36Sopenharmony_ci DRM_ERROR("failed to reserve new abo buffer before flip\n"); 23262306a36Sopenharmony_ci goto cleanup; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (!adev->enable_virtual_display) { 23662306a36Sopenharmony_ci r = amdgpu_bo_pin(new_abo, 23762306a36Sopenharmony_ci amdgpu_display_supported_domains(adev, new_abo->flags)); 23862306a36Sopenharmony_ci if (unlikely(r != 0)) { 23962306a36Sopenharmony_ci DRM_ERROR("failed to pin new abo buffer before flip\n"); 24062306a36Sopenharmony_ci goto unreserve; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci r = amdgpu_ttm_alloc_gart(&new_abo->tbo); 24562306a36Sopenharmony_ci if (unlikely(r != 0)) { 24662306a36Sopenharmony_ci DRM_ERROR("%p bind failed\n", new_abo); 24762306a36Sopenharmony_ci goto unpin; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci r = dma_resv_get_fences(new_abo->tbo.base.resv, DMA_RESV_USAGE_WRITE, 25162306a36Sopenharmony_ci &work->shared_count, 25262306a36Sopenharmony_ci &work->shared); 25362306a36Sopenharmony_ci if (unlikely(r != 0)) { 25462306a36Sopenharmony_ci DRM_ERROR("failed to get fences for buffer\n"); 25562306a36Sopenharmony_ci goto unpin; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci amdgpu_bo_get_tiling_flags(new_abo, &tiling_flags); 25962306a36Sopenharmony_ci amdgpu_bo_unreserve(new_abo); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (!adev->enable_virtual_display) 26262306a36Sopenharmony_ci work->base = amdgpu_bo_gpu_offset(new_abo); 26362306a36Sopenharmony_ci work->target_vblank = target - (uint32_t)drm_crtc_vblank_count(crtc) + 26462306a36Sopenharmony_ci amdgpu_get_vblank_counter_kms(crtc); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* we borrow the event spin lock for protecting flip_wrok */ 26762306a36Sopenharmony_ci spin_lock_irqsave(&crtc->dev->event_lock, flags); 26862306a36Sopenharmony_ci if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_NONE) { 26962306a36Sopenharmony_ci DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); 27062306a36Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 27162306a36Sopenharmony_ci r = -EBUSY; 27262306a36Sopenharmony_ci goto pflip_cleanup; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci amdgpu_crtc->pflip_status = AMDGPU_FLIP_PENDING; 27662306a36Sopenharmony_ci amdgpu_crtc->pflip_works = work; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci DRM_DEBUG_DRIVER("crtc:%d[%p], pflip_stat:AMDGPU_FLIP_PENDING, work: %p,\n", 28062306a36Sopenharmony_ci amdgpu_crtc->crtc_id, amdgpu_crtc, work); 28162306a36Sopenharmony_ci /* update crtc fb */ 28262306a36Sopenharmony_ci crtc->primary->fb = fb; 28362306a36Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 28462306a36Sopenharmony_ci amdgpu_display_flip_work_func(&work->flip_work.work); 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cipflip_cleanup: 28862306a36Sopenharmony_ci if (unlikely(amdgpu_bo_reserve(new_abo, false) != 0)) { 28962306a36Sopenharmony_ci DRM_ERROR("failed to reserve new abo in error path\n"); 29062306a36Sopenharmony_ci goto cleanup; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ciunpin: 29362306a36Sopenharmony_ci if (!adev->enable_virtual_display) 29462306a36Sopenharmony_ci amdgpu_bo_unpin(new_abo); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ciunreserve: 29762306a36Sopenharmony_ci amdgpu_bo_unreserve(new_abo); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cicleanup: 30062306a36Sopenharmony_ci amdgpu_bo_unref(&work->old_abo); 30162306a36Sopenharmony_ci for (i = 0; i < work->shared_count; ++i) 30262306a36Sopenharmony_ci dma_fence_put(work->shared[i]); 30362306a36Sopenharmony_ci kfree(work->shared); 30462306a36Sopenharmony_ci kfree(work); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return r; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ciint amdgpu_display_crtc_set_config(struct drm_mode_set *set, 31062306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct drm_device *dev; 31362306a36Sopenharmony_ci struct amdgpu_device *adev; 31462306a36Sopenharmony_ci struct drm_crtc *crtc; 31562306a36Sopenharmony_ci bool active = false; 31662306a36Sopenharmony_ci int ret; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (!set || !set->crtc) 31962306a36Sopenharmony_ci return -EINVAL; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci dev = set->crtc->dev; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci ret = pm_runtime_get_sync(dev->dev); 32462306a36Sopenharmony_ci if (ret < 0) 32562306a36Sopenharmony_ci goto out; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci ret = drm_crtc_helper_set_config(set, ctx); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 33062306a36Sopenharmony_ci if (crtc->enabled) 33162306a36Sopenharmony_ci active = true; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci adev = drm_to_adev(dev); 33662306a36Sopenharmony_ci /* if we have active crtcs and we don't have a power ref, 33762306a36Sopenharmony_ci * take the current one 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ci if (active && !adev->have_disp_power_ref) { 34062306a36Sopenharmony_ci adev->have_disp_power_ref = true; 34162306a36Sopenharmony_ci return ret; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci /* if we have no active crtcs, then go to 34462306a36Sopenharmony_ci * drop the power ref we got before 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci if (!active && adev->have_disp_power_ref) 34762306a36Sopenharmony_ci adev->have_disp_power_ref = false; 34862306a36Sopenharmony_ciout: 34962306a36Sopenharmony_ci /* drop the power reference we got coming in here */ 35062306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 35162306a36Sopenharmony_ci return ret; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic const char *encoder_names[41] = { 35562306a36Sopenharmony_ci "NONE", 35662306a36Sopenharmony_ci "INTERNAL_LVDS", 35762306a36Sopenharmony_ci "INTERNAL_TMDS1", 35862306a36Sopenharmony_ci "INTERNAL_TMDS2", 35962306a36Sopenharmony_ci "INTERNAL_DAC1", 36062306a36Sopenharmony_ci "INTERNAL_DAC2", 36162306a36Sopenharmony_ci "INTERNAL_SDVOA", 36262306a36Sopenharmony_ci "INTERNAL_SDVOB", 36362306a36Sopenharmony_ci "SI170B", 36462306a36Sopenharmony_ci "CH7303", 36562306a36Sopenharmony_ci "CH7301", 36662306a36Sopenharmony_ci "INTERNAL_DVO1", 36762306a36Sopenharmony_ci "EXTERNAL_SDVOA", 36862306a36Sopenharmony_ci "EXTERNAL_SDVOB", 36962306a36Sopenharmony_ci "TITFP513", 37062306a36Sopenharmony_ci "INTERNAL_LVTM1", 37162306a36Sopenharmony_ci "VT1623", 37262306a36Sopenharmony_ci "HDMI_SI1930", 37362306a36Sopenharmony_ci "HDMI_INTERNAL", 37462306a36Sopenharmony_ci "INTERNAL_KLDSCP_TMDS1", 37562306a36Sopenharmony_ci "INTERNAL_KLDSCP_DVO1", 37662306a36Sopenharmony_ci "INTERNAL_KLDSCP_DAC1", 37762306a36Sopenharmony_ci "INTERNAL_KLDSCP_DAC2", 37862306a36Sopenharmony_ci "SI178", 37962306a36Sopenharmony_ci "MVPU_FPGA", 38062306a36Sopenharmony_ci "INTERNAL_DDI", 38162306a36Sopenharmony_ci "VT1625", 38262306a36Sopenharmony_ci "HDMI_SI1932", 38362306a36Sopenharmony_ci "DP_AN9801", 38462306a36Sopenharmony_ci "DP_DP501", 38562306a36Sopenharmony_ci "INTERNAL_UNIPHY", 38662306a36Sopenharmony_ci "INTERNAL_KLDSCP_LVTMA", 38762306a36Sopenharmony_ci "INTERNAL_UNIPHY1", 38862306a36Sopenharmony_ci "INTERNAL_UNIPHY2", 38962306a36Sopenharmony_ci "NUTMEG", 39062306a36Sopenharmony_ci "TRAVIS", 39162306a36Sopenharmony_ci "INTERNAL_VCE", 39262306a36Sopenharmony_ci "INTERNAL_UNIPHY3", 39362306a36Sopenharmony_ci "HDMI_ANX9805", 39462306a36Sopenharmony_ci "INTERNAL_AMCLK", 39562306a36Sopenharmony_ci "VIRTUAL", 39662306a36Sopenharmony_ci}; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic const char *hpd_names[6] = { 39962306a36Sopenharmony_ci "HPD1", 40062306a36Sopenharmony_ci "HPD2", 40162306a36Sopenharmony_ci "HPD3", 40262306a36Sopenharmony_ci "HPD4", 40362306a36Sopenharmony_ci "HPD5", 40462306a36Sopenharmony_ci "HPD6", 40562306a36Sopenharmony_ci}; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_civoid amdgpu_display_print_display_setup(struct drm_device *dev) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct drm_connector *connector; 41062306a36Sopenharmony_ci struct amdgpu_connector *amdgpu_connector; 41162306a36Sopenharmony_ci struct drm_encoder *encoder; 41262306a36Sopenharmony_ci struct amdgpu_encoder *amdgpu_encoder; 41362306a36Sopenharmony_ci struct drm_connector_list_iter iter; 41462306a36Sopenharmony_ci uint32_t devices; 41562306a36Sopenharmony_ci int i = 0; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &iter); 41862306a36Sopenharmony_ci DRM_INFO("AMDGPU Display Connectors\n"); 41962306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &iter) { 42062306a36Sopenharmony_ci amdgpu_connector = to_amdgpu_connector(connector); 42162306a36Sopenharmony_ci DRM_INFO("Connector %d:\n", i); 42262306a36Sopenharmony_ci DRM_INFO(" %s\n", connector->name); 42362306a36Sopenharmony_ci if (amdgpu_connector->hpd.hpd != AMDGPU_HPD_NONE) 42462306a36Sopenharmony_ci DRM_INFO(" %s\n", hpd_names[amdgpu_connector->hpd.hpd]); 42562306a36Sopenharmony_ci if (amdgpu_connector->ddc_bus) { 42662306a36Sopenharmony_ci DRM_INFO(" DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", 42762306a36Sopenharmony_ci amdgpu_connector->ddc_bus->rec.mask_clk_reg, 42862306a36Sopenharmony_ci amdgpu_connector->ddc_bus->rec.mask_data_reg, 42962306a36Sopenharmony_ci amdgpu_connector->ddc_bus->rec.a_clk_reg, 43062306a36Sopenharmony_ci amdgpu_connector->ddc_bus->rec.a_data_reg, 43162306a36Sopenharmony_ci amdgpu_connector->ddc_bus->rec.en_clk_reg, 43262306a36Sopenharmony_ci amdgpu_connector->ddc_bus->rec.en_data_reg, 43362306a36Sopenharmony_ci amdgpu_connector->ddc_bus->rec.y_clk_reg, 43462306a36Sopenharmony_ci amdgpu_connector->ddc_bus->rec.y_data_reg); 43562306a36Sopenharmony_ci if (amdgpu_connector->router.ddc_valid) 43662306a36Sopenharmony_ci DRM_INFO(" DDC Router 0x%x/0x%x\n", 43762306a36Sopenharmony_ci amdgpu_connector->router.ddc_mux_control_pin, 43862306a36Sopenharmony_ci amdgpu_connector->router.ddc_mux_state); 43962306a36Sopenharmony_ci if (amdgpu_connector->router.cd_valid) 44062306a36Sopenharmony_ci DRM_INFO(" Clock/Data Router 0x%x/0x%x\n", 44162306a36Sopenharmony_ci amdgpu_connector->router.cd_mux_control_pin, 44262306a36Sopenharmony_ci amdgpu_connector->router.cd_mux_state); 44362306a36Sopenharmony_ci } else { 44462306a36Sopenharmony_ci if (connector->connector_type == DRM_MODE_CONNECTOR_VGA || 44562306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_DVII || 44662306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_DVID || 44762306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_DVIA || 44862306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || 44962306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) 45062306a36Sopenharmony_ci DRM_INFO(" DDC: no ddc bus - possible BIOS bug - please report to xorg-driver-ati@lists.x.org\n"); 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci DRM_INFO(" Encoders:\n"); 45362306a36Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 45462306a36Sopenharmony_ci amdgpu_encoder = to_amdgpu_encoder(encoder); 45562306a36Sopenharmony_ci devices = amdgpu_encoder->devices & amdgpu_connector->devices; 45662306a36Sopenharmony_ci if (devices) { 45762306a36Sopenharmony_ci if (devices & ATOM_DEVICE_CRT1_SUPPORT) 45862306a36Sopenharmony_ci DRM_INFO(" CRT1: %s\n", encoder_names[amdgpu_encoder->encoder_id]); 45962306a36Sopenharmony_ci if (devices & ATOM_DEVICE_CRT2_SUPPORT) 46062306a36Sopenharmony_ci DRM_INFO(" CRT2: %s\n", encoder_names[amdgpu_encoder->encoder_id]); 46162306a36Sopenharmony_ci if (devices & ATOM_DEVICE_LCD1_SUPPORT) 46262306a36Sopenharmony_ci DRM_INFO(" LCD1: %s\n", encoder_names[amdgpu_encoder->encoder_id]); 46362306a36Sopenharmony_ci if (devices & ATOM_DEVICE_DFP1_SUPPORT) 46462306a36Sopenharmony_ci DRM_INFO(" DFP1: %s\n", encoder_names[amdgpu_encoder->encoder_id]); 46562306a36Sopenharmony_ci if (devices & ATOM_DEVICE_DFP2_SUPPORT) 46662306a36Sopenharmony_ci DRM_INFO(" DFP2: %s\n", encoder_names[amdgpu_encoder->encoder_id]); 46762306a36Sopenharmony_ci if (devices & ATOM_DEVICE_DFP3_SUPPORT) 46862306a36Sopenharmony_ci DRM_INFO(" DFP3: %s\n", encoder_names[amdgpu_encoder->encoder_id]); 46962306a36Sopenharmony_ci if (devices & ATOM_DEVICE_DFP4_SUPPORT) 47062306a36Sopenharmony_ci DRM_INFO(" DFP4: %s\n", encoder_names[amdgpu_encoder->encoder_id]); 47162306a36Sopenharmony_ci if (devices & ATOM_DEVICE_DFP5_SUPPORT) 47262306a36Sopenharmony_ci DRM_INFO(" DFP5: %s\n", encoder_names[amdgpu_encoder->encoder_id]); 47362306a36Sopenharmony_ci if (devices & ATOM_DEVICE_DFP6_SUPPORT) 47462306a36Sopenharmony_ci DRM_INFO(" DFP6: %s\n", encoder_names[amdgpu_encoder->encoder_id]); 47562306a36Sopenharmony_ci if (devices & ATOM_DEVICE_TV1_SUPPORT) 47662306a36Sopenharmony_ci DRM_INFO(" TV1: %s\n", encoder_names[amdgpu_encoder->encoder_id]); 47762306a36Sopenharmony_ci if (devices & ATOM_DEVICE_CV_SUPPORT) 47862306a36Sopenharmony_ci DRM_INFO(" CV: %s\n", encoder_names[amdgpu_encoder->encoder_id]); 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci i++; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci drm_connector_list_iter_end(&iter); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cibool amdgpu_display_ddc_probe(struct amdgpu_connector *amdgpu_connector, 48762306a36Sopenharmony_ci bool use_aux) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci u8 out = 0x0; 49062306a36Sopenharmony_ci u8 buf[8]; 49162306a36Sopenharmony_ci int ret; 49262306a36Sopenharmony_ci struct i2c_msg msgs[] = { 49362306a36Sopenharmony_ci { 49462306a36Sopenharmony_ci .addr = DDC_ADDR, 49562306a36Sopenharmony_ci .flags = 0, 49662306a36Sopenharmony_ci .len = 1, 49762306a36Sopenharmony_ci .buf = &out, 49862306a36Sopenharmony_ci }, 49962306a36Sopenharmony_ci { 50062306a36Sopenharmony_ci .addr = DDC_ADDR, 50162306a36Sopenharmony_ci .flags = I2C_M_RD, 50262306a36Sopenharmony_ci .len = 8, 50362306a36Sopenharmony_ci .buf = buf, 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci }; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* on hw with routers, select right port */ 50862306a36Sopenharmony_ci if (amdgpu_connector->router.ddc_valid) 50962306a36Sopenharmony_ci amdgpu_i2c_router_select_ddc_port(amdgpu_connector); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (use_aux) 51262306a36Sopenharmony_ci ret = i2c_transfer(&amdgpu_connector->ddc_bus->aux.ddc, msgs, 2); 51362306a36Sopenharmony_ci else 51462306a36Sopenharmony_ci ret = i2c_transfer(&amdgpu_connector->ddc_bus->adapter, msgs, 2); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (ret != 2) 51762306a36Sopenharmony_ci /* Couldn't find an accessible DDC on this connector */ 51862306a36Sopenharmony_ci return false; 51962306a36Sopenharmony_ci /* Probe also for valid EDID header 52062306a36Sopenharmony_ci * EDID header starts with: 52162306a36Sopenharmony_ci * 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00. 52262306a36Sopenharmony_ci * Only the first 6 bytes must be valid as 52362306a36Sopenharmony_ci * drm_edid_block_valid() can fix the last 2 bytes 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_ci if (drm_edid_header_is_valid(buf) < 6) { 52662306a36Sopenharmony_ci /* Couldn't find an accessible EDID on this 52762306a36Sopenharmony_ci * connector 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_ci return false; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci return true; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int amdgpu_dirtyfb(struct drm_framebuffer *fb, struct drm_file *file, 53562306a36Sopenharmony_ci unsigned int flags, unsigned int color, 53662306a36Sopenharmony_ci struct drm_clip_rect *clips, unsigned int num_clips) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (file) 54062306a36Sopenharmony_ci return -ENOSYS; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return drm_atomic_helper_dirtyfb(fb, file, flags, color, clips, 54362306a36Sopenharmony_ci num_clips); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic const struct drm_framebuffer_funcs amdgpu_fb_funcs = { 54762306a36Sopenharmony_ci .destroy = drm_gem_fb_destroy, 54862306a36Sopenharmony_ci .create_handle = drm_gem_fb_create_handle, 54962306a36Sopenharmony_ci}; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic const struct drm_framebuffer_funcs amdgpu_fb_funcs_atomic = { 55262306a36Sopenharmony_ci .destroy = drm_gem_fb_destroy, 55362306a36Sopenharmony_ci .create_handle = drm_gem_fb_create_handle, 55462306a36Sopenharmony_ci .dirty = amdgpu_dirtyfb 55562306a36Sopenharmony_ci}; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ciuint32_t amdgpu_display_supported_domains(struct amdgpu_device *adev, 55862306a36Sopenharmony_ci uint64_t bo_flags) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci uint32_t domain = AMDGPU_GEM_DOMAIN_VRAM; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci#if defined(CONFIG_DRM_AMD_DC) 56362306a36Sopenharmony_ci /* 56462306a36Sopenharmony_ci * if amdgpu_bo_support_uswc returns false it means that USWC mappings 56562306a36Sopenharmony_ci * is not supported for this board. But this mapping is required 56662306a36Sopenharmony_ci * to avoid hang caused by placement of scanout BO in GTT on certain 56762306a36Sopenharmony_ci * APUs. So force the BO placement to VRAM in case this architecture 56862306a36Sopenharmony_ci * will not allow USWC mappings. 56962306a36Sopenharmony_ci * Also, don't allow GTT domain if the BO doesn't have USWC flag set. 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_ci if ((bo_flags & AMDGPU_GEM_CREATE_CPU_GTT_USWC) && 57262306a36Sopenharmony_ci amdgpu_bo_support_uswc(bo_flags) && 57362306a36Sopenharmony_ci adev->dc_enabled && 57462306a36Sopenharmony_ci adev->mode_info.gpu_vm_support) 57562306a36Sopenharmony_ci domain |= AMDGPU_GEM_DOMAIN_GTT; 57662306a36Sopenharmony_ci#endif 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci return domain; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic const struct drm_format_info dcc_formats[] = { 58262306a36Sopenharmony_ci { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 2, 58362306a36Sopenharmony_ci .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, 58462306a36Sopenharmony_ci { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 2, 58562306a36Sopenharmony_ci .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, 58662306a36Sopenharmony_ci { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 2, 58762306a36Sopenharmony_ci .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, 58862306a36Sopenharmony_ci .has_alpha = true, }, 58962306a36Sopenharmony_ci { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 2, 59062306a36Sopenharmony_ci .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, 59162306a36Sopenharmony_ci .has_alpha = true, }, 59262306a36Sopenharmony_ci { .format = DRM_FORMAT_BGRA8888, .depth = 32, .num_planes = 2, 59362306a36Sopenharmony_ci .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, 59462306a36Sopenharmony_ci .has_alpha = true, }, 59562306a36Sopenharmony_ci { .format = DRM_FORMAT_XRGB2101010, .depth = 30, .num_planes = 2, 59662306a36Sopenharmony_ci .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, 59762306a36Sopenharmony_ci { .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 2, 59862306a36Sopenharmony_ci .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, 59962306a36Sopenharmony_ci { .format = DRM_FORMAT_ARGB2101010, .depth = 30, .num_planes = 2, 60062306a36Sopenharmony_ci .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, 60162306a36Sopenharmony_ci .has_alpha = true, }, 60262306a36Sopenharmony_ci { .format = DRM_FORMAT_ABGR2101010, .depth = 30, .num_planes = 2, 60362306a36Sopenharmony_ci .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, 60462306a36Sopenharmony_ci .has_alpha = true, }, 60562306a36Sopenharmony_ci { .format = DRM_FORMAT_RGB565, .depth = 16, .num_planes = 2, 60662306a36Sopenharmony_ci .cpp = { 2, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, 60762306a36Sopenharmony_ci}; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic const struct drm_format_info dcc_retile_formats[] = { 61062306a36Sopenharmony_ci { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 3, 61162306a36Sopenharmony_ci .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, 61262306a36Sopenharmony_ci { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 3, 61362306a36Sopenharmony_ci .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, 61462306a36Sopenharmony_ci { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 3, 61562306a36Sopenharmony_ci .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, 61662306a36Sopenharmony_ci .has_alpha = true, }, 61762306a36Sopenharmony_ci { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 3, 61862306a36Sopenharmony_ci .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, 61962306a36Sopenharmony_ci .has_alpha = true, }, 62062306a36Sopenharmony_ci { .format = DRM_FORMAT_BGRA8888, .depth = 32, .num_planes = 3, 62162306a36Sopenharmony_ci .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, 62262306a36Sopenharmony_ci .has_alpha = true, }, 62362306a36Sopenharmony_ci { .format = DRM_FORMAT_XRGB2101010, .depth = 30, .num_planes = 3, 62462306a36Sopenharmony_ci .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, 62562306a36Sopenharmony_ci { .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 3, 62662306a36Sopenharmony_ci .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, 62762306a36Sopenharmony_ci { .format = DRM_FORMAT_ARGB2101010, .depth = 30, .num_planes = 3, 62862306a36Sopenharmony_ci .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, 62962306a36Sopenharmony_ci .has_alpha = true, }, 63062306a36Sopenharmony_ci { .format = DRM_FORMAT_ABGR2101010, .depth = 30, .num_planes = 3, 63162306a36Sopenharmony_ci .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, 63262306a36Sopenharmony_ci .has_alpha = true, }, 63362306a36Sopenharmony_ci { .format = DRM_FORMAT_RGB565, .depth = 16, .num_planes = 3, 63462306a36Sopenharmony_ci .cpp = { 2, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, }, 63562306a36Sopenharmony_ci}; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic const struct drm_format_info * 63862306a36Sopenharmony_cilookup_format_info(const struct drm_format_info formats[], 63962306a36Sopenharmony_ci int num_formats, u32 format) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci int i; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci for (i = 0; i < num_formats; i++) { 64462306a36Sopenharmony_ci if (formats[i].format == format) 64562306a36Sopenharmony_ci return &formats[i]; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci return NULL; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ciconst struct drm_format_info * 65262306a36Sopenharmony_ciamdgpu_lookup_format_info(u32 format, uint64_t modifier) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci if (!IS_AMD_FMT_MOD(modifier)) 65562306a36Sopenharmony_ci return NULL; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (AMD_FMT_MOD_GET(DCC_RETILE, modifier)) 65862306a36Sopenharmony_ci return lookup_format_info(dcc_retile_formats, 65962306a36Sopenharmony_ci ARRAY_SIZE(dcc_retile_formats), 66062306a36Sopenharmony_ci format); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (AMD_FMT_MOD_GET(DCC, modifier)) 66362306a36Sopenharmony_ci return lookup_format_info(dcc_formats, ARRAY_SIZE(dcc_formats), 66462306a36Sopenharmony_ci format); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* returning NULL will cause the default format structs to be used. */ 66762306a36Sopenharmony_ci return NULL; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci/* 67262306a36Sopenharmony_ci * Tries to extract the renderable DCC offset from the opaque metadata attached 67362306a36Sopenharmony_ci * to the buffer. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_cistatic int 67662306a36Sopenharmony_ciextract_render_dcc_offset(struct amdgpu_device *adev, 67762306a36Sopenharmony_ci struct drm_gem_object *obj, 67862306a36Sopenharmony_ci uint64_t *offset) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci struct amdgpu_bo *rbo; 68162306a36Sopenharmony_ci int r = 0; 68262306a36Sopenharmony_ci uint32_t metadata[10]; /* Something that fits a descriptor + header. */ 68362306a36Sopenharmony_ci uint32_t size; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci rbo = gem_to_amdgpu_bo(obj); 68662306a36Sopenharmony_ci r = amdgpu_bo_reserve(rbo, false); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (unlikely(r)) { 68962306a36Sopenharmony_ci /* Don't show error message when returning -ERESTARTSYS */ 69062306a36Sopenharmony_ci if (r != -ERESTARTSYS) 69162306a36Sopenharmony_ci DRM_ERROR("Unable to reserve buffer: %d\n", r); 69262306a36Sopenharmony_ci return r; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci r = amdgpu_bo_get_metadata(rbo, metadata, sizeof(metadata), &size, NULL); 69662306a36Sopenharmony_ci amdgpu_bo_unreserve(rbo); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (r) 69962306a36Sopenharmony_ci return r; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* 70262306a36Sopenharmony_ci * The first word is the metadata version, and we need space for at least 70362306a36Sopenharmony_ci * the version + pci vendor+device id + 8 words for a descriptor. 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_ci if (size < 40 || metadata[0] != 1) 70662306a36Sopenharmony_ci return -EINVAL; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (adev->family >= AMDGPU_FAMILY_NV) { 70962306a36Sopenharmony_ci /* resource word 6/7 META_DATA_ADDRESS{_LO} */ 71062306a36Sopenharmony_ci *offset = ((u64)metadata[9] << 16u) | 71162306a36Sopenharmony_ci ((metadata[8] & 0xFF000000u) >> 16); 71262306a36Sopenharmony_ci } else { 71362306a36Sopenharmony_ci /* resource word 5/7 META_DATA_ADDRESS */ 71462306a36Sopenharmony_ci *offset = ((u64)metadata[9] << 8u) | 71562306a36Sopenharmony_ci ((u64)(metadata[7] & 0x1FE0000u) << 23); 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci return 0; 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic int convert_tiling_flags_to_modifier(struct amdgpu_framebuffer *afb) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci struct amdgpu_device *adev = drm_to_adev(afb->base.dev); 72462306a36Sopenharmony_ci uint64_t modifier = 0; 72562306a36Sopenharmony_ci int num_pipes = 0; 72662306a36Sopenharmony_ci int num_pkrs = 0; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci num_pkrs = adev->gfx.config.gb_addr_config_fields.num_pkrs; 72962306a36Sopenharmony_ci num_pipes = adev->gfx.config.gb_addr_config_fields.num_pipes; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (!afb->tiling_flags || !AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE)) { 73262306a36Sopenharmony_ci modifier = DRM_FORMAT_MOD_LINEAR; 73362306a36Sopenharmony_ci } else { 73462306a36Sopenharmony_ci int swizzle = AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE); 73562306a36Sopenharmony_ci bool has_xor = swizzle >= 16; 73662306a36Sopenharmony_ci int block_size_bits; 73762306a36Sopenharmony_ci int version; 73862306a36Sopenharmony_ci int pipe_xor_bits = 0; 73962306a36Sopenharmony_ci int bank_xor_bits = 0; 74062306a36Sopenharmony_ci int packers = 0; 74162306a36Sopenharmony_ci int rb = 0; 74262306a36Sopenharmony_ci int pipes = ilog2(num_pipes); 74362306a36Sopenharmony_ci uint32_t dcc_offset = AMDGPU_TILING_GET(afb->tiling_flags, DCC_OFFSET_256B); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci switch (swizzle >> 2) { 74662306a36Sopenharmony_ci case 0: /* 256B */ 74762306a36Sopenharmony_ci block_size_bits = 8; 74862306a36Sopenharmony_ci break; 74962306a36Sopenharmony_ci case 1: /* 4KiB */ 75062306a36Sopenharmony_ci case 5: /* 4KiB _X */ 75162306a36Sopenharmony_ci block_size_bits = 12; 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci case 2: /* 64KiB */ 75462306a36Sopenharmony_ci case 4: /* 64 KiB _T */ 75562306a36Sopenharmony_ci case 6: /* 64 KiB _X */ 75662306a36Sopenharmony_ci block_size_bits = 16; 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci case 7: /* 256 KiB */ 75962306a36Sopenharmony_ci block_size_bits = 18; 76062306a36Sopenharmony_ci break; 76162306a36Sopenharmony_ci default: 76262306a36Sopenharmony_ci /* RESERVED or VAR */ 76362306a36Sopenharmony_ci return -EINVAL; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (adev->ip_versions[GC_HWIP][0] >= IP_VERSION(11, 0, 0)) 76762306a36Sopenharmony_ci version = AMD_FMT_MOD_TILE_VER_GFX11; 76862306a36Sopenharmony_ci else if (adev->ip_versions[GC_HWIP][0] >= IP_VERSION(10, 3, 0)) 76962306a36Sopenharmony_ci version = AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS; 77062306a36Sopenharmony_ci else if (adev->ip_versions[GC_HWIP][0] >= IP_VERSION(10, 0, 0)) 77162306a36Sopenharmony_ci version = AMD_FMT_MOD_TILE_VER_GFX10; 77262306a36Sopenharmony_ci else 77362306a36Sopenharmony_ci version = AMD_FMT_MOD_TILE_VER_GFX9; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci switch (swizzle & 3) { 77662306a36Sopenharmony_ci case 0: /* Z microtiling */ 77762306a36Sopenharmony_ci return -EINVAL; 77862306a36Sopenharmony_ci case 1: /* S microtiling */ 77962306a36Sopenharmony_ci if (adev->ip_versions[GC_HWIP][0] < IP_VERSION(11, 0, 0)) { 78062306a36Sopenharmony_ci if (!has_xor) 78162306a36Sopenharmony_ci version = AMD_FMT_MOD_TILE_VER_GFX9; 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci break; 78462306a36Sopenharmony_ci case 2: 78562306a36Sopenharmony_ci if (adev->ip_versions[GC_HWIP][0] < IP_VERSION(11, 0, 0)) { 78662306a36Sopenharmony_ci if (!has_xor && afb->base.format->cpp[0] != 4) 78762306a36Sopenharmony_ci version = AMD_FMT_MOD_TILE_VER_GFX9; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci break; 79062306a36Sopenharmony_ci case 3: 79162306a36Sopenharmony_ci break; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (has_xor) { 79562306a36Sopenharmony_ci if (num_pipes == num_pkrs && num_pkrs == 0) { 79662306a36Sopenharmony_ci DRM_ERROR("invalid number of pipes and packers\n"); 79762306a36Sopenharmony_ci return -EINVAL; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci switch (version) { 80162306a36Sopenharmony_ci case AMD_FMT_MOD_TILE_VER_GFX11: 80262306a36Sopenharmony_ci pipe_xor_bits = min(block_size_bits - 8, pipes); 80362306a36Sopenharmony_ci packers = ilog2(adev->gfx.config.gb_addr_config_fields.num_pkrs); 80462306a36Sopenharmony_ci break; 80562306a36Sopenharmony_ci case AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS: 80662306a36Sopenharmony_ci pipe_xor_bits = min(block_size_bits - 8, pipes); 80762306a36Sopenharmony_ci packers = min(block_size_bits - 8 - pipe_xor_bits, 80862306a36Sopenharmony_ci ilog2(adev->gfx.config.gb_addr_config_fields.num_pkrs)); 80962306a36Sopenharmony_ci break; 81062306a36Sopenharmony_ci case AMD_FMT_MOD_TILE_VER_GFX10: 81162306a36Sopenharmony_ci pipe_xor_bits = min(block_size_bits - 8, pipes); 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci case AMD_FMT_MOD_TILE_VER_GFX9: 81462306a36Sopenharmony_ci rb = ilog2(adev->gfx.config.gb_addr_config_fields.num_se) + 81562306a36Sopenharmony_ci ilog2(adev->gfx.config.gb_addr_config_fields.num_rb_per_se); 81662306a36Sopenharmony_ci pipe_xor_bits = min(block_size_bits - 8, pipes + 81762306a36Sopenharmony_ci ilog2(adev->gfx.config.gb_addr_config_fields.num_se)); 81862306a36Sopenharmony_ci bank_xor_bits = min(block_size_bits - 8 - pipe_xor_bits, 81962306a36Sopenharmony_ci ilog2(adev->gfx.config.gb_addr_config_fields.num_banks)); 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci modifier = AMD_FMT_MOD | 82562306a36Sopenharmony_ci AMD_FMT_MOD_SET(TILE, AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE)) | 82662306a36Sopenharmony_ci AMD_FMT_MOD_SET(TILE_VERSION, version) | 82762306a36Sopenharmony_ci AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | 82862306a36Sopenharmony_ci AMD_FMT_MOD_SET(BANK_XOR_BITS, bank_xor_bits) | 82962306a36Sopenharmony_ci AMD_FMT_MOD_SET(PACKERS, packers); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci if (dcc_offset != 0) { 83262306a36Sopenharmony_ci bool dcc_i64b = AMDGPU_TILING_GET(afb->tiling_flags, DCC_INDEPENDENT_64B) != 0; 83362306a36Sopenharmony_ci bool dcc_i128b = version >= AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS; 83462306a36Sopenharmony_ci const struct drm_format_info *format_info; 83562306a36Sopenharmony_ci u64 render_dcc_offset; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* Enable constant encode on RAVEN2 and later. */ 83862306a36Sopenharmony_ci bool dcc_constant_encode = (adev->asic_type > CHIP_RAVEN || 83962306a36Sopenharmony_ci (adev->asic_type == CHIP_RAVEN && 84062306a36Sopenharmony_ci adev->external_rev_id >= 0x81)) && 84162306a36Sopenharmony_ci adev->ip_versions[GC_HWIP][0] < IP_VERSION(11, 0, 0); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci int max_cblock_size = dcc_i64b ? AMD_FMT_MOD_DCC_BLOCK_64B : 84462306a36Sopenharmony_ci dcc_i128b ? AMD_FMT_MOD_DCC_BLOCK_128B : 84562306a36Sopenharmony_ci AMD_FMT_MOD_DCC_BLOCK_256B; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci modifier |= AMD_FMT_MOD_SET(DCC, 1) | 84862306a36Sopenharmony_ci AMD_FMT_MOD_SET(DCC_CONSTANT_ENCODE, dcc_constant_encode) | 84962306a36Sopenharmony_ci AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, dcc_i64b) | 85062306a36Sopenharmony_ci AMD_FMT_MOD_SET(DCC_INDEPENDENT_128B, dcc_i128b) | 85162306a36Sopenharmony_ci AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, max_cblock_size); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci afb->base.offsets[1] = dcc_offset * 256 + afb->base.offsets[0]; 85462306a36Sopenharmony_ci afb->base.pitches[1] = 85562306a36Sopenharmony_ci AMDGPU_TILING_GET(afb->tiling_flags, DCC_PITCH_MAX) + 1; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* 85862306a36Sopenharmony_ci * If the userspace driver uses retiling the tiling flags do not contain 85962306a36Sopenharmony_ci * info on the renderable DCC buffer. Luckily the opaque metadata contains 86062306a36Sopenharmony_ci * the info so we can try to extract it. The kernel does not use this info 86162306a36Sopenharmony_ci * but we should convert it to a modifier plane for getfb2, so the 86262306a36Sopenharmony_ci * userspace driver that gets it doesn't have to juggle around another DCC 86362306a36Sopenharmony_ci * plane internally. 86462306a36Sopenharmony_ci */ 86562306a36Sopenharmony_ci if (extract_render_dcc_offset(adev, afb->base.obj[0], 86662306a36Sopenharmony_ci &render_dcc_offset) == 0 && 86762306a36Sopenharmony_ci render_dcc_offset != 0 && 86862306a36Sopenharmony_ci render_dcc_offset != afb->base.offsets[1] && 86962306a36Sopenharmony_ci render_dcc_offset < UINT_MAX) { 87062306a36Sopenharmony_ci uint32_t dcc_block_bits; /* of base surface data */ 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci modifier |= AMD_FMT_MOD_SET(DCC_RETILE, 1); 87362306a36Sopenharmony_ci afb->base.offsets[2] = render_dcc_offset; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci if (adev->family >= AMDGPU_FAMILY_NV) { 87662306a36Sopenharmony_ci int extra_pipe = 0; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if ((adev->ip_versions[GC_HWIP][0] >= IP_VERSION(10, 3, 0)) && 87962306a36Sopenharmony_ci pipes == packers && pipes > 1) 88062306a36Sopenharmony_ci extra_pipe = 1; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci dcc_block_bits = max(20, 16 + pipes + extra_pipe); 88362306a36Sopenharmony_ci } else { 88462306a36Sopenharmony_ci modifier |= AMD_FMT_MOD_SET(RB, rb) | 88562306a36Sopenharmony_ci AMD_FMT_MOD_SET(PIPE, pipes); 88662306a36Sopenharmony_ci dcc_block_bits = max(20, 18 + rb); 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci dcc_block_bits -= ilog2(afb->base.format->cpp[0]); 89062306a36Sopenharmony_ci afb->base.pitches[2] = ALIGN(afb->base.width, 89162306a36Sopenharmony_ci 1u << ((dcc_block_bits + 1) / 2)); 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci format_info = amdgpu_lookup_format_info(afb->base.format->format, 89462306a36Sopenharmony_ci modifier); 89562306a36Sopenharmony_ci if (!format_info) 89662306a36Sopenharmony_ci return -EINVAL; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci afb->base.format = format_info; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci afb->base.modifier = modifier; 90362306a36Sopenharmony_ci afb->base.flags |= DRM_MODE_FB_MODIFIERS; 90462306a36Sopenharmony_ci return 0; 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci/* Mirrors the is_displayable check in radeonsi's gfx6_compute_surface */ 90862306a36Sopenharmony_cistatic int check_tiling_flags_gfx6(struct amdgpu_framebuffer *afb) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci u64 micro_tile_mode; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci /* Zero swizzle mode means linear */ 91362306a36Sopenharmony_ci if (AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE) == 0) 91462306a36Sopenharmony_ci return 0; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci micro_tile_mode = AMDGPU_TILING_GET(afb->tiling_flags, MICRO_TILE_MODE); 91762306a36Sopenharmony_ci switch (micro_tile_mode) { 91862306a36Sopenharmony_ci case 0: /* DISPLAY */ 91962306a36Sopenharmony_ci case 3: /* RENDER */ 92062306a36Sopenharmony_ci return 0; 92162306a36Sopenharmony_ci default: 92262306a36Sopenharmony_ci drm_dbg_kms(afb->base.dev, 92362306a36Sopenharmony_ci "Micro tile mode %llu not supported for scanout\n", 92462306a36Sopenharmony_ci micro_tile_mode); 92562306a36Sopenharmony_ci return -EINVAL; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_cistatic void get_block_dimensions(unsigned int block_log2, unsigned int cpp, 93062306a36Sopenharmony_ci unsigned int *width, unsigned int *height) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci unsigned int cpp_log2 = ilog2(cpp); 93362306a36Sopenharmony_ci unsigned int pixel_log2 = block_log2 - cpp_log2; 93462306a36Sopenharmony_ci unsigned int width_log2 = (pixel_log2 + 1) / 2; 93562306a36Sopenharmony_ci unsigned int height_log2 = pixel_log2 - width_log2; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci *width = 1 << width_log2; 93862306a36Sopenharmony_ci *height = 1 << height_log2; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic unsigned int get_dcc_block_size(uint64_t modifier, bool rb_aligned, 94262306a36Sopenharmony_ci bool pipe_aligned) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci unsigned int ver = AMD_FMT_MOD_GET(TILE_VERSION, modifier); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci switch (ver) { 94762306a36Sopenharmony_ci case AMD_FMT_MOD_TILE_VER_GFX9: { 94862306a36Sopenharmony_ci /* 94962306a36Sopenharmony_ci * TODO: for pipe aligned we may need to check the alignment of the 95062306a36Sopenharmony_ci * total size of the surface, which may need to be bigger than the 95162306a36Sopenharmony_ci * natural alignment due to some HW workarounds 95262306a36Sopenharmony_ci */ 95362306a36Sopenharmony_ci return max(10 + (rb_aligned ? (int)AMD_FMT_MOD_GET(RB, modifier) : 0), 12); 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci case AMD_FMT_MOD_TILE_VER_GFX10: 95662306a36Sopenharmony_ci case AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS: 95762306a36Sopenharmony_ci case AMD_FMT_MOD_TILE_VER_GFX11: { 95862306a36Sopenharmony_ci int pipes_log2 = AMD_FMT_MOD_GET(PIPE_XOR_BITS, modifier); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (ver >= AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS && pipes_log2 > 1 && 96162306a36Sopenharmony_ci AMD_FMT_MOD_GET(PACKERS, modifier) == pipes_log2) 96262306a36Sopenharmony_ci ++pipes_log2; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci return max(8 + (pipe_aligned ? pipes_log2 : 0), 12); 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci default: 96762306a36Sopenharmony_ci return 0; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic int amdgpu_display_verify_plane(struct amdgpu_framebuffer *rfb, int plane, 97262306a36Sopenharmony_ci const struct drm_format_info *format, 97362306a36Sopenharmony_ci unsigned int block_width, unsigned int block_height, 97462306a36Sopenharmony_ci unsigned int block_size_log2) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci unsigned int width = rfb->base.width / 97762306a36Sopenharmony_ci ((plane && plane < format->num_planes) ? format->hsub : 1); 97862306a36Sopenharmony_ci unsigned int height = rfb->base.height / 97962306a36Sopenharmony_ci ((plane && plane < format->num_planes) ? format->vsub : 1); 98062306a36Sopenharmony_ci unsigned int cpp = plane < format->num_planes ? format->cpp[plane] : 1; 98162306a36Sopenharmony_ci unsigned int block_pitch = block_width * cpp; 98262306a36Sopenharmony_ci unsigned int min_pitch = ALIGN(width * cpp, block_pitch); 98362306a36Sopenharmony_ci unsigned int block_size = 1 << block_size_log2; 98462306a36Sopenharmony_ci uint64_t size; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (rfb->base.pitches[plane] % block_pitch) { 98762306a36Sopenharmony_ci drm_dbg_kms(rfb->base.dev, 98862306a36Sopenharmony_ci "pitch %d for plane %d is not a multiple of block pitch %d\n", 98962306a36Sopenharmony_ci rfb->base.pitches[plane], plane, block_pitch); 99062306a36Sopenharmony_ci return -EINVAL; 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci if (rfb->base.pitches[plane] < min_pitch) { 99362306a36Sopenharmony_ci drm_dbg_kms(rfb->base.dev, 99462306a36Sopenharmony_ci "pitch %d for plane %d is less than minimum pitch %d\n", 99562306a36Sopenharmony_ci rfb->base.pitches[plane], plane, min_pitch); 99662306a36Sopenharmony_ci return -EINVAL; 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci /* Force at least natural alignment. */ 100062306a36Sopenharmony_ci if (rfb->base.offsets[plane] % block_size) { 100162306a36Sopenharmony_ci drm_dbg_kms(rfb->base.dev, 100262306a36Sopenharmony_ci "offset 0x%x for plane %d is not a multiple of block pitch 0x%x\n", 100362306a36Sopenharmony_ci rfb->base.offsets[plane], plane, block_size); 100462306a36Sopenharmony_ci return -EINVAL; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci size = rfb->base.offsets[plane] + 100862306a36Sopenharmony_ci (uint64_t)rfb->base.pitches[plane] / block_pitch * 100962306a36Sopenharmony_ci block_size * DIV_ROUND_UP(height, block_height); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci if (rfb->base.obj[0]->size < size) { 101262306a36Sopenharmony_ci drm_dbg_kms(rfb->base.dev, 101362306a36Sopenharmony_ci "BO size 0x%zx is less than 0x%llx required for plane %d\n", 101462306a36Sopenharmony_ci rfb->base.obj[0]->size, size, plane); 101562306a36Sopenharmony_ci return -EINVAL; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci return 0; 101962306a36Sopenharmony_ci} 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic int amdgpu_display_verify_sizes(struct amdgpu_framebuffer *rfb) 102362306a36Sopenharmony_ci{ 102462306a36Sopenharmony_ci const struct drm_format_info *format_info = drm_format_info(rfb->base.format->format); 102562306a36Sopenharmony_ci uint64_t modifier = rfb->base.modifier; 102662306a36Sopenharmony_ci int ret; 102762306a36Sopenharmony_ci unsigned int i, block_width, block_height, block_size_log2; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (rfb->base.dev->mode_config.fb_modifiers_not_supported) 103062306a36Sopenharmony_ci return 0; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci for (i = 0; i < format_info->num_planes; ++i) { 103362306a36Sopenharmony_ci if (modifier == DRM_FORMAT_MOD_LINEAR) { 103462306a36Sopenharmony_ci block_width = 256 / format_info->cpp[i]; 103562306a36Sopenharmony_ci block_height = 1; 103662306a36Sopenharmony_ci block_size_log2 = 8; 103762306a36Sopenharmony_ci } else { 103862306a36Sopenharmony_ci int swizzle = AMD_FMT_MOD_GET(TILE, modifier); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci switch ((swizzle & ~3) + 1) { 104162306a36Sopenharmony_ci case DC_SW_256B_S: 104262306a36Sopenharmony_ci block_size_log2 = 8; 104362306a36Sopenharmony_ci break; 104462306a36Sopenharmony_ci case DC_SW_4KB_S: 104562306a36Sopenharmony_ci case DC_SW_4KB_S_X: 104662306a36Sopenharmony_ci block_size_log2 = 12; 104762306a36Sopenharmony_ci break; 104862306a36Sopenharmony_ci case DC_SW_64KB_S: 104962306a36Sopenharmony_ci case DC_SW_64KB_S_T: 105062306a36Sopenharmony_ci case DC_SW_64KB_S_X: 105162306a36Sopenharmony_ci block_size_log2 = 16; 105262306a36Sopenharmony_ci break; 105362306a36Sopenharmony_ci case DC_SW_VAR_S_X: 105462306a36Sopenharmony_ci block_size_log2 = 18; 105562306a36Sopenharmony_ci break; 105662306a36Sopenharmony_ci default: 105762306a36Sopenharmony_ci drm_dbg_kms(rfb->base.dev, 105862306a36Sopenharmony_ci "Swizzle mode with unknown block size: %d\n", swizzle); 105962306a36Sopenharmony_ci return -EINVAL; 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci get_block_dimensions(block_size_log2, format_info->cpp[i], 106362306a36Sopenharmony_ci &block_width, &block_height); 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci ret = amdgpu_display_verify_plane(rfb, i, format_info, 106762306a36Sopenharmony_ci block_width, block_height, block_size_log2); 106862306a36Sopenharmony_ci if (ret) 106962306a36Sopenharmony_ci return ret; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci if (AMD_FMT_MOD_GET(DCC, modifier)) { 107362306a36Sopenharmony_ci if (AMD_FMT_MOD_GET(DCC_RETILE, modifier)) { 107462306a36Sopenharmony_ci block_size_log2 = get_dcc_block_size(modifier, false, false); 107562306a36Sopenharmony_ci get_block_dimensions(block_size_log2 + 8, format_info->cpp[0], 107662306a36Sopenharmony_ci &block_width, &block_height); 107762306a36Sopenharmony_ci ret = amdgpu_display_verify_plane(rfb, i, format_info, 107862306a36Sopenharmony_ci block_width, block_height, 107962306a36Sopenharmony_ci block_size_log2); 108062306a36Sopenharmony_ci if (ret) 108162306a36Sopenharmony_ci return ret; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci ++i; 108462306a36Sopenharmony_ci block_size_log2 = get_dcc_block_size(modifier, true, true); 108562306a36Sopenharmony_ci } else { 108662306a36Sopenharmony_ci bool pipe_aligned = AMD_FMT_MOD_GET(DCC_PIPE_ALIGN, modifier); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci block_size_log2 = get_dcc_block_size(modifier, true, pipe_aligned); 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci get_block_dimensions(block_size_log2 + 8, format_info->cpp[0], 109162306a36Sopenharmony_ci &block_width, &block_height); 109262306a36Sopenharmony_ci ret = amdgpu_display_verify_plane(rfb, i, format_info, 109362306a36Sopenharmony_ci block_width, block_height, block_size_log2); 109462306a36Sopenharmony_ci if (ret) 109562306a36Sopenharmony_ci return ret; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci return 0; 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cistatic int amdgpu_display_get_fb_info(const struct amdgpu_framebuffer *amdgpu_fb, 110262306a36Sopenharmony_ci uint64_t *tiling_flags, bool *tmz_surface) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci struct amdgpu_bo *rbo; 110562306a36Sopenharmony_ci int r; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci if (!amdgpu_fb) { 110862306a36Sopenharmony_ci *tiling_flags = 0; 110962306a36Sopenharmony_ci *tmz_surface = false; 111062306a36Sopenharmony_ci return 0; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci rbo = gem_to_amdgpu_bo(amdgpu_fb->base.obj[0]); 111462306a36Sopenharmony_ci r = amdgpu_bo_reserve(rbo, false); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci if (unlikely(r)) { 111762306a36Sopenharmony_ci /* Don't show error message when returning -ERESTARTSYS */ 111862306a36Sopenharmony_ci if (r != -ERESTARTSYS) 111962306a36Sopenharmony_ci DRM_ERROR("Unable to reserve buffer: %d\n", r); 112062306a36Sopenharmony_ci return r; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (tiling_flags) 112462306a36Sopenharmony_ci amdgpu_bo_get_tiling_flags(rbo, tiling_flags); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci if (tmz_surface) 112762306a36Sopenharmony_ci *tmz_surface = amdgpu_bo_encrypted(rbo); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci amdgpu_bo_unreserve(rbo); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci return r; 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic int amdgpu_display_gem_fb_verify_and_init(struct drm_device *dev, 113562306a36Sopenharmony_ci struct amdgpu_framebuffer *rfb, 113662306a36Sopenharmony_ci struct drm_file *file_priv, 113762306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd, 113862306a36Sopenharmony_ci struct drm_gem_object *obj) 113962306a36Sopenharmony_ci{ 114062306a36Sopenharmony_ci int ret; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci rfb->base.obj[0] = obj; 114362306a36Sopenharmony_ci drm_helper_mode_fill_fb_struct(dev, &rfb->base, mode_cmd); 114462306a36Sopenharmony_ci /* Verify that the modifier is supported. */ 114562306a36Sopenharmony_ci if (!drm_any_plane_has_format(dev, mode_cmd->pixel_format, 114662306a36Sopenharmony_ci mode_cmd->modifier[0])) { 114762306a36Sopenharmony_ci drm_dbg_kms(dev, 114862306a36Sopenharmony_ci "unsupported pixel format %p4cc / modifier 0x%llx\n", 114962306a36Sopenharmony_ci &mode_cmd->pixel_format, mode_cmd->modifier[0]); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci ret = -EINVAL; 115262306a36Sopenharmony_ci goto err; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci ret = amdgpu_display_framebuffer_init(dev, rfb, mode_cmd, obj); 115662306a36Sopenharmony_ci if (ret) 115762306a36Sopenharmony_ci goto err; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (drm_drv_uses_atomic_modeset(dev)) 116062306a36Sopenharmony_ci ret = drm_framebuffer_init(dev, &rfb->base, 116162306a36Sopenharmony_ci &amdgpu_fb_funcs_atomic); 116262306a36Sopenharmony_ci else 116362306a36Sopenharmony_ci ret = drm_framebuffer_init(dev, &rfb->base, &amdgpu_fb_funcs); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci if (ret) 116662306a36Sopenharmony_ci goto err; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci return 0; 116962306a36Sopenharmony_cierr: 117062306a36Sopenharmony_ci drm_dbg_kms(dev, "Failed to verify and init gem fb: %d\n", ret); 117162306a36Sopenharmony_ci rfb->base.obj[0] = NULL; 117262306a36Sopenharmony_ci return ret; 117362306a36Sopenharmony_ci} 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_cistatic int amdgpu_display_framebuffer_init(struct drm_device *dev, 117662306a36Sopenharmony_ci struct amdgpu_framebuffer *rfb, 117762306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd, 117862306a36Sopenharmony_ci struct drm_gem_object *obj) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci struct amdgpu_device *adev = drm_to_adev(dev); 118162306a36Sopenharmony_ci int ret, i; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci /* 118462306a36Sopenharmony_ci * This needs to happen before modifier conversion as that might change 118562306a36Sopenharmony_ci * the number of planes. 118662306a36Sopenharmony_ci */ 118762306a36Sopenharmony_ci for (i = 1; i < rfb->base.format->num_planes; ++i) { 118862306a36Sopenharmony_ci if (mode_cmd->handles[i] != mode_cmd->handles[0]) { 118962306a36Sopenharmony_ci drm_dbg_kms(dev, "Plane 0 and %d have different BOs: %u vs. %u\n", 119062306a36Sopenharmony_ci i, mode_cmd->handles[0], mode_cmd->handles[i]); 119162306a36Sopenharmony_ci ret = -EINVAL; 119262306a36Sopenharmony_ci return ret; 119362306a36Sopenharmony_ci } 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci ret = amdgpu_display_get_fb_info(rfb, &rfb->tiling_flags, &rfb->tmz_surface); 119762306a36Sopenharmony_ci if (ret) 119862306a36Sopenharmony_ci return ret; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci if (dev->mode_config.fb_modifiers_not_supported && !adev->enable_virtual_display) { 120162306a36Sopenharmony_ci drm_WARN_ONCE(dev, adev->family >= AMDGPU_FAMILY_AI, 120262306a36Sopenharmony_ci "GFX9+ requires FB check based on format modifier\n"); 120362306a36Sopenharmony_ci ret = check_tiling_flags_gfx6(rfb); 120462306a36Sopenharmony_ci if (ret) 120562306a36Sopenharmony_ci return ret; 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci if (!dev->mode_config.fb_modifiers_not_supported && 120962306a36Sopenharmony_ci !(rfb->base.flags & DRM_MODE_FB_MODIFIERS)) { 121062306a36Sopenharmony_ci ret = convert_tiling_flags_to_modifier(rfb); 121162306a36Sopenharmony_ci if (ret) { 121262306a36Sopenharmony_ci drm_dbg_kms(dev, "Failed to convert tiling flags 0x%llX to a modifier", 121362306a36Sopenharmony_ci rfb->tiling_flags); 121462306a36Sopenharmony_ci return ret; 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci ret = amdgpu_display_verify_sizes(rfb); 121962306a36Sopenharmony_ci if (ret) 122062306a36Sopenharmony_ci return ret; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci for (i = 0; i < rfb->base.format->num_planes; ++i) { 122362306a36Sopenharmony_ci drm_gem_object_get(rfb->base.obj[0]); 122462306a36Sopenharmony_ci rfb->base.obj[i] = rfb->base.obj[0]; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci return 0; 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistruct drm_framebuffer * 123162306a36Sopenharmony_ciamdgpu_display_user_framebuffer_create(struct drm_device *dev, 123262306a36Sopenharmony_ci struct drm_file *file_priv, 123362306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci struct amdgpu_framebuffer *amdgpu_fb; 123662306a36Sopenharmony_ci struct drm_gem_object *obj; 123762306a36Sopenharmony_ci struct amdgpu_bo *bo; 123862306a36Sopenharmony_ci uint32_t domains; 123962306a36Sopenharmony_ci int ret; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]); 124262306a36Sopenharmony_ci if (obj == NULL) { 124362306a36Sopenharmony_ci drm_dbg_kms(dev, 124462306a36Sopenharmony_ci "No GEM object associated to handle 0x%08X, can't create framebuffer\n", 124562306a36Sopenharmony_ci mode_cmd->handles[0]); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci /* Handle is imported dma-buf, so cannot be migrated to VRAM for scanout */ 125162306a36Sopenharmony_ci bo = gem_to_amdgpu_bo(obj); 125262306a36Sopenharmony_ci domains = amdgpu_display_supported_domains(drm_to_adev(dev), bo->flags); 125362306a36Sopenharmony_ci if (obj->import_attach && !(domains & AMDGPU_GEM_DOMAIN_GTT)) { 125462306a36Sopenharmony_ci drm_dbg_kms(dev, "Cannot create framebuffer from imported dma_buf\n"); 125562306a36Sopenharmony_ci drm_gem_object_put(obj); 125662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci amdgpu_fb = kzalloc(sizeof(*amdgpu_fb), GFP_KERNEL); 126062306a36Sopenharmony_ci if (amdgpu_fb == NULL) { 126162306a36Sopenharmony_ci drm_gem_object_put(obj); 126262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci ret = amdgpu_display_gem_fb_verify_and_init(dev, amdgpu_fb, file_priv, 126662306a36Sopenharmony_ci mode_cmd, obj); 126762306a36Sopenharmony_ci if (ret) { 126862306a36Sopenharmony_ci kfree(amdgpu_fb); 126962306a36Sopenharmony_ci drm_gem_object_put(obj); 127062306a36Sopenharmony_ci return ERR_PTR(ret); 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci drm_gem_object_put(obj); 127462306a36Sopenharmony_ci return &amdgpu_fb->base; 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ciconst struct drm_mode_config_funcs amdgpu_mode_funcs = { 127862306a36Sopenharmony_ci .fb_create = amdgpu_display_user_framebuffer_create, 127962306a36Sopenharmony_ci}; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic const struct drm_prop_enum_list amdgpu_underscan_enum_list[] = { 128262306a36Sopenharmony_ci { UNDERSCAN_OFF, "off" }, 128362306a36Sopenharmony_ci { UNDERSCAN_ON, "on" }, 128462306a36Sopenharmony_ci { UNDERSCAN_AUTO, "auto" }, 128562306a36Sopenharmony_ci}; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_cistatic const struct drm_prop_enum_list amdgpu_audio_enum_list[] = { 128862306a36Sopenharmony_ci { AMDGPU_AUDIO_DISABLE, "off" }, 128962306a36Sopenharmony_ci { AMDGPU_AUDIO_ENABLE, "on" }, 129062306a36Sopenharmony_ci { AMDGPU_AUDIO_AUTO, "auto" }, 129162306a36Sopenharmony_ci}; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci/* XXX support different dither options? spatial, temporal, both, etc. */ 129462306a36Sopenharmony_cistatic const struct drm_prop_enum_list amdgpu_dither_enum_list[] = { 129562306a36Sopenharmony_ci { AMDGPU_FMT_DITHER_DISABLE, "off" }, 129662306a36Sopenharmony_ci { AMDGPU_FMT_DITHER_ENABLE, "on" }, 129762306a36Sopenharmony_ci}; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ciint amdgpu_display_modeset_create_props(struct amdgpu_device *adev) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci int sz; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci adev->mode_info.coherent_mode_property = 130462306a36Sopenharmony_ci drm_property_create_range(adev_to_drm(adev), 0, "coherent", 0, 1); 130562306a36Sopenharmony_ci if (!adev->mode_info.coherent_mode_property) 130662306a36Sopenharmony_ci return -ENOMEM; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci adev->mode_info.load_detect_property = 130962306a36Sopenharmony_ci drm_property_create_range(adev_to_drm(adev), 0, "load detection", 0, 1); 131062306a36Sopenharmony_ci if (!adev->mode_info.load_detect_property) 131162306a36Sopenharmony_ci return -ENOMEM; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci drm_mode_create_scaling_mode_property(adev_to_drm(adev)); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci sz = ARRAY_SIZE(amdgpu_underscan_enum_list); 131662306a36Sopenharmony_ci adev->mode_info.underscan_property = 131762306a36Sopenharmony_ci drm_property_create_enum(adev_to_drm(adev), 0, 131862306a36Sopenharmony_ci "underscan", 131962306a36Sopenharmony_ci amdgpu_underscan_enum_list, sz); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci adev->mode_info.underscan_hborder_property = 132262306a36Sopenharmony_ci drm_property_create_range(adev_to_drm(adev), 0, 132362306a36Sopenharmony_ci "underscan hborder", 0, 128); 132462306a36Sopenharmony_ci if (!adev->mode_info.underscan_hborder_property) 132562306a36Sopenharmony_ci return -ENOMEM; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci adev->mode_info.underscan_vborder_property = 132862306a36Sopenharmony_ci drm_property_create_range(adev_to_drm(adev), 0, 132962306a36Sopenharmony_ci "underscan vborder", 0, 128); 133062306a36Sopenharmony_ci if (!adev->mode_info.underscan_vborder_property) 133162306a36Sopenharmony_ci return -ENOMEM; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci sz = ARRAY_SIZE(amdgpu_audio_enum_list); 133462306a36Sopenharmony_ci adev->mode_info.audio_property = 133562306a36Sopenharmony_ci drm_property_create_enum(adev_to_drm(adev), 0, 133662306a36Sopenharmony_ci "audio", 133762306a36Sopenharmony_ci amdgpu_audio_enum_list, sz); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci sz = ARRAY_SIZE(amdgpu_dither_enum_list); 134062306a36Sopenharmony_ci adev->mode_info.dither_property = 134162306a36Sopenharmony_ci drm_property_create_enum(adev_to_drm(adev), 0, 134262306a36Sopenharmony_ci "dither", 134362306a36Sopenharmony_ci amdgpu_dither_enum_list, sz); 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci if (adev->dc_enabled) { 134662306a36Sopenharmony_ci adev->mode_info.abm_level_property = 134762306a36Sopenharmony_ci drm_property_create_range(adev_to_drm(adev), 0, 134862306a36Sopenharmony_ci "abm level", 0, 4); 134962306a36Sopenharmony_ci if (!adev->mode_info.abm_level_property) 135062306a36Sopenharmony_ci return -ENOMEM; 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci return 0; 135462306a36Sopenharmony_ci} 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_civoid amdgpu_display_update_priority(struct amdgpu_device *adev) 135762306a36Sopenharmony_ci{ 135862306a36Sopenharmony_ci /* adjustment options for the display watermarks */ 135962306a36Sopenharmony_ci if ((amdgpu_disp_priority == 0) || (amdgpu_disp_priority > 2)) 136062306a36Sopenharmony_ci adev->mode_info.disp_priority = 0; 136162306a36Sopenharmony_ci else 136262306a36Sopenharmony_ci adev->mode_info.disp_priority = amdgpu_disp_priority; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci} 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_cistatic bool amdgpu_display_is_hdtv_mode(const struct drm_display_mode *mode) 136762306a36Sopenharmony_ci{ 136862306a36Sopenharmony_ci /* try and guess if this is a tv or a monitor */ 136962306a36Sopenharmony_ci if ((mode->vdisplay == 480 && mode->hdisplay == 720) || /* 480p */ 137062306a36Sopenharmony_ci (mode->vdisplay == 576) || /* 576p */ 137162306a36Sopenharmony_ci (mode->vdisplay == 720) || /* 720p */ 137262306a36Sopenharmony_ci (mode->vdisplay == 1080)) /* 1080p */ 137362306a36Sopenharmony_ci return true; 137462306a36Sopenharmony_ci else 137562306a36Sopenharmony_ci return false; 137662306a36Sopenharmony_ci} 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_cibool amdgpu_display_crtc_scaling_mode_fixup(struct drm_crtc *crtc, 137962306a36Sopenharmony_ci const struct drm_display_mode *mode, 138062306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode) 138162306a36Sopenharmony_ci{ 138262306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 138362306a36Sopenharmony_ci struct drm_encoder *encoder; 138462306a36Sopenharmony_ci struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); 138562306a36Sopenharmony_ci struct amdgpu_encoder *amdgpu_encoder; 138662306a36Sopenharmony_ci struct drm_connector *connector; 138762306a36Sopenharmony_ci u32 src_v = 1, dst_v = 1; 138862306a36Sopenharmony_ci u32 src_h = 1, dst_h = 1; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci amdgpu_crtc->h_border = 0; 139162306a36Sopenharmony_ci amdgpu_crtc->v_border = 0; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 139462306a36Sopenharmony_ci if (encoder->crtc != crtc) 139562306a36Sopenharmony_ci continue; 139662306a36Sopenharmony_ci amdgpu_encoder = to_amdgpu_encoder(encoder); 139762306a36Sopenharmony_ci connector = amdgpu_get_connector_for_encoder(encoder); 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci /* set scaling */ 140062306a36Sopenharmony_ci if (amdgpu_encoder->rmx_type == RMX_OFF) 140162306a36Sopenharmony_ci amdgpu_crtc->rmx_type = RMX_OFF; 140262306a36Sopenharmony_ci else if (mode->hdisplay < amdgpu_encoder->native_mode.hdisplay || 140362306a36Sopenharmony_ci mode->vdisplay < amdgpu_encoder->native_mode.vdisplay) 140462306a36Sopenharmony_ci amdgpu_crtc->rmx_type = amdgpu_encoder->rmx_type; 140562306a36Sopenharmony_ci else 140662306a36Sopenharmony_ci amdgpu_crtc->rmx_type = RMX_OFF; 140762306a36Sopenharmony_ci /* copy native mode */ 140862306a36Sopenharmony_ci memcpy(&amdgpu_crtc->native_mode, 140962306a36Sopenharmony_ci &amdgpu_encoder->native_mode, 141062306a36Sopenharmony_ci sizeof(struct drm_display_mode)); 141162306a36Sopenharmony_ci src_v = crtc->mode.vdisplay; 141262306a36Sopenharmony_ci dst_v = amdgpu_crtc->native_mode.vdisplay; 141362306a36Sopenharmony_ci src_h = crtc->mode.hdisplay; 141462306a36Sopenharmony_ci dst_h = amdgpu_crtc->native_mode.hdisplay; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci /* fix up for overscan on hdmi */ 141762306a36Sopenharmony_ci if ((!(mode->flags & DRM_MODE_FLAG_INTERLACE)) && 141862306a36Sopenharmony_ci ((amdgpu_encoder->underscan_type == UNDERSCAN_ON) || 141962306a36Sopenharmony_ci ((amdgpu_encoder->underscan_type == UNDERSCAN_AUTO) && 142062306a36Sopenharmony_ci connector->display_info.is_hdmi && 142162306a36Sopenharmony_ci amdgpu_display_is_hdtv_mode(mode)))) { 142262306a36Sopenharmony_ci if (amdgpu_encoder->underscan_hborder != 0) 142362306a36Sopenharmony_ci amdgpu_crtc->h_border = amdgpu_encoder->underscan_hborder; 142462306a36Sopenharmony_ci else 142562306a36Sopenharmony_ci amdgpu_crtc->h_border = (mode->hdisplay >> 5) + 16; 142662306a36Sopenharmony_ci if (amdgpu_encoder->underscan_vborder != 0) 142762306a36Sopenharmony_ci amdgpu_crtc->v_border = amdgpu_encoder->underscan_vborder; 142862306a36Sopenharmony_ci else 142962306a36Sopenharmony_ci amdgpu_crtc->v_border = (mode->vdisplay >> 5) + 16; 143062306a36Sopenharmony_ci amdgpu_crtc->rmx_type = RMX_FULL; 143162306a36Sopenharmony_ci src_v = crtc->mode.vdisplay; 143262306a36Sopenharmony_ci dst_v = crtc->mode.vdisplay - (amdgpu_crtc->v_border * 2); 143362306a36Sopenharmony_ci src_h = crtc->mode.hdisplay; 143462306a36Sopenharmony_ci dst_h = crtc->mode.hdisplay - (amdgpu_crtc->h_border * 2); 143562306a36Sopenharmony_ci } 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci if (amdgpu_crtc->rmx_type != RMX_OFF) { 143862306a36Sopenharmony_ci fixed20_12 a, b; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci a.full = dfixed_const(src_v); 144162306a36Sopenharmony_ci b.full = dfixed_const(dst_v); 144262306a36Sopenharmony_ci amdgpu_crtc->vsc.full = dfixed_div(a, b); 144362306a36Sopenharmony_ci a.full = dfixed_const(src_h); 144462306a36Sopenharmony_ci b.full = dfixed_const(dst_h); 144562306a36Sopenharmony_ci amdgpu_crtc->hsc.full = dfixed_div(a, b); 144662306a36Sopenharmony_ci } else { 144762306a36Sopenharmony_ci amdgpu_crtc->vsc.full = dfixed_const(1); 144862306a36Sopenharmony_ci amdgpu_crtc->hsc.full = dfixed_const(1); 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci return true; 145162306a36Sopenharmony_ci} 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci/* 145462306a36Sopenharmony_ci * Retrieve current video scanout position of crtc on a given gpu, and 145562306a36Sopenharmony_ci * an optional accurate timestamp of when query happened. 145662306a36Sopenharmony_ci * 145762306a36Sopenharmony_ci * \param dev Device to query. 145862306a36Sopenharmony_ci * \param pipe Crtc to query. 145962306a36Sopenharmony_ci * \param flags from caller (DRM_CALLED_FROM_VBLIRQ or 0). 146062306a36Sopenharmony_ci * For driver internal use only also supports these flags: 146162306a36Sopenharmony_ci * 146262306a36Sopenharmony_ci * USE_REAL_VBLANKSTART to use the real start of vblank instead 146362306a36Sopenharmony_ci * of a fudged earlier start of vblank. 146462306a36Sopenharmony_ci * 146562306a36Sopenharmony_ci * GET_DISTANCE_TO_VBLANKSTART to return distance to the 146662306a36Sopenharmony_ci * fudged earlier start of vblank in *vpos and the distance 146762306a36Sopenharmony_ci * to true start of vblank in *hpos. 146862306a36Sopenharmony_ci * 146962306a36Sopenharmony_ci * \param *vpos Location where vertical scanout position should be stored. 147062306a36Sopenharmony_ci * \param *hpos Location where horizontal scanout position should go. 147162306a36Sopenharmony_ci * \param *stime Target location for timestamp taken immediately before 147262306a36Sopenharmony_ci * scanout position query. Can be NULL to skip timestamp. 147362306a36Sopenharmony_ci * \param *etime Target location for timestamp taken immediately after 147462306a36Sopenharmony_ci * scanout position query. Can be NULL to skip timestamp. 147562306a36Sopenharmony_ci * 147662306a36Sopenharmony_ci * Returns vpos as a positive number while in active scanout area. 147762306a36Sopenharmony_ci * Returns vpos as a negative number inside vblank, counting the number 147862306a36Sopenharmony_ci * of scanlines to go until end of vblank, e.g., -1 means "one scanline 147962306a36Sopenharmony_ci * until start of active scanout / end of vblank." 148062306a36Sopenharmony_ci * 148162306a36Sopenharmony_ci * \return Flags, or'ed together as follows: 148262306a36Sopenharmony_ci * 148362306a36Sopenharmony_ci * DRM_SCANOUTPOS_VALID = Query successful. 148462306a36Sopenharmony_ci * DRM_SCANOUTPOS_INVBL = Inside vblank. 148562306a36Sopenharmony_ci * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of 148662306a36Sopenharmony_ci * this flag means that returned position may be offset by a constant but 148762306a36Sopenharmony_ci * unknown small number of scanlines wrt. real scanout position. 148862306a36Sopenharmony_ci * 148962306a36Sopenharmony_ci */ 149062306a36Sopenharmony_ciint amdgpu_display_get_crtc_scanoutpos(struct drm_device *dev, 149162306a36Sopenharmony_ci unsigned int pipe, unsigned int flags, int *vpos, 149262306a36Sopenharmony_ci int *hpos, ktime_t *stime, ktime_t *etime, 149362306a36Sopenharmony_ci const struct drm_display_mode *mode) 149462306a36Sopenharmony_ci{ 149562306a36Sopenharmony_ci u32 vbl = 0, position = 0; 149662306a36Sopenharmony_ci int vbl_start, vbl_end, vtotal, ret = 0; 149762306a36Sopenharmony_ci bool in_vbl = true; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci struct amdgpu_device *adev = drm_to_adev(dev); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */ 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci /* Get optional system timestamp before query. */ 150462306a36Sopenharmony_ci if (stime) 150562306a36Sopenharmony_ci *stime = ktime_get(); 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci if (amdgpu_display_page_flip_get_scanoutpos(adev, pipe, &vbl, &position) == 0) 150862306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci /* Get optional system timestamp after query. */ 151162306a36Sopenharmony_ci if (etime) 151262306a36Sopenharmony_ci *etime = ktime_get(); 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */ 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci /* Decode into vertical and horizontal scanout position. */ 151762306a36Sopenharmony_ci *vpos = position & 0x1fff; 151862306a36Sopenharmony_ci *hpos = (position >> 16) & 0x1fff; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci /* Valid vblank area boundaries from gpu retrieved? */ 152162306a36Sopenharmony_ci if (vbl > 0) { 152262306a36Sopenharmony_ci /* Yes: Decode. */ 152362306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_ACCURATE; 152462306a36Sopenharmony_ci vbl_start = vbl & 0x1fff; 152562306a36Sopenharmony_ci vbl_end = (vbl >> 16) & 0x1fff; 152662306a36Sopenharmony_ci } else { 152762306a36Sopenharmony_ci /* No: Fake something reasonable which gives at least ok results. */ 152862306a36Sopenharmony_ci vbl_start = mode->crtc_vdisplay; 152962306a36Sopenharmony_ci vbl_end = 0; 153062306a36Sopenharmony_ci } 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci /* Called from driver internal vblank counter query code? */ 153362306a36Sopenharmony_ci if (flags & GET_DISTANCE_TO_VBLANKSTART) { 153462306a36Sopenharmony_ci /* Caller wants distance from real vbl_start in *hpos */ 153562306a36Sopenharmony_ci *hpos = *vpos - vbl_start; 153662306a36Sopenharmony_ci } 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci /* Fudge vblank to start a few scanlines earlier to handle the 153962306a36Sopenharmony_ci * problem that vblank irqs fire a few scanlines before start 154062306a36Sopenharmony_ci * of vblank. Some driver internal callers need the true vblank 154162306a36Sopenharmony_ci * start to be used and signal this via the USE_REAL_VBLANKSTART flag. 154262306a36Sopenharmony_ci * 154362306a36Sopenharmony_ci * The cause of the "early" vblank irq is that the irq is triggered 154462306a36Sopenharmony_ci * by the line buffer logic when the line buffer read position enters 154562306a36Sopenharmony_ci * the vblank, whereas our crtc scanout position naturally lags the 154662306a36Sopenharmony_ci * line buffer read position. 154762306a36Sopenharmony_ci */ 154862306a36Sopenharmony_ci if (!(flags & USE_REAL_VBLANKSTART)) 154962306a36Sopenharmony_ci vbl_start -= adev->mode_info.crtcs[pipe]->lb_vblank_lead_lines; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci /* Test scanout position against vblank region. */ 155262306a36Sopenharmony_ci if ((*vpos < vbl_start) && (*vpos >= vbl_end)) 155362306a36Sopenharmony_ci in_vbl = false; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci /* In vblank? */ 155662306a36Sopenharmony_ci if (in_vbl) 155762306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_IN_VBLANK; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci /* Called from driver internal vblank counter query code? */ 156062306a36Sopenharmony_ci if (flags & GET_DISTANCE_TO_VBLANKSTART) { 156162306a36Sopenharmony_ci /* Caller wants distance from fudged earlier vbl_start */ 156262306a36Sopenharmony_ci *vpos -= vbl_start; 156362306a36Sopenharmony_ci return ret; 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci /* Check if inside vblank area and apply corrective offsets: 156762306a36Sopenharmony_ci * vpos will then be >=0 in video scanout area, but negative 156862306a36Sopenharmony_ci * within vblank area, counting down the number of lines until 156962306a36Sopenharmony_ci * start of scanout. 157062306a36Sopenharmony_ci */ 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci /* Inside "upper part" of vblank area? Apply corrective offset if so: */ 157362306a36Sopenharmony_ci if (in_vbl && (*vpos >= vbl_start)) { 157462306a36Sopenharmony_ci vtotal = mode->crtc_vtotal; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci /* With variable refresh rate displays the vpos can exceed 157762306a36Sopenharmony_ci * the vtotal value. Clamp to 0 to return -vbl_end instead 157862306a36Sopenharmony_ci * of guessing the remaining number of lines until scanout. 157962306a36Sopenharmony_ci */ 158062306a36Sopenharmony_ci *vpos = (*vpos < vtotal) ? (*vpos - vtotal) : 0; 158162306a36Sopenharmony_ci } 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci /* Correct for shifted end of vbl at vbl_end. */ 158462306a36Sopenharmony_ci *vpos = *vpos - vbl_end; 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci return ret; 158762306a36Sopenharmony_ci} 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ciint amdgpu_display_crtc_idx_to_irq_type(struct amdgpu_device *adev, int crtc) 159062306a36Sopenharmony_ci{ 159162306a36Sopenharmony_ci if (crtc < 0 || crtc >= adev->mode_info.num_crtc) 159262306a36Sopenharmony_ci return AMDGPU_CRTC_IRQ_NONE; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci switch (crtc) { 159562306a36Sopenharmony_ci case 0: 159662306a36Sopenharmony_ci return AMDGPU_CRTC_IRQ_VBLANK1; 159762306a36Sopenharmony_ci case 1: 159862306a36Sopenharmony_ci return AMDGPU_CRTC_IRQ_VBLANK2; 159962306a36Sopenharmony_ci case 2: 160062306a36Sopenharmony_ci return AMDGPU_CRTC_IRQ_VBLANK3; 160162306a36Sopenharmony_ci case 3: 160262306a36Sopenharmony_ci return AMDGPU_CRTC_IRQ_VBLANK4; 160362306a36Sopenharmony_ci case 4: 160462306a36Sopenharmony_ci return AMDGPU_CRTC_IRQ_VBLANK5; 160562306a36Sopenharmony_ci case 5: 160662306a36Sopenharmony_ci return AMDGPU_CRTC_IRQ_VBLANK6; 160762306a36Sopenharmony_ci default: 160862306a36Sopenharmony_ci return AMDGPU_CRTC_IRQ_NONE; 160962306a36Sopenharmony_ci } 161062306a36Sopenharmony_ci} 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_cibool amdgpu_crtc_get_scanout_position(struct drm_crtc *crtc, 161362306a36Sopenharmony_ci bool in_vblank_irq, int *vpos, 161462306a36Sopenharmony_ci int *hpos, ktime_t *stime, ktime_t *etime, 161562306a36Sopenharmony_ci const struct drm_display_mode *mode) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 161862306a36Sopenharmony_ci unsigned int pipe = crtc->index; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci return amdgpu_display_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos, 162162306a36Sopenharmony_ci stime, etime, mode); 162262306a36Sopenharmony_ci} 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_cistatic bool 162562306a36Sopenharmony_ciamdgpu_display_robj_is_fb(struct amdgpu_device *adev, struct amdgpu_bo *robj) 162662306a36Sopenharmony_ci{ 162762306a36Sopenharmony_ci struct drm_device *dev = adev_to_drm(adev); 162862306a36Sopenharmony_ci struct drm_fb_helper *fb_helper = dev->fb_helper; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci if (!fb_helper || !fb_helper->buffer) 163162306a36Sopenharmony_ci return false; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci if (gem_to_amdgpu_bo(fb_helper->buffer->gem) != robj) 163462306a36Sopenharmony_ci return false; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci return true; 163762306a36Sopenharmony_ci} 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ciint amdgpu_display_suspend_helper(struct amdgpu_device *adev) 164062306a36Sopenharmony_ci{ 164162306a36Sopenharmony_ci struct drm_device *dev = adev_to_drm(adev); 164262306a36Sopenharmony_ci struct drm_crtc *crtc; 164362306a36Sopenharmony_ci struct drm_connector *connector; 164462306a36Sopenharmony_ci struct drm_connector_list_iter iter; 164562306a36Sopenharmony_ci int r; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci drm_kms_helper_poll_disable(dev); 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci /* turn off display hw */ 165062306a36Sopenharmony_ci drm_modeset_lock_all(dev); 165162306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &iter); 165262306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &iter) 165362306a36Sopenharmony_ci drm_helper_connector_dpms(connector, 165462306a36Sopenharmony_ci DRM_MODE_DPMS_OFF); 165562306a36Sopenharmony_ci drm_connector_list_iter_end(&iter); 165662306a36Sopenharmony_ci drm_modeset_unlock_all(dev); 165762306a36Sopenharmony_ci /* unpin the front buffers and cursors */ 165862306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 165962306a36Sopenharmony_ci struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); 166062306a36Sopenharmony_ci struct drm_framebuffer *fb = crtc->primary->fb; 166162306a36Sopenharmony_ci struct amdgpu_bo *robj; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci if (amdgpu_crtc->cursor_bo && !adev->enable_virtual_display) { 166462306a36Sopenharmony_ci struct amdgpu_bo *aobj = gem_to_amdgpu_bo(amdgpu_crtc->cursor_bo); 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci r = amdgpu_bo_reserve(aobj, true); 166762306a36Sopenharmony_ci if (r == 0) { 166862306a36Sopenharmony_ci amdgpu_bo_unpin(aobj); 166962306a36Sopenharmony_ci amdgpu_bo_unreserve(aobj); 167062306a36Sopenharmony_ci } 167162306a36Sopenharmony_ci } 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci if (!fb || !fb->obj[0]) 167462306a36Sopenharmony_ci continue; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci robj = gem_to_amdgpu_bo(fb->obj[0]); 167762306a36Sopenharmony_ci if (!amdgpu_display_robj_is_fb(adev, robj)) { 167862306a36Sopenharmony_ci r = amdgpu_bo_reserve(robj, true); 167962306a36Sopenharmony_ci if (r == 0) { 168062306a36Sopenharmony_ci amdgpu_bo_unpin(robj); 168162306a36Sopenharmony_ci amdgpu_bo_unreserve(robj); 168262306a36Sopenharmony_ci } 168362306a36Sopenharmony_ci } 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci return 0; 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ciint amdgpu_display_resume_helper(struct amdgpu_device *adev) 168962306a36Sopenharmony_ci{ 169062306a36Sopenharmony_ci struct drm_device *dev = adev_to_drm(adev); 169162306a36Sopenharmony_ci struct drm_connector *connector; 169262306a36Sopenharmony_ci struct drm_connector_list_iter iter; 169362306a36Sopenharmony_ci struct drm_crtc *crtc; 169462306a36Sopenharmony_ci int r; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci /* pin cursors */ 169762306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 169862306a36Sopenharmony_ci struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci if (amdgpu_crtc->cursor_bo && !adev->enable_virtual_display) { 170162306a36Sopenharmony_ci struct amdgpu_bo *aobj = gem_to_amdgpu_bo(amdgpu_crtc->cursor_bo); 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci r = amdgpu_bo_reserve(aobj, true); 170462306a36Sopenharmony_ci if (r == 0) { 170562306a36Sopenharmony_ci r = amdgpu_bo_pin(aobj, AMDGPU_GEM_DOMAIN_VRAM); 170662306a36Sopenharmony_ci if (r != 0) 170762306a36Sopenharmony_ci dev_err(adev->dev, "Failed to pin cursor BO (%d)\n", r); 170862306a36Sopenharmony_ci amdgpu_crtc->cursor_addr = amdgpu_bo_gpu_offset(aobj); 170962306a36Sopenharmony_ci amdgpu_bo_unreserve(aobj); 171062306a36Sopenharmony_ci } 171162306a36Sopenharmony_ci } 171262306a36Sopenharmony_ci } 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci drm_helper_resume_force_mode(dev); 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci /* turn on display hw */ 171762306a36Sopenharmony_ci drm_modeset_lock_all(dev); 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &iter); 172062306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &iter) 172162306a36Sopenharmony_ci drm_helper_connector_dpms(connector, 172262306a36Sopenharmony_ci DRM_MODE_DPMS_ON); 172362306a36Sopenharmony_ci drm_connector_list_iter_end(&iter); 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci drm_modeset_unlock_all(dev); 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci drm_kms_helper_poll_enable(dev); 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci return 0; 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 1732