1 | /* |
2 | * Copyright 2012-15 Advanced Micro Devices, Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice shall be included in |
12 | * all copies or substantial portions of the Software. |
13 | * |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
20 | * OTHER DEALINGS IN THE SOFTWARE. |
21 | * |
22 | * Authors: AMD |
23 | * |
24 | */ |
25 | |
26 | #include "dm_services.h" |
27 | #include "basics/dc_common.h" |
28 | #include "dc.h" |
29 | #include "core_types.h" |
30 | #include "resource.h" |
31 | #include "ipp.h" |
32 | #include "timing_generator.h" |
33 | #include "dc_dmub_srv.h" |
34 | #include "dc_state_priv.h" |
35 | #include "dc_stream_priv.h" |
36 | |
37 | #define DC_LOGGER dc->ctx->logger |
38 | |
39 | /******************************************************************************* |
40 | * Private functions |
41 | ******************************************************************************/ |
42 | void update_stream_signal(struct dc_stream_state *stream, struct dc_sink *sink) |
43 | { |
44 | if (sink->sink_signal == SIGNAL_TYPE_NONE) |
45 | stream->signal = stream->link->connector_signal; |
46 | else |
47 | stream->signal = sink->sink_signal; |
48 | |
49 | if (dc_is_dvi_signal(signal: stream->signal)) { |
50 | if (stream->ctx->dc->caps.dual_link_dvi && |
51 | (stream->timing.pix_clk_100hz / 10) > TMDS_MAX_PIXEL_CLOCK && |
52 | sink->sink_signal != SIGNAL_TYPE_DVI_SINGLE_LINK) |
53 | stream->signal = SIGNAL_TYPE_DVI_DUAL_LINK; |
54 | else |
55 | stream->signal = SIGNAL_TYPE_DVI_SINGLE_LINK; |
56 | } |
57 | } |
58 | |
59 | bool dc_stream_construct(struct dc_stream_state *stream, |
60 | struct dc_sink *dc_sink_data) |
61 | { |
62 | uint32_t i = 0; |
63 | |
64 | stream->sink = dc_sink_data; |
65 | dc_sink_retain(sink: dc_sink_data); |
66 | |
67 | stream->ctx = dc_sink_data->ctx; |
68 | stream->link = dc_sink_data->link; |
69 | stream->sink_patches = dc_sink_data->edid_caps.panel_patch; |
70 | stream->converter_disable_audio = dc_sink_data->converter_disable_audio; |
71 | stream->qs_bit = dc_sink_data->edid_caps.qs_bit; |
72 | stream->qy_bit = dc_sink_data->edid_caps.qy_bit; |
73 | |
74 | /* Copy audio modes */ |
75 | /* TODO - Remove this translation */ |
76 | for (i = 0; i < (dc_sink_data->edid_caps.audio_mode_count); i++) { |
77 | stream->audio_info.modes[i].channel_count = dc_sink_data->edid_caps.audio_modes[i].channel_count; |
78 | stream->audio_info.modes[i].format_code = dc_sink_data->edid_caps.audio_modes[i].format_code; |
79 | stream->audio_info.modes[i].sample_rates.all = dc_sink_data->edid_caps.audio_modes[i].sample_rate; |
80 | stream->audio_info.modes[i].sample_size = dc_sink_data->edid_caps.audio_modes[i].sample_size; |
81 | } |
82 | stream->audio_info.mode_count = dc_sink_data->edid_caps.audio_mode_count; |
83 | stream->audio_info.audio_latency = dc_sink_data->edid_caps.audio_latency; |
84 | stream->audio_info.video_latency = dc_sink_data->edid_caps.video_latency; |
85 | memmove( |
86 | stream->audio_info.display_name, |
87 | dc_sink_data->edid_caps.display_name, |
88 | AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS); |
89 | stream->audio_info.manufacture_id = dc_sink_data->edid_caps.manufacturer_id; |
90 | stream->audio_info.product_id = dc_sink_data->edid_caps.product_id; |
91 | stream->audio_info.flags.all = dc_sink_data->edid_caps.speaker_flags; |
92 | |
93 | if (dc_sink_data->dc_container_id != NULL) { |
94 | struct dc_container_id *dc_container_id = dc_sink_data->dc_container_id; |
95 | |
96 | stream->audio_info.port_id[0] = dc_container_id->portId[0]; |
97 | stream->audio_info.port_id[1] = dc_container_id->portId[1]; |
98 | } else { |
99 | /* TODO - WindowDM has implemented, |
100 | other DMs need Unhardcode port_id */ |
101 | stream->audio_info.port_id[0] = 0x5558859e; |
102 | stream->audio_info.port_id[1] = 0xd989449; |
103 | } |
104 | |
105 | /* EDID CAP translation for HDMI 2.0 */ |
106 | stream->timing.flags.LTE_340MCSC_SCRAMBLE = dc_sink_data->edid_caps.lte_340mcsc_scramble; |
107 | |
108 | memset(&stream->timing.dsc_cfg, 0, sizeof(stream->timing.dsc_cfg)); |
109 | stream->timing.dsc_cfg.num_slices_h = 0; |
110 | stream->timing.dsc_cfg.num_slices_v = 0; |
111 | stream->timing.dsc_cfg.bits_per_pixel = 128; |
112 | stream->timing.dsc_cfg.block_pred_enable = 1; |
113 | stream->timing.dsc_cfg.linebuf_depth = 9; |
114 | stream->timing.dsc_cfg.version_minor = 2; |
115 | stream->timing.dsc_cfg.ycbcr422_simple = 0; |
116 | |
117 | update_stream_signal(stream, sink: dc_sink_data); |
118 | |
119 | stream->out_transfer_func = dc_create_transfer_func(); |
120 | if (stream->out_transfer_func == NULL) { |
121 | dc_sink_release(sink: dc_sink_data); |
122 | return false; |
123 | } |
124 | stream->out_transfer_func->type = TF_TYPE_BYPASS; |
125 | |
126 | dc_stream_assign_stream_id(stream); |
127 | |
128 | return true; |
129 | } |
130 | |
131 | void dc_stream_destruct(struct dc_stream_state *stream) |
132 | { |
133 | dc_sink_release(sink: stream->sink); |
134 | if (stream->out_transfer_func != NULL) { |
135 | dc_transfer_func_release(dc_tf: stream->out_transfer_func); |
136 | stream->out_transfer_func = NULL; |
137 | } |
138 | } |
139 | |
140 | void dc_stream_assign_stream_id(struct dc_stream_state *stream) |
141 | { |
142 | /* MSB is reserved to indicate phantoms */ |
143 | stream->stream_id = stream->ctx->dc_stream_id_count; |
144 | stream->ctx->dc_stream_id_count++; |
145 | } |
146 | |
147 | void dc_stream_retain(struct dc_stream_state *stream) |
148 | { |
149 | kref_get(kref: &stream->refcount); |
150 | } |
151 | |
152 | static void dc_stream_free(struct kref *kref) |
153 | { |
154 | struct dc_stream_state *stream = container_of(kref, struct dc_stream_state, refcount); |
155 | |
156 | dc_stream_destruct(stream); |
157 | kfree(objp: stream); |
158 | } |
159 | |
160 | void dc_stream_release(struct dc_stream_state *stream) |
161 | { |
162 | if (stream != NULL) { |
163 | kref_put(kref: &stream->refcount, release: dc_stream_free); |
164 | } |
165 | } |
166 | |
167 | struct dc_stream_state *dc_create_stream_for_sink( |
168 | struct dc_sink *sink) |
169 | { |
170 | struct dc_stream_state *stream; |
171 | |
172 | if (sink == NULL) |
173 | return NULL; |
174 | |
175 | stream = kzalloc(size: sizeof(struct dc_stream_state), GFP_KERNEL); |
176 | if (stream == NULL) |
177 | goto alloc_fail; |
178 | |
179 | if (dc_stream_construct(stream, dc_sink_data: sink) == false) |
180 | goto construct_fail; |
181 | |
182 | kref_init(kref: &stream->refcount); |
183 | |
184 | return stream; |
185 | |
186 | construct_fail: |
187 | kfree(objp: stream); |
188 | |
189 | alloc_fail: |
190 | return NULL; |
191 | } |
192 | |
193 | struct dc_stream_state *dc_copy_stream(const struct dc_stream_state *stream) |
194 | { |
195 | struct dc_stream_state *new_stream; |
196 | |
197 | new_stream = kmemdup(p: stream, size: sizeof(struct dc_stream_state), GFP_KERNEL); |
198 | if (!new_stream) |
199 | return NULL; |
200 | |
201 | if (new_stream->sink) |
202 | dc_sink_retain(sink: new_stream->sink); |
203 | |
204 | if (new_stream->out_transfer_func) |
205 | dc_transfer_func_retain(dc_tf: new_stream->out_transfer_func); |
206 | |
207 | dc_stream_assign_stream_id(stream: new_stream); |
208 | |
209 | /* If using dynamic encoder assignment, wait till stream committed to assign encoder. */ |
210 | if (new_stream->ctx->dc->res_pool->funcs->link_encs_assign) |
211 | new_stream->link_enc = NULL; |
212 | |
213 | kref_init(kref: &new_stream->refcount); |
214 | |
215 | return new_stream; |
216 | } |
217 | |
218 | /** |
219 | * dc_stream_get_status() - Get current stream status of the given stream state |
220 | * @stream: The stream to get the stream status for. |
221 | * |
222 | * The given stream is expected to exist in dc->current_state. Otherwise, NULL |
223 | * will be returned. |
224 | */ |
225 | struct dc_stream_status *dc_stream_get_status( |
226 | struct dc_stream_state *stream) |
227 | { |
228 | struct dc *dc = stream->ctx->dc; |
229 | return dc_state_get_stream_status(state: dc->current_state, stream); |
230 | } |
231 | |
232 | static void program_cursor_attributes( |
233 | struct dc *dc, |
234 | struct dc_stream_state *stream, |
235 | const struct dc_cursor_attributes *attributes) |
236 | { |
237 | int i; |
238 | struct resource_context *res_ctx; |
239 | struct pipe_ctx *pipe_to_program = NULL; |
240 | |
241 | if (!stream) |
242 | return; |
243 | |
244 | res_ctx = &dc->current_state->res_ctx; |
245 | |
246 | for (i = 0; i < MAX_PIPES; i++) { |
247 | struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; |
248 | |
249 | if (pipe_ctx->stream != stream) |
250 | continue; |
251 | |
252 | if (!pipe_to_program) { |
253 | pipe_to_program = pipe_ctx; |
254 | dc->hwss.cursor_lock(dc, pipe_to_program, true); |
255 | if (pipe_to_program->next_odm_pipe) |
256 | dc->hwss.cursor_lock(dc, pipe_to_program->next_odm_pipe, true); |
257 | } |
258 | |
259 | dc->hwss.set_cursor_attribute(pipe_ctx); |
260 | if (dc->ctx->dmub_srv) |
261 | dc_send_update_cursor_info_to_dmu(pCtx: pipe_ctx, pipe_idx: i); |
262 | if (dc->hwss.set_cursor_sdr_white_level) |
263 | dc->hwss.set_cursor_sdr_white_level(pipe_ctx); |
264 | } |
265 | |
266 | if (pipe_to_program) { |
267 | dc->hwss.cursor_lock(dc, pipe_to_program, false); |
268 | if (pipe_to_program->next_odm_pipe) |
269 | dc->hwss.cursor_lock(dc, pipe_to_program->next_odm_pipe, false); |
270 | } |
271 | } |
272 | |
273 | /* |
274 | * dc_stream_set_cursor_attributes() - Update cursor attributes and set cursor surface address |
275 | */ |
276 | bool dc_stream_set_cursor_attributes( |
277 | struct dc_stream_state *stream, |
278 | const struct dc_cursor_attributes *attributes) |
279 | { |
280 | struct dc *dc; |
281 | bool reset_idle_optimizations = false; |
282 | |
283 | if (NULL == stream) { |
284 | dm_error("DC: dc_stream is NULL!\n" ); |
285 | return false; |
286 | } |
287 | if (NULL == attributes) { |
288 | dm_error("DC: attributes is NULL!\n" ); |
289 | return false; |
290 | } |
291 | |
292 | if (attributes->address.quad_part == 0) { |
293 | dm_output_to_console("DC: Cursor address is 0!\n" ); |
294 | return false; |
295 | } |
296 | |
297 | dc = stream->ctx->dc; |
298 | |
299 | /* SubVP is not compatible with HW cursor larger than 64 x 64 x 4. |
300 | * Therefore, if cursor is greater than 64 x 64 x 4, fallback to SW cursor in the following case: |
301 | * 1. If the config is a candidate for SubVP high refresh (both single an dual display configs) |
302 | * 2. If not subvp high refresh, for single display cases, if resolution is >= 5K and refresh rate < 120hz |
303 | * 3. If not subvp high refresh, for multi display cases, if resolution is >= 4K and refresh rate < 120hz |
304 | */ |
305 | if (dc->debug.allow_sw_cursor_fallback && attributes->height * attributes->width * 4 > 16384) { |
306 | if (check_subvp_sw_cursor_fallback_req(dc, stream)) |
307 | return false; |
308 | } |
309 | |
310 | stream->cursor_attributes = *attributes; |
311 | |
312 | dc_z10_restore(dc); |
313 | /* disable idle optimizations while updating cursor */ |
314 | if (dc->idle_optimizations_allowed) { |
315 | dc_allow_idle_optimizations(dc, allow: false); |
316 | reset_idle_optimizations = true; |
317 | } |
318 | |
319 | program_cursor_attributes(dc, stream, attributes); |
320 | |
321 | /* re-enable idle optimizations if necessary */ |
322 | if (reset_idle_optimizations) |
323 | dc_allow_idle_optimizations(dc, allow: true); |
324 | |
325 | return true; |
326 | } |
327 | |
328 | static void program_cursor_position( |
329 | struct dc *dc, |
330 | struct dc_stream_state *stream, |
331 | const struct dc_cursor_position *position) |
332 | { |
333 | int i; |
334 | struct resource_context *res_ctx; |
335 | struct pipe_ctx *pipe_to_program = NULL; |
336 | |
337 | if (!stream) |
338 | return; |
339 | |
340 | res_ctx = &dc->current_state->res_ctx; |
341 | |
342 | for (i = 0; i < MAX_PIPES; i++) { |
343 | struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; |
344 | |
345 | if (pipe_ctx->stream != stream || |
346 | (!pipe_ctx->plane_res.mi && !pipe_ctx->plane_res.hubp) || |
347 | !pipe_ctx->plane_state || |
348 | (!pipe_ctx->plane_res.xfm && !pipe_ctx->plane_res.dpp) || |
349 | (!pipe_ctx->plane_res.ipp && !pipe_ctx->plane_res.dpp)) |
350 | continue; |
351 | |
352 | if (!pipe_to_program) { |
353 | pipe_to_program = pipe_ctx; |
354 | dc->hwss.cursor_lock(dc, pipe_to_program, true); |
355 | } |
356 | |
357 | dc->hwss.set_cursor_position(pipe_ctx); |
358 | if (dc->ctx->dmub_srv) |
359 | dc_send_update_cursor_info_to_dmu(pCtx: pipe_ctx, pipe_idx: i); |
360 | } |
361 | |
362 | if (pipe_to_program) |
363 | dc->hwss.cursor_lock(dc, pipe_to_program, false); |
364 | } |
365 | |
366 | bool dc_stream_set_cursor_position( |
367 | struct dc_stream_state *stream, |
368 | const struct dc_cursor_position *position) |
369 | { |
370 | struct dc *dc; |
371 | bool reset_idle_optimizations = false; |
372 | |
373 | if (NULL == stream) { |
374 | dm_error("DC: dc_stream is NULL!\n" ); |
375 | return false; |
376 | } |
377 | |
378 | if (NULL == position) { |
379 | dm_error("DC: cursor position is NULL!\n" ); |
380 | return false; |
381 | } |
382 | |
383 | dc = stream->ctx->dc; |
384 | dc_z10_restore(dc); |
385 | |
386 | /* disable idle optimizations if enabling cursor */ |
387 | if (dc->idle_optimizations_allowed && (!stream->cursor_position.enable || dc->debug.exit_idle_opt_for_cursor_updates) |
388 | && position->enable) { |
389 | dc_allow_idle_optimizations(dc, allow: false); |
390 | reset_idle_optimizations = true; |
391 | } |
392 | |
393 | stream->cursor_position = *position; |
394 | |
395 | program_cursor_position(dc, stream, position); |
396 | /* re-enable idle optimizations if necessary */ |
397 | if (reset_idle_optimizations) |
398 | dc_allow_idle_optimizations(dc, allow: true); |
399 | |
400 | return true; |
401 | } |
402 | |
403 | bool dc_stream_add_writeback(struct dc *dc, |
404 | struct dc_stream_state *stream, |
405 | struct dc_writeback_info *wb_info) |
406 | { |
407 | bool isDrc = false; |
408 | int i = 0; |
409 | struct dwbc *dwb; |
410 | |
411 | if (stream == NULL) { |
412 | dm_error("DC: dc_stream is NULL!\n" ); |
413 | return false; |
414 | } |
415 | |
416 | if (wb_info == NULL) { |
417 | dm_error("DC: dc_writeback_info is NULL!\n" ); |
418 | return false; |
419 | } |
420 | |
421 | if (wb_info->dwb_pipe_inst >= MAX_DWB_PIPES) { |
422 | dm_error("DC: writeback pipe is invalid!\n" ); |
423 | return false; |
424 | } |
425 | |
426 | dc_exit_ips_for_hw_access(dc); |
427 | |
428 | wb_info->dwb_params.out_transfer_func = stream->out_transfer_func; |
429 | |
430 | dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst]; |
431 | dwb->dwb_is_drc = false; |
432 | |
433 | /* recalculate and apply DML parameters */ |
434 | |
435 | for (i = 0; i < stream->num_wb_info; i++) { |
436 | /*dynamic update*/ |
437 | if (stream->writeback_info[i].wb_enabled && |
438 | stream->writeback_info[i].dwb_pipe_inst == wb_info->dwb_pipe_inst) { |
439 | stream->writeback_info[i] = *wb_info; |
440 | isDrc = true; |
441 | } |
442 | } |
443 | |
444 | if (!isDrc) { |
445 | ASSERT(stream->num_wb_info + 1 <= MAX_DWB_PIPES); |
446 | stream->writeback_info[stream->num_wb_info++] = *wb_info; |
447 | } |
448 | |
449 | if (dc->hwss.enable_writeback) { |
450 | struct dc_stream_status *stream_status = dc_stream_get_status(stream); |
451 | struct dwbc *dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst]; |
452 | if (stream_status) |
453 | dwb->otg_inst = stream_status->primary_otg_inst; |
454 | } |
455 | |
456 | if (!dc->hwss.update_bandwidth(dc, dc->current_state)) { |
457 | dm_error("DC: update_bandwidth failed!\n" ); |
458 | return false; |
459 | } |
460 | |
461 | /* enable writeback */ |
462 | if (dc->hwss.enable_writeback) { |
463 | struct dwbc *dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst]; |
464 | |
465 | if (dwb->funcs->is_enabled(dwb)) { |
466 | /* writeback pipe already enabled, only need to update */ |
467 | dc->hwss.update_writeback(dc, wb_info, dc->current_state); |
468 | } else { |
469 | /* Enable writeback pipe from scratch*/ |
470 | dc->hwss.enable_writeback(dc, wb_info, dc->current_state); |
471 | } |
472 | } |
473 | |
474 | return true; |
475 | } |
476 | |
477 | bool dc_stream_fc_disable_writeback(struct dc *dc, |
478 | struct dc_stream_state *stream, |
479 | uint32_t dwb_pipe_inst) |
480 | { |
481 | struct dwbc *dwb = dc->res_pool->dwbc[dwb_pipe_inst]; |
482 | |
483 | if (stream == NULL) { |
484 | dm_error("DC: dc_stream is NULL!\n" ); |
485 | return false; |
486 | } |
487 | |
488 | if (dwb_pipe_inst >= MAX_DWB_PIPES) { |
489 | dm_error("DC: writeback pipe is invalid!\n" ); |
490 | return false; |
491 | } |
492 | |
493 | if (stream->num_wb_info > MAX_DWB_PIPES) { |
494 | dm_error("DC: num_wb_info is invalid!\n" ); |
495 | return false; |
496 | } |
497 | |
498 | dc_exit_ips_for_hw_access(dc); |
499 | |
500 | if (dwb->funcs->set_fc_enable) |
501 | dwb->funcs->set_fc_enable(dwb, DWB_FRAME_CAPTURE_DISABLE); |
502 | |
503 | return true; |
504 | } |
505 | |
506 | bool dc_stream_remove_writeback(struct dc *dc, |
507 | struct dc_stream_state *stream, |
508 | uint32_t dwb_pipe_inst) |
509 | { |
510 | int i = 0, j = 0; |
511 | if (stream == NULL) { |
512 | dm_error("DC: dc_stream is NULL!\n" ); |
513 | return false; |
514 | } |
515 | |
516 | if (dwb_pipe_inst >= MAX_DWB_PIPES) { |
517 | dm_error("DC: writeback pipe is invalid!\n" ); |
518 | return false; |
519 | } |
520 | |
521 | if (stream->num_wb_info > MAX_DWB_PIPES) { |
522 | dm_error("DC: num_wb_info is invalid!\n" ); |
523 | return false; |
524 | } |
525 | |
526 | /* remove writeback info for disabled writeback pipes from stream */ |
527 | for (i = 0, j = 0; i < stream->num_wb_info; i++) { |
528 | if (stream->writeback_info[i].wb_enabled) { |
529 | |
530 | if (stream->writeback_info[i].dwb_pipe_inst == dwb_pipe_inst) |
531 | stream->writeback_info[i].wb_enabled = false; |
532 | |
533 | /* trim the array */ |
534 | if (j < i) { |
535 | memcpy(&stream->writeback_info[j], &stream->writeback_info[i], |
536 | sizeof(struct dc_writeback_info)); |
537 | j++; |
538 | } |
539 | } |
540 | } |
541 | stream->num_wb_info = j; |
542 | |
543 | /* recalculate and apply DML parameters */ |
544 | if (!dc->hwss.update_bandwidth(dc, dc->current_state)) { |
545 | dm_error("DC: update_bandwidth failed!\n" ); |
546 | return false; |
547 | } |
548 | |
549 | dc_exit_ips_for_hw_access(dc); |
550 | |
551 | /* disable writeback */ |
552 | if (dc->hwss.disable_writeback) { |
553 | struct dwbc *dwb = dc->res_pool->dwbc[dwb_pipe_inst]; |
554 | |
555 | if (dwb->funcs->is_enabled(dwb)) |
556 | dc->hwss.disable_writeback(dc, dwb_pipe_inst); |
557 | } |
558 | |
559 | return true; |
560 | } |
561 | |
562 | bool dc_stream_warmup_writeback(struct dc *dc, |
563 | int num_dwb, |
564 | struct dc_writeback_info *wb_info) |
565 | { |
566 | dc_exit_ips_for_hw_access(dc); |
567 | |
568 | if (dc->hwss.mmhubbub_warmup) |
569 | return dc->hwss.mmhubbub_warmup(dc, num_dwb, wb_info); |
570 | else |
571 | return false; |
572 | } |
573 | uint32_t dc_stream_get_vblank_counter(const struct dc_stream_state *stream) |
574 | { |
575 | uint8_t i; |
576 | struct dc *dc = stream->ctx->dc; |
577 | struct resource_context *res_ctx = |
578 | &dc->current_state->res_ctx; |
579 | |
580 | dc_exit_ips_for_hw_access(dc); |
581 | |
582 | for (i = 0; i < MAX_PIPES; i++) { |
583 | struct timing_generator *tg = res_ctx->pipe_ctx[i].stream_res.tg; |
584 | |
585 | if (res_ctx->pipe_ctx[i].stream != stream || !tg) |
586 | continue; |
587 | |
588 | return tg->funcs->get_frame_count(tg); |
589 | } |
590 | |
591 | return 0; |
592 | } |
593 | |
594 | bool dc_stream_send_dp_sdp(const struct dc_stream_state *stream, |
595 | const uint8_t *custom_sdp_message, |
596 | unsigned int sdp_message_size) |
597 | { |
598 | int i; |
599 | struct dc *dc; |
600 | struct resource_context *res_ctx; |
601 | |
602 | if (stream == NULL) { |
603 | dm_error("DC: dc_stream is NULL!\n" ); |
604 | return false; |
605 | } |
606 | |
607 | dc = stream->ctx->dc; |
608 | res_ctx = &dc->current_state->res_ctx; |
609 | |
610 | dc_exit_ips_for_hw_access(dc); |
611 | |
612 | for (i = 0; i < MAX_PIPES; i++) { |
613 | struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; |
614 | |
615 | if (pipe_ctx->stream != stream) |
616 | continue; |
617 | |
618 | if (dc->hwss.send_immediate_sdp_message != NULL) |
619 | dc->hwss.send_immediate_sdp_message(pipe_ctx, |
620 | custom_sdp_message, |
621 | sdp_message_size); |
622 | else |
623 | DC_LOG_WARNING("%s:send_immediate_sdp_message not implemented on this ASIC\n" , |
624 | __func__); |
625 | |
626 | } |
627 | |
628 | return true; |
629 | } |
630 | |
631 | bool dc_stream_get_scanoutpos(const struct dc_stream_state *stream, |
632 | uint32_t *v_blank_start, |
633 | uint32_t *v_blank_end, |
634 | uint32_t *h_position, |
635 | uint32_t *v_position) |
636 | { |
637 | uint8_t i; |
638 | bool ret = false; |
639 | struct dc *dc = stream->ctx->dc; |
640 | struct resource_context *res_ctx = |
641 | &dc->current_state->res_ctx; |
642 | |
643 | dc_exit_ips_for_hw_access(dc); |
644 | |
645 | for (i = 0; i < MAX_PIPES; i++) { |
646 | struct timing_generator *tg = res_ctx->pipe_ctx[i].stream_res.tg; |
647 | |
648 | if (res_ctx->pipe_ctx[i].stream != stream || !tg) |
649 | continue; |
650 | |
651 | tg->funcs->get_scanoutpos(tg, |
652 | v_blank_start, |
653 | v_blank_end, |
654 | h_position, |
655 | v_position); |
656 | |
657 | ret = true; |
658 | break; |
659 | } |
660 | |
661 | return ret; |
662 | } |
663 | |
664 | bool dc_stream_dmdata_status_done(struct dc *dc, struct dc_stream_state *stream) |
665 | { |
666 | struct pipe_ctx *pipe = NULL; |
667 | int i; |
668 | |
669 | if (!dc->hwss.dmdata_status_done) |
670 | return false; |
671 | |
672 | for (i = 0; i < MAX_PIPES; i++) { |
673 | pipe = &dc->current_state->res_ctx.pipe_ctx[i]; |
674 | if (pipe->stream == stream) |
675 | break; |
676 | } |
677 | /* Stream not found, by default we'll assume HUBP fetched dm data */ |
678 | if (i == MAX_PIPES) |
679 | return true; |
680 | |
681 | dc_exit_ips_for_hw_access(dc); |
682 | |
683 | return dc->hwss.dmdata_status_done(pipe); |
684 | } |
685 | |
686 | bool dc_stream_set_dynamic_metadata(struct dc *dc, |
687 | struct dc_stream_state *stream, |
688 | struct dc_dmdata_attributes *attr) |
689 | { |
690 | struct pipe_ctx *pipe_ctx = NULL; |
691 | struct hubp *hubp; |
692 | int i; |
693 | |
694 | /* Dynamic metadata is only supported on HDMI or DP */ |
695 | if (!dc_is_hdmi_signal(signal: stream->signal) && !dc_is_dp_signal(signal: stream->signal)) |
696 | return false; |
697 | |
698 | /* Check hardware support */ |
699 | if (!dc->hwss.program_dmdata_engine) |
700 | return false; |
701 | |
702 | for (i = 0; i < MAX_PIPES; i++) { |
703 | pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; |
704 | if (pipe_ctx->stream == stream) |
705 | break; |
706 | } |
707 | |
708 | if (i == MAX_PIPES) |
709 | return false; |
710 | |
711 | hubp = pipe_ctx->plane_res.hubp; |
712 | if (hubp == NULL) |
713 | return false; |
714 | |
715 | pipe_ctx->stream->dmdata_address = attr->address; |
716 | |
717 | dc_exit_ips_for_hw_access(dc); |
718 | |
719 | dc->hwss.program_dmdata_engine(pipe_ctx); |
720 | |
721 | if (hubp->funcs->dmdata_set_attributes != NULL && |
722 | pipe_ctx->stream->dmdata_address.quad_part != 0) { |
723 | hubp->funcs->dmdata_set_attributes(hubp, attr); |
724 | } |
725 | |
726 | return true; |
727 | } |
728 | |
729 | enum dc_status dc_stream_add_dsc_to_resource(struct dc *dc, |
730 | struct dc_state *state, |
731 | struct dc_stream_state *stream) |
732 | { |
733 | if (dc->res_pool->funcs->add_dsc_to_stream_resource) { |
734 | return dc->res_pool->funcs->add_dsc_to_stream_resource(dc, state, stream); |
735 | } else { |
736 | return DC_NO_DSC_RESOURCE; |
737 | } |
738 | } |
739 | |
740 | struct pipe_ctx *dc_stream_get_pipe_ctx(struct dc_stream_state *stream) |
741 | { |
742 | int i = 0; |
743 | |
744 | for (i = 0; i < MAX_PIPES; i++) { |
745 | struct pipe_ctx *pipe = &stream->ctx->dc->current_state->res_ctx.pipe_ctx[i]; |
746 | |
747 | if (pipe->stream == stream) |
748 | return pipe; |
749 | } |
750 | |
751 | return NULL; |
752 | } |
753 | |
754 | void dc_stream_log(const struct dc *dc, const struct dc_stream_state *stream) |
755 | { |
756 | DC_LOG_DC( |
757 | "core_stream 0x%p: src: %d, %d, %d, %d; dst: %d, %d, %d, %d, colorSpace:%d\n" , |
758 | stream, |
759 | stream->src.x, |
760 | stream->src.y, |
761 | stream->src.width, |
762 | stream->src.height, |
763 | stream->dst.x, |
764 | stream->dst.y, |
765 | stream->dst.width, |
766 | stream->dst.height, |
767 | stream->output_color_space); |
768 | DC_LOG_DC( |
769 | "\tpix_clk_khz: %d, h_total: %d, v_total: %d, pixelencoder:%d, displaycolorDepth:%d\n" , |
770 | stream->timing.pix_clk_100hz / 10, |
771 | stream->timing.h_total, |
772 | stream->timing.v_total, |
773 | stream->timing.pixel_encoding, |
774 | stream->timing.display_color_depth); |
775 | DC_LOG_DC( |
776 | "\tlink: %d\n" , |
777 | stream->link->link_index); |
778 | |
779 | DC_LOG_DC( |
780 | "\tdsc: %d, mst_pbn: %d\n" , |
781 | stream->timing.flags.DSC, |
782 | stream->timing.dsc_cfg.mst_pbn); |
783 | |
784 | if (stream->sink) { |
785 | if (stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL && |
786 | stream->sink->sink_signal != SIGNAL_TYPE_NONE) { |
787 | |
788 | DC_LOG_DC( |
789 | "\tdispname: %s signal: %x\n" , |
790 | stream->sink->edid_caps.display_name, |
791 | stream->signal); |
792 | } |
793 | } |
794 | } |
795 | |
796 | |