xref: /dflybsd-src/sys/dev/drm/amd/amdgpu/dce_virtual.c (revision 789731325bde747251c28a37e0a00ed4efb88c46)
1b843c749SSergey Zigachev /*
2b843c749SSergey Zigachev  * Copyright 2014 Advanced Micro Devices, Inc.
3b843c749SSergey Zigachev  *
4b843c749SSergey Zigachev  * Permission is hereby granted, free of charge, to any person obtaining a
5b843c749SSergey Zigachev  * copy of this software and associated documentation files (the "Software"),
6b843c749SSergey Zigachev  * to deal in the Software without restriction, including without limitation
7b843c749SSergey Zigachev  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8b843c749SSergey Zigachev  * and/or sell copies of the Software, and to permit persons to whom the
9b843c749SSergey Zigachev  * Software is furnished to do so, subject to the following conditions:
10b843c749SSergey Zigachev  *
11b843c749SSergey Zigachev  * The above copyright notice and this permission notice shall be included in
12b843c749SSergey Zigachev  * all copies or substantial portions of the Software.
13b843c749SSergey Zigachev  *
14b843c749SSergey Zigachev  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15b843c749SSergey Zigachev  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16b843c749SSergey Zigachev  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17b843c749SSergey Zigachev  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18b843c749SSergey Zigachev  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19b843c749SSergey Zigachev  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20b843c749SSergey Zigachev  * OTHER DEALINGS IN THE SOFTWARE.
21b843c749SSergey Zigachev  *
22b843c749SSergey Zigachev  */
23b843c749SSergey Zigachev #include <drm/drmP.h>
24b843c749SSergey Zigachev #include "amdgpu.h"
25b843c749SSergey Zigachev #include "amdgpu_pm.h"
26b843c749SSergey Zigachev #include "amdgpu_i2c.h"
27b843c749SSergey Zigachev #include "atom.h"
28b843c749SSergey Zigachev #include "amdgpu_pll.h"
29b843c749SSergey Zigachev #include "amdgpu_connectors.h"
30b843c749SSergey Zigachev #ifdef CONFIG_DRM_AMDGPU_SI
31b843c749SSergey Zigachev #include "dce_v6_0.h"
32b843c749SSergey Zigachev #endif
33b843c749SSergey Zigachev #ifdef CONFIG_DRM_AMDGPU_CIK
34b843c749SSergey Zigachev #include "dce_v8_0.h"
35b843c749SSergey Zigachev #endif
36b843c749SSergey Zigachev #include "dce_v10_0.h"
37b843c749SSergey Zigachev #include "dce_v11_0.h"
38b843c749SSergey Zigachev #include "dce_virtual.h"
39b843c749SSergey Zigachev #include "ivsrcid/ivsrcid_vislands30.h"
40b843c749SSergey Zigachev 
41b843c749SSergey Zigachev #define DCE_VIRTUAL_VBLANK_PERIOD 16666666
42b843c749SSergey Zigachev 
43b843c749SSergey Zigachev 
44b843c749SSergey Zigachev static void dce_virtual_set_display_funcs(struct amdgpu_device *adev);
45b843c749SSergey Zigachev static void dce_virtual_set_irq_funcs(struct amdgpu_device *adev);
46b843c749SSergey Zigachev static int dce_virtual_connector_encoder_init(struct amdgpu_device *adev,
47b843c749SSergey Zigachev 					      int index);
48b843c749SSergey Zigachev static void dce_virtual_set_crtc_vblank_interrupt_state(struct amdgpu_device *adev,
49b843c749SSergey Zigachev 							int crtc,
50b843c749SSergey Zigachev 							enum amdgpu_interrupt_state state);
51b843c749SSergey Zigachev 
dce_virtual_vblank_get_counter(struct amdgpu_device * adev,int crtc)52b843c749SSergey Zigachev static u32 dce_virtual_vblank_get_counter(struct amdgpu_device *adev, int crtc)
53b843c749SSergey Zigachev {
54b843c749SSergey Zigachev 	return 0;
55b843c749SSergey Zigachev }
56b843c749SSergey Zigachev 
dce_virtual_page_flip(struct amdgpu_device * adev,int crtc_id,u64 crtc_base,bool async)57b843c749SSergey Zigachev static void dce_virtual_page_flip(struct amdgpu_device *adev,
58b843c749SSergey Zigachev 			      int crtc_id, u64 crtc_base, bool async)
59b843c749SSergey Zigachev {
60b843c749SSergey Zigachev 	return;
61b843c749SSergey Zigachev }
62b843c749SSergey Zigachev 
dce_virtual_crtc_get_scanoutpos(struct amdgpu_device * adev,int crtc,u32 * vbl,u32 * position)63b843c749SSergey Zigachev static int dce_virtual_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc,
64b843c749SSergey Zigachev 					u32 *vbl, u32 *position)
65b843c749SSergey Zigachev {
66b843c749SSergey Zigachev 	*vbl = 0;
67b843c749SSergey Zigachev 	*position = 0;
68b843c749SSergey Zigachev 
69b843c749SSergey Zigachev 	return -EINVAL;
70b843c749SSergey Zigachev }
71b843c749SSergey Zigachev 
dce_virtual_hpd_sense(struct amdgpu_device * adev,enum amdgpu_hpd_id hpd)72b843c749SSergey Zigachev static bool dce_virtual_hpd_sense(struct amdgpu_device *adev,
73b843c749SSergey Zigachev 			       enum amdgpu_hpd_id hpd)
74b843c749SSergey Zigachev {
75b843c749SSergey Zigachev 	return true;
76b843c749SSergey Zigachev }
77b843c749SSergey Zigachev 
dce_virtual_hpd_set_polarity(struct amdgpu_device * adev,enum amdgpu_hpd_id hpd)78b843c749SSergey Zigachev static void dce_virtual_hpd_set_polarity(struct amdgpu_device *adev,
79b843c749SSergey Zigachev 				      enum amdgpu_hpd_id hpd)
80b843c749SSergey Zigachev {
81b843c749SSergey Zigachev 	return;
82b843c749SSergey Zigachev }
83b843c749SSergey Zigachev 
dce_virtual_hpd_get_gpio_reg(struct amdgpu_device * adev)84b843c749SSergey Zigachev static u32 dce_virtual_hpd_get_gpio_reg(struct amdgpu_device *adev)
85b843c749SSergey Zigachev {
86b843c749SSergey Zigachev 	return 0;
87b843c749SSergey Zigachev }
88b843c749SSergey Zigachev 
89b843c749SSergey Zigachev /**
90b843c749SSergey Zigachev  * dce_virtual_bandwidth_update - program display watermarks
91b843c749SSergey Zigachev  *
92b843c749SSergey Zigachev  * @adev: amdgpu_device pointer
93b843c749SSergey Zigachev  *
94b843c749SSergey Zigachev  * Calculate and program the display watermarks and line
95b843c749SSergey Zigachev  * buffer allocation (CIK).
96b843c749SSergey Zigachev  */
dce_virtual_bandwidth_update(struct amdgpu_device * adev)97b843c749SSergey Zigachev static void dce_virtual_bandwidth_update(struct amdgpu_device *adev)
98b843c749SSergey Zigachev {
99b843c749SSergey Zigachev 	return;
100b843c749SSergey Zigachev }
101b843c749SSergey Zigachev 
dce_virtual_crtc_gamma_set(struct drm_crtc * crtc,u16 * red,u16 * green,u16 * blue,uint32_t size,struct drm_modeset_acquire_ctx * ctx)102b843c749SSergey Zigachev static int dce_virtual_crtc_gamma_set(struct drm_crtc *crtc, u16 *red,
103b843c749SSergey Zigachev 				      u16 *green, u16 *blue, uint32_t size,
104b843c749SSergey Zigachev 				      struct drm_modeset_acquire_ctx *ctx)
105b843c749SSergey Zigachev {
106b843c749SSergey Zigachev 	return 0;
107b843c749SSergey Zigachev }
108b843c749SSergey Zigachev 
dce_virtual_crtc_destroy(struct drm_crtc * crtc)109b843c749SSergey Zigachev static void dce_virtual_crtc_destroy(struct drm_crtc *crtc)
110b843c749SSergey Zigachev {
111b843c749SSergey Zigachev 	struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
112b843c749SSergey Zigachev 
113b843c749SSergey Zigachev 	drm_crtc_cleanup(crtc);
114b843c749SSergey Zigachev 	kfree(amdgpu_crtc);
115b843c749SSergey Zigachev }
116b843c749SSergey Zigachev 
117b843c749SSergey Zigachev static const struct drm_crtc_funcs dce_virtual_crtc_funcs = {
118b843c749SSergey Zigachev 	.cursor_set2 = NULL,
119b843c749SSergey Zigachev 	.cursor_move = NULL,
120b843c749SSergey Zigachev 	.gamma_set = dce_virtual_crtc_gamma_set,
121b843c749SSergey Zigachev 	.set_config = amdgpu_display_crtc_set_config,
122b843c749SSergey Zigachev 	.destroy = dce_virtual_crtc_destroy,
123b843c749SSergey Zigachev 	.page_flip_target = amdgpu_display_crtc_page_flip_target,
124b843c749SSergey Zigachev };
125b843c749SSergey Zigachev 
dce_virtual_crtc_dpms(struct drm_crtc * crtc,int mode)126b843c749SSergey Zigachev static void dce_virtual_crtc_dpms(struct drm_crtc *crtc, int mode)
127b843c749SSergey Zigachev {
128b843c749SSergey Zigachev 	struct drm_device *dev = crtc->dev;
129b843c749SSergey Zigachev 	struct amdgpu_device *adev = dev->dev_private;
130b843c749SSergey Zigachev 	struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
131b843c749SSergey Zigachev 	unsigned type;
132b843c749SSergey Zigachev 
133b843c749SSergey Zigachev 	if (amdgpu_sriov_vf(adev))
134b843c749SSergey Zigachev 		return;
135b843c749SSergey Zigachev 
136b843c749SSergey Zigachev 	switch (mode) {
137b843c749SSergey Zigachev 	case DRM_MODE_DPMS_ON:
138b843c749SSergey Zigachev 		amdgpu_crtc->enabled = true;
139b843c749SSergey Zigachev 		/* Make sure VBLANK interrupts are still enabled */
140b843c749SSergey Zigachev 		type = amdgpu_display_crtc_idx_to_irq_type(adev,
141b843c749SSergey Zigachev 						amdgpu_crtc->crtc_id);
142b843c749SSergey Zigachev 		amdgpu_irq_update(adev, &adev->crtc_irq, type);
143b843c749SSergey Zigachev 		drm_crtc_vblank_on(crtc);
144b843c749SSergey Zigachev 		break;
145b843c749SSergey Zigachev 	case DRM_MODE_DPMS_STANDBY:
146b843c749SSergey Zigachev 	case DRM_MODE_DPMS_SUSPEND:
147b843c749SSergey Zigachev 	case DRM_MODE_DPMS_OFF:
148b843c749SSergey Zigachev 		drm_crtc_vblank_off(crtc);
149b843c749SSergey Zigachev 		amdgpu_crtc->enabled = false;
150b843c749SSergey Zigachev 		break;
151b843c749SSergey Zigachev 	}
152b843c749SSergey Zigachev }
153b843c749SSergey Zigachev 
154b843c749SSergey Zigachev 
dce_virtual_crtc_prepare(struct drm_crtc * crtc)155b843c749SSergey Zigachev static void dce_virtual_crtc_prepare(struct drm_crtc *crtc)
156b843c749SSergey Zigachev {
157b843c749SSergey Zigachev 	dce_virtual_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
158b843c749SSergey Zigachev }
159b843c749SSergey Zigachev 
dce_virtual_crtc_commit(struct drm_crtc * crtc)160b843c749SSergey Zigachev static void dce_virtual_crtc_commit(struct drm_crtc *crtc)
161b843c749SSergey Zigachev {
162b843c749SSergey Zigachev 	dce_virtual_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
163b843c749SSergey Zigachev }
164b843c749SSergey Zigachev 
dce_virtual_crtc_disable(struct drm_crtc * crtc)165b843c749SSergey Zigachev static void dce_virtual_crtc_disable(struct drm_crtc *crtc)
166b843c749SSergey Zigachev {
167b843c749SSergey Zigachev 	struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
168b843c749SSergey Zigachev 
169b843c749SSergey Zigachev 	dce_virtual_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
170b843c749SSergey Zigachev 	if (crtc->primary->fb) {
171b843c749SSergey Zigachev 		int r;
172b843c749SSergey Zigachev 		struct amdgpu_bo *abo;
173b843c749SSergey Zigachev 
174b843c749SSergey Zigachev 		abo = gem_to_amdgpu_bo(crtc->primary->fb->obj[0]);
175b843c749SSergey Zigachev 		r = amdgpu_bo_reserve(abo, true);
176b843c749SSergey Zigachev 		if (unlikely(r))
177b843c749SSergey Zigachev 			DRM_ERROR("failed to reserve abo before unpin\n");
178b843c749SSergey Zigachev 		else {
179b843c749SSergey Zigachev 			amdgpu_bo_unpin(abo);
180b843c749SSergey Zigachev 			amdgpu_bo_unreserve(abo);
181b843c749SSergey Zigachev 		}
182b843c749SSergey Zigachev 	}
183b843c749SSergey Zigachev 
184b843c749SSergey Zigachev 	amdgpu_crtc->pll_id = ATOM_PPLL_INVALID;
185b843c749SSergey Zigachev 	amdgpu_crtc->encoder = NULL;
186b843c749SSergey Zigachev 	amdgpu_crtc->connector = NULL;
187b843c749SSergey Zigachev }
188b843c749SSergey Zigachev 
dce_virtual_crtc_mode_set(struct drm_crtc * crtc,struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode,int x,int y,struct drm_framebuffer * old_fb)189b843c749SSergey Zigachev static int dce_virtual_crtc_mode_set(struct drm_crtc *crtc,
190b843c749SSergey Zigachev 				  struct drm_display_mode *mode,
191b843c749SSergey Zigachev 				  struct drm_display_mode *adjusted_mode,
192b843c749SSergey Zigachev 				  int x, int y, struct drm_framebuffer *old_fb)
193b843c749SSergey Zigachev {
194b843c749SSergey Zigachev 	struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
195b843c749SSergey Zigachev 
196b843c749SSergey Zigachev 	/* update the hw version fpr dpm */
197b843c749SSergey Zigachev 	amdgpu_crtc->hw_mode = *adjusted_mode;
198b843c749SSergey Zigachev 
199b843c749SSergey Zigachev 	return 0;
200b843c749SSergey Zigachev }
201b843c749SSergey Zigachev 
dce_virtual_crtc_mode_fixup(struct drm_crtc * crtc,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)202b843c749SSergey Zigachev static bool dce_virtual_crtc_mode_fixup(struct drm_crtc *crtc,
203b843c749SSergey Zigachev 				     const struct drm_display_mode *mode,
204b843c749SSergey Zigachev 				     struct drm_display_mode *adjusted_mode)
205b843c749SSergey Zigachev {
206b843c749SSergey Zigachev 	return true;
207b843c749SSergey Zigachev }
208b843c749SSergey Zigachev 
209b843c749SSergey Zigachev 
dce_virtual_crtc_set_base(struct drm_crtc * crtc,int x,int y,struct drm_framebuffer * old_fb)210b843c749SSergey Zigachev static int dce_virtual_crtc_set_base(struct drm_crtc *crtc, int x, int y,
211b843c749SSergey Zigachev 				  struct drm_framebuffer *old_fb)
212b843c749SSergey Zigachev {
213b843c749SSergey Zigachev 	return 0;
214b843c749SSergey Zigachev }
215b843c749SSergey Zigachev 
dce_virtual_crtc_set_base_atomic(struct drm_crtc * crtc,struct drm_framebuffer * fb,int x,int y,enum mode_set_atomic state)216b843c749SSergey Zigachev static int dce_virtual_crtc_set_base_atomic(struct drm_crtc *crtc,
217b843c749SSergey Zigachev 					 struct drm_framebuffer *fb,
218b843c749SSergey Zigachev 					 int x, int y, enum mode_set_atomic state)
219b843c749SSergey Zigachev {
220b843c749SSergey Zigachev 	return 0;
221b843c749SSergey Zigachev }
222b843c749SSergey Zigachev 
223b843c749SSergey Zigachev static const struct drm_crtc_helper_funcs dce_virtual_crtc_helper_funcs = {
224b843c749SSergey Zigachev 	.dpms = dce_virtual_crtc_dpms,
225b843c749SSergey Zigachev 	.mode_fixup = dce_virtual_crtc_mode_fixup,
226b843c749SSergey Zigachev 	.mode_set = dce_virtual_crtc_mode_set,
227b843c749SSergey Zigachev 	.mode_set_base = dce_virtual_crtc_set_base,
228b843c749SSergey Zigachev 	.mode_set_base_atomic = dce_virtual_crtc_set_base_atomic,
229b843c749SSergey Zigachev 	.prepare = dce_virtual_crtc_prepare,
230b843c749SSergey Zigachev 	.commit = dce_virtual_crtc_commit,
231b843c749SSergey Zigachev 	.disable = dce_virtual_crtc_disable,
232b843c749SSergey Zigachev };
233b843c749SSergey Zigachev 
dce_virtual_crtc_init(struct amdgpu_device * adev,int index)234b843c749SSergey Zigachev static int dce_virtual_crtc_init(struct amdgpu_device *adev, int index)
235b843c749SSergey Zigachev {
236b843c749SSergey Zigachev 	struct amdgpu_crtc *amdgpu_crtc;
237b843c749SSergey Zigachev 
238b843c749SSergey Zigachev 	amdgpu_crtc = kzalloc(sizeof(struct amdgpu_crtc) +
239b843c749SSergey Zigachev 			      (AMDGPUFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL);
240b843c749SSergey Zigachev 	if (amdgpu_crtc == NULL)
241b843c749SSergey Zigachev 		return -ENOMEM;
242b843c749SSergey Zigachev 
243b843c749SSergey Zigachev 	drm_crtc_init(adev->ddev, &amdgpu_crtc->base, &dce_virtual_crtc_funcs);
244b843c749SSergey Zigachev 
245b843c749SSergey Zigachev 	drm_mode_crtc_set_gamma_size(&amdgpu_crtc->base, 256);
246b843c749SSergey Zigachev 	amdgpu_crtc->crtc_id = index;
247b843c749SSergey Zigachev 	adev->mode_info.crtcs[index] = amdgpu_crtc;
248b843c749SSergey Zigachev 
249b843c749SSergey Zigachev 	amdgpu_crtc->pll_id = ATOM_PPLL_INVALID;
250b843c749SSergey Zigachev 	amdgpu_crtc->encoder = NULL;
251b843c749SSergey Zigachev 	amdgpu_crtc->connector = NULL;
252b843c749SSergey Zigachev 	amdgpu_crtc->vsync_timer_enabled = AMDGPU_IRQ_STATE_DISABLE;
253b843c749SSergey Zigachev 	drm_crtc_helper_add(&amdgpu_crtc->base, &dce_virtual_crtc_helper_funcs);
254b843c749SSergey Zigachev 
255b843c749SSergey Zigachev 	return 0;
256b843c749SSergey Zigachev }
257b843c749SSergey Zigachev 
dce_virtual_early_init(void * handle)258b843c749SSergey Zigachev static int dce_virtual_early_init(void *handle)
259b843c749SSergey Zigachev {
260b843c749SSergey Zigachev 	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
261b843c749SSergey Zigachev 
262b843c749SSergey Zigachev 	dce_virtual_set_display_funcs(adev);
263b843c749SSergey Zigachev 	dce_virtual_set_irq_funcs(adev);
264b843c749SSergey Zigachev 
265b843c749SSergey Zigachev 	adev->mode_info.num_hpd = 1;
266b843c749SSergey Zigachev 	adev->mode_info.num_dig = 1;
267b843c749SSergey Zigachev 	return 0;
268b843c749SSergey Zigachev }
269b843c749SSergey Zigachev 
270b843c749SSergey Zigachev static struct drm_encoder *
dce_virtual_encoder(struct drm_connector * connector)271b843c749SSergey Zigachev dce_virtual_encoder(struct drm_connector *connector)
272b843c749SSergey Zigachev {
273b843c749SSergey Zigachev 	struct drm_encoder *encoder;
274b843c749SSergey Zigachev 	int i;
275b843c749SSergey Zigachev 
276b843c749SSergey Zigachev 	drm_connector_for_each_possible_encoder(connector, encoder, i) {
277b843c749SSergey Zigachev 		if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL)
278b843c749SSergey Zigachev 			return encoder;
279b843c749SSergey Zigachev 	}
280b843c749SSergey Zigachev 
281b843c749SSergey Zigachev 	/* pick the first one */
282b843c749SSergey Zigachev 	drm_connector_for_each_possible_encoder(connector, encoder, i)
283b843c749SSergey Zigachev 		return encoder;
284b843c749SSergey Zigachev 
285b843c749SSergey Zigachev 	return NULL;
286b843c749SSergey Zigachev }
287b843c749SSergey Zigachev 
dce_virtual_get_modes(struct drm_connector * connector)288b843c749SSergey Zigachev static int dce_virtual_get_modes(struct drm_connector *connector)
289b843c749SSergey Zigachev {
290b843c749SSergey Zigachev 	struct drm_device *dev = connector->dev;
291b843c749SSergey Zigachev 	struct drm_display_mode *mode = NULL;
292b843c749SSergey Zigachev 	unsigned i;
293b843c749SSergey Zigachev 	static const struct mode_size {
294b843c749SSergey Zigachev 		int w;
295b843c749SSergey Zigachev 		int h;
296b843c749SSergey Zigachev 	} common_modes[17] = {
297b843c749SSergey Zigachev 		{ 640,  480},
298b843c749SSergey Zigachev 		{ 720,  480},
299b843c749SSergey Zigachev 		{ 800,  600},
300b843c749SSergey Zigachev 		{ 848,  480},
301b843c749SSergey Zigachev 		{1024,  768},
302b843c749SSergey Zigachev 		{1152,  768},
303b843c749SSergey Zigachev 		{1280,  720},
304b843c749SSergey Zigachev 		{1280,  800},
305b843c749SSergey Zigachev 		{1280,  854},
306b843c749SSergey Zigachev 		{1280,  960},
307b843c749SSergey Zigachev 		{1280, 1024},
308b843c749SSergey Zigachev 		{1440,  900},
309b843c749SSergey Zigachev 		{1400, 1050},
310b843c749SSergey Zigachev 		{1680, 1050},
311b843c749SSergey Zigachev 		{1600, 1200},
312b843c749SSergey Zigachev 		{1920, 1080},
313b843c749SSergey Zigachev 		{1920, 1200}
314b843c749SSergey Zigachev 	};
315b843c749SSergey Zigachev 
316b843c749SSergey Zigachev 	for (i = 0; i < 17; i++) {
317b843c749SSergey Zigachev 		mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, 60, false, false, false);
318b843c749SSergey Zigachev 		drm_mode_probed_add(connector, mode);
319b843c749SSergey Zigachev 	}
320b843c749SSergey Zigachev 
321b843c749SSergey Zigachev 	return 0;
322b843c749SSergey Zigachev }
323b843c749SSergey Zigachev 
dce_virtual_mode_valid(struct drm_connector * connector,struct drm_display_mode * mode)324b843c749SSergey Zigachev static enum drm_mode_status dce_virtual_mode_valid(struct drm_connector *connector,
325b843c749SSergey Zigachev 				  struct drm_display_mode *mode)
326b843c749SSergey Zigachev {
327b843c749SSergey Zigachev 	return MODE_OK;
328b843c749SSergey Zigachev }
329b843c749SSergey Zigachev 
330b843c749SSergey Zigachev static int
dce_virtual_dpms(struct drm_connector * connector,int mode)331b843c749SSergey Zigachev dce_virtual_dpms(struct drm_connector *connector, int mode)
332b843c749SSergey Zigachev {
333b843c749SSergey Zigachev 	return 0;
334b843c749SSergey Zigachev }
335b843c749SSergey Zigachev 
336b843c749SSergey Zigachev static int
dce_virtual_set_property(struct drm_connector * connector,struct drm_property * property,uint64_t val)337b843c749SSergey Zigachev dce_virtual_set_property(struct drm_connector *connector,
338b843c749SSergey Zigachev 			 struct drm_property *property,
339b843c749SSergey Zigachev 			 uint64_t val)
340b843c749SSergey Zigachev {
341b843c749SSergey Zigachev 	return 0;
342b843c749SSergey Zigachev }
343b843c749SSergey Zigachev 
dce_virtual_destroy(struct drm_connector * connector)344b843c749SSergey Zigachev static void dce_virtual_destroy(struct drm_connector *connector)
345b843c749SSergey Zigachev {
346b843c749SSergey Zigachev 	drm_connector_unregister(connector);
347b843c749SSergey Zigachev 	drm_connector_cleanup(connector);
348b843c749SSergey Zigachev 	kfree(connector);
349b843c749SSergey Zigachev }
350b843c749SSergey Zigachev 
dce_virtual_force(struct drm_connector * connector)351b843c749SSergey Zigachev static void dce_virtual_force(struct drm_connector *connector)
352b843c749SSergey Zigachev {
353b843c749SSergey Zigachev 	return;
354b843c749SSergey Zigachev }
355b843c749SSergey Zigachev 
356b843c749SSergey Zigachev static const struct drm_connector_helper_funcs dce_virtual_connector_helper_funcs = {
357b843c749SSergey Zigachev 	.get_modes = dce_virtual_get_modes,
358b843c749SSergey Zigachev 	.mode_valid = dce_virtual_mode_valid,
359b843c749SSergey Zigachev 	.best_encoder = dce_virtual_encoder,
360b843c749SSergey Zigachev };
361b843c749SSergey Zigachev 
362b843c749SSergey Zigachev static const struct drm_connector_funcs dce_virtual_connector_funcs = {
363b843c749SSergey Zigachev 	.dpms = dce_virtual_dpms,
364b843c749SSergey Zigachev 	.fill_modes = drm_helper_probe_single_connector_modes,
365b843c749SSergey Zigachev 	.set_property = dce_virtual_set_property,
366b843c749SSergey Zigachev 	.destroy = dce_virtual_destroy,
367b843c749SSergey Zigachev 	.force = dce_virtual_force,
368b843c749SSergey Zigachev };
369b843c749SSergey Zigachev 
dce_virtual_sw_init(void * handle)370b843c749SSergey Zigachev static int dce_virtual_sw_init(void *handle)
371b843c749SSergey Zigachev {
372b843c749SSergey Zigachev 	int r, i;
373b843c749SSergey Zigachev 	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
374b843c749SSergey Zigachev 
375b843c749SSergey Zigachev 	r = amdgpu_irq_add_id(adev, AMDGPU_IH_CLIENTID_LEGACY, VISLANDS30_IV_SRCID_SMU_DISP_TIMER2_TRIGGER, &adev->crtc_irq);
376b843c749SSergey Zigachev 	if (r)
377b843c749SSergey Zigachev 		return r;
378b843c749SSergey Zigachev 
379b843c749SSergey Zigachev 	adev->ddev->max_vblank_count = 0;
380b843c749SSergey Zigachev 
381b843c749SSergey Zigachev 	adev->ddev->mode_config.funcs = &amdgpu_mode_funcs;
382b843c749SSergey Zigachev 
383b843c749SSergey Zigachev 	adev->ddev->mode_config.max_width = 16384;
384b843c749SSergey Zigachev 	adev->ddev->mode_config.max_height = 16384;
385b843c749SSergey Zigachev 
386b843c749SSergey Zigachev 	adev->ddev->mode_config.preferred_depth = 24;
387b843c749SSergey Zigachev 	adev->ddev->mode_config.prefer_shadow = 1;
388b843c749SSergey Zigachev 
389b843c749SSergey Zigachev 	adev->ddev->mode_config.fb_base = adev->gmc.aper_base;
390b843c749SSergey Zigachev 
391b843c749SSergey Zigachev 	r = amdgpu_display_modeset_create_props(adev);
392b843c749SSergey Zigachev 	if (r)
393b843c749SSergey Zigachev 		return r;
394b843c749SSergey Zigachev 
395b843c749SSergey Zigachev 	adev->ddev->mode_config.max_width = 16384;
396b843c749SSergey Zigachev 	adev->ddev->mode_config.max_height = 16384;
397b843c749SSergey Zigachev 
398b843c749SSergey Zigachev 	/* allocate crtcs, encoders, connectors */
399b843c749SSergey Zigachev 	for (i = 0; i < adev->mode_info.num_crtc; i++) {
400b843c749SSergey Zigachev 		r = dce_virtual_crtc_init(adev, i);
401b843c749SSergey Zigachev 		if (r)
402b843c749SSergey Zigachev 			return r;
403b843c749SSergey Zigachev 		r = dce_virtual_connector_encoder_init(adev, i);
404b843c749SSergey Zigachev 		if (r)
405b843c749SSergey Zigachev 			return r;
406b843c749SSergey Zigachev 	}
407b843c749SSergey Zigachev 
408b843c749SSergey Zigachev 	drm_kms_helper_poll_init(adev->ddev);
409b843c749SSergey Zigachev 
410b843c749SSergey Zigachev 	adev->mode_info.mode_config_initialized = true;
411b843c749SSergey Zigachev 	return 0;
412b843c749SSergey Zigachev }
413b843c749SSergey Zigachev 
dce_virtual_sw_fini(void * handle)414b843c749SSergey Zigachev static int dce_virtual_sw_fini(void *handle)
415b843c749SSergey Zigachev {
416b843c749SSergey Zigachev 	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
417b843c749SSergey Zigachev 
418b843c749SSergey Zigachev 	kfree(adev->mode_info.bios_hardcoded_edid);
419b843c749SSergey Zigachev 
420b843c749SSergey Zigachev 	drm_kms_helper_poll_fini(adev->ddev);
421b843c749SSergey Zigachev 
422b843c749SSergey Zigachev 	drm_mode_config_cleanup(adev->ddev);
423b843c749SSergey Zigachev 	/* clear crtcs pointer to avoid dce irq finish routine access freed data */
424b843c749SSergey Zigachev 	memset(adev->mode_info.crtcs, 0, sizeof(adev->mode_info.crtcs[0]) * AMDGPU_MAX_CRTCS);
425b843c749SSergey Zigachev 	adev->mode_info.mode_config_initialized = false;
426b843c749SSergey Zigachev 	return 0;
427b843c749SSergey Zigachev }
428b843c749SSergey Zigachev 
dce_virtual_hw_init(void * handle)429b843c749SSergey Zigachev static int dce_virtual_hw_init(void *handle)
430b843c749SSergey Zigachev {
431b843c749SSergey Zigachev 	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
432b843c749SSergey Zigachev 
433b843c749SSergey Zigachev 	switch (adev->asic_type) {
434b843c749SSergey Zigachev #ifdef CONFIG_DRM_AMDGPU_SI
435b843c749SSergey Zigachev 	case CHIP_TAHITI:
436b843c749SSergey Zigachev 	case CHIP_PITCAIRN:
437b843c749SSergey Zigachev 	case CHIP_VERDE:
438b843c749SSergey Zigachev 	case CHIP_OLAND:
439b843c749SSergey Zigachev 		dce_v6_0_disable_dce(adev);
440b843c749SSergey Zigachev 		break;
441b843c749SSergey Zigachev #endif
442b843c749SSergey Zigachev #ifdef CONFIG_DRM_AMDGPU_CIK
443b843c749SSergey Zigachev 	case CHIP_BONAIRE:
444b843c749SSergey Zigachev 	case CHIP_HAWAII:
445b843c749SSergey Zigachev 	case CHIP_KAVERI:
446b843c749SSergey Zigachev 	case CHIP_KABINI:
447b843c749SSergey Zigachev 	case CHIP_MULLINS:
448b843c749SSergey Zigachev 		dce_v8_0_disable_dce(adev);
449b843c749SSergey Zigachev 		break;
450b843c749SSergey Zigachev #endif
451b843c749SSergey Zigachev 	case CHIP_FIJI:
452b843c749SSergey Zigachev 	case CHIP_TONGA:
453b843c749SSergey Zigachev 		dce_v10_0_disable_dce(adev);
454b843c749SSergey Zigachev 		break;
455b843c749SSergey Zigachev 	case CHIP_CARRIZO:
456b843c749SSergey Zigachev 	case CHIP_STONEY:
457b843c749SSergey Zigachev 	case CHIP_POLARIS10:
458b843c749SSergey Zigachev 	case CHIP_POLARIS11:
459b843c749SSergey Zigachev 	case CHIP_VEGAM:
460b843c749SSergey Zigachev 		dce_v11_0_disable_dce(adev);
461b843c749SSergey Zigachev 		break;
462b843c749SSergey Zigachev 	case CHIP_TOPAZ:
463b843c749SSergey Zigachev #ifdef CONFIG_DRM_AMDGPU_SI
464b843c749SSergey Zigachev 	case CHIP_HAINAN:
465b843c749SSergey Zigachev #endif
466b843c749SSergey Zigachev 		/* no DCE */
467b843c749SSergey Zigachev 		break;
468b843c749SSergey Zigachev 	case CHIP_VEGA10:
469b843c749SSergey Zigachev 	case CHIP_VEGA12:
470b843c749SSergey Zigachev 	case CHIP_VEGA20:
471b843c749SSergey Zigachev 		break;
472b843c749SSergey Zigachev 	default:
473b843c749SSergey Zigachev 		DRM_ERROR("Virtual display unsupported ASIC type: 0x%X\n", adev->asic_type);
474b843c749SSergey Zigachev 	}
475b843c749SSergey Zigachev 	return 0;
476b843c749SSergey Zigachev }
477b843c749SSergey Zigachev 
dce_virtual_hw_fini(void * handle)478b843c749SSergey Zigachev static int dce_virtual_hw_fini(void *handle)
479b843c749SSergey Zigachev {
480b843c749SSergey Zigachev 	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
481b843c749SSergey Zigachev 	int i = 0;
482b843c749SSergey Zigachev 
483b843c749SSergey Zigachev 	for (i = 0; i<adev->mode_info.num_crtc; i++)
484b843c749SSergey Zigachev 		if (adev->mode_info.crtcs[i])
485b843c749SSergey Zigachev 			dce_virtual_set_crtc_vblank_interrupt_state(adev, i, AMDGPU_IRQ_STATE_DISABLE);
486b843c749SSergey Zigachev 
487b843c749SSergey Zigachev 	return 0;
488b843c749SSergey Zigachev }
489b843c749SSergey Zigachev 
dce_virtual_suspend(void * handle)490b843c749SSergey Zigachev static int dce_virtual_suspend(void *handle)
491b843c749SSergey Zigachev {
492b843c749SSergey Zigachev 	return dce_virtual_hw_fini(handle);
493b843c749SSergey Zigachev }
494b843c749SSergey Zigachev 
dce_virtual_resume(void * handle)495b843c749SSergey Zigachev static int dce_virtual_resume(void *handle)
496b843c749SSergey Zigachev {
497b843c749SSergey Zigachev 	return dce_virtual_hw_init(handle);
498b843c749SSergey Zigachev }
499b843c749SSergey Zigachev 
dce_virtual_is_idle(void * handle)500b843c749SSergey Zigachev static bool dce_virtual_is_idle(void *handle)
501b843c749SSergey Zigachev {
502b843c749SSergey Zigachev 	return true;
503b843c749SSergey Zigachev }
504b843c749SSergey Zigachev 
dce_virtual_wait_for_idle(void * handle)505b843c749SSergey Zigachev static int dce_virtual_wait_for_idle(void *handle)
506b843c749SSergey Zigachev {
507b843c749SSergey Zigachev 	return 0;
508b843c749SSergey Zigachev }
509b843c749SSergey Zigachev 
dce_virtual_soft_reset(void * handle)510b843c749SSergey Zigachev static int dce_virtual_soft_reset(void *handle)
511b843c749SSergey Zigachev {
512b843c749SSergey Zigachev 	return 0;
513b843c749SSergey Zigachev }
514b843c749SSergey Zigachev 
dce_virtual_set_clockgating_state(void * handle,enum amd_clockgating_state state)515b843c749SSergey Zigachev static int dce_virtual_set_clockgating_state(void *handle,
516b843c749SSergey Zigachev 					  enum amd_clockgating_state state)
517b843c749SSergey Zigachev {
518b843c749SSergey Zigachev 	return 0;
519b843c749SSergey Zigachev }
520b843c749SSergey Zigachev 
dce_virtual_set_powergating_state(void * handle,enum amd_powergating_state state)521b843c749SSergey Zigachev static int dce_virtual_set_powergating_state(void *handle,
522b843c749SSergey Zigachev 					  enum amd_powergating_state state)
523b843c749SSergey Zigachev {
524b843c749SSergey Zigachev 	return 0;
525b843c749SSergey Zigachev }
526b843c749SSergey Zigachev 
527b843c749SSergey Zigachev static const struct amd_ip_funcs dce_virtual_ip_funcs = {
528b843c749SSergey Zigachev 	.name = "dce_virtual",
529b843c749SSergey Zigachev 	.early_init = dce_virtual_early_init,
530b843c749SSergey Zigachev 	.late_init = NULL,
531b843c749SSergey Zigachev 	.sw_init = dce_virtual_sw_init,
532b843c749SSergey Zigachev 	.sw_fini = dce_virtual_sw_fini,
533b843c749SSergey Zigachev 	.hw_init = dce_virtual_hw_init,
534b843c749SSergey Zigachev 	.hw_fini = dce_virtual_hw_fini,
535b843c749SSergey Zigachev 	.suspend = dce_virtual_suspend,
536b843c749SSergey Zigachev 	.resume = dce_virtual_resume,
537b843c749SSergey Zigachev 	.is_idle = dce_virtual_is_idle,
538b843c749SSergey Zigachev 	.wait_for_idle = dce_virtual_wait_for_idle,
539b843c749SSergey Zigachev 	.soft_reset = dce_virtual_soft_reset,
540b843c749SSergey Zigachev 	.set_clockgating_state = dce_virtual_set_clockgating_state,
541b843c749SSergey Zigachev 	.set_powergating_state = dce_virtual_set_powergating_state,
542b843c749SSergey Zigachev };
543b843c749SSergey Zigachev 
544b843c749SSergey Zigachev /* these are handled by the primary encoders */
dce_virtual_encoder_prepare(struct drm_encoder * encoder)545b843c749SSergey Zigachev static void dce_virtual_encoder_prepare(struct drm_encoder *encoder)
546b843c749SSergey Zigachev {
547b843c749SSergey Zigachev 	return;
548b843c749SSergey Zigachev }
549b843c749SSergey Zigachev 
dce_virtual_encoder_commit(struct drm_encoder * encoder)550b843c749SSergey Zigachev static void dce_virtual_encoder_commit(struct drm_encoder *encoder)
551b843c749SSergey Zigachev {
552b843c749SSergey Zigachev 	return;
553b843c749SSergey Zigachev }
554b843c749SSergey Zigachev 
555b843c749SSergey Zigachev static void
dce_virtual_encoder_mode_set(struct drm_encoder * encoder,struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)556b843c749SSergey Zigachev dce_virtual_encoder_mode_set(struct drm_encoder *encoder,
557b843c749SSergey Zigachev 			     struct drm_display_mode *mode,
558b843c749SSergey Zigachev 			     struct drm_display_mode *adjusted_mode)
559b843c749SSergey Zigachev {
560b843c749SSergey Zigachev 	return;
561b843c749SSergey Zigachev }
562b843c749SSergey Zigachev 
dce_virtual_encoder_disable(struct drm_encoder * encoder)563b843c749SSergey Zigachev static void dce_virtual_encoder_disable(struct drm_encoder *encoder)
564b843c749SSergey Zigachev {
565b843c749SSergey Zigachev 	return;
566b843c749SSergey Zigachev }
567b843c749SSergey Zigachev 
568b843c749SSergey Zigachev static void
dce_virtual_encoder_dpms(struct drm_encoder * encoder,int mode)569b843c749SSergey Zigachev dce_virtual_encoder_dpms(struct drm_encoder *encoder, int mode)
570b843c749SSergey Zigachev {
571b843c749SSergey Zigachev 	return;
572b843c749SSergey Zigachev }
573b843c749SSergey Zigachev 
dce_virtual_encoder_mode_fixup(struct drm_encoder * encoder,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)574b843c749SSergey Zigachev static bool dce_virtual_encoder_mode_fixup(struct drm_encoder *encoder,
575b843c749SSergey Zigachev 				    const struct drm_display_mode *mode,
576b843c749SSergey Zigachev 				    struct drm_display_mode *adjusted_mode)
577b843c749SSergey Zigachev {
578b843c749SSergey Zigachev 	return true;
579b843c749SSergey Zigachev }
580b843c749SSergey Zigachev 
581b843c749SSergey Zigachev static const struct drm_encoder_helper_funcs dce_virtual_encoder_helper_funcs = {
582b843c749SSergey Zigachev 	.dpms = dce_virtual_encoder_dpms,
583b843c749SSergey Zigachev 	.mode_fixup = dce_virtual_encoder_mode_fixup,
584b843c749SSergey Zigachev 	.prepare = dce_virtual_encoder_prepare,
585b843c749SSergey Zigachev 	.mode_set = dce_virtual_encoder_mode_set,
586b843c749SSergey Zigachev 	.commit = dce_virtual_encoder_commit,
587b843c749SSergey Zigachev 	.disable = dce_virtual_encoder_disable,
588b843c749SSergey Zigachev };
589b843c749SSergey Zigachev 
dce_virtual_encoder_destroy(struct drm_encoder * encoder)590b843c749SSergey Zigachev static void dce_virtual_encoder_destroy(struct drm_encoder *encoder)
591b843c749SSergey Zigachev {
592b843c749SSergey Zigachev 	drm_encoder_cleanup(encoder);
593b843c749SSergey Zigachev 	kfree(encoder);
594b843c749SSergey Zigachev }
595b843c749SSergey Zigachev 
596b843c749SSergey Zigachev static const struct drm_encoder_funcs dce_virtual_encoder_funcs = {
597b843c749SSergey Zigachev 	.destroy = dce_virtual_encoder_destroy,
598b843c749SSergey Zigachev };
599b843c749SSergey Zigachev 
dce_virtual_connector_encoder_init(struct amdgpu_device * adev,int index)600b843c749SSergey Zigachev static int dce_virtual_connector_encoder_init(struct amdgpu_device *adev,
601b843c749SSergey Zigachev 					      int index)
602b843c749SSergey Zigachev {
603b843c749SSergey Zigachev 	struct drm_encoder *encoder;
604b843c749SSergey Zigachev 	struct drm_connector *connector;
605b843c749SSergey Zigachev 
606b843c749SSergey Zigachev 	/* add a new encoder */
607b843c749SSergey Zigachev 	encoder = kzalloc(sizeof(struct drm_encoder), GFP_KERNEL);
608b843c749SSergey Zigachev 	if (!encoder)
609b843c749SSergey Zigachev 		return -ENOMEM;
610b843c749SSergey Zigachev 	encoder->possible_crtcs = 1 << index;
611b843c749SSergey Zigachev 	drm_encoder_init(adev->ddev, encoder, &dce_virtual_encoder_funcs,
612b843c749SSergey Zigachev 			 DRM_MODE_ENCODER_VIRTUAL, NULL);
613b843c749SSergey Zigachev 	drm_encoder_helper_add(encoder, &dce_virtual_encoder_helper_funcs);
614b843c749SSergey Zigachev 
615b843c749SSergey Zigachev 	connector = kzalloc(sizeof(struct drm_connector), GFP_KERNEL);
616b843c749SSergey Zigachev 	if (!connector) {
617b843c749SSergey Zigachev 		kfree(encoder);
618b843c749SSergey Zigachev 		return -ENOMEM;
619b843c749SSergey Zigachev 	}
620b843c749SSergey Zigachev 
621b843c749SSergey Zigachev 	/* add a new connector */
622b843c749SSergey Zigachev 	drm_connector_init(adev->ddev, connector, &dce_virtual_connector_funcs,
623b843c749SSergey Zigachev 			   DRM_MODE_CONNECTOR_VIRTUAL);
624b843c749SSergey Zigachev 	drm_connector_helper_add(connector, &dce_virtual_connector_helper_funcs);
625b843c749SSergey Zigachev 	connector->display_info.subpixel_order = SubPixelHorizontalRGB;
626b843c749SSergey Zigachev 	connector->interlace_allowed = false;
627b843c749SSergey Zigachev 	connector->doublescan_allowed = false;
628b843c749SSergey Zigachev 	drm_connector_register(connector);
629b843c749SSergey Zigachev 
630b843c749SSergey Zigachev 	/* link them */
631*78973132SSergey Zigachev 	drm_mode_connector_attach_encoder(connector, encoder);
632b843c749SSergey Zigachev 
633b843c749SSergey Zigachev 	return 0;
634b843c749SSergey Zigachev }
635b843c749SSergey Zigachev 
636b843c749SSergey Zigachev static const struct amdgpu_display_funcs dce_virtual_display_funcs = {
637b843c749SSergey Zigachev 	.bandwidth_update = &dce_virtual_bandwidth_update,
638b843c749SSergey Zigachev 	.vblank_get_counter = &dce_virtual_vblank_get_counter,
639b843c749SSergey Zigachev 	.backlight_set_level = NULL,
640b843c749SSergey Zigachev 	.backlight_get_level = NULL,
641b843c749SSergey Zigachev 	.hpd_sense = &dce_virtual_hpd_sense,
642b843c749SSergey Zigachev 	.hpd_set_polarity = &dce_virtual_hpd_set_polarity,
643b843c749SSergey Zigachev 	.hpd_get_gpio_reg = &dce_virtual_hpd_get_gpio_reg,
644b843c749SSergey Zigachev 	.page_flip = &dce_virtual_page_flip,
645b843c749SSergey Zigachev 	.page_flip_get_scanoutpos = &dce_virtual_crtc_get_scanoutpos,
646b843c749SSergey Zigachev 	.add_encoder = NULL,
647b843c749SSergey Zigachev 	.add_connector = NULL,
648b843c749SSergey Zigachev };
649b843c749SSergey Zigachev 
dce_virtual_set_display_funcs(struct amdgpu_device * adev)650b843c749SSergey Zigachev static void dce_virtual_set_display_funcs(struct amdgpu_device *adev)
651b843c749SSergey Zigachev {
652b843c749SSergey Zigachev 	if (adev->mode_info.funcs == NULL)
653b843c749SSergey Zigachev 		adev->mode_info.funcs = &dce_virtual_display_funcs;
654b843c749SSergey Zigachev }
655b843c749SSergey Zigachev 
dce_virtual_pageflip(struct amdgpu_device * adev,unsigned crtc_id)656b843c749SSergey Zigachev static int dce_virtual_pageflip(struct amdgpu_device *adev,
657b843c749SSergey Zigachev 				unsigned crtc_id)
658b843c749SSergey Zigachev {
659b843c749SSergey Zigachev 	unsigned long flags;
660b843c749SSergey Zigachev 	struct amdgpu_crtc *amdgpu_crtc;
661b843c749SSergey Zigachev 	struct amdgpu_flip_work *works;
662b843c749SSergey Zigachev 
663b843c749SSergey Zigachev 	amdgpu_crtc = adev->mode_info.crtcs[crtc_id];
664b843c749SSergey Zigachev 
665b843c749SSergey Zigachev 	if (crtc_id >= adev->mode_info.num_crtc) {
666b843c749SSergey Zigachev 		DRM_ERROR("invalid pageflip crtc %d\n", crtc_id);
667b843c749SSergey Zigachev 		return -EINVAL;
668b843c749SSergey Zigachev 	}
669b843c749SSergey Zigachev 
670b843c749SSergey Zigachev 	/* IRQ could occur when in initial stage */
671b843c749SSergey Zigachev 	if (amdgpu_crtc == NULL)
672b843c749SSergey Zigachev 		return 0;
673b843c749SSergey Zigachev 
674b843c749SSergey Zigachev 	spin_lock_irqsave(&adev->ddev->event_lock, flags);
675b843c749SSergey Zigachev 	works = amdgpu_crtc->pflip_works;
676b843c749SSergey Zigachev 	if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_SUBMITTED) {
677b843c749SSergey Zigachev 		DRM_DEBUG_DRIVER("amdgpu_crtc->pflip_status = %d != "
678b843c749SSergey Zigachev 			"AMDGPU_FLIP_SUBMITTED(%d)\n",
679b843c749SSergey Zigachev 			amdgpu_crtc->pflip_status,
680b843c749SSergey Zigachev 			AMDGPU_FLIP_SUBMITTED);
681b843c749SSergey Zigachev 		spin_unlock_irqrestore(&adev->ddev->event_lock, flags);
682b843c749SSergey Zigachev 		return 0;
683b843c749SSergey Zigachev 	}
684b843c749SSergey Zigachev 
685b843c749SSergey Zigachev 	/* page flip completed. clean up */
686b843c749SSergey Zigachev 	amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE;
687b843c749SSergey Zigachev 	amdgpu_crtc->pflip_works = NULL;
688b843c749SSergey Zigachev 
689b843c749SSergey Zigachev 	/* wakeup usersapce */
690b843c749SSergey Zigachev 	if (works->event)
691b843c749SSergey Zigachev 		drm_crtc_send_vblank_event(&amdgpu_crtc->base, works->event);
692b843c749SSergey Zigachev 
693b843c749SSergey Zigachev 	spin_unlock_irqrestore(&adev->ddev->event_lock, flags);
694b843c749SSergey Zigachev 
695b843c749SSergey Zigachev 	drm_crtc_vblank_put(&amdgpu_crtc->base);
696b843c749SSergey Zigachev 	schedule_work(&works->unpin_work);
697b843c749SSergey Zigachev 
698b843c749SSergey Zigachev 	return 0;
699b843c749SSergey Zigachev }
700b843c749SSergey Zigachev 
dce_virtual_vblank_timer_handle(struct hrtimer * vblank_timer)701b843c749SSergey Zigachev static enum hrtimer_restart dce_virtual_vblank_timer_handle(struct hrtimer *vblank_timer)
702b843c749SSergey Zigachev {
703b843c749SSergey Zigachev 	struct amdgpu_crtc *amdgpu_crtc = container_of(vblank_timer,
704b843c749SSergey Zigachev 						       struct amdgpu_crtc, vblank_timer);
705b843c749SSergey Zigachev 	struct drm_device *ddev = amdgpu_crtc->base.dev;
706b843c749SSergey Zigachev 	struct amdgpu_device *adev = ddev->dev_private;
707b843c749SSergey Zigachev 
708b843c749SSergey Zigachev 	drm_handle_vblank(ddev, amdgpu_crtc->crtc_id);
709b843c749SSergey Zigachev 	dce_virtual_pageflip(adev, amdgpu_crtc->crtc_id);
710b843c749SSergey Zigachev 	hrtimer_start(vblank_timer, DCE_VIRTUAL_VBLANK_PERIOD,
711b843c749SSergey Zigachev 		      HRTIMER_MODE_REL);
712b843c749SSergey Zigachev 
713b843c749SSergey Zigachev 	return HRTIMER_NORESTART;
714b843c749SSergey Zigachev }
715b843c749SSergey Zigachev 
dce_virtual_set_crtc_vblank_interrupt_state(struct amdgpu_device * adev,int crtc,enum amdgpu_interrupt_state state)716b843c749SSergey Zigachev static void dce_virtual_set_crtc_vblank_interrupt_state(struct amdgpu_device *adev,
717b843c749SSergey Zigachev 							int crtc,
718b843c749SSergey Zigachev 							enum amdgpu_interrupt_state state)
719b843c749SSergey Zigachev {
720b843c749SSergey Zigachev 	if (crtc >= adev->mode_info.num_crtc || !adev->mode_info.crtcs[crtc]) {
721b843c749SSergey Zigachev 		DRM_DEBUG("invalid crtc %d\n", crtc);
722b843c749SSergey Zigachev 		return;
723b843c749SSergey Zigachev 	}
724b843c749SSergey Zigachev 
725b843c749SSergey Zigachev 	if (state && !adev->mode_info.crtcs[crtc]->vsync_timer_enabled) {
726b843c749SSergey Zigachev 		DRM_DEBUG("Enable software vsync timer\n");
727b843c749SSergey Zigachev 		hrtimer_init(&adev->mode_info.crtcs[crtc]->vblank_timer,
728b843c749SSergey Zigachev 			     CLOCK_MONOTONIC, HRTIMER_MODE_REL);
729b843c749SSergey Zigachev 		hrtimer_set_expires(&adev->mode_info.crtcs[crtc]->vblank_timer,
730b843c749SSergey Zigachev 				    DCE_VIRTUAL_VBLANK_PERIOD);
731b843c749SSergey Zigachev 		adev->mode_info.crtcs[crtc]->vblank_timer.function =
732b843c749SSergey Zigachev 			dce_virtual_vblank_timer_handle;
733b843c749SSergey Zigachev 		hrtimer_start(&adev->mode_info.crtcs[crtc]->vblank_timer,
734b843c749SSergey Zigachev 			      DCE_VIRTUAL_VBLANK_PERIOD, HRTIMER_MODE_REL);
735b843c749SSergey Zigachev 	} else if (!state && adev->mode_info.crtcs[crtc]->vsync_timer_enabled) {
736b843c749SSergey Zigachev 		DRM_DEBUG("Disable software vsync timer\n");
737b843c749SSergey Zigachev 		hrtimer_cancel(&adev->mode_info.crtcs[crtc]->vblank_timer);
738b843c749SSergey Zigachev 	}
739b843c749SSergey Zigachev 
740b843c749SSergey Zigachev 	adev->mode_info.crtcs[crtc]->vsync_timer_enabled = state;
741b843c749SSergey Zigachev 	DRM_DEBUG("[FM]set crtc %d vblank interrupt state %d\n", crtc, state);
742b843c749SSergey Zigachev }
743b843c749SSergey Zigachev 
744b843c749SSergey Zigachev 
dce_virtual_set_crtc_irq_state(struct amdgpu_device * adev,struct amdgpu_irq_src * source,unsigned type,enum amdgpu_interrupt_state state)745b843c749SSergey Zigachev static int dce_virtual_set_crtc_irq_state(struct amdgpu_device *adev,
746b843c749SSergey Zigachev 					  struct amdgpu_irq_src *source,
747b843c749SSergey Zigachev 					  unsigned type,
748b843c749SSergey Zigachev 					  enum amdgpu_interrupt_state state)
749b843c749SSergey Zigachev {
750b843c749SSergey Zigachev 	if (type > AMDGPU_CRTC_IRQ_VBLANK6)
751b843c749SSergey Zigachev 		return -EINVAL;
752b843c749SSergey Zigachev 
753b843c749SSergey Zigachev 	dce_virtual_set_crtc_vblank_interrupt_state(adev, type, state);
754b843c749SSergey Zigachev 
755b843c749SSergey Zigachev 	return 0;
756b843c749SSergey Zigachev }
757b843c749SSergey Zigachev 
758b843c749SSergey Zigachev static const struct amdgpu_irq_src_funcs dce_virtual_crtc_irq_funcs = {
759b843c749SSergey Zigachev 	.set = dce_virtual_set_crtc_irq_state,
760b843c749SSergey Zigachev 	.process = NULL,
761b843c749SSergey Zigachev };
762b843c749SSergey Zigachev 
dce_virtual_set_irq_funcs(struct amdgpu_device * adev)763b843c749SSergey Zigachev static void dce_virtual_set_irq_funcs(struct amdgpu_device *adev)
764b843c749SSergey Zigachev {
765b843c749SSergey Zigachev 	adev->crtc_irq.num_types = AMDGPU_CRTC_IRQ_VBLANK6 + 1;
766b843c749SSergey Zigachev 	adev->crtc_irq.funcs = &dce_virtual_crtc_irq_funcs;
767b843c749SSergey Zigachev }
768b843c749SSergey Zigachev 
769b843c749SSergey Zigachev const struct amdgpu_ip_block_version dce_virtual_ip_block =
770b843c749SSergey Zigachev {
771b843c749SSergey Zigachev 	.type = AMD_IP_BLOCK_TYPE_DCE,
772b843c749SSergey Zigachev 	.major = 1,
773b843c749SSergey Zigachev 	.minor = 0,
774b843c749SSergey Zigachev 	.rev = 0,
775b843c749SSergey Zigachev 	.funcs = &dce_virtual_ip_funcs,
776b843c749SSergey Zigachev };
777