162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 462306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 562306a36Sopenharmony_ci#include <drm/drm_vblank.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "amdgpu.h" 862306a36Sopenharmony_ci#ifdef CONFIG_DRM_AMDGPU_SI 962306a36Sopenharmony_ci#include "dce_v6_0.h" 1062306a36Sopenharmony_ci#endif 1162306a36Sopenharmony_ci#ifdef CONFIG_DRM_AMDGPU_CIK 1262306a36Sopenharmony_ci#include "dce_v8_0.h" 1362306a36Sopenharmony_ci#endif 1462306a36Sopenharmony_ci#include "dce_v10_0.h" 1562306a36Sopenharmony_ci#include "dce_v11_0.h" 1662306a36Sopenharmony_ci#include "ivsrcid/ivsrcid_vislands30.h" 1762306a36Sopenharmony_ci#include "amdgpu_vkms.h" 1862306a36Sopenharmony_ci#include "amdgpu_display.h" 1962306a36Sopenharmony_ci#include "atom.h" 2062306a36Sopenharmony_ci#include "amdgpu_irq.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/** 2362306a36Sopenharmony_ci * DOC: amdgpu_vkms 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * The amdgpu vkms interface provides a virtual KMS interface for several use 2662306a36Sopenharmony_ci * cases: devices without display hardware, platforms where the actual display 2762306a36Sopenharmony_ci * hardware is not useful (e.g., servers), SR-IOV virtual functions, device 2862306a36Sopenharmony_ci * emulation/simulation, and device bring up prior to display hardware being 2962306a36Sopenharmony_ci * usable. We previously emulated a legacy KMS interface, but there was a desire 3062306a36Sopenharmony_ci * to move to the atomic KMS interface. The vkms driver did everything we 3162306a36Sopenharmony_ci * needed, but we wanted KMS support natively in the driver without buffer 3262306a36Sopenharmony_ci * sharing and the ability to support an instance of VKMS per device. We first 3362306a36Sopenharmony_ci * looked at splitting vkms into a stub driver and a helper module that other 3462306a36Sopenharmony_ci * drivers could use to implement a virtual display, but this strategy ended up 3562306a36Sopenharmony_ci * being messy due to driver specific callbacks needed for buffer management. 3662306a36Sopenharmony_ci * Ultimately, it proved easier to import the vkms code as it mostly used core 3762306a36Sopenharmony_ci * drm helpers anyway. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic const u32 amdgpu_vkms_formats[] = { 4162306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic enum hrtimer_restart amdgpu_vkms_vblank_simulate(struct hrtimer *timer) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct amdgpu_crtc *amdgpu_crtc = container_of(timer, struct amdgpu_crtc, vblank_timer); 4762306a36Sopenharmony_ci struct drm_crtc *crtc = &amdgpu_crtc->base; 4862306a36Sopenharmony_ci struct amdgpu_vkms_output *output = drm_crtc_to_amdgpu_vkms_output(crtc); 4962306a36Sopenharmony_ci u64 ret_overrun; 5062306a36Sopenharmony_ci bool ret; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci ret_overrun = hrtimer_forward_now(&amdgpu_crtc->vblank_timer, 5362306a36Sopenharmony_ci output->period_ns); 5462306a36Sopenharmony_ci if (ret_overrun != 1) 5562306a36Sopenharmony_ci DRM_WARN("%s: vblank timer overrun\n", __func__); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci ret = drm_crtc_handle_vblank(crtc); 5862306a36Sopenharmony_ci /* Don't queue timer again when vblank is disabled. */ 5962306a36Sopenharmony_ci if (!ret) 6062306a36Sopenharmony_ci return HRTIMER_NORESTART; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return HRTIMER_RESTART; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int amdgpu_vkms_enable_vblank(struct drm_crtc *crtc) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 6862306a36Sopenharmony_ci unsigned int pipe = drm_crtc_index(crtc); 6962306a36Sopenharmony_ci struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; 7062306a36Sopenharmony_ci struct amdgpu_vkms_output *out = drm_crtc_to_amdgpu_vkms_output(crtc); 7162306a36Sopenharmony_ci struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci drm_calc_timestamping_constants(crtc, &crtc->mode); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci out->period_ns = ktime_set(0, vblank->framedur_ns); 7662306a36Sopenharmony_ci hrtimer_start(&amdgpu_crtc->vblank_timer, out->period_ns, HRTIMER_MODE_REL); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void amdgpu_vkms_disable_vblank(struct drm_crtc *crtc) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci hrtimer_try_to_cancel(&amdgpu_crtc->vblank_timer); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic bool amdgpu_vkms_get_vblank_timestamp(struct drm_crtc *crtc, 8962306a36Sopenharmony_ci int *max_error, 9062306a36Sopenharmony_ci ktime_t *vblank_time, 9162306a36Sopenharmony_ci bool in_vblank_irq) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 9462306a36Sopenharmony_ci unsigned int pipe = crtc->index; 9562306a36Sopenharmony_ci struct amdgpu_vkms_output *output = drm_crtc_to_amdgpu_vkms_output(crtc); 9662306a36Sopenharmony_ci struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; 9762306a36Sopenharmony_ci struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (!READ_ONCE(vblank->enabled)) { 10062306a36Sopenharmony_ci *vblank_time = ktime_get(); 10162306a36Sopenharmony_ci return true; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci *vblank_time = READ_ONCE(amdgpu_crtc->vblank_timer.node.expires); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (WARN_ON(*vblank_time == vblank->time)) 10762306a36Sopenharmony_ci return true; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * To prevent races we roll the hrtimer forward before we do any 11162306a36Sopenharmony_ci * interrupt processing - this is how real hw works (the interrupt is 11262306a36Sopenharmony_ci * only generated after all the vblank registers are updated) and what 11362306a36Sopenharmony_ci * the vblank core expects. Therefore we need to always correct the 11462306a36Sopenharmony_ci * timestampe by one frame. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci *vblank_time -= output->period_ns; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return true; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic const struct drm_crtc_funcs amdgpu_vkms_crtc_funcs = { 12262306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 12362306a36Sopenharmony_ci .destroy = drm_crtc_cleanup, 12462306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 12562306a36Sopenharmony_ci .reset = drm_atomic_helper_crtc_reset, 12662306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 12762306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 12862306a36Sopenharmony_ci .enable_vblank = amdgpu_vkms_enable_vblank, 12962306a36Sopenharmony_ci .disable_vblank = amdgpu_vkms_disable_vblank, 13062306a36Sopenharmony_ci .get_vblank_timestamp = amdgpu_vkms_get_vblank_timestamp, 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void amdgpu_vkms_crtc_atomic_enable(struct drm_crtc *crtc, 13462306a36Sopenharmony_ci struct drm_atomic_state *state) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void amdgpu_vkms_crtc_atomic_disable(struct drm_crtc *crtc, 14062306a36Sopenharmony_ci struct drm_atomic_state *state) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci drm_crtc_vblank_off(crtc); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void amdgpu_vkms_crtc_atomic_flush(struct drm_crtc *crtc, 14662306a36Sopenharmony_ci struct drm_atomic_state *state) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci unsigned long flags; 14962306a36Sopenharmony_ci if (crtc->state->event) { 15062306a36Sopenharmony_ci spin_lock_irqsave(&crtc->dev->event_lock, flags); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (drm_crtc_vblank_get(crtc) != 0) 15362306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 15462306a36Sopenharmony_ci else 15562306a36Sopenharmony_ci drm_crtc_arm_vblank_event(crtc, crtc->state->event); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci crtc->state->event = NULL; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs amdgpu_vkms_crtc_helper_funcs = { 16462306a36Sopenharmony_ci .atomic_flush = amdgpu_vkms_crtc_atomic_flush, 16562306a36Sopenharmony_ci .atomic_enable = amdgpu_vkms_crtc_atomic_enable, 16662306a36Sopenharmony_ci .atomic_disable = amdgpu_vkms_crtc_atomic_disable, 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int amdgpu_vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, 17062306a36Sopenharmony_ci struct drm_plane *primary, struct drm_plane *cursor) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct amdgpu_device *adev = drm_to_adev(dev); 17362306a36Sopenharmony_ci struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); 17462306a36Sopenharmony_ci int ret; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ret = drm_crtc_init_with_planes(dev, crtc, primary, cursor, 17762306a36Sopenharmony_ci &amdgpu_vkms_crtc_funcs, NULL); 17862306a36Sopenharmony_ci if (ret) { 17962306a36Sopenharmony_ci DRM_ERROR("Failed to init CRTC\n"); 18062306a36Sopenharmony_ci return ret; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci drm_crtc_helper_add(crtc, &amdgpu_vkms_crtc_helper_funcs); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci amdgpu_crtc->crtc_id = drm_crtc_index(crtc); 18662306a36Sopenharmony_ci adev->mode_info.crtcs[drm_crtc_index(crtc)] = amdgpu_crtc; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci amdgpu_crtc->pll_id = ATOM_PPLL_INVALID; 18962306a36Sopenharmony_ci amdgpu_crtc->encoder = NULL; 19062306a36Sopenharmony_ci amdgpu_crtc->connector = NULL; 19162306a36Sopenharmony_ci amdgpu_crtc->vsync_timer_enabled = AMDGPU_IRQ_STATE_DISABLE; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci hrtimer_init(&amdgpu_crtc->vblank_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 19462306a36Sopenharmony_ci amdgpu_crtc->vblank_timer.function = &amdgpu_vkms_vblank_simulate; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return ret; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const struct drm_connector_funcs amdgpu_vkms_connector_funcs = { 20062306a36Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 20162306a36Sopenharmony_ci .destroy = drm_connector_cleanup, 20262306a36Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 20362306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 20462306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 20562306a36Sopenharmony_ci}; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int amdgpu_vkms_conn_get_modes(struct drm_connector *connector) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 21062306a36Sopenharmony_ci struct drm_display_mode *mode = NULL; 21162306a36Sopenharmony_ci unsigned i; 21262306a36Sopenharmony_ci static const struct mode_size { 21362306a36Sopenharmony_ci int w; 21462306a36Sopenharmony_ci int h; 21562306a36Sopenharmony_ci } common_modes[] = { 21662306a36Sopenharmony_ci { 640, 480}, 21762306a36Sopenharmony_ci { 720, 480}, 21862306a36Sopenharmony_ci { 800, 600}, 21962306a36Sopenharmony_ci { 848, 480}, 22062306a36Sopenharmony_ci {1024, 768}, 22162306a36Sopenharmony_ci {1152, 768}, 22262306a36Sopenharmony_ci {1280, 720}, 22362306a36Sopenharmony_ci {1280, 800}, 22462306a36Sopenharmony_ci {1280, 854}, 22562306a36Sopenharmony_ci {1280, 960}, 22662306a36Sopenharmony_ci {1280, 1024}, 22762306a36Sopenharmony_ci {1440, 900}, 22862306a36Sopenharmony_ci {1400, 1050}, 22962306a36Sopenharmony_ci {1680, 1050}, 23062306a36Sopenharmony_ci {1600, 1200}, 23162306a36Sopenharmony_ci {1920, 1080}, 23262306a36Sopenharmony_ci {1920, 1200}, 23362306a36Sopenharmony_ci {2560, 1440}, 23462306a36Sopenharmony_ci {4096, 3112}, 23562306a36Sopenharmony_ci {3656, 2664}, 23662306a36Sopenharmony_ci {3840, 2160}, 23762306a36Sopenharmony_ci {4096, 2160}, 23862306a36Sopenharmony_ci }; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(common_modes); i++) { 24162306a36Sopenharmony_ci mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, 60, false, false, false); 24262306a36Sopenharmony_ci if (!mode) 24362306a36Sopenharmony_ci continue; 24462306a36Sopenharmony_ci drm_mode_probed_add(connector, mode); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return ARRAY_SIZE(common_modes); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs amdgpu_vkms_conn_helper_funcs = { 25362306a36Sopenharmony_ci .get_modes = amdgpu_vkms_conn_get_modes, 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic const struct drm_plane_funcs amdgpu_vkms_plane_funcs = { 25762306a36Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 25862306a36Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 25962306a36Sopenharmony_ci .destroy = drm_plane_cleanup, 26062306a36Sopenharmony_ci .reset = drm_atomic_helper_plane_reset, 26162306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 26262306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 26362306a36Sopenharmony_ci}; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic void amdgpu_vkms_plane_atomic_update(struct drm_plane *plane, 26662306a36Sopenharmony_ci struct drm_atomic_state *old_state) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci return; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int amdgpu_vkms_plane_atomic_check(struct drm_plane *plane, 27262306a36Sopenharmony_ci struct drm_atomic_state *state) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 27562306a36Sopenharmony_ci plane); 27662306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 27762306a36Sopenharmony_ci int ret; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (!new_plane_state->fb || WARN_ON(!new_plane_state->crtc)) 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci crtc_state = drm_atomic_get_crtc_state(state, 28362306a36Sopenharmony_ci new_plane_state->crtc); 28462306a36Sopenharmony_ci if (IS_ERR(crtc_state)) 28562306a36Sopenharmony_ci return PTR_ERR(crtc_state); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, 28862306a36Sopenharmony_ci DRM_PLANE_NO_SCALING, 28962306a36Sopenharmony_ci DRM_PLANE_NO_SCALING, 29062306a36Sopenharmony_ci false, true); 29162306a36Sopenharmony_ci if (ret != 0) 29262306a36Sopenharmony_ci return ret; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* for now primary plane must be visible and full screen */ 29562306a36Sopenharmony_ci if (!new_plane_state->visible) 29662306a36Sopenharmony_ci return -EINVAL; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int amdgpu_vkms_prepare_fb(struct drm_plane *plane, 30262306a36Sopenharmony_ci struct drm_plane_state *new_state) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct amdgpu_framebuffer *afb; 30562306a36Sopenharmony_ci struct drm_gem_object *obj; 30662306a36Sopenharmony_ci struct amdgpu_device *adev; 30762306a36Sopenharmony_ci struct amdgpu_bo *rbo; 30862306a36Sopenharmony_ci uint32_t domain; 30962306a36Sopenharmony_ci int r; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (!new_state->fb) { 31262306a36Sopenharmony_ci DRM_DEBUG_KMS("No FB bound\n"); 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci afb = to_amdgpu_framebuffer(new_state->fb); 31662306a36Sopenharmony_ci obj = new_state->fb->obj[0]; 31762306a36Sopenharmony_ci rbo = gem_to_amdgpu_bo(obj); 31862306a36Sopenharmony_ci adev = amdgpu_ttm_adev(rbo->tbo.bdev); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci r = amdgpu_bo_reserve(rbo, true); 32162306a36Sopenharmony_ci if (r) { 32262306a36Sopenharmony_ci dev_err(adev->dev, "fail to reserve bo (%d)\n", r); 32362306a36Sopenharmony_ci return r; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci r = dma_resv_reserve_fences(rbo->tbo.base.resv, 1); 32762306a36Sopenharmony_ci if (r) { 32862306a36Sopenharmony_ci dev_err(adev->dev, "allocating fence slot failed (%d)\n", r); 32962306a36Sopenharmony_ci goto error_unlock; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (plane->type != DRM_PLANE_TYPE_CURSOR) 33362306a36Sopenharmony_ci domain = amdgpu_display_supported_domains(adev, rbo->flags); 33462306a36Sopenharmony_ci else 33562306a36Sopenharmony_ci domain = AMDGPU_GEM_DOMAIN_VRAM; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci r = amdgpu_bo_pin(rbo, domain); 33862306a36Sopenharmony_ci if (unlikely(r != 0)) { 33962306a36Sopenharmony_ci if (r != -ERESTARTSYS) 34062306a36Sopenharmony_ci DRM_ERROR("Failed to pin framebuffer with error %d\n", r); 34162306a36Sopenharmony_ci goto error_unlock; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci r = amdgpu_ttm_alloc_gart(&rbo->tbo); 34562306a36Sopenharmony_ci if (unlikely(r != 0)) { 34662306a36Sopenharmony_ci DRM_ERROR("%p bind failed\n", rbo); 34762306a36Sopenharmony_ci goto error_unpin; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci amdgpu_bo_unreserve(rbo); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci afb->address = amdgpu_bo_gpu_offset(rbo); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci amdgpu_bo_ref(rbo); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return 0; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cierror_unpin: 35962306a36Sopenharmony_ci amdgpu_bo_unpin(rbo); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cierror_unlock: 36262306a36Sopenharmony_ci amdgpu_bo_unreserve(rbo); 36362306a36Sopenharmony_ci return r; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void amdgpu_vkms_cleanup_fb(struct drm_plane *plane, 36762306a36Sopenharmony_ci struct drm_plane_state *old_state) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct amdgpu_bo *rbo; 37062306a36Sopenharmony_ci int r; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (!old_state->fb) 37362306a36Sopenharmony_ci return; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci rbo = gem_to_amdgpu_bo(old_state->fb->obj[0]); 37662306a36Sopenharmony_ci r = amdgpu_bo_reserve(rbo, false); 37762306a36Sopenharmony_ci if (unlikely(r)) { 37862306a36Sopenharmony_ci DRM_ERROR("failed to reserve rbo before unpin\n"); 37962306a36Sopenharmony_ci return; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci amdgpu_bo_unpin(rbo); 38362306a36Sopenharmony_ci amdgpu_bo_unreserve(rbo); 38462306a36Sopenharmony_ci amdgpu_bo_unref(&rbo); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs amdgpu_vkms_primary_helper_funcs = { 38862306a36Sopenharmony_ci .atomic_update = amdgpu_vkms_plane_atomic_update, 38962306a36Sopenharmony_ci .atomic_check = amdgpu_vkms_plane_atomic_check, 39062306a36Sopenharmony_ci .prepare_fb = amdgpu_vkms_prepare_fb, 39162306a36Sopenharmony_ci .cleanup_fb = amdgpu_vkms_cleanup_fb, 39262306a36Sopenharmony_ci}; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic struct drm_plane *amdgpu_vkms_plane_init(struct drm_device *dev, 39562306a36Sopenharmony_ci enum drm_plane_type type, 39662306a36Sopenharmony_ci int index) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct drm_plane *plane; 39962306a36Sopenharmony_ci int ret; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci plane = kzalloc(sizeof(*plane), GFP_KERNEL); 40262306a36Sopenharmony_ci if (!plane) 40362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci ret = drm_universal_plane_init(dev, plane, 1 << index, 40662306a36Sopenharmony_ci &amdgpu_vkms_plane_funcs, 40762306a36Sopenharmony_ci amdgpu_vkms_formats, 40862306a36Sopenharmony_ci ARRAY_SIZE(amdgpu_vkms_formats), 40962306a36Sopenharmony_ci NULL, type, NULL); 41062306a36Sopenharmony_ci if (ret) { 41162306a36Sopenharmony_ci kfree(plane); 41262306a36Sopenharmony_ci return ERR_PTR(ret); 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci drm_plane_helper_add(plane, &amdgpu_vkms_primary_helper_funcs); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return plane; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic int amdgpu_vkms_output_init(struct drm_device *dev, struct 42162306a36Sopenharmony_ci amdgpu_vkms_output *output, int index) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct drm_connector *connector = &output->connector; 42462306a36Sopenharmony_ci struct drm_encoder *encoder = &output->encoder; 42562306a36Sopenharmony_ci struct drm_crtc *crtc = &output->crtc.base; 42662306a36Sopenharmony_ci struct drm_plane *primary, *cursor = NULL; 42762306a36Sopenharmony_ci int ret; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci primary = amdgpu_vkms_plane_init(dev, DRM_PLANE_TYPE_PRIMARY, index); 43062306a36Sopenharmony_ci if (IS_ERR(primary)) 43162306a36Sopenharmony_ci return PTR_ERR(primary); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci ret = amdgpu_vkms_crtc_init(dev, crtc, primary, cursor); 43462306a36Sopenharmony_ci if (ret) 43562306a36Sopenharmony_ci goto err_crtc; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci ret = drm_connector_init(dev, connector, &amdgpu_vkms_connector_funcs, 43862306a36Sopenharmony_ci DRM_MODE_CONNECTOR_VIRTUAL); 43962306a36Sopenharmony_ci if (ret) { 44062306a36Sopenharmony_ci DRM_ERROR("Failed to init connector\n"); 44162306a36Sopenharmony_ci goto err_connector; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci drm_connector_helper_add(connector, &amdgpu_vkms_conn_helper_funcs); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_VIRTUAL); 44762306a36Sopenharmony_ci if (ret) { 44862306a36Sopenharmony_ci DRM_ERROR("Failed to init encoder\n"); 44962306a36Sopenharmony_ci goto err_encoder; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci encoder->possible_crtcs = 1 << index; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci ret = drm_connector_attach_encoder(connector, encoder); 45462306a36Sopenharmony_ci if (ret) { 45562306a36Sopenharmony_ci DRM_ERROR("Failed to attach connector to encoder\n"); 45662306a36Sopenharmony_ci goto err_attach; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci drm_mode_config_reset(dev); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cierr_attach: 46462306a36Sopenharmony_ci drm_encoder_cleanup(encoder); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cierr_encoder: 46762306a36Sopenharmony_ci drm_connector_cleanup(connector); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cierr_connector: 47062306a36Sopenharmony_ci drm_crtc_cleanup(crtc); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cierr_crtc: 47362306a36Sopenharmony_ci drm_plane_cleanup(primary); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return ret; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ciconst struct drm_mode_config_funcs amdgpu_vkms_mode_funcs = { 47962306a36Sopenharmony_ci .fb_create = amdgpu_display_user_framebuffer_create, 48062306a36Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 48162306a36Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 48262306a36Sopenharmony_ci}; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int amdgpu_vkms_sw_init(void *handle) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci int r, i; 48762306a36Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci adev->amdgpu_vkms_output = kcalloc(adev->mode_info.num_crtc, 49062306a36Sopenharmony_ci sizeof(struct amdgpu_vkms_output), GFP_KERNEL); 49162306a36Sopenharmony_ci if (!adev->amdgpu_vkms_output) 49262306a36Sopenharmony_ci return -ENOMEM; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci adev_to_drm(adev)->max_vblank_count = 0; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci adev_to_drm(adev)->mode_config.funcs = &amdgpu_vkms_mode_funcs; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci adev_to_drm(adev)->mode_config.max_width = XRES_MAX; 49962306a36Sopenharmony_ci adev_to_drm(adev)->mode_config.max_height = YRES_MAX; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci adev_to_drm(adev)->mode_config.preferred_depth = 24; 50262306a36Sopenharmony_ci adev_to_drm(adev)->mode_config.prefer_shadow = 1; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci adev_to_drm(adev)->mode_config.fb_modifiers_not_supported = true; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci r = amdgpu_display_modeset_create_props(adev); 50762306a36Sopenharmony_ci if (r) 50862306a36Sopenharmony_ci return r; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* allocate crtcs, encoders, connectors */ 51162306a36Sopenharmony_ci for (i = 0; i < adev->mode_info.num_crtc; i++) { 51262306a36Sopenharmony_ci r = amdgpu_vkms_output_init(adev_to_drm(adev), &adev->amdgpu_vkms_output[i], i); 51362306a36Sopenharmony_ci if (r) 51462306a36Sopenharmony_ci return r; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci r = drm_vblank_init(adev_to_drm(adev), adev->mode_info.num_crtc); 51862306a36Sopenharmony_ci if (r) 51962306a36Sopenharmony_ci return r; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci drm_kms_helper_poll_init(adev_to_drm(adev)); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci adev->mode_info.mode_config_initialized = true; 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic int amdgpu_vkms_sw_fini(void *handle) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 53062306a36Sopenharmony_ci int i = 0; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci for (i = 0; i < adev->mode_info.num_crtc; i++) 53362306a36Sopenharmony_ci if (adev->mode_info.crtcs[i]) 53462306a36Sopenharmony_ci hrtimer_cancel(&adev->mode_info.crtcs[i]->vblank_timer); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci drm_kms_helper_poll_fini(adev_to_drm(adev)); 53762306a36Sopenharmony_ci drm_mode_config_cleanup(adev_to_drm(adev)); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci adev->mode_info.mode_config_initialized = false; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci kfree(adev->mode_info.bios_hardcoded_edid); 54262306a36Sopenharmony_ci kfree(adev->amdgpu_vkms_output); 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic int amdgpu_vkms_hw_init(void *handle) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci switch (adev->asic_type) { 55162306a36Sopenharmony_ci#ifdef CONFIG_DRM_AMDGPU_SI 55262306a36Sopenharmony_ci case CHIP_TAHITI: 55362306a36Sopenharmony_ci case CHIP_PITCAIRN: 55462306a36Sopenharmony_ci case CHIP_VERDE: 55562306a36Sopenharmony_ci case CHIP_OLAND: 55662306a36Sopenharmony_ci dce_v6_0_disable_dce(adev); 55762306a36Sopenharmony_ci break; 55862306a36Sopenharmony_ci#endif 55962306a36Sopenharmony_ci#ifdef CONFIG_DRM_AMDGPU_CIK 56062306a36Sopenharmony_ci case CHIP_BONAIRE: 56162306a36Sopenharmony_ci case CHIP_HAWAII: 56262306a36Sopenharmony_ci case CHIP_KAVERI: 56362306a36Sopenharmony_ci case CHIP_KABINI: 56462306a36Sopenharmony_ci case CHIP_MULLINS: 56562306a36Sopenharmony_ci dce_v8_0_disable_dce(adev); 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci#endif 56862306a36Sopenharmony_ci case CHIP_FIJI: 56962306a36Sopenharmony_ci case CHIP_TONGA: 57062306a36Sopenharmony_ci dce_v10_0_disable_dce(adev); 57162306a36Sopenharmony_ci break; 57262306a36Sopenharmony_ci case CHIP_CARRIZO: 57362306a36Sopenharmony_ci case CHIP_STONEY: 57462306a36Sopenharmony_ci case CHIP_POLARIS10: 57562306a36Sopenharmony_ci case CHIP_POLARIS11: 57662306a36Sopenharmony_ci case CHIP_VEGAM: 57762306a36Sopenharmony_ci dce_v11_0_disable_dce(adev); 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci case CHIP_TOPAZ: 58062306a36Sopenharmony_ci#ifdef CONFIG_DRM_AMDGPU_SI 58162306a36Sopenharmony_ci case CHIP_HAINAN: 58262306a36Sopenharmony_ci#endif 58362306a36Sopenharmony_ci /* no DCE */ 58462306a36Sopenharmony_ci break; 58562306a36Sopenharmony_ci default: 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic int amdgpu_vkms_hw_fini(void *handle) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci return 0; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int amdgpu_vkms_suspend(void *handle) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 59962306a36Sopenharmony_ci int r; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci r = drm_mode_config_helper_suspend(adev_to_drm(adev)); 60262306a36Sopenharmony_ci if (r) 60362306a36Sopenharmony_ci return r; 60462306a36Sopenharmony_ci return amdgpu_vkms_hw_fini(handle); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int amdgpu_vkms_resume(void *handle) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 61062306a36Sopenharmony_ci int r; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci r = amdgpu_vkms_hw_init(handle); 61362306a36Sopenharmony_ci if (r) 61462306a36Sopenharmony_ci return r; 61562306a36Sopenharmony_ci return drm_mode_config_helper_resume(adev_to_drm(adev)); 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic bool amdgpu_vkms_is_idle(void *handle) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci return true; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int amdgpu_vkms_wait_for_idle(void *handle) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic int amdgpu_vkms_soft_reset(void *handle) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic int amdgpu_vkms_set_clockgating_state(void *handle, 63462306a36Sopenharmony_ci enum amd_clockgating_state state) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci return 0; 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic int amdgpu_vkms_set_powergating_state(void *handle, 64062306a36Sopenharmony_ci enum amd_powergating_state state) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci return 0; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic const struct amd_ip_funcs amdgpu_vkms_ip_funcs = { 64662306a36Sopenharmony_ci .name = "amdgpu_vkms", 64762306a36Sopenharmony_ci .early_init = NULL, 64862306a36Sopenharmony_ci .late_init = NULL, 64962306a36Sopenharmony_ci .sw_init = amdgpu_vkms_sw_init, 65062306a36Sopenharmony_ci .sw_fini = amdgpu_vkms_sw_fini, 65162306a36Sopenharmony_ci .hw_init = amdgpu_vkms_hw_init, 65262306a36Sopenharmony_ci .hw_fini = amdgpu_vkms_hw_fini, 65362306a36Sopenharmony_ci .suspend = amdgpu_vkms_suspend, 65462306a36Sopenharmony_ci .resume = amdgpu_vkms_resume, 65562306a36Sopenharmony_ci .is_idle = amdgpu_vkms_is_idle, 65662306a36Sopenharmony_ci .wait_for_idle = amdgpu_vkms_wait_for_idle, 65762306a36Sopenharmony_ci .soft_reset = amdgpu_vkms_soft_reset, 65862306a36Sopenharmony_ci .set_clockgating_state = amdgpu_vkms_set_clockgating_state, 65962306a36Sopenharmony_ci .set_powergating_state = amdgpu_vkms_set_powergating_state, 66062306a36Sopenharmony_ci}; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ciconst struct amdgpu_ip_block_version amdgpu_vkms_ip_block = 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci .type = AMD_IP_BLOCK_TYPE_DCE, 66562306a36Sopenharmony_ci .major = 1, 66662306a36Sopenharmony_ci .minor = 0, 66762306a36Sopenharmony_ci .rev = 0, 66862306a36Sopenharmony_ci .funcs = &amdgpu_vkms_ip_funcs, 66962306a36Sopenharmony_ci}; 67062306a36Sopenharmony_ci 671