1473683a0SRodrigo Siqueira // SPDX-License-Identifier: MIT
2473683a0SRodrigo Siqueira /*
3473683a0SRodrigo Siqueira  * Copyright 2022 Advanced Micro Devices, Inc.
4473683a0SRodrigo Siqueira  *
5473683a0SRodrigo Siqueira  * Permission is hereby granted, free of charge, to any person obtaining a
6473683a0SRodrigo Siqueira  * copy of this software and associated documentation files (the "Software"),
7473683a0SRodrigo Siqueira  * to deal in the Software without restriction, including without limitation
8473683a0SRodrigo Siqueira  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9473683a0SRodrigo Siqueira  * and/or sell copies of the Software, and to permit persons to whom the
10473683a0SRodrigo Siqueira  * Software is furnished to do so, subject to the following conditions:
11473683a0SRodrigo Siqueira  *
12473683a0SRodrigo Siqueira  * The above copyright notice and this permission notice shall be included in
13473683a0SRodrigo Siqueira  * all copies or substantial portions of the Software.
14473683a0SRodrigo Siqueira  *
15473683a0SRodrigo Siqueira  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16473683a0SRodrigo Siqueira  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17473683a0SRodrigo Siqueira  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18473683a0SRodrigo Siqueira  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19473683a0SRodrigo Siqueira  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20473683a0SRodrigo Siqueira  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21473683a0SRodrigo Siqueira  * OTHER DEALINGS IN THE SOFTWARE.
22473683a0SRodrigo Siqueira  *
23473683a0SRodrigo Siqueira  * Authors: AMD
24473683a0SRodrigo Siqueira  *
25473683a0SRodrigo Siqueira  */
26473683a0SRodrigo Siqueira #include <drm/drm_vblank.h>
27473683a0SRodrigo Siqueira #include <drm/drm_atomic_helper.h>
28473683a0SRodrigo Siqueira 
29473683a0SRodrigo Siqueira #include "dc.h"
30473683a0SRodrigo Siqueira #include "amdgpu.h"
31473683a0SRodrigo Siqueira #include "amdgpu_dm_psr.h"
325950efe2STom Chung #include "amdgpu_dm_replay.h"
33473683a0SRodrigo Siqueira #include "amdgpu_dm_crtc.h"
34473683a0SRodrigo Siqueira #include "amdgpu_dm_plane.h"
35473683a0SRodrigo Siqueira #include "amdgpu_dm_trace.h"
36473683a0SRodrigo Siqueira #include "amdgpu_dm_debugfs.h"
37473683a0SRodrigo Siqueira 
38a88b19b1SFangzhi Zuo #define HPD_DETECTION_PERIOD_uS 2000000
3960612f75SRoman Li #define HPD_DETECTION_TIME_uS 100000
40afca033fSRoman Li 
amdgpu_dm_crtc_handle_vblank(struct amdgpu_crtc * acrtc)416c5e25a0SDavid Tadokoro void amdgpu_dm_crtc_handle_vblank(struct amdgpu_crtc *acrtc)
42473683a0SRodrigo Siqueira {
43473683a0SRodrigo Siqueira 	struct drm_crtc *crtc = &acrtc->base;
44473683a0SRodrigo Siqueira 	struct drm_device *dev = crtc->dev;
45473683a0SRodrigo Siqueira 	unsigned long flags;
46473683a0SRodrigo Siqueira 
47473683a0SRodrigo Siqueira 	drm_crtc_handle_vblank(crtc);
48473683a0SRodrigo Siqueira 
49473683a0SRodrigo Siqueira 	spin_lock_irqsave(&dev->event_lock, flags);
50473683a0SRodrigo Siqueira 
51473683a0SRodrigo Siqueira 	/* Send completion event for cursor-only commits */
52473683a0SRodrigo Siqueira 	if (acrtc->event && acrtc->pflip_status != AMDGPU_FLIP_SUBMITTED) {
53473683a0SRodrigo Siqueira 		drm_crtc_send_vblank_event(crtc, acrtc->event);
54473683a0SRodrigo Siqueira 		drm_crtc_vblank_put(crtc);
55473683a0SRodrigo Siqueira 		acrtc->event = NULL;
56473683a0SRodrigo Siqueira 	}
57473683a0SRodrigo Siqueira 
58473683a0SRodrigo Siqueira 	spin_unlock_irqrestore(&dev->event_lock, flags);
59473683a0SRodrigo Siqueira }
60473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_modeset_required(struct drm_crtc_state * crtc_state,struct dc_stream_state * new_stream,struct dc_stream_state * old_stream)616c5e25a0SDavid Tadokoro bool amdgpu_dm_crtc_modeset_required(struct drm_crtc_state *crtc_state,
62473683a0SRodrigo Siqueira 			     struct dc_stream_state *new_stream,
63473683a0SRodrigo Siqueira 			     struct dc_stream_state *old_stream)
64473683a0SRodrigo Siqueira {
65473683a0SRodrigo Siqueira 	return crtc_state->active && drm_atomic_crtc_needs_modeset(crtc_state);
66473683a0SRodrigo Siqueira }
67473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_vrr_active_irq(struct amdgpu_crtc * acrtc)686c5e25a0SDavid Tadokoro bool amdgpu_dm_crtc_vrr_active_irq(struct amdgpu_crtc *acrtc)
69473683a0SRodrigo Siqueira 
70473683a0SRodrigo Siqueira {
71473683a0SRodrigo Siqueira 	return acrtc->dm_irq_params.freesync_config.state ==
72473683a0SRodrigo Siqueira 		       VRR_STATE_ACTIVE_VARIABLE ||
73473683a0SRodrigo Siqueira 	       acrtc->dm_irq_params.freesync_config.state ==
74473683a0SRodrigo Siqueira 		       VRR_STATE_ACTIVE_FIXED;
75473683a0SRodrigo Siqueira }
76473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc * crtc,bool enable)776c5e25a0SDavid Tadokoro int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable)
78473683a0SRodrigo Siqueira {
79473683a0SRodrigo Siqueira 	enum dc_irq_source irq_source;
80473683a0SRodrigo Siqueira 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
81473683a0SRodrigo Siqueira 	struct amdgpu_device *adev = drm_to_adev(crtc->dev);
82473683a0SRodrigo Siqueira 	int rc;
83473683a0SRodrigo Siqueira 
844936458bSHamza Mahfooz 	if (acrtc->otg_inst == -1)
854936458bSHamza Mahfooz 		return 0;
864936458bSHamza Mahfooz 
87473683a0SRodrigo Siqueira 	irq_source = IRQ_TYPE_VUPDATE + acrtc->otg_inst;
88473683a0SRodrigo Siqueira 
89473683a0SRodrigo Siqueira 	rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY;
90473683a0SRodrigo Siqueira 
91473683a0SRodrigo Siqueira 	DRM_DEBUG_VBL("crtc %d - vupdate irq %sabling: r=%d\n",
92473683a0SRodrigo Siqueira 		      acrtc->crtc_id, enable ? "en" : "dis", rc);
93473683a0SRodrigo Siqueira 	return rc;
94473683a0SRodrigo Siqueira }
95473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_vrr_active(const struct dm_crtc_state * dm_state)9667edb81dSTom Chung bool amdgpu_dm_crtc_vrr_active(const struct dm_crtc_state *dm_state)
97473683a0SRodrigo Siqueira {
98473683a0SRodrigo Siqueira 	return dm_state->freesync_config.state == VRR_STATE_ACTIVE_VARIABLE ||
99473683a0SRodrigo Siqueira 	       dm_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED;
100473683a0SRodrigo Siqueira }
101473683a0SRodrigo Siqueira 
1025950efe2STom Chung /**
1038feca9f3SSrinivasan Shanmugam  * amdgpu_dm_crtc_set_panel_sr_feature() - Manage panel self-refresh features.
1048feca9f3SSrinivasan Shanmugam  *
1058feca9f3SSrinivasan Shanmugam  * @vblank_work:    is a pointer to a struct vblank_control_work object.
1068feca9f3SSrinivasan Shanmugam  * @vblank_enabled: indicates whether the DRM vblank counter is currently
1078feca9f3SSrinivasan Shanmugam  *                  enabled (true) or disabled (false).
1088feca9f3SSrinivasan Shanmugam  * @allow_sr_entry: represents whether entry into the self-refresh mode is
1098feca9f3SSrinivasan Shanmugam  *                  allowed (true) or not allowed (false).
1108feca9f3SSrinivasan Shanmugam  *
1115950efe2STom Chung  * The DRM vblank counter enable/disable action is used as the trigger to enable
1125950efe2STom Chung  * or disable various panel self-refresh features:
1135950efe2STom Chung  *
1145950efe2STom Chung  * Panel Replay and PSR SU
1155950efe2STom Chung  * - Enable when:
11669a46ce1STom Chung  *      - VRR is disabled
1175950efe2STom Chung  *      - vblank counter is disabled
1185950efe2STom Chung  *      - entry is allowed: usermode demonstrates an adequate number of fast
1195950efe2STom Chung  *        commits)
1205950efe2STom Chung  *     - CRC capture window isn't active
1215950efe2STom Chung  * - Keep enabled even when vblank counter gets enabled
1225950efe2STom Chung  *
1235950efe2STom Chung  * PSR1
1245950efe2STom Chung  * - Enable condition same as above
1255950efe2STom Chung  * - Disable when vblank counter is enabled
1265950efe2STom Chung  */
amdgpu_dm_crtc_set_panel_sr_feature(struct vblank_control_work * vblank_work,bool vblank_enabled,bool allow_sr_entry)1275950efe2STom Chung static void amdgpu_dm_crtc_set_panel_sr_feature(
1285950efe2STom Chung 	struct vblank_control_work *vblank_work,
1295950efe2STom Chung 	bool vblank_enabled, bool allow_sr_entry)
1305950efe2STom Chung {
1315950efe2STom Chung 	struct dc_link *link = vblank_work->stream->link;
1325950efe2STom Chung 	bool is_sr_active = (link->replay_settings.replay_allow_active ||
1335950efe2STom Chung 				 link->psr_settings.psr_allow_active);
1345950efe2STom Chung 	bool is_crc_window_active = false;
13569a46ce1STom Chung 	bool vrr_active = amdgpu_dm_crtc_vrr_active_irq(vblank_work->acrtc);
1365950efe2STom Chung 
1375950efe2STom Chung #ifdef CONFIG_DRM_AMD_SECURE_DISPLAY
1385950efe2STom Chung 	is_crc_window_active =
1395950efe2STom Chung 		amdgpu_dm_crc_window_is_activated(&vblank_work->acrtc->base);
1405950efe2STom Chung #endif
1415950efe2STom Chung 
14269a46ce1STom Chung 	if (link->replay_settings.replay_feature_enabled && !vrr_active &&
1435950efe2STom Chung 		allow_sr_entry && !is_sr_active && !is_crc_window_active) {
1445950efe2STom Chung 		amdgpu_dm_replay_enable(vblank_work->stream, true);
1455950efe2STom Chung 	} else if (vblank_enabled) {
1465950efe2STom Chung 		if (link->psr_settings.psr_version < DC_PSR_VERSION_SU_1 && is_sr_active)
147ff2e4d87SLeo Li 			amdgpu_dm_psr_disable(vblank_work->stream, false);
14869a46ce1STom Chung 	} else if (link->psr_settings.psr_feature_enabled && !vrr_active &&
1495950efe2STom Chung 		allow_sr_entry && !is_sr_active && !is_crc_window_active) {
15013b3d6bdSHersen Wu 
15113b3d6bdSHersen Wu 		struct amdgpu_dm_connector *aconn =
15213b3d6bdSHersen Wu 			(struct amdgpu_dm_connector *) vblank_work->stream->dm_stream_context;
15313b3d6bdSHersen Wu 
154afca033fSRoman Li 		if (!aconn->disallow_edp_enter_psr) {
155afca033fSRoman Li 			struct amdgpu_display_manager *dm = vblank_work->dm;
156afca033fSRoman Li 
1575950efe2STom Chung 			amdgpu_dm_psr_enable(vblank_work->stream);
158afca033fSRoman Li 			if (dm->idle_workqueue &&
159442702b4SRoman Li 			    (dm->dc->config.disable_ips == DMUB_IPS_ENABLE) &&
160afca033fSRoman Li 			    dm->dc->idle_optimizations_allowed &&
161afca033fSRoman Li 			    dm->idle_workqueue->enable &&
162afca033fSRoman Li 			    !dm->idle_workqueue->running)
163afca033fSRoman Li 				schedule_work(&dm->idle_workqueue->work);
1645950efe2STom Chung 		}
1655950efe2STom Chung 	}
166afca033fSRoman Li }
167afca033fSRoman Li 
amdgpu_dm_is_headless(struct amdgpu_device * adev)1689862ef7bSRoman Li bool amdgpu_dm_is_headless(struct amdgpu_device *adev)
1699862ef7bSRoman Li {
1709862ef7bSRoman Li 	struct drm_connector *connector;
1719862ef7bSRoman Li 	struct drm_connector_list_iter iter;
1729862ef7bSRoman Li 	struct drm_device *dev;
1739862ef7bSRoman Li 	bool is_headless = true;
1749862ef7bSRoman Li 
1759862ef7bSRoman Li 	if (adev == NULL)
1769862ef7bSRoman Li 		return true;
1779862ef7bSRoman Li 
1789862ef7bSRoman Li 	dev = adev->dm.ddev;
1799862ef7bSRoman Li 
1809862ef7bSRoman Li 	drm_connector_list_iter_begin(dev, &iter);
1819862ef7bSRoman Li 	drm_for_each_connector_iter(connector, &iter) {
1829862ef7bSRoman Li 
1839862ef7bSRoman Li 		if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
1849862ef7bSRoman Li 			continue;
1859862ef7bSRoman Li 
1869862ef7bSRoman Li 		if (connector->status == connector_status_connected) {
1879862ef7bSRoman Li 			is_headless = false;
1889862ef7bSRoman Li 			break;
1899862ef7bSRoman Li 		}
1909862ef7bSRoman Li 	}
1919862ef7bSRoman Li 	drm_connector_list_iter_end(&iter);
1929862ef7bSRoman Li 	return is_headless;
1939862ef7bSRoman Li }
1949862ef7bSRoman Li 
amdgpu_dm_idle_worker(struct work_struct * work)195afca033fSRoman Li static void amdgpu_dm_idle_worker(struct work_struct *work)
196afca033fSRoman Li {
197afca033fSRoman Li 	struct idle_workqueue *idle_work;
198afca033fSRoman Li 
199afca033fSRoman Li 	idle_work = container_of(work, struct idle_workqueue, work);
200afca033fSRoman Li 	idle_work->dm->idle_workqueue->running = true;
2019862ef7bSRoman Li 
2029862ef7bSRoman Li 	while (idle_work->enable) {
203afca033fSRoman Li 		fsleep(HPD_DETECTION_PERIOD_uS);
204afca033fSRoman Li 		mutex_lock(&idle_work->dm->dc_lock);
2059862ef7bSRoman Li 		if (!idle_work->dm->dc->idle_optimizations_allowed) {
2069862ef7bSRoman Li 			mutex_unlock(&idle_work->dm->dc_lock);
207afca033fSRoman Li 			break;
2089862ef7bSRoman Li 		}
209afca033fSRoman Li 		dc_allow_idle_optimizations(idle_work->dm->dc, false);
210afca033fSRoman Li 
211afca033fSRoman Li 		mutex_unlock(&idle_work->dm->dc_lock);
212afca033fSRoman Li 		fsleep(HPD_DETECTION_TIME_uS);
213afca033fSRoman Li 		mutex_lock(&idle_work->dm->dc_lock);
214afca033fSRoman Li 
2159862ef7bSRoman Li 		if (!amdgpu_dm_is_headless(idle_work->dm->adev) &&
2169862ef7bSRoman Li 		    !amdgpu_dm_psr_is_active_allowed(idle_work->dm)) {
2179862ef7bSRoman Li 			mutex_unlock(&idle_work->dm->dc_lock);
218afca033fSRoman Li 			break;
2199862ef7bSRoman Li 		}
220afca033fSRoman Li 
2219862ef7bSRoman Li 		if (idle_work->enable)
222afca033fSRoman Li 			dc_allow_idle_optimizations(idle_work->dm->dc, true);
223afca033fSRoman Li 		mutex_unlock(&idle_work->dm->dc_lock);
224afca033fSRoman Li 	}
225afca033fSRoman Li 	idle_work->dm->idle_workqueue->running = false;
226afca033fSRoman Li }
227afca033fSRoman Li 
idle_create_workqueue(struct amdgpu_device * adev)228afca033fSRoman Li struct idle_workqueue *idle_create_workqueue(struct amdgpu_device *adev)
229afca033fSRoman Li {
230afca033fSRoman Li 	struct idle_workqueue *idle_work;
231afca033fSRoman Li 
232afca033fSRoman Li 	idle_work = kzalloc(sizeof(*idle_work), GFP_KERNEL);
233afca033fSRoman Li 	if (ZERO_OR_NULL_PTR(idle_work))
234afca033fSRoman Li 		return NULL;
235afca033fSRoman Li 
236afca033fSRoman Li 	idle_work->dm = &adev->dm;
237afca033fSRoman Li 	idle_work->enable = false;
238afca033fSRoman Li 	idle_work->running = false;
239afca033fSRoman Li 	INIT_WORK(&idle_work->work, amdgpu_dm_idle_worker);
240afca033fSRoman Li 
241afca033fSRoman Li 	return idle_work;
242afca033fSRoman Li }
2435950efe2STom Chung 
amdgpu_dm_crtc_vblank_control_worker(struct work_struct * work)2446ce4f9eeSRodrigo Siqueira static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
245473683a0SRodrigo Siqueira {
246473683a0SRodrigo Siqueira 	struct vblank_control_work *vblank_work =
247473683a0SRodrigo Siqueira 		container_of(work, struct vblank_control_work, work);
248473683a0SRodrigo Siqueira 	struct amdgpu_display_manager *dm = vblank_work->dm;
249*50f29eadSKenneth Feng 	struct amdgpu_device *adev = drm_to_adev(dm->ddev);
250*50f29eadSKenneth Feng 	int r;
251473683a0SRodrigo Siqueira 
252473683a0SRodrigo Siqueira 	mutex_lock(&dm->dc_lock);
253473683a0SRodrigo Siqueira 
254473683a0SRodrigo Siqueira 	if (vblank_work->enable)
255473683a0SRodrigo Siqueira 		dm->active_vblank_irq_count++;
256473683a0SRodrigo Siqueira 	else if (dm->active_vblank_irq_count)
257473683a0SRodrigo Siqueira 		dm->active_vblank_irq_count--;
258473683a0SRodrigo Siqueira 
2591b7ac448SAurabindo Pillai 	if (dm->active_vblank_irq_count > 0)
26017e68f89SLeo Li 		dc_allow_idle_optimizations(dm->dc, false);
261473683a0SRodrigo Siqueira 
262473683a0SRodrigo Siqueira 	/*
263473683a0SRodrigo Siqueira 	 * Control PSR based on vblank requirements from OS
264473683a0SRodrigo Siqueira 	 *
265473683a0SRodrigo Siqueira 	 * If panel supports PSR SU, there's no need to disable PSR when OS is
266473683a0SRodrigo Siqueira 	 * submitting fast atomic commits (we infer this by whether the OS
267473683a0SRodrigo Siqueira 	 * requests vblank events). Fast atomic commits will simply trigger a
268473683a0SRodrigo Siqueira 	 * full-frame-update (FFU); a specific case of selective-update (SU)
269473683a0SRodrigo Siqueira 	 * where the SU region is the full hactive*vactive region. See
270473683a0SRodrigo Siqueira 	 * fill_dc_dirty_rects().
271473683a0SRodrigo Siqueira 	 */
272ca628f0eSTom Chung 	if (vblank_work->stream && vblank_work->stream->link && vblank_work->acrtc) {
2735950efe2STom Chung 		amdgpu_dm_crtc_set_panel_sr_feature(
2745950efe2STom Chung 			vblank_work, vblank_work->enable,
275ca628f0eSTom Chung 			vblank_work->acrtc->dm_irq_params.allow_sr_entry);
276473683a0SRodrigo Siqueira 	}
277473683a0SRodrigo Siqueira 
278*50f29eadSKenneth Feng 	if (dm->active_vblank_irq_count == 0) {
279*50f29eadSKenneth Feng 		r = amdgpu_dpm_pause_power_profile(adev, true);
280*50f29eadSKenneth Feng 		if (r)
281*50f29eadSKenneth Feng 			dev_warn(adev->dev, "failed to set default power profile mode\n");
28217e68f89SLeo Li 		dc_allow_idle_optimizations(dm->dc, true);
283*50f29eadSKenneth Feng 		r = amdgpu_dpm_pause_power_profile(adev, false);
284*50f29eadSKenneth Feng 		if (r)
285*50f29eadSKenneth Feng 			dev_warn(adev->dev, "failed to restore the power profile mode\n");
286*50f29eadSKenneth Feng 	}
28717e68f89SLeo Li 
288473683a0SRodrigo Siqueira 	mutex_unlock(&dm->dc_lock);
289473683a0SRodrigo Siqueira 
290473683a0SRodrigo Siqueira 	dc_stream_release(vblank_work->stream);
291473683a0SRodrigo Siqueira 
292473683a0SRodrigo Siqueira 	kfree(vblank_work);
293473683a0SRodrigo Siqueira }
294473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_set_vblank(struct drm_crtc * crtc,bool enable)2956ce4f9eeSRodrigo Siqueira static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
296473683a0SRodrigo Siqueira {
297473683a0SRodrigo Siqueira 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
298473683a0SRodrigo Siqueira 	struct amdgpu_device *adev = drm_to_adev(crtc->dev);
299473683a0SRodrigo Siqueira 	struct dm_crtc_state *acrtc_state = to_dm_crtc_state(crtc->state);
300473683a0SRodrigo Siqueira 	struct amdgpu_display_manager *dm = &adev->dm;
301473683a0SRodrigo Siqueira 	struct vblank_control_work *work;
3027fb363c5SLeo Li 	int irq_type;
303473683a0SRodrigo Siqueira 	int rc = 0;
304473683a0SRodrigo Siqueira 
3054936458bSHamza Mahfooz 	if (acrtc->otg_inst == -1)
3064936458bSHamza Mahfooz 		goto skip;
3074936458bSHamza Mahfooz 
3087fb363c5SLeo Li 	irq_type = amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id);
3097fb363c5SLeo Li 
310473683a0SRodrigo Siqueira 	if (enable) {
311473683a0SRodrigo Siqueira 		/* vblank irq on -> Only need vupdate irq in vrr mode */
3126c5e25a0SDavid Tadokoro 		if (amdgpu_dm_crtc_vrr_active(acrtc_state))
3136c5e25a0SDavid Tadokoro 			rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, true);
314473683a0SRodrigo Siqueira 	} else {
315473683a0SRodrigo Siqueira 		/* vblank irq off -> vupdate irq off */
3166c5e25a0SDavid Tadokoro 		rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, false);
317473683a0SRodrigo Siqueira 	}
318473683a0SRodrigo Siqueira 
319473683a0SRodrigo Siqueira 	if (rc)
320473683a0SRodrigo Siqueira 		return rc;
321473683a0SRodrigo Siqueira 
3227fb363c5SLeo Li 	/* crtc vblank or vstartup interrupt */
3237fb363c5SLeo Li 	if (enable) {
3247fb363c5SLeo Li 		rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type);
3257fb363c5SLeo Li 		drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc);
3267fb363c5SLeo Li 	} else {
3277fb363c5SLeo Li 		rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type);
3287fb363c5SLeo Li 		drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc);
3297fb363c5SLeo Li 	}
330c8b5a95bSAlan Liu 
331c8b5a95bSAlan Liu 	if (rc)
332c8b5a95bSAlan Liu 		return rc;
333473683a0SRodrigo Siqueira 
3347fb363c5SLeo Li 	/*
3357fb363c5SLeo Li 	 * hubp surface flip interrupt
3367fb363c5SLeo Li 	 *
3377fb363c5SLeo Li 	 * We have no guarantee that the frontend index maps to the same
3387fb363c5SLeo Li 	 * backend index - some even map to more than one.
3397fb363c5SLeo Li 	 *
3407fb363c5SLeo Li 	 * TODO: Use a different interrupt or check DC itself for the mapping.
3417fb363c5SLeo Li 	 */
3427fb363c5SLeo Li 	if (enable) {
3437fb363c5SLeo Li 		rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type);
3447fb363c5SLeo Li 		drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc);
3457fb363c5SLeo Li 	} else {
3467fb363c5SLeo Li 		rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type);
3477fb363c5SLeo Li 		drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc);
3487fb363c5SLeo Li 	}
3497fb363c5SLeo Li 
3507fb363c5SLeo Li 	if (rc)
3517fb363c5SLeo Li 		return rc;
3527fb363c5SLeo Li 
3537fb363c5SLeo Li #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
3547fb363c5SLeo Li 	/* crtc vline0 interrupt, only available on DCN+ */
3557fb363c5SLeo Li 	if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
3567fb363c5SLeo Li 		if (enable) {
3577fb363c5SLeo Li 			rc = amdgpu_irq_get(adev, &adev->vline0_irq, irq_type);
3587fb363c5SLeo Li 			drm_dbg_vbl(crtc->dev, "Get vline0_irq ret=%d\n", rc);
3597fb363c5SLeo Li 		} else {
3607fb363c5SLeo Li 			rc = amdgpu_irq_put(adev, &adev->vline0_irq, irq_type);
3617fb363c5SLeo Li 			drm_dbg_vbl(crtc->dev, "Put vline0_irq ret=%d\n", rc);
3627fb363c5SLeo Li 		}
3637fb363c5SLeo Li 
3647fb363c5SLeo Li 		if (rc)
3657fb363c5SLeo Li 			return rc;
3667fb363c5SLeo Li 	}
3677fb363c5SLeo Li #endif
3684936458bSHamza Mahfooz skip:
369473683a0SRodrigo Siqueira 	if (amdgpu_in_reset(adev))
370473683a0SRodrigo Siqueira 		return 0;
371473683a0SRodrigo Siqueira 
372473683a0SRodrigo Siqueira 	if (dm->vblank_control_workqueue) {
373473683a0SRodrigo Siqueira 		work = kzalloc(sizeof(*work), GFP_ATOMIC);
374473683a0SRodrigo Siqueira 		if (!work)
375473683a0SRodrigo Siqueira 			return -ENOMEM;
376473683a0SRodrigo Siqueira 
3776ce4f9eeSRodrigo Siqueira 		INIT_WORK(&work->work, amdgpu_dm_crtc_vblank_control_worker);
378473683a0SRodrigo Siqueira 		work->dm = dm;
379473683a0SRodrigo Siqueira 		work->acrtc = acrtc;
380473683a0SRodrigo Siqueira 		work->enable = enable;
381473683a0SRodrigo Siqueira 
382473683a0SRodrigo Siqueira 		if (acrtc_state->stream) {
383473683a0SRodrigo Siqueira 			dc_stream_retain(acrtc_state->stream);
384473683a0SRodrigo Siqueira 			work->stream = acrtc_state->stream;
385473683a0SRodrigo Siqueira 		}
386473683a0SRodrigo Siqueira 
387473683a0SRodrigo Siqueira 		queue_work(dm->vblank_control_workqueue, &work->work);
388473683a0SRodrigo Siqueira 	}
389473683a0SRodrigo Siqueira 
390473683a0SRodrigo Siqueira 	return 0;
391473683a0SRodrigo Siqueira }
392473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_enable_vblank(struct drm_crtc * crtc)3936c5e25a0SDavid Tadokoro int amdgpu_dm_crtc_enable_vblank(struct drm_crtc *crtc)
394473683a0SRodrigo Siqueira {
3956ce4f9eeSRodrigo Siqueira 	return amdgpu_dm_crtc_set_vblank(crtc, true);
396473683a0SRodrigo Siqueira }
397473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_disable_vblank(struct drm_crtc * crtc)3986c5e25a0SDavid Tadokoro void amdgpu_dm_crtc_disable_vblank(struct drm_crtc *crtc)
399473683a0SRodrigo Siqueira {
4006ce4f9eeSRodrigo Siqueira 	amdgpu_dm_crtc_set_vblank(crtc, false);
401473683a0SRodrigo Siqueira }
402473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_destroy_state(struct drm_crtc * crtc,struct drm_crtc_state * state)4036ce4f9eeSRodrigo Siqueira static void amdgpu_dm_crtc_destroy_state(struct drm_crtc *crtc,
404473683a0SRodrigo Siqueira 				  struct drm_crtc_state *state)
405473683a0SRodrigo Siqueira {
406473683a0SRodrigo Siqueira 	struct dm_crtc_state *cur = to_dm_crtc_state(state);
407473683a0SRodrigo Siqueira 
408473683a0SRodrigo Siqueira 	/* TODO Destroy dc_stream objects are stream object is flattened */
409473683a0SRodrigo Siqueira 	if (cur->stream)
410473683a0SRodrigo Siqueira 		dc_stream_release(cur->stream);
411473683a0SRodrigo Siqueira 
412473683a0SRodrigo Siqueira 
413473683a0SRodrigo Siqueira 	__drm_atomic_helper_crtc_destroy_state(state);
414473683a0SRodrigo Siqueira 
415473683a0SRodrigo Siqueira 
416473683a0SRodrigo Siqueira 	kfree(state);
417473683a0SRodrigo Siqueira }
418473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_duplicate_state(struct drm_crtc * crtc)4196ce4f9eeSRodrigo Siqueira static struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct drm_crtc *crtc)
420473683a0SRodrigo Siqueira {
421473683a0SRodrigo Siqueira 	struct dm_crtc_state *state, *cur;
422473683a0SRodrigo Siqueira 
423473683a0SRodrigo Siqueira 	cur = to_dm_crtc_state(crtc->state);
424473683a0SRodrigo Siqueira 
425473683a0SRodrigo Siqueira 	if (WARN_ON(!crtc->state))
426473683a0SRodrigo Siqueira 		return NULL;
427473683a0SRodrigo Siqueira 
428473683a0SRodrigo Siqueira 	state = kzalloc(sizeof(*state), GFP_KERNEL);
429473683a0SRodrigo Siqueira 	if (!state)
430473683a0SRodrigo Siqueira 		return NULL;
431473683a0SRodrigo Siqueira 
432473683a0SRodrigo Siqueira 	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
433473683a0SRodrigo Siqueira 
434473683a0SRodrigo Siqueira 	if (cur->stream) {
435473683a0SRodrigo Siqueira 		state->stream = cur->stream;
436473683a0SRodrigo Siqueira 		dc_stream_retain(state->stream);
437473683a0SRodrigo Siqueira 	}
438473683a0SRodrigo Siqueira 
439473683a0SRodrigo Siqueira 	state->active_planes = cur->active_planes;
440473683a0SRodrigo Siqueira 	state->vrr_infopacket = cur->vrr_infopacket;
441473683a0SRodrigo Siqueira 	state->abm_level = cur->abm_level;
442473683a0SRodrigo Siqueira 	state->vrr_supported = cur->vrr_supported;
443473683a0SRodrigo Siqueira 	state->freesync_config = cur->freesync_config;
444473683a0SRodrigo Siqueira 	state->cm_has_degamma = cur->cm_has_degamma;
445473683a0SRodrigo Siqueira 	state->cm_is_degamma_srgb = cur->cm_is_degamma_srgb;
4460f5afa19SMelissa Wen 	state->regamma_tf = cur->regamma_tf;
447c13423c6SLeo Li 	state->crc_skip_count = cur->crc_skip_count;
448473683a0SRodrigo Siqueira 	state->mpo_requested = cur->mpo_requested;
4491b04dccaSLeo Li 	state->cursor_mode = cur->cursor_mode;
450473683a0SRodrigo Siqueira 	/* TODO Duplicate dc_stream after objects are stream object is flattened */
451473683a0SRodrigo Siqueira 
452473683a0SRodrigo Siqueira 	return &state->base;
453473683a0SRodrigo Siqueira }
454473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_destroy(struct drm_crtc * crtc)455473683a0SRodrigo Siqueira static void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc)
456473683a0SRodrigo Siqueira {
457473683a0SRodrigo Siqueira 	drm_crtc_cleanup(crtc);
458473683a0SRodrigo Siqueira 	kfree(crtc);
459473683a0SRodrigo Siqueira }
460473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_reset_state(struct drm_crtc * crtc)4616ce4f9eeSRodrigo Siqueira static void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc)
462473683a0SRodrigo Siqueira {
463473683a0SRodrigo Siqueira 	struct dm_crtc_state *state;
464473683a0SRodrigo Siqueira 
465473683a0SRodrigo Siqueira 	if (crtc->state)
4666ce4f9eeSRodrigo Siqueira 		amdgpu_dm_crtc_destroy_state(crtc, crtc->state);
467473683a0SRodrigo Siqueira 
468473683a0SRodrigo Siqueira 	state = kzalloc(sizeof(*state), GFP_KERNEL);
469473683a0SRodrigo Siqueira 	if (WARN_ON(!state))
470473683a0SRodrigo Siqueira 		return;
471473683a0SRodrigo Siqueira 
472473683a0SRodrigo Siqueira 	__drm_atomic_helper_crtc_reset(crtc, &state->base);
473473683a0SRodrigo Siqueira }
474473683a0SRodrigo Siqueira 
475473683a0SRodrigo Siqueira #ifdef CONFIG_DEBUG_FS
amdgpu_dm_crtc_late_register(struct drm_crtc * crtc)476473683a0SRodrigo Siqueira static int amdgpu_dm_crtc_late_register(struct drm_crtc *crtc)
477473683a0SRodrigo Siqueira {
478473683a0SRodrigo Siqueira 	crtc_debugfs_init(crtc);
479473683a0SRodrigo Siqueira 
480473683a0SRodrigo Siqueira 	return 0;
481473683a0SRodrigo Siqueira }
482473683a0SRodrigo Siqueira #endif
483473683a0SRodrigo Siqueira 
4840f5afa19SMelissa Wen #ifdef AMD_PRIVATE_COLOR
4850f5afa19SMelissa Wen /**
486c6ef0a22SMelissa Wen  * dm_crtc_additional_color_mgmt - enable additional color properties
4870f5afa19SMelissa Wen  * @crtc: DRM CRTC
4880f5afa19SMelissa Wen  *
4890f5afa19SMelissa Wen  * This function lets the driver enable post-blending CRTC regamma transfer
4900f5afa19SMelissa Wen  * function property in addition to DRM CRTC gamma LUT. Default value means
4910f5afa19SMelissa Wen  * linear transfer function, which is the default CRTC gamma LUT behaviour
4920f5afa19SMelissa Wen  * without this property.
4930f5afa19SMelissa Wen  */
4940f5afa19SMelissa Wen static void
dm_crtc_additional_color_mgmt(struct drm_crtc * crtc)4950f5afa19SMelissa Wen dm_crtc_additional_color_mgmt(struct drm_crtc *crtc)
4960f5afa19SMelissa Wen {
4970f5afa19SMelissa Wen 	struct amdgpu_device *adev = drm_to_adev(crtc->dev);
4980f5afa19SMelissa Wen 
4990f5afa19SMelissa Wen 	if (adev->dm.dc->caps.color.mpc.ogam_ram)
5000f5afa19SMelissa Wen 		drm_object_attach_property(&crtc->base,
5010f5afa19SMelissa Wen 					   adev->mode_info.regamma_tf_property,
5020f5afa19SMelissa Wen 					   AMDGPU_TRANSFER_FUNCTION_DEFAULT);
5030f5afa19SMelissa Wen }
5040f5afa19SMelissa Wen 
5050f5afa19SMelissa Wen static int
amdgpu_dm_atomic_crtc_set_property(struct drm_crtc * crtc,struct drm_crtc_state * state,struct drm_property * property,uint64_t val)5060f5afa19SMelissa Wen amdgpu_dm_atomic_crtc_set_property(struct drm_crtc *crtc,
5070f5afa19SMelissa Wen 				   struct drm_crtc_state *state,
5080f5afa19SMelissa Wen 				   struct drm_property *property,
5090f5afa19SMelissa Wen 				   uint64_t val)
5100f5afa19SMelissa Wen {
5110f5afa19SMelissa Wen 	struct amdgpu_device *adev = drm_to_adev(crtc->dev);
5120f5afa19SMelissa Wen 	struct dm_crtc_state *acrtc_state = to_dm_crtc_state(state);
5130f5afa19SMelissa Wen 
5140f5afa19SMelissa Wen 	if (property == adev->mode_info.regamma_tf_property) {
5150f5afa19SMelissa Wen 		if (acrtc_state->regamma_tf != val) {
5160f5afa19SMelissa Wen 			acrtc_state->regamma_tf = val;
5170f5afa19SMelissa Wen 			acrtc_state->base.color_mgmt_changed |= 1;
5180f5afa19SMelissa Wen 		}
5190f5afa19SMelissa Wen 	} else {
5200f5afa19SMelissa Wen 		drm_dbg_atomic(crtc->dev,
5210f5afa19SMelissa Wen 			       "[CRTC:%d:%s] unknown property [PROP:%d:%s]]\n",
5220f5afa19SMelissa Wen 			       crtc->base.id, crtc->name,
5230f5afa19SMelissa Wen 			       property->base.id, property->name);
5240f5afa19SMelissa Wen 		return -EINVAL;
5250f5afa19SMelissa Wen 	}
5260f5afa19SMelissa Wen 
5270f5afa19SMelissa Wen 	return 0;
5280f5afa19SMelissa Wen }
5290f5afa19SMelissa Wen 
5300f5afa19SMelissa Wen static int
amdgpu_dm_atomic_crtc_get_property(struct drm_crtc * crtc,const struct drm_crtc_state * state,struct drm_property * property,uint64_t * val)5310f5afa19SMelissa Wen amdgpu_dm_atomic_crtc_get_property(struct drm_crtc *crtc,
5320f5afa19SMelissa Wen 				   const struct drm_crtc_state *state,
5330f5afa19SMelissa Wen 				   struct drm_property *property,
5340f5afa19SMelissa Wen 				   uint64_t *val)
5350f5afa19SMelissa Wen {
5360f5afa19SMelissa Wen 	struct amdgpu_device *adev = drm_to_adev(crtc->dev);
5370f5afa19SMelissa Wen 	struct dm_crtc_state *acrtc_state = to_dm_crtc_state(state);
5380f5afa19SMelissa Wen 
5390f5afa19SMelissa Wen 	if (property == adev->mode_info.regamma_tf_property)
5400f5afa19SMelissa Wen 		*val = acrtc_state->regamma_tf;
5410f5afa19SMelissa Wen 	else
5420f5afa19SMelissa Wen 		return -EINVAL;
5430f5afa19SMelissa Wen 
5440f5afa19SMelissa Wen 	return 0;
5450f5afa19SMelissa Wen }
5460f5afa19SMelissa Wen #endif
5470f5afa19SMelissa Wen 
548473683a0SRodrigo Siqueira /* Implemented only the options currently available for the driver */
549473683a0SRodrigo Siqueira static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
5506ce4f9eeSRodrigo Siqueira 	.reset = amdgpu_dm_crtc_reset_state,
551473683a0SRodrigo Siqueira 	.destroy = amdgpu_dm_crtc_destroy,
552473683a0SRodrigo Siqueira 	.set_config = drm_atomic_helper_set_config,
553473683a0SRodrigo Siqueira 	.page_flip = drm_atomic_helper_page_flip,
5546ce4f9eeSRodrigo Siqueira 	.atomic_duplicate_state = amdgpu_dm_crtc_duplicate_state,
5556ce4f9eeSRodrigo Siqueira 	.atomic_destroy_state = amdgpu_dm_crtc_destroy_state,
556473683a0SRodrigo Siqueira 	.set_crc_source = amdgpu_dm_crtc_set_crc_source,
557473683a0SRodrigo Siqueira 	.verify_crc_source = amdgpu_dm_crtc_verify_crc_source,
558473683a0SRodrigo Siqueira 	.get_crc_sources = amdgpu_dm_crtc_get_crc_sources,
559473683a0SRodrigo Siqueira 	.get_vblank_counter = amdgpu_get_vblank_counter_kms,
5606c5e25a0SDavid Tadokoro 	.enable_vblank = amdgpu_dm_crtc_enable_vblank,
5616c5e25a0SDavid Tadokoro 	.disable_vblank = amdgpu_dm_crtc_disable_vblank,
562473683a0SRodrigo Siqueira 	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
563473683a0SRodrigo Siqueira #if defined(CONFIG_DEBUG_FS)
564473683a0SRodrigo Siqueira 	.late_register = amdgpu_dm_crtc_late_register,
565473683a0SRodrigo Siqueira #endif
5660f5afa19SMelissa Wen #ifdef AMD_PRIVATE_COLOR
5670f5afa19SMelissa Wen 	.atomic_set_property = amdgpu_dm_atomic_crtc_set_property,
5680f5afa19SMelissa Wen 	.atomic_get_property = amdgpu_dm_atomic_crtc_get_property,
5690f5afa19SMelissa Wen #endif
570473683a0SRodrigo Siqueira };
571473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_helper_disable(struct drm_crtc * crtc)5726ce4f9eeSRodrigo Siqueira static void amdgpu_dm_crtc_helper_disable(struct drm_crtc *crtc)
573473683a0SRodrigo Siqueira {
574473683a0SRodrigo Siqueira }
575473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_count_crtc_active_planes(struct drm_crtc_state * new_crtc_state)5766ce4f9eeSRodrigo Siqueira static int amdgpu_dm_crtc_count_crtc_active_planes(struct drm_crtc_state *new_crtc_state)
577473683a0SRodrigo Siqueira {
578473683a0SRodrigo Siqueira 	struct drm_atomic_state *state = new_crtc_state->state;
579473683a0SRodrigo Siqueira 	struct drm_plane *plane;
580473683a0SRodrigo Siqueira 	int num_active = 0;
581473683a0SRodrigo Siqueira 
582473683a0SRodrigo Siqueira 	drm_for_each_plane_mask(plane, state->dev, new_crtc_state->plane_mask) {
583473683a0SRodrigo Siqueira 		struct drm_plane_state *new_plane_state;
584473683a0SRodrigo Siqueira 
585473683a0SRodrigo Siqueira 		/* Cursor planes are "fake". */
586473683a0SRodrigo Siqueira 		if (plane->type == DRM_PLANE_TYPE_CURSOR)
587473683a0SRodrigo Siqueira 			continue;
588473683a0SRodrigo Siqueira 
589473683a0SRodrigo Siqueira 		new_plane_state = drm_atomic_get_new_plane_state(state, plane);
590473683a0SRodrigo Siqueira 
591473683a0SRodrigo Siqueira 		if (!new_plane_state) {
592473683a0SRodrigo Siqueira 			/*
593473683a0SRodrigo Siqueira 			 * The plane is enable on the CRTC and hasn't changed
594473683a0SRodrigo Siqueira 			 * state. This means that it previously passed
595473683a0SRodrigo Siqueira 			 * validation and is therefore enabled.
596473683a0SRodrigo Siqueira 			 */
597473683a0SRodrigo Siqueira 			num_active += 1;
598473683a0SRodrigo Siqueira 			continue;
599473683a0SRodrigo Siqueira 		}
600473683a0SRodrigo Siqueira 
601473683a0SRodrigo Siqueira 		/* We need a framebuffer to be considered enabled. */
602473683a0SRodrigo Siqueira 		num_active += (new_plane_state->fb != NULL);
603473683a0SRodrigo Siqueira 	}
604473683a0SRodrigo Siqueira 
605473683a0SRodrigo Siqueira 	return num_active;
606473683a0SRodrigo Siqueira }
607473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc * crtc,struct drm_crtc_state * new_crtc_state)6086ce4f9eeSRodrigo Siqueira static void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc,
609473683a0SRodrigo Siqueira 						     struct drm_crtc_state *new_crtc_state)
610473683a0SRodrigo Siqueira {
611473683a0SRodrigo Siqueira 	struct dm_crtc_state *dm_new_crtc_state =
612473683a0SRodrigo Siqueira 		to_dm_crtc_state(new_crtc_state);
613473683a0SRodrigo Siqueira 
614473683a0SRodrigo Siqueira 	dm_new_crtc_state->active_planes = 0;
615473683a0SRodrigo Siqueira 
616473683a0SRodrigo Siqueira 	if (!dm_new_crtc_state->stream)
617473683a0SRodrigo Siqueira 		return;
618473683a0SRodrigo Siqueira 
619473683a0SRodrigo Siqueira 	dm_new_crtc_state->active_planes =
6206ce4f9eeSRodrigo Siqueira 		amdgpu_dm_crtc_count_crtc_active_planes(new_crtc_state);
621473683a0SRodrigo Siqueira }
622473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc * crtc,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)6236ce4f9eeSRodrigo Siqueira static bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc,
624473683a0SRodrigo Siqueira 				      const struct drm_display_mode *mode,
625473683a0SRodrigo Siqueira 				      struct drm_display_mode *adjusted_mode)
626473683a0SRodrigo Siqueira {
627473683a0SRodrigo Siqueira 	return true;
628473683a0SRodrigo Siqueira }
629473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_helper_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * state)6306ce4f9eeSRodrigo Siqueira static int amdgpu_dm_crtc_helper_atomic_check(struct drm_crtc *crtc,
631473683a0SRodrigo Siqueira 					      struct drm_atomic_state *state)
632473683a0SRodrigo Siqueira {
633473683a0SRodrigo Siqueira 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
634473683a0SRodrigo Siqueira 										crtc);
635473683a0SRodrigo Siqueira 	struct amdgpu_device *adev = drm_to_adev(crtc->dev);
636473683a0SRodrigo Siqueira 	struct dc *dc = adev->dm.dc;
637473683a0SRodrigo Siqueira 	struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(crtc_state);
638473683a0SRodrigo Siqueira 	int ret = -EINVAL;
639473683a0SRodrigo Siqueira 
640473683a0SRodrigo Siqueira 	trace_amdgpu_dm_crtc_atomic_check(crtc_state);
641473683a0SRodrigo Siqueira 
6426ce4f9eeSRodrigo Siqueira 	amdgpu_dm_crtc_update_crtc_active_planes(crtc, crtc_state);
643473683a0SRodrigo Siqueira 
644473683a0SRodrigo Siqueira 	if (WARN_ON(unlikely(!dm_crtc_state->stream &&
6456c5e25a0SDavid Tadokoro 			amdgpu_dm_crtc_modeset_required(crtc_state, NULL, dm_crtc_state->stream)))) {
646473683a0SRodrigo Siqueira 		return ret;
647473683a0SRodrigo Siqueira 	}
648473683a0SRodrigo Siqueira 
649473683a0SRodrigo Siqueira 	/*
650473683a0SRodrigo Siqueira 	 * We require the primary plane to be enabled whenever the CRTC is, otherwise
651473683a0SRodrigo Siqueira 	 * drm_mode_cursor_universal may end up trying to enable the cursor plane while all other
652473683a0SRodrigo Siqueira 	 * planes are disabled, which is not supported by the hardware. And there is legacy
653473683a0SRodrigo Siqueira 	 * userspace which stops using the HW cursor altogether in response to the resulting EINVAL.
654473683a0SRodrigo Siqueira 	 */
655473683a0SRodrigo Siqueira 	if (crtc_state->enable &&
656473683a0SRodrigo Siqueira 		!(crtc_state->plane_mask & drm_plane_mask(crtc->primary))) {
657473683a0SRodrigo Siqueira 		DRM_DEBUG_ATOMIC("Can't enable a CRTC without enabling the primary plane\n");
658473683a0SRodrigo Siqueira 		return -EINVAL;
659473683a0SRodrigo Siqueira 	}
660473683a0SRodrigo Siqueira 
6611ca67abaSSimon Ser 	/*
6621ca67abaSSimon Ser 	 * Only allow async flips for fast updates that don't change the FB
6631ca67abaSSimon Ser 	 * pitch, the DCC state, rotation, etc.
6641ca67abaSSimon Ser 	 */
6651ca67abaSSimon Ser 	if (crtc_state->async_flip &&
6661ca67abaSSimon Ser 	    dm_crtc_state->update_type != UPDATE_TYPE_FAST) {
6671ca67abaSSimon Ser 		drm_dbg_atomic(crtc->dev,
6681ca67abaSSimon Ser 			       "[CRTC:%d:%s] async flips are only supported for fast updates\n",
6691ca67abaSSimon Ser 			       crtc->base.id, crtc->name);
6701ca67abaSSimon Ser 		return -EINVAL;
6711ca67abaSSimon Ser 	}
6721ca67abaSSimon Ser 
673473683a0SRodrigo Siqueira 	/* In some use cases, like reset, no stream is attached */
674473683a0SRodrigo Siqueira 	if (!dm_crtc_state->stream)
675473683a0SRodrigo Siqueira 		return 0;
676473683a0SRodrigo Siqueira 
677473683a0SRodrigo Siqueira 	if (dc_validate_stream(dc, dm_crtc_state->stream) == DC_OK)
678473683a0SRodrigo Siqueira 		return 0;
679473683a0SRodrigo Siqueira 
680473683a0SRodrigo Siqueira 	DRM_DEBUG_ATOMIC("Failed DC stream validation\n");
681473683a0SRodrigo Siqueira 	return ret;
682473683a0SRodrigo Siqueira }
683473683a0SRodrigo Siqueira 
684473683a0SRodrigo Siqueira static const struct drm_crtc_helper_funcs amdgpu_dm_crtc_helper_funcs = {
6856ce4f9eeSRodrigo Siqueira 	.disable = amdgpu_dm_crtc_helper_disable,
6866ce4f9eeSRodrigo Siqueira 	.atomic_check = amdgpu_dm_crtc_helper_atomic_check,
6876ce4f9eeSRodrigo Siqueira 	.mode_fixup = amdgpu_dm_crtc_helper_mode_fixup,
688473683a0SRodrigo Siqueira 	.get_scanout_position = amdgpu_crtc_get_scanout_position,
689473683a0SRodrigo Siqueira };
690473683a0SRodrigo Siqueira 
amdgpu_dm_crtc_init(struct amdgpu_display_manager * dm,struct drm_plane * plane,uint32_t crtc_index)691473683a0SRodrigo Siqueira int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm,
692473683a0SRodrigo Siqueira 			       struct drm_plane *plane,
693473683a0SRodrigo Siqueira 			       uint32_t crtc_index)
694473683a0SRodrigo Siqueira {
695473683a0SRodrigo Siqueira 	struct amdgpu_crtc *acrtc = NULL;
696473683a0SRodrigo Siqueira 	struct drm_plane *cursor_plane;
697c17b7a58SMelissa Wen 	bool is_dcn;
698473683a0SRodrigo Siqueira 	int res = -ENOMEM;
699473683a0SRodrigo Siqueira 
700473683a0SRodrigo Siqueira 	cursor_plane = kzalloc(sizeof(*cursor_plane), GFP_KERNEL);
701473683a0SRodrigo Siqueira 	if (!cursor_plane)
702473683a0SRodrigo Siqueira 		goto fail;
703473683a0SRodrigo Siqueira 
704473683a0SRodrigo Siqueira 	cursor_plane->type = DRM_PLANE_TYPE_CURSOR;
705473683a0SRodrigo Siqueira 	res = amdgpu_dm_plane_init(dm, cursor_plane, 0, NULL);
706473683a0SRodrigo Siqueira 
707473683a0SRodrigo Siqueira 	acrtc = kzalloc(sizeof(struct amdgpu_crtc), GFP_KERNEL);
708473683a0SRodrigo Siqueira 	if (!acrtc)
709473683a0SRodrigo Siqueira 		goto fail;
710473683a0SRodrigo Siqueira 
711473683a0SRodrigo Siqueira 	res = drm_crtc_init_with_planes(
712473683a0SRodrigo Siqueira 			dm->ddev,
713473683a0SRodrigo Siqueira 			&acrtc->base,
714473683a0SRodrigo Siqueira 			plane,
715473683a0SRodrigo Siqueira 			cursor_plane,
716473683a0SRodrigo Siqueira 			&amdgpu_dm_crtc_funcs, NULL);
717473683a0SRodrigo Siqueira 
718473683a0SRodrigo Siqueira 	if (res)
719473683a0SRodrigo Siqueira 		goto fail;
720473683a0SRodrigo Siqueira 
721473683a0SRodrigo Siqueira 	drm_crtc_helper_add(&acrtc->base, &amdgpu_dm_crtc_helper_funcs);
722473683a0SRodrigo Siqueira 
723473683a0SRodrigo Siqueira 	/* Create (reset) the plane state */
724473683a0SRodrigo Siqueira 	if (acrtc->base.funcs->reset)
725473683a0SRodrigo Siqueira 		acrtc->base.funcs->reset(&acrtc->base);
726473683a0SRodrigo Siqueira 
727473683a0SRodrigo Siqueira 	acrtc->max_cursor_width = dm->adev->dm.dc->caps.max_cursor_size;
728473683a0SRodrigo Siqueira 	acrtc->max_cursor_height = dm->adev->dm.dc->caps.max_cursor_size;
729473683a0SRodrigo Siqueira 
730473683a0SRodrigo Siqueira 	acrtc->crtc_id = crtc_index;
731473683a0SRodrigo Siqueira 	acrtc->base.enabled = false;
732473683a0SRodrigo Siqueira 	acrtc->otg_inst = -1;
733473683a0SRodrigo Siqueira 
734473683a0SRodrigo Siqueira 	dm->adev->mode_info.crtcs[crtc_index] = acrtc;
735c17b7a58SMelissa Wen 
736c17b7a58SMelissa Wen 	/* Don't enable DRM CRTC degamma property for DCE since it doesn't
737c17b7a58SMelissa Wen 	 * support programmable degamma anywhere.
738c17b7a58SMelissa Wen 	 */
739c17b7a58SMelissa Wen 	is_dcn = dm->adev->dm.dc->caps.color.dpp.dcn_arch;
740c17b7a58SMelissa Wen 	drm_crtc_enable_color_mgmt(&acrtc->base, is_dcn ? MAX_COLOR_LUT_ENTRIES : 0,
741473683a0SRodrigo Siqueira 				   true, MAX_COLOR_LUT_ENTRIES);
742c17b7a58SMelissa Wen 
743473683a0SRodrigo Siqueira 	drm_mode_crtc_set_gamma_size(&acrtc->base, MAX_COLOR_LEGACY_LUT_ENTRIES);
744473683a0SRodrigo Siqueira 
7450f5afa19SMelissa Wen #ifdef AMD_PRIVATE_COLOR
7460f5afa19SMelissa Wen 	dm_crtc_additional_color_mgmt(&acrtc->base);
7470f5afa19SMelissa Wen #endif
748473683a0SRodrigo Siqueira 	return 0;
749473683a0SRodrigo Siqueira 
750473683a0SRodrigo Siqueira fail:
751473683a0SRodrigo Siqueira 	kfree(acrtc);
752473683a0SRodrigo Siqueira 	kfree(cursor_plane);
753473683a0SRodrigo Siqueira 	return res;
754473683a0SRodrigo Siqueira }
755473683a0SRodrigo Siqueira 
756