Commit | Line | Data |
---|---|---|
473683a0 RS |
1 | // SPDX-License-Identifier: MIT |
2 | /* | |
3 | * Copyright 2022 Advanced Micro Devices, Inc. | |
4 | * | |
5 | * Permission is hereby granted, free of charge, to any person obtaining a | |
6 | * copy of this software and associated documentation files (the "Software"), | |
7 | * to deal in the Software without restriction, including without limitation | |
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
9 | * and/or sell copies of the Software, and to permit persons to whom the | |
10 | * Software is furnished to do so, subject to the following conditions: | |
11 | * | |
12 | * The above copyright notice and this permission notice shall be included in | |
13 | * all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
18 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
21 | * OTHER DEALINGS IN THE SOFTWARE. | |
22 | * | |
23 | * Authors: AMD | |
24 | * | |
25 | */ | |
26 | #include <drm/drm_vblank.h> | |
27 | #include <drm/drm_atomic_helper.h> | |
28 | ||
29 | #include "dc.h" | |
30 | #include "amdgpu.h" | |
31 | #include "amdgpu_dm_psr.h" | |
5950efe2 | 32 | #include "amdgpu_dm_replay.h" |
473683a0 RS |
33 | #include "amdgpu_dm_crtc.h" |
34 | #include "amdgpu_dm_plane.h" | |
35 | #include "amdgpu_dm_trace.h" | |
36 | #include "amdgpu_dm_debugfs.h" | |
37 | ||
a88b19b1 | 38 | #define HPD_DETECTION_PERIOD_uS 2000000 |
60612f75 | 39 | #define HPD_DETECTION_TIME_uS 100000 |
afca033f | 40 | |
6c5e25a0 | 41 | void amdgpu_dm_crtc_handle_vblank(struct amdgpu_crtc *acrtc) |
473683a0 RS |
42 | { |
43 | struct drm_crtc *crtc = &acrtc->base; | |
44 | struct drm_device *dev = crtc->dev; | |
45 | unsigned long flags; | |
46 | ||
47 | drm_crtc_handle_vblank(crtc); | |
48 | ||
49 | spin_lock_irqsave(&dev->event_lock, flags); | |
50 | ||
51 | /* Send completion event for cursor-only commits */ | |
52 | if (acrtc->event && acrtc->pflip_status != AMDGPU_FLIP_SUBMITTED) { | |
53 | drm_crtc_send_vblank_event(crtc, acrtc->event); | |
54 | drm_crtc_vblank_put(crtc); | |
55 | acrtc->event = NULL; | |
56 | } | |
57 | ||
58 | spin_unlock_irqrestore(&dev->event_lock, flags); | |
59 | } | |
60 | ||
6c5e25a0 | 61 | bool amdgpu_dm_crtc_modeset_required(struct drm_crtc_state *crtc_state, |
473683a0 RS |
62 | struct dc_stream_state *new_stream, |
63 | struct dc_stream_state *old_stream) | |
64 | { | |
65 | return crtc_state->active && drm_atomic_crtc_needs_modeset(crtc_state); | |
66 | } | |
67 | ||
6c5e25a0 | 68 | bool amdgpu_dm_crtc_vrr_active_irq(struct amdgpu_crtc *acrtc) |
473683a0 RS |
69 | |
70 | { | |
71 | return acrtc->dm_irq_params.freesync_config.state == | |
72 | VRR_STATE_ACTIVE_VARIABLE || | |
73 | acrtc->dm_irq_params.freesync_config.state == | |
74 | VRR_STATE_ACTIVE_FIXED; | |
75 | } | |
76 | ||
6c5e25a0 | 77 | int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable) |
473683a0 RS |
78 | { |
79 | enum dc_irq_source irq_source; | |
80 | struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); | |
81 | struct amdgpu_device *adev = drm_to_adev(crtc->dev); | |
82 | int rc; | |
83 | ||
4936458b HM |
84 | if (acrtc->otg_inst == -1) |
85 | return 0; | |
86 | ||
473683a0 RS |
87 | irq_source = IRQ_TYPE_VUPDATE + acrtc->otg_inst; |
88 | ||
89 | rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY; | |
90 | ||
91 | DRM_DEBUG_VBL("crtc %d - vupdate irq %sabling: r=%d\n", | |
92 | acrtc->crtc_id, enable ? "en" : "dis", rc); | |
93 | return rc; | |
94 | } | |
95 | ||
67edb81d | 96 | bool amdgpu_dm_crtc_vrr_active(const struct dm_crtc_state *dm_state) |
473683a0 RS |
97 | { |
98 | return dm_state->freesync_config.state == VRR_STATE_ACTIVE_VARIABLE || | |
99 | dm_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED; | |
100 | } | |
101 | ||
5950efe2 | 102 | /** |
8feca9f3 SS |
103 | * amdgpu_dm_crtc_set_panel_sr_feature() - Manage panel self-refresh features. |
104 | * | |
105 | * @vblank_work: is a pointer to a struct vblank_control_work object. | |
106 | * @vblank_enabled: indicates whether the DRM vblank counter is currently | |
107 | * enabled (true) or disabled (false). | |
108 | * @allow_sr_entry: represents whether entry into the self-refresh mode is | |
109 | * allowed (true) or not allowed (false). | |
110 | * | |
5950efe2 TC |
111 | * The DRM vblank counter enable/disable action is used as the trigger to enable |
112 | * or disable various panel self-refresh features: | |
113 | * | |
114 | * Panel Replay and PSR SU | |
115 | * - Enable when: | |
116 | * - vblank counter is disabled | |
117 | * - entry is allowed: usermode demonstrates an adequate number of fast | |
118 | * commits) | |
119 | * - CRC capture window isn't active | |
120 | * - Keep enabled even when vblank counter gets enabled | |
121 | * | |
122 | * PSR1 | |
123 | * - Enable condition same as above | |
124 | * - Disable when vblank counter is enabled | |
125 | */ | |
126 | static void amdgpu_dm_crtc_set_panel_sr_feature( | |
127 | struct vblank_control_work *vblank_work, | |
128 | bool vblank_enabled, bool allow_sr_entry) | |
129 | { | |
130 | struct dc_link *link = vblank_work->stream->link; | |
131 | bool is_sr_active = (link->replay_settings.replay_allow_active || | |
132 | link->psr_settings.psr_allow_active); | |
133 | bool is_crc_window_active = false; | |
134 | ||
135 | #ifdef CONFIG_DRM_AMD_SECURE_DISPLAY | |
136 | is_crc_window_active = | |
137 | amdgpu_dm_crc_window_is_activated(&vblank_work->acrtc->base); | |
138 | #endif | |
139 | ||
140 | if (link->replay_settings.replay_feature_enabled && | |
141 | allow_sr_entry && !is_sr_active && !is_crc_window_active) { | |
142 | amdgpu_dm_replay_enable(vblank_work->stream, true); | |
143 | } else if (vblank_enabled) { | |
144 | if (link->psr_settings.psr_version < DC_PSR_VERSION_SU_1 && is_sr_active) | |
ff2e4d87 | 145 | amdgpu_dm_psr_disable(vblank_work->stream, false); |
5950efe2 TC |
146 | } else if (link->psr_settings.psr_feature_enabled && |
147 | allow_sr_entry && !is_sr_active && !is_crc_window_active) { | |
13b3d6bd HW |
148 | |
149 | struct amdgpu_dm_connector *aconn = | |
150 | (struct amdgpu_dm_connector *) vblank_work->stream->dm_stream_context; | |
151 | ||
afca033f RL |
152 | if (!aconn->disallow_edp_enter_psr) { |
153 | struct amdgpu_display_manager *dm = vblank_work->dm; | |
154 | ||
13b3d6bd | 155 | amdgpu_dm_psr_enable(vblank_work->stream); |
afca033f | 156 | if (dm->idle_workqueue && |
442702b4 | 157 | (dm->dc->config.disable_ips == DMUB_IPS_ENABLE) && |
afca033f RL |
158 | dm->dc->idle_optimizations_allowed && |
159 | dm->idle_workqueue->enable && | |
160 | !dm->idle_workqueue->running) | |
161 | schedule_work(&dm->idle_workqueue->work); | |
162 | } | |
5950efe2 TC |
163 | } |
164 | } | |
165 | ||
9862ef7b RL |
166 | bool amdgpu_dm_is_headless(struct amdgpu_device *adev) |
167 | { | |
168 | struct drm_connector *connector; | |
169 | struct drm_connector_list_iter iter; | |
170 | struct drm_device *dev; | |
171 | bool is_headless = true; | |
172 | ||
173 | if (adev == NULL) | |
174 | return true; | |
175 | ||
176 | dev = adev->dm.ddev; | |
177 | ||
178 | drm_connector_list_iter_begin(dev, &iter); | |
179 | drm_for_each_connector_iter(connector, &iter) { | |
180 | ||
181 | if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) | |
182 | continue; | |
183 | ||
184 | if (connector->status == connector_status_connected) { | |
185 | is_headless = false; | |
186 | break; | |
187 | } | |
188 | } | |
189 | drm_connector_list_iter_end(&iter); | |
190 | return is_headless; | |
191 | } | |
192 | ||
afca033f RL |
193 | static void amdgpu_dm_idle_worker(struct work_struct *work) |
194 | { | |
195 | struct idle_workqueue *idle_work; | |
196 | ||
197 | idle_work = container_of(work, struct idle_workqueue, work); | |
198 | idle_work->dm->idle_workqueue->running = true; | |
9862ef7b | 199 | |
afca033f | 200 | while (idle_work->enable) { |
9862ef7b RL |
201 | fsleep(HPD_DETECTION_PERIOD_uS); |
202 | mutex_lock(&idle_work->dm->dc_lock); | |
203 | if (!idle_work->dm->dc->idle_optimizations_allowed) { | |
204 | mutex_unlock(&idle_work->dm->dc_lock); | |
afca033f | 205 | break; |
9862ef7b | 206 | } |
afca033f RL |
207 | dc_allow_idle_optimizations(idle_work->dm->dc, false); |
208 | ||
209 | mutex_unlock(&idle_work->dm->dc_lock); | |
210 | fsleep(HPD_DETECTION_TIME_uS); | |
211 | mutex_lock(&idle_work->dm->dc_lock); | |
212 | ||
9862ef7b RL |
213 | if (!amdgpu_dm_is_headless(idle_work->dm->adev) && |
214 | !amdgpu_dm_psr_is_active_allowed(idle_work->dm)) { | |
215 | mutex_unlock(&idle_work->dm->dc_lock); | |
afca033f | 216 | break; |
9862ef7b | 217 | } |
afca033f | 218 | |
9862ef7b RL |
219 | if (idle_work->enable) |
220 | dc_allow_idle_optimizations(idle_work->dm->dc, true); | |
afca033f | 221 | mutex_unlock(&idle_work->dm->dc_lock); |
afca033f | 222 | } |
afca033f RL |
223 | idle_work->dm->idle_workqueue->running = false; |
224 | } | |
225 | ||
226 | struct idle_workqueue *idle_create_workqueue(struct amdgpu_device *adev) | |
227 | { | |
228 | struct idle_workqueue *idle_work; | |
229 | ||
230 | idle_work = kzalloc(sizeof(*idle_work), GFP_KERNEL); | |
231 | if (ZERO_OR_NULL_PTR(idle_work)) | |
232 | return NULL; | |
233 | ||
234 | idle_work->dm = &adev->dm; | |
235 | idle_work->enable = false; | |
236 | idle_work->running = false; | |
237 | INIT_WORK(&idle_work->work, amdgpu_dm_idle_worker); | |
238 | ||
239 | return idle_work; | |
240 | } | |
241 | ||
6ce4f9ee | 242 | static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work) |
473683a0 RS |
243 | { |
244 | struct vblank_control_work *vblank_work = | |
245 | container_of(work, struct vblank_control_work, work); | |
246 | struct amdgpu_display_manager *dm = vblank_work->dm; | |
247 | ||
248 | mutex_lock(&dm->dc_lock); | |
249 | ||
250 | if (vblank_work->enable) | |
251 | dm->active_vblank_irq_count++; | |
252 | else if (dm->active_vblank_irq_count) | |
253 | dm->active_vblank_irq_count--; | |
254 | ||
1b7ac448 | 255 | if (dm->active_vblank_irq_count > 0) |
17e68f89 | 256 | dc_allow_idle_optimizations(dm->dc, false); |
473683a0 RS |
257 | |
258 | /* | |
259 | * Control PSR based on vblank requirements from OS | |
260 | * | |
261 | * If panel supports PSR SU, there's no need to disable PSR when OS is | |
262 | * submitting fast atomic commits (we infer this by whether the OS | |
263 | * requests vblank events). Fast atomic commits will simply trigger a | |
264 | * full-frame-update (FFU); a specific case of selective-update (SU) | |
265 | * where the SU region is the full hactive*vactive region. See | |
266 | * fill_dc_dirty_rects(). | |
267 | */ | |
ca628f0e | 268 | if (vblank_work->stream && vblank_work->stream->link && vblank_work->acrtc) { |
5950efe2 TC |
269 | amdgpu_dm_crtc_set_panel_sr_feature( |
270 | vblank_work, vblank_work->enable, | |
ca628f0e | 271 | vblank_work->acrtc->dm_irq_params.allow_sr_entry); |
473683a0 RS |
272 | } |
273 | ||
1b7ac448 | 274 | if (dm->active_vblank_irq_count == 0) |
17e68f89 | 275 | dc_allow_idle_optimizations(dm->dc, true); |
17e68f89 | 276 | |
473683a0 RS |
277 | mutex_unlock(&dm->dc_lock); |
278 | ||
279 | dc_stream_release(vblank_work->stream); | |
280 | ||
281 | kfree(vblank_work); | |
282 | } | |
283 | ||
6ce4f9ee | 284 | static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable) |
473683a0 | 285 | { |
473683a0 RS |
286 | struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); |
287 | struct amdgpu_device *adev = drm_to_adev(crtc->dev); | |
288 | struct dm_crtc_state *acrtc_state = to_dm_crtc_state(crtc->state); | |
289 | struct amdgpu_display_manager *dm = &adev->dm; | |
290 | struct vblank_control_work *work; | |
7fb363c5 | 291 | int irq_type; |
473683a0 RS |
292 | int rc = 0; |
293 | ||
4936458b HM |
294 | if (acrtc->otg_inst == -1) |
295 | goto skip; | |
296 | ||
7fb363c5 LL |
297 | irq_type = amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id); |
298 | ||
473683a0 RS |
299 | if (enable) { |
300 | /* vblank irq on -> Only need vupdate irq in vrr mode */ | |
6c5e25a0 DT |
301 | if (amdgpu_dm_crtc_vrr_active(acrtc_state)) |
302 | rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, true); | |
473683a0 RS |
303 | } else { |
304 | /* vblank irq off -> vupdate irq off */ | |
6c5e25a0 | 305 | rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, false); |
473683a0 RS |
306 | } |
307 | ||
308 | if (rc) | |
309 | return rc; | |
310 | ||
7fb363c5 LL |
311 | /* crtc vblank or vstartup interrupt */ |
312 | if (enable) { | |
313 | rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type); | |
314 | drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc); | |
315 | } else { | |
316 | rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type); | |
317 | drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc); | |
318 | } | |
473683a0 | 319 | |
c8b5a95b AL |
320 | if (rc) |
321 | return rc; | |
473683a0 | 322 | |
7fb363c5 LL |
323 | /* |
324 | * hubp surface flip interrupt | |
325 | * | |
326 | * We have no guarantee that the frontend index maps to the same | |
327 | * backend index - some even map to more than one. | |
328 | * | |
329 | * TODO: Use a different interrupt or check DC itself for the mapping. | |
330 | */ | |
331 | if (enable) { | |
332 | rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type); | |
333 | drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc); | |
334 | } else { | |
335 | rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type); | |
336 | drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc); | |
337 | } | |
338 | ||
339 | if (rc) | |
340 | return rc; | |
341 | ||
342 | #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) | |
343 | /* crtc vline0 interrupt, only available on DCN+ */ | |
344 | if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) { | |
345 | if (enable) { | |
346 | rc = amdgpu_irq_get(adev, &adev->vline0_irq, irq_type); | |
347 | drm_dbg_vbl(crtc->dev, "Get vline0_irq ret=%d\n", rc); | |
348 | } else { | |
349 | rc = amdgpu_irq_put(adev, &adev->vline0_irq, irq_type); | |
350 | drm_dbg_vbl(crtc->dev, "Put vline0_irq ret=%d\n", rc); | |
351 | } | |
352 | ||
353 | if (rc) | |
354 | return rc; | |
355 | } | |
356 | #endif | |
4936458b | 357 | skip: |
473683a0 RS |
358 | if (amdgpu_in_reset(adev)) |
359 | return 0; | |
360 | ||
361 | if (dm->vblank_control_workqueue) { | |
362 | work = kzalloc(sizeof(*work), GFP_ATOMIC); | |
363 | if (!work) | |
364 | return -ENOMEM; | |
365 | ||
6ce4f9ee | 366 | INIT_WORK(&work->work, amdgpu_dm_crtc_vblank_control_worker); |
473683a0 RS |
367 | work->dm = dm; |
368 | work->acrtc = acrtc; | |
369 | work->enable = enable; | |
370 | ||
371 | if (acrtc_state->stream) { | |
372 | dc_stream_retain(acrtc_state->stream); | |
373 | work->stream = acrtc_state->stream; | |
374 | } | |
375 | ||
376 | queue_work(dm->vblank_control_workqueue, &work->work); | |
377 | } | |
378 | ||
379 | return 0; | |
380 | } | |
381 | ||
6c5e25a0 | 382 | int amdgpu_dm_crtc_enable_vblank(struct drm_crtc *crtc) |
473683a0 | 383 | { |
6ce4f9ee | 384 | return amdgpu_dm_crtc_set_vblank(crtc, true); |
473683a0 RS |
385 | } |
386 | ||
6c5e25a0 | 387 | void amdgpu_dm_crtc_disable_vblank(struct drm_crtc *crtc) |
473683a0 | 388 | { |
6ce4f9ee | 389 | amdgpu_dm_crtc_set_vblank(crtc, false); |
473683a0 RS |
390 | } |
391 | ||
6ce4f9ee | 392 | static void amdgpu_dm_crtc_destroy_state(struct drm_crtc *crtc, |
473683a0 RS |
393 | struct drm_crtc_state *state) |
394 | { | |
395 | struct dm_crtc_state *cur = to_dm_crtc_state(state); | |
396 | ||
397 | /* TODO Destroy dc_stream objects are stream object is flattened */ | |
398 | if (cur->stream) | |
399 | dc_stream_release(cur->stream); | |
400 | ||
401 | ||
402 | __drm_atomic_helper_crtc_destroy_state(state); | |
403 | ||
404 | ||
405 | kfree(state); | |
406 | } | |
407 | ||
6ce4f9ee | 408 | static struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct drm_crtc *crtc) |
473683a0 RS |
409 | { |
410 | struct dm_crtc_state *state, *cur; | |
411 | ||
412 | cur = to_dm_crtc_state(crtc->state); | |
413 | ||
414 | if (WARN_ON(!crtc->state)) | |
415 | return NULL; | |
416 | ||
417 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
418 | if (!state) | |
419 | return NULL; | |
420 | ||
421 | __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); | |
422 | ||
423 | if (cur->stream) { | |
424 | state->stream = cur->stream; | |
425 | dc_stream_retain(state->stream); | |
426 | } | |
427 | ||
428 | state->active_planes = cur->active_planes; | |
429 | state->vrr_infopacket = cur->vrr_infopacket; | |
430 | state->abm_level = cur->abm_level; | |
431 | state->vrr_supported = cur->vrr_supported; | |
432 | state->freesync_config = cur->freesync_config; | |
433 | state->cm_has_degamma = cur->cm_has_degamma; | |
434 | state->cm_is_degamma_srgb = cur->cm_is_degamma_srgb; | |
0f5afa19 | 435 | state->regamma_tf = cur->regamma_tf; |
c13423c6 | 436 | state->crc_skip_count = cur->crc_skip_count; |
473683a0 | 437 | state->mpo_requested = cur->mpo_requested; |
1b04dcca | 438 | state->cursor_mode = cur->cursor_mode; |
473683a0 RS |
439 | /* TODO Duplicate dc_stream after objects are stream object is flattened */ |
440 | ||
441 | return &state->base; | |
442 | } | |
443 | ||
444 | static void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc) | |
445 | { | |
446 | drm_crtc_cleanup(crtc); | |
447 | kfree(crtc); | |
448 | } | |
449 | ||
6ce4f9ee | 450 | static void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc) |
473683a0 RS |
451 | { |
452 | struct dm_crtc_state *state; | |
453 | ||
454 | if (crtc->state) | |
6ce4f9ee | 455 | amdgpu_dm_crtc_destroy_state(crtc, crtc->state); |
473683a0 RS |
456 | |
457 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
458 | if (WARN_ON(!state)) | |
459 | return; | |
460 | ||
461 | __drm_atomic_helper_crtc_reset(crtc, &state->base); | |
462 | } | |
463 | ||
464 | #ifdef CONFIG_DEBUG_FS | |
465 | static int amdgpu_dm_crtc_late_register(struct drm_crtc *crtc) | |
466 | { | |
467 | crtc_debugfs_init(crtc); | |
468 | ||
469 | return 0; | |
470 | } | |
471 | #endif | |
472 | ||
0f5afa19 MW |
473 | #ifdef AMD_PRIVATE_COLOR |
474 | /** | |
c6ef0a22 | 475 | * dm_crtc_additional_color_mgmt - enable additional color properties |
0f5afa19 MW |
476 | * @crtc: DRM CRTC |
477 | * | |
478 | * This function lets the driver enable post-blending CRTC regamma transfer | |
479 | * function property in addition to DRM CRTC gamma LUT. Default value means | |
480 | * linear transfer function, which is the default CRTC gamma LUT behaviour | |
481 | * without this property. | |
482 | */ | |
483 | static void | |
484 | dm_crtc_additional_color_mgmt(struct drm_crtc *crtc) | |
485 | { | |
486 | struct amdgpu_device *adev = drm_to_adev(crtc->dev); | |
487 | ||
af7cefc6 | 488 | if (adev->dm.dc->caps.color.mpc.ogam_ram) |
0f5afa19 MW |
489 | drm_object_attach_property(&crtc->base, |
490 | adev->mode_info.regamma_tf_property, | |
491 | AMDGPU_TRANSFER_FUNCTION_DEFAULT); | |
492 | } | |
493 | ||
494 | static int | |
495 | amdgpu_dm_atomic_crtc_set_property(struct drm_crtc *crtc, | |
496 | struct drm_crtc_state *state, | |
497 | struct drm_property *property, | |
498 | uint64_t val) | |
499 | { | |
500 | struct amdgpu_device *adev = drm_to_adev(crtc->dev); | |
501 | struct dm_crtc_state *acrtc_state = to_dm_crtc_state(state); | |
502 | ||
503 | if (property == adev->mode_info.regamma_tf_property) { | |
504 | if (acrtc_state->regamma_tf != val) { | |
505 | acrtc_state->regamma_tf = val; | |
506 | acrtc_state->base.color_mgmt_changed |= 1; | |
507 | } | |
508 | } else { | |
509 | drm_dbg_atomic(crtc->dev, | |
510 | "[CRTC:%d:%s] unknown property [PROP:%d:%s]]\n", | |
511 | crtc->base.id, crtc->name, | |
512 | property->base.id, property->name); | |
513 | return -EINVAL; | |
514 | } | |
515 | ||
516 | return 0; | |
517 | } | |
518 | ||
519 | static int | |
520 | amdgpu_dm_atomic_crtc_get_property(struct drm_crtc *crtc, | |
521 | const struct drm_crtc_state *state, | |
522 | struct drm_property *property, | |
523 | uint64_t *val) | |
524 | { | |
525 | struct amdgpu_device *adev = drm_to_adev(crtc->dev); | |
526 | struct dm_crtc_state *acrtc_state = to_dm_crtc_state(state); | |
527 | ||
528 | if (property == adev->mode_info.regamma_tf_property) | |
529 | *val = acrtc_state->regamma_tf; | |
530 | else | |
531 | return -EINVAL; | |
532 | ||
533 | return 0; | |
534 | } | |
535 | #endif | |
536 | ||
473683a0 RS |
537 | /* Implemented only the options currently available for the driver */ |
538 | static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = { | |
6ce4f9ee | 539 | .reset = amdgpu_dm_crtc_reset_state, |
473683a0 RS |
540 | .destroy = amdgpu_dm_crtc_destroy, |
541 | .set_config = drm_atomic_helper_set_config, | |
542 | .page_flip = drm_atomic_helper_page_flip, | |
6ce4f9ee RS |
543 | .atomic_duplicate_state = amdgpu_dm_crtc_duplicate_state, |
544 | .atomic_destroy_state = amdgpu_dm_crtc_destroy_state, | |
473683a0 RS |
545 | .set_crc_source = amdgpu_dm_crtc_set_crc_source, |
546 | .verify_crc_source = amdgpu_dm_crtc_verify_crc_source, | |
547 | .get_crc_sources = amdgpu_dm_crtc_get_crc_sources, | |
548 | .get_vblank_counter = amdgpu_get_vblank_counter_kms, | |
6c5e25a0 DT |
549 | .enable_vblank = amdgpu_dm_crtc_enable_vblank, |
550 | .disable_vblank = amdgpu_dm_crtc_disable_vblank, | |
473683a0 RS |
551 | .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, |
552 | #if defined(CONFIG_DEBUG_FS) | |
553 | .late_register = amdgpu_dm_crtc_late_register, | |
554 | #endif | |
0f5afa19 MW |
555 | #ifdef AMD_PRIVATE_COLOR |
556 | .atomic_set_property = amdgpu_dm_atomic_crtc_set_property, | |
557 | .atomic_get_property = amdgpu_dm_atomic_crtc_get_property, | |
558 | #endif | |
473683a0 RS |
559 | }; |
560 | ||
6ce4f9ee | 561 | static void amdgpu_dm_crtc_helper_disable(struct drm_crtc *crtc) |
473683a0 RS |
562 | { |
563 | } | |
564 | ||
6ce4f9ee | 565 | static int amdgpu_dm_crtc_count_crtc_active_planes(struct drm_crtc_state *new_crtc_state) |
473683a0 RS |
566 | { |
567 | struct drm_atomic_state *state = new_crtc_state->state; | |
568 | struct drm_plane *plane; | |
569 | int num_active = 0; | |
570 | ||
571 | drm_for_each_plane_mask(plane, state->dev, new_crtc_state->plane_mask) { | |
572 | struct drm_plane_state *new_plane_state; | |
573 | ||
574 | /* Cursor planes are "fake". */ | |
575 | if (plane->type == DRM_PLANE_TYPE_CURSOR) | |
576 | continue; | |
577 | ||
578 | new_plane_state = drm_atomic_get_new_plane_state(state, plane); | |
579 | ||
580 | if (!new_plane_state) { | |
581 | /* | |
582 | * The plane is enable on the CRTC and hasn't changed | |
583 | * state. This means that it previously passed | |
584 | * validation and is therefore enabled. | |
585 | */ | |
586 | num_active += 1; | |
587 | continue; | |
588 | } | |
589 | ||
590 | /* We need a framebuffer to be considered enabled. */ | |
591 | num_active += (new_plane_state->fb != NULL); | |
592 | } | |
593 | ||
594 | return num_active; | |
595 | } | |
596 | ||
6ce4f9ee RS |
597 | static void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc, |
598 | struct drm_crtc_state *new_crtc_state) | |
473683a0 RS |
599 | { |
600 | struct dm_crtc_state *dm_new_crtc_state = | |
601 | to_dm_crtc_state(new_crtc_state); | |
602 | ||
603 | dm_new_crtc_state->active_planes = 0; | |
604 | ||
605 | if (!dm_new_crtc_state->stream) | |
606 | return; | |
607 | ||
608 | dm_new_crtc_state->active_planes = | |
6ce4f9ee | 609 | amdgpu_dm_crtc_count_crtc_active_planes(new_crtc_state); |
473683a0 RS |
610 | } |
611 | ||
6ce4f9ee | 612 | static bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc, |
473683a0 RS |
613 | const struct drm_display_mode *mode, |
614 | struct drm_display_mode *adjusted_mode) | |
615 | { | |
616 | return true; | |
617 | } | |
618 | ||
6ce4f9ee RS |
619 | static int amdgpu_dm_crtc_helper_atomic_check(struct drm_crtc *crtc, |
620 | struct drm_atomic_state *state) | |
473683a0 RS |
621 | { |
622 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, | |
623 | crtc); | |
624 | struct amdgpu_device *adev = drm_to_adev(crtc->dev); | |
625 | struct dc *dc = adev->dm.dc; | |
626 | struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(crtc_state); | |
627 | int ret = -EINVAL; | |
628 | ||
629 | trace_amdgpu_dm_crtc_atomic_check(crtc_state); | |
630 | ||
6ce4f9ee | 631 | amdgpu_dm_crtc_update_crtc_active_planes(crtc, crtc_state); |
473683a0 RS |
632 | |
633 | if (WARN_ON(unlikely(!dm_crtc_state->stream && | |
6c5e25a0 | 634 | amdgpu_dm_crtc_modeset_required(crtc_state, NULL, dm_crtc_state->stream)))) { |
473683a0 RS |
635 | return ret; |
636 | } | |
637 | ||
638 | /* | |
639 | * We require the primary plane to be enabled whenever the CRTC is, otherwise | |
640 | * drm_mode_cursor_universal may end up trying to enable the cursor plane while all other | |
641 | * planes are disabled, which is not supported by the hardware. And there is legacy | |
642 | * userspace which stops using the HW cursor altogether in response to the resulting EINVAL. | |
643 | */ | |
644 | if (crtc_state->enable && | |
645 | !(crtc_state->plane_mask & drm_plane_mask(crtc->primary))) { | |
646 | DRM_DEBUG_ATOMIC("Can't enable a CRTC without enabling the primary plane\n"); | |
647 | return -EINVAL; | |
1ca67aba SS |
648 | } |
649 | ||
650 | /* | |
651 | * Only allow async flips for fast updates that don't change the FB | |
652 | * pitch, the DCC state, rotation, etc. | |
653 | */ | |
654 | if (crtc_state->async_flip && | |
655 | dm_crtc_state->update_type != UPDATE_TYPE_FAST) { | |
656 | drm_dbg_atomic(crtc->dev, | |
657 | "[CRTC:%d:%s] async flips are only supported for fast updates\n", | |
658 | crtc->base.id, crtc->name); | |
659 | return -EINVAL; | |
473683a0 RS |
660 | } |
661 | ||
662 | /* In some use cases, like reset, no stream is attached */ | |
663 | if (!dm_crtc_state->stream) | |
664 | return 0; | |
665 | ||
666 | if (dc_validate_stream(dc, dm_crtc_state->stream) == DC_OK) | |
667 | return 0; | |
668 | ||
669 | DRM_DEBUG_ATOMIC("Failed DC stream validation\n"); | |
670 | return ret; | |
671 | } | |
672 | ||
673 | static const struct drm_crtc_helper_funcs amdgpu_dm_crtc_helper_funcs = { | |
6ce4f9ee RS |
674 | .disable = amdgpu_dm_crtc_helper_disable, |
675 | .atomic_check = amdgpu_dm_crtc_helper_atomic_check, | |
676 | .mode_fixup = amdgpu_dm_crtc_helper_mode_fixup, | |
473683a0 RS |
677 | .get_scanout_position = amdgpu_crtc_get_scanout_position, |
678 | }; | |
679 | ||
680 | int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm, | |
681 | struct drm_plane *plane, | |
682 | uint32_t crtc_index) | |
683 | { | |
684 | struct amdgpu_crtc *acrtc = NULL; | |
685 | struct drm_plane *cursor_plane; | |
c17b7a58 | 686 | bool is_dcn; |
473683a0 RS |
687 | int res = -ENOMEM; |
688 | ||
689 | cursor_plane = kzalloc(sizeof(*cursor_plane), GFP_KERNEL); | |
690 | if (!cursor_plane) | |
691 | goto fail; | |
692 | ||
693 | cursor_plane->type = DRM_PLANE_TYPE_CURSOR; | |
694 | res = amdgpu_dm_plane_init(dm, cursor_plane, 0, NULL); | |
695 | ||
696 | acrtc = kzalloc(sizeof(struct amdgpu_crtc), GFP_KERNEL); | |
697 | if (!acrtc) | |
698 | goto fail; | |
699 | ||
700 | res = drm_crtc_init_with_planes( | |
701 | dm->ddev, | |
702 | &acrtc->base, | |
703 | plane, | |
704 | cursor_plane, | |
705 | &amdgpu_dm_crtc_funcs, NULL); | |
706 | ||
707 | if (res) | |
708 | goto fail; | |
709 | ||
710 | drm_crtc_helper_add(&acrtc->base, &amdgpu_dm_crtc_helper_funcs); | |
711 | ||
712 | /* Create (reset) the plane state */ | |
713 | if (acrtc->base.funcs->reset) | |
714 | acrtc->base.funcs->reset(&acrtc->base); | |
715 | ||
716 | acrtc->max_cursor_width = dm->adev->dm.dc->caps.max_cursor_size; | |
717 | acrtc->max_cursor_height = dm->adev->dm.dc->caps.max_cursor_size; | |
718 | ||
719 | acrtc->crtc_id = crtc_index; | |
720 | acrtc->base.enabled = false; | |
721 | acrtc->otg_inst = -1; | |
722 | ||
723 | dm->adev->mode_info.crtcs[crtc_index] = acrtc; | |
c17b7a58 MW |
724 | |
725 | /* Don't enable DRM CRTC degamma property for DCE since it doesn't | |
726 | * support programmable degamma anywhere. | |
727 | */ | |
728 | is_dcn = dm->adev->dm.dc->caps.color.dpp.dcn_arch; | |
729 | drm_crtc_enable_color_mgmt(&acrtc->base, is_dcn ? MAX_COLOR_LUT_ENTRIES : 0, | |
473683a0 | 730 | true, MAX_COLOR_LUT_ENTRIES); |
c17b7a58 | 731 | |
473683a0 RS |
732 | drm_mode_crtc_set_gamma_size(&acrtc->base, MAX_COLOR_LEGACY_LUT_ENTRIES); |
733 | ||
0f5afa19 MW |
734 | #ifdef AMD_PRIVATE_COLOR |
735 | dm_crtc_additional_color_mgmt(&acrtc->base); | |
736 | #endif | |
473683a0 RS |
737 | return 0; |
738 | ||
739 | fail: | |
740 | kfree(acrtc); | |
741 | kfree(cursor_plane); | |
742 | return res; | |
743 | } | |
744 |