]>
Commit | Line | Data |
---|---|---|
4562236b HW |
1 | /* |
2 | * Copyright 2015 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 | #include "dm_services.h" | |
26 | ||
27 | #include "dc.h" | |
28 | ||
29 | #include "core_status.h" | |
30 | #include "core_types.h" | |
31 | #include "hw_sequencer.h" | |
32 | ||
33 | #include "resource.h" | |
34 | ||
35 | #include "clock_source.h" | |
36 | #include "dc_bios_types.h" | |
37 | ||
4562236b HW |
38 | #include "bios_parser_interface.h" |
39 | #include "include/irq_service_interface.h" | |
40 | #include "transform.h" | |
d94585a0 | 41 | #include "dpp.h" |
4562236b HW |
42 | #include "timing_generator.h" |
43 | #include "virtual/virtual_link_encoder.h" | |
44 | ||
45 | #include "link_hwss.h" | |
46 | #include "link_encoder.h" | |
47 | ||
48 | #include "dc_link_ddc.h" | |
49 | #include "dm_helpers.h" | |
50 | #include "mem_input.h" | |
8feabd03 | 51 | #include "hubp.h" |
4562236b | 52 | |
7fb77c51 | 53 | |
4562236b HW |
54 | /******************************************************************************* |
55 | * Private functions | |
56 | ******************************************************************************/ | |
fb3466a4 | 57 | static void destroy_links(struct dc *dc) |
4562236b HW |
58 | { |
59 | uint32_t i; | |
60 | ||
61 | for (i = 0; i < dc->link_count; i++) { | |
62 | if (NULL != dc->links[i]) | |
63 | link_destroy(&dc->links[i]); | |
64 | } | |
65 | } | |
66 | ||
67 | static bool create_links( | |
fb3466a4 | 68 | struct dc *dc, |
4562236b HW |
69 | uint32_t num_virtual_links) |
70 | { | |
71 | int i; | |
72 | int connectors_num; | |
73 | struct dc_bios *bios = dc->ctx->dc_bios; | |
74 | ||
75 | dc->link_count = 0; | |
76 | ||
77 | connectors_num = bios->funcs->get_connectors_number(bios); | |
78 | ||
79 | if (connectors_num > ENUM_ID_COUNT) { | |
80 | dm_error( | |
81 | "DC: Number of connectors %d exceeds maximum of %d!\n", | |
82 | connectors_num, | |
83 | ENUM_ID_COUNT); | |
84 | return false; | |
85 | } | |
86 | ||
87 | if (connectors_num == 0 && num_virtual_links == 0) { | |
88 | dm_error("DC: Number of connectors is zero!\n"); | |
89 | } | |
90 | ||
91 | dm_output_to_console( | |
92 | "DC: %s: connectors_num: physical:%d, virtual:%d\n", | |
93 | __func__, | |
94 | connectors_num, | |
95 | num_virtual_links); | |
96 | ||
97 | for (i = 0; i < connectors_num; i++) { | |
98 | struct link_init_data link_init_params = {0}; | |
d0778ebf | 99 | struct dc_link *link; |
4562236b HW |
100 | |
101 | link_init_params.ctx = dc->ctx; | |
e4bf0a0e | 102 | /* next BIOS object table connector */ |
4562236b HW |
103 | link_init_params.connector_index = i; |
104 | link_init_params.link_index = dc->link_count; | |
105 | link_init_params.dc = dc; | |
106 | link = link_create(&link_init_params); | |
107 | ||
108 | if (link) { | |
109 | dc->links[dc->link_count] = link; | |
110 | link->dc = dc; | |
111 | ++dc->link_count; | |
4562236b HW |
112 | } |
113 | } | |
114 | ||
115 | for (i = 0; i < num_virtual_links; i++) { | |
2004f45e | 116 | struct dc_link *link = kzalloc(sizeof(*link), GFP_KERNEL); |
4562236b HW |
117 | struct encoder_init_data enc_init = {0}; |
118 | ||
119 | if (link == NULL) { | |
120 | BREAK_TO_DEBUGGER(); | |
121 | goto failed_alloc; | |
122 | } | |
123 | ||
124 | link->ctx = dc->ctx; | |
125 | link->dc = dc; | |
d0778ebf | 126 | link->connector_signal = SIGNAL_TYPE_VIRTUAL; |
4562236b HW |
127 | link->link_id.type = OBJECT_TYPE_CONNECTOR; |
128 | link->link_id.id = CONNECTOR_ID_VIRTUAL; | |
129 | link->link_id.enum_id = ENUM_ID_1; | |
2004f45e | 130 | link->link_enc = kzalloc(sizeof(*link->link_enc), GFP_KERNEL); |
4562236b HW |
131 | |
132 | enc_init.ctx = dc->ctx; | |
133 | enc_init.channel = CHANNEL_ID_UNKNOWN; | |
134 | enc_init.hpd_source = HPD_SOURCEID_UNKNOWN; | |
135 | enc_init.transmitter = TRANSMITTER_UNKNOWN; | |
136 | enc_init.connector = link->link_id; | |
137 | enc_init.encoder.type = OBJECT_TYPE_ENCODER; | |
138 | enc_init.encoder.id = ENCODER_ID_INTERNAL_VIRTUAL; | |
139 | enc_init.encoder.enum_id = ENUM_ID_1; | |
140 | virtual_link_encoder_construct(link->link_enc, &enc_init); | |
141 | ||
d0778ebf | 142 | link->link_index = dc->link_count; |
4562236b HW |
143 | dc->links[dc->link_count] = link; |
144 | dc->link_count++; | |
145 | } | |
146 | ||
147 | return true; | |
148 | ||
149 | failed_alloc: | |
150 | return false; | |
151 | } | |
152 | ||
153 | static bool stream_adjust_vmin_vmax(struct dc *dc, | |
0971c40e | 154 | struct dc_stream_state **streams, int num_streams, |
4562236b HW |
155 | int vmin, int vmax) |
156 | { | |
157 | /* TODO: Support multiple streams */ | |
0971c40e | 158 | struct dc_stream_state *stream = streams[0]; |
4562236b HW |
159 | int i = 0; |
160 | bool ret = false; | |
4562236b HW |
161 | |
162 | for (i = 0; i < MAX_PIPES; i++) { | |
608ac7bb | 163 | struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i]; |
4562236b | 164 | |
8e9c4c8c | 165 | if (pipe->stream == stream && pipe->stream_res.stream_enc) { |
15659045 | 166 | dc->hwss.set_drr(&pipe, 1, vmin, vmax); |
4562236b HW |
167 | |
168 | /* build and update the info frame */ | |
6680b6a1 | 169 | resource_build_info_frame(pipe); |
15659045 | 170 | dc->hwss.update_info_frame(pipe); |
4562236b HW |
171 | |
172 | ret = true; | |
173 | } | |
174 | } | |
4562236b HW |
175 | return ret; |
176 | } | |
177 | ||
72ada5f7 | 178 | static bool stream_get_crtc_position(struct dc *dc, |
0971c40e | 179 | struct dc_stream_state **streams, int num_streams, |
72ada5f7 EC |
180 | unsigned int *v_pos, unsigned int *nom_v_pos) |
181 | { | |
182 | /* TODO: Support multiple streams */ | |
0971c40e | 183 | struct dc_stream_state *stream = streams[0]; |
72ada5f7 EC |
184 | int i = 0; |
185 | bool ret = false; | |
186 | struct crtc_position position; | |
187 | ||
188 | for (i = 0; i < MAX_PIPES; i++) { | |
189 | struct pipe_ctx *pipe = | |
608ac7bb | 190 | &dc->current_state->res_ctx.pipe_ctx[i]; |
72ada5f7 | 191 | |
8e9c4c8c | 192 | if (pipe->stream == stream && pipe->stream_res.stream_enc) { |
15659045 | 193 | dc->hwss.get_position(&pipe, 1, &position); |
72ada5f7 EC |
194 | |
195 | *v_pos = position.vertical_count; | |
196 | *nom_v_pos = position.nominal_vcount; | |
197 | ret = true; | |
198 | } | |
199 | } | |
200 | return ret; | |
201 | } | |
4562236b | 202 | |
0971c40e | 203 | static bool set_gamut_remap(struct dc *dc, const struct dc_stream_state *stream) |
4562236b | 204 | { |
4562236b HW |
205 | int i = 0; |
206 | bool ret = false; | |
207 | struct pipe_ctx *pipes; | |
208 | ||
209 | for (i = 0; i < MAX_PIPES; i++) { | |
608ac7bb JZ |
210 | if (dc->current_state->res_ctx.pipe_ctx[i].stream == stream) { |
211 | pipes = &dc->current_state->res_ctx.pipe_ctx[i]; | |
15659045 | 212 | dc->hwss.program_gamut_remap(pipes); |
4562236b HW |
213 | ret = true; |
214 | } | |
215 | } | |
216 | ||
217 | return ret; | |
218 | } | |
219 | ||
0971c40e | 220 | static bool program_csc_matrix(struct dc *dc, struct dc_stream_state *stream) |
abe07e80 | 221 | { |
abe07e80 YHL |
222 | int i = 0; |
223 | bool ret = false; | |
224 | struct pipe_ctx *pipes; | |
225 | ||
226 | for (i = 0; i < MAX_PIPES; i++) { | |
608ac7bb | 227 | if (dc->current_state->res_ctx.pipe_ctx[i].stream |
4fa086b9 | 228 | == stream) { |
abe07e80 | 229 | |
608ac7bb | 230 | pipes = &dc->current_state->res_ctx.pipe_ctx[i]; |
15659045 | 231 | dc->hwss.program_csc_matrix(pipes, |
4fa086b9 LSL |
232 | stream->output_color_space, |
233 | stream->csc_color_matrix.matrix); | |
abe07e80 YHL |
234 | ret = true; |
235 | } | |
236 | } | |
237 | ||
238 | return ret; | |
239 | } | |
240 | ||
94267b3d | 241 | static void set_static_screen_events(struct dc *dc, |
0971c40e | 242 | struct dc_stream_state **streams, |
94267b3d ST |
243 | int num_streams, |
244 | const struct dc_static_screen_events *events) | |
245 | { | |
94267b3d ST |
246 | int i = 0; |
247 | int j = 0; | |
248 | struct pipe_ctx *pipes_affected[MAX_PIPES]; | |
249 | int num_pipes_affected = 0; | |
250 | ||
251 | for (i = 0; i < num_streams; i++) { | |
0971c40e | 252 | struct dc_stream_state *stream = streams[i]; |
94267b3d ST |
253 | |
254 | for (j = 0; j < MAX_PIPES; j++) { | |
608ac7bb | 255 | if (dc->current_state->res_ctx.pipe_ctx[j].stream |
4fa086b9 | 256 | == stream) { |
94267b3d | 257 | pipes_affected[num_pipes_affected++] = |
608ac7bb | 258 | &dc->current_state->res_ctx.pipe_ctx[j]; |
94267b3d ST |
259 | } |
260 | } | |
261 | } | |
262 | ||
15659045 | 263 | dc->hwss.set_static_screen_control(pipes_affected, num_pipes_affected, events); |
94267b3d ST |
264 | } |
265 | ||
4562236b | 266 | static void set_drive_settings(struct dc *dc, |
bf5cda33 HW |
267 | struct link_training_settings *lt_settings, |
268 | const struct dc_link *link) | |
4562236b | 269 | { |
15659045 | 270 | |
4562236b HW |
271 | int i; |
272 | ||
15659045 BL |
273 | for (i = 0; i < dc->link_count; i++) { |
274 | if (dc->links[i] == link) | |
bf5cda33 HW |
275 | break; |
276 | } | |
277 | ||
15659045 | 278 | if (i >= dc->link_count) |
bf5cda33 HW |
279 | ASSERT_CRITICAL(false); |
280 | ||
15659045 | 281 | dc_link_dp_set_drive_settings(dc->links[i], lt_settings); |
4562236b HW |
282 | } |
283 | ||
284 | static void perform_link_training(struct dc *dc, | |
285 | struct dc_link_settings *link_setting, | |
286 | bool skip_video_pattern) | |
287 | { | |
4562236b HW |
288 | int i; |
289 | ||
15659045 | 290 | for (i = 0; i < dc->link_count; i++) |
4562236b | 291 | dc_link_dp_perform_link_training( |
15659045 | 292 | dc->links[i], |
4562236b HW |
293 | link_setting, |
294 | skip_video_pattern); | |
295 | } | |
296 | ||
297 | static void set_preferred_link_settings(struct dc *dc, | |
88639168 | 298 | struct dc_link_settings *link_setting, |
d0778ebf | 299 | struct dc_link *link) |
4562236b | 300 | { |
d0778ebf HW |
301 | link->preferred_link_setting = *link_setting; |
302 | dp_retrain_link_dp_test(link, link_setting, false); | |
4562236b HW |
303 | } |
304 | ||
305 | static void enable_hpd(const struct dc_link *link) | |
306 | { | |
307 | dc_link_dp_enable_hpd(link); | |
308 | } | |
309 | ||
310 | static void disable_hpd(const struct dc_link *link) | |
311 | { | |
312 | dc_link_dp_disable_hpd(link); | |
313 | } | |
314 | ||
315 | ||
316 | static void set_test_pattern( | |
d0778ebf | 317 | struct dc_link *link, |
4562236b HW |
318 | enum dp_test_pattern test_pattern, |
319 | const struct link_training_settings *p_link_settings, | |
320 | const unsigned char *p_custom_pattern, | |
321 | unsigned int cust_pattern_size) | |
322 | { | |
323 | if (link != NULL) | |
324 | dc_link_dp_set_test_pattern( | |
325 | link, | |
326 | test_pattern, | |
327 | p_link_settings, | |
328 | p_custom_pattern, | |
329 | cust_pattern_size); | |
330 | } | |
331 | ||
44858055 | 332 | static void set_dither_option(struct dc_stream_state *stream, |
529cad0f DW |
333 | enum dc_dither_option option) |
334 | { | |
529cad0f | 335 | struct bit_depth_reduction_params params; |
d0778ebf | 336 | struct dc_link *link = stream->status.link; |
c196cbe0 | 337 | struct pipe_ctx *pipes = NULL; |
d050f8ed HW |
338 | int i; |
339 | ||
340 | for (i = 0; i < MAX_PIPES; i++) { | |
341 | if (link->dc->current_state->res_ctx.pipe_ctx[i].stream == | |
342 | stream) { | |
343 | pipes = &link->dc->current_state->res_ctx.pipe_ctx[i]; | |
344 | break; | |
345 | } | |
346 | } | |
529cad0f DW |
347 | |
348 | memset(¶ms, 0, sizeof(params)); | |
c196cbe0 | 349 | if (!pipes) |
529cad0f DW |
350 | return; |
351 | if (option > DITHER_OPTION_MAX) | |
352 | return; | |
603767f9 TC |
353 | |
354 | stream->dither_option = option; | |
355 | ||
529cad0f DW |
356 | resource_build_bit_depth_reduction_params(stream, |
357 | ¶ms); | |
358 | stream->bit_depth_params = params; | |
a6a6cb34 HW |
359 | pipes->stream_res.opp->funcs-> |
360 | opp_program_bit_depth_reduction(pipes->stream_res.opp, ¶ms); | |
529cad0f DW |
361 | } |
362 | ||
d050f8ed HW |
363 | void set_dpms( |
364 | struct dc *dc, | |
365 | struct dc_stream_state *stream, | |
366 | bool dpms_off) | |
367 | { | |
8459f633 | 368 | struct pipe_ctx *pipe_ctx = NULL; |
d050f8ed HW |
369 | int i; |
370 | ||
371 | for (i = 0; i < MAX_PIPES; i++) { | |
372 | if (dc->current_state->res_ctx.pipe_ctx[i].stream == stream) { | |
373 | pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; | |
374 | break; | |
375 | } | |
376 | } | |
377 | ||
8459f633 EY |
378 | if (!pipe_ctx) { |
379 | ASSERT(0); | |
380 | return; | |
381 | } | |
382 | ||
d050f8ed HW |
383 | if (stream->dpms_off != dpms_off) { |
384 | stream->dpms_off = dpms_off; | |
385 | if (dpms_off) | |
386 | core_link_disable_stream(pipe_ctx, | |
387 | KEEP_ACQUIRED_RESOURCE); | |
388 | else | |
389 | core_link_enable_stream(dc->current_state, pipe_ctx); | |
390 | } | |
391 | } | |
392 | ||
15659045 | 393 | static void allocate_dc_stream_funcs(struct dc *dc) |
4562236b | 394 | { |
15659045 BL |
395 | if (dc->hwss.set_drr != NULL) { |
396 | dc->stream_funcs.adjust_vmin_vmax = | |
4562236b HW |
397 | stream_adjust_vmin_vmax; |
398 | } | |
399 | ||
15659045 | 400 | dc->stream_funcs.set_static_screen_events = |
94267b3d ST |
401 | set_static_screen_events; |
402 | ||
15659045 | 403 | dc->stream_funcs.get_crtc_position = |
72ada5f7 EC |
404 | stream_get_crtc_position; |
405 | ||
15659045 | 406 | dc->stream_funcs.set_gamut_remap = |
4562236b HW |
407 | set_gamut_remap; |
408 | ||
15659045 | 409 | dc->stream_funcs.program_csc_matrix = |
abe07e80 YHL |
410 | program_csc_matrix; |
411 | ||
15659045 | 412 | dc->stream_funcs.set_dither_option = |
529cad0f DW |
413 | set_dither_option; |
414 | ||
d050f8ed HW |
415 | dc->stream_funcs.set_dpms = |
416 | set_dpms; | |
417 | ||
15659045 | 418 | dc->link_funcs.set_drive_settings = |
4562236b HW |
419 | set_drive_settings; |
420 | ||
15659045 | 421 | dc->link_funcs.perform_link_training = |
4562236b HW |
422 | perform_link_training; |
423 | ||
15659045 | 424 | dc->link_funcs.set_preferred_link_settings = |
4562236b HW |
425 | set_preferred_link_settings; |
426 | ||
15659045 | 427 | dc->link_funcs.enable_hpd = |
4562236b HW |
428 | enable_hpd; |
429 | ||
15659045 | 430 | dc->link_funcs.disable_hpd = |
4562236b HW |
431 | disable_hpd; |
432 | ||
15659045 | 433 | dc->link_funcs.set_test_pattern = |
4562236b HW |
434 | set_test_pattern; |
435 | } | |
436 | ||
fb3466a4 | 437 | static void destruct(struct dc *dc) |
4562236b | 438 | { |
608ac7bb JZ |
439 | dc_release_state(dc->current_state); |
440 | dc->current_state = NULL; | |
4562236b | 441 | |
4562236b HW |
442 | destroy_links(dc); |
443 | ||
444 | dc_destroy_resource_pool(dc); | |
445 | ||
446 | if (dc->ctx->gpio_service) | |
447 | dal_gpio_service_destroy(&dc->ctx->gpio_service); | |
448 | ||
449 | if (dc->ctx->i2caux) | |
450 | dal_i2caux_destroy(&dc->ctx->i2caux); | |
451 | ||
452 | if (dc->ctx->created_bios) | |
453 | dal_bios_parser_destroy(&dc->ctx->dc_bios); | |
454 | ||
455 | if (dc->ctx->logger) | |
456 | dal_logger_destroy(&dc->ctx->logger); | |
457 | ||
2004f45e | 458 | kfree(dc->ctx); |
4562236b | 459 | dc->ctx = NULL; |
77a4ea53 | 460 | |
2004f45e | 461 | kfree(dc->bw_vbios); |
77a4ea53 BL |
462 | dc->bw_vbios = NULL; |
463 | ||
2004f45e | 464 | kfree(dc->bw_dceip); |
77a4ea53 BL |
465 | dc->bw_dceip = NULL; |
466 | ||
65111f25 | 467 | #ifdef CONFIG_DRM_AMD_DC_DCN1_0 |
2004f45e | 468 | kfree(dc->dcn_soc); |
65111f25 BL |
469 | dc->dcn_soc = NULL; |
470 | ||
2004f45e | 471 | kfree(dc->dcn_ip); |
65111f25 | 472 | dc->dcn_ip = NULL; |
fb3466a4 | 473 | |
65111f25 | 474 | #endif |
4562236b HW |
475 | } |
476 | ||
fb3466a4 | 477 | static bool construct(struct dc *dc, |
4562236b HW |
478 | const struct dc_init_data *init_params) |
479 | { | |
480 | struct dal_logger *logger; | |
2004f45e HW |
481 | struct dc_context *dc_ctx = kzalloc(sizeof(*dc_ctx), GFP_KERNEL); |
482 | struct bw_calcs_dceip *dc_dceip = kzalloc(sizeof(*dc_dceip), | |
483 | GFP_KERNEL); | |
484 | struct bw_calcs_vbios *dc_vbios = kzalloc(sizeof(*dc_vbios), | |
485 | GFP_KERNEL); | |
65111f25 | 486 | #ifdef CONFIG_DRM_AMD_DC_DCN1_0 |
2004f45e HW |
487 | struct dcn_soc_bounding_box *dcn_soc = kzalloc(sizeof(*dcn_soc), |
488 | GFP_KERNEL); | |
489 | struct dcn_ip_params *dcn_ip = kzalloc(sizeof(*dcn_ip), GFP_KERNEL); | |
65111f25 | 490 | #endif |
77a4ea53 | 491 | |
4562236b HW |
492 | enum dce_version dc_version = DCE_VERSION_UNKNOWN; |
493 | ||
77a4ea53 BL |
494 | if (!dc_dceip) { |
495 | dm_error("%s: failed to create dceip\n", __func__); | |
65111f25 | 496 | goto fail; |
77a4ea53 BL |
497 | } |
498 | ||
499 | dc->bw_dceip = dc_dceip; | |
500 | ||
501 | if (!dc_vbios) { | |
502 | dm_error("%s: failed to create vbios\n", __func__); | |
65111f25 | 503 | goto fail; |
77a4ea53 BL |
504 | } |
505 | ||
506 | dc->bw_vbios = dc_vbios; | |
65111f25 BL |
507 | #ifdef CONFIG_DRM_AMD_DC_DCN1_0 |
508 | if (!dcn_soc) { | |
509 | dm_error("%s: failed to create dcn_soc\n", __func__); | |
510 | goto fail; | |
511 | } | |
512 | ||
513 | dc->dcn_soc = dcn_soc; | |
514 | ||
515 | if (!dcn_ip) { | |
516 | dm_error("%s: failed to create dcn_ip\n", __func__); | |
517 | goto fail; | |
518 | } | |
519 | ||
520 | dc->dcn_ip = dcn_ip; | |
521 | #endif | |
77a4ea53 | 522 | |
4562236b HW |
523 | if (!dc_ctx) { |
524 | dm_error("%s: failed to create ctx\n", __func__); | |
65111f25 | 525 | goto fail; |
4562236b HW |
526 | } |
527 | ||
608ac7bb | 528 | dc->current_state = dc_create_state(); |
4562236b | 529 | |
608ac7bb | 530 | if (!dc->current_state) { |
4562236b | 531 | dm_error("%s: failed to create validate ctx\n", __func__); |
65111f25 | 532 | goto fail; |
4562236b HW |
533 | } |
534 | ||
535 | dc_ctx->cgs_device = init_params->cgs_device; | |
536 | dc_ctx->driver_context = init_params->driver; | |
fb3466a4 | 537 | dc_ctx->dc = dc; |
4562236b HW |
538 | dc_ctx->asic_id = init_params->asic_id; |
539 | ||
540 | /* Create logger */ | |
01a526f3 | 541 | logger = dal_logger_create(dc_ctx, init_params->log_mask); |
4562236b HW |
542 | |
543 | if (!logger) { | |
544 | /* can *not* call logger. call base driver 'print error' */ | |
545 | dm_error("%s: failed to create Logger!\n", __func__); | |
65111f25 | 546 | goto fail; |
4562236b HW |
547 | } |
548 | dc_ctx->logger = logger; | |
549 | dc->ctx = dc_ctx; | |
550 | dc->ctx->dce_environment = init_params->dce_environment; | |
551 | ||
552 | dc_version = resource_parse_asic_id(init_params->asic_id); | |
553 | dc->ctx->dce_version = dc_version; | |
3eab7916 | 554 | #if defined(CONFIG_DRM_AMD_DC_FBC) |
690b5e39 RL |
555 | dc->ctx->fbc_gpu_addr = init_params->fbc_gpu_addr; |
556 | #endif | |
4562236b HW |
557 | /* Resource should construct all asic specific resources. |
558 | * This should be the only place where we need to parse the asic id | |
559 | */ | |
560 | if (init_params->vbios_override) | |
561 | dc_ctx->dc_bios = init_params->vbios_override; | |
562 | else { | |
563 | /* Create BIOS parser */ | |
564 | struct bp_init_data bp_init_data; | |
e8c963d6 | 565 | |
4562236b HW |
566 | bp_init_data.ctx = dc_ctx; |
567 | bp_init_data.bios = init_params->asic_id.atombios_base_address; | |
568 | ||
569 | dc_ctx->dc_bios = dal_bios_parser_create( | |
570 | &bp_init_data, dc_version); | |
571 | ||
572 | if (!dc_ctx->dc_bios) { | |
573 | ASSERT_CRITICAL(false); | |
65111f25 | 574 | goto fail; |
4562236b HW |
575 | } |
576 | ||
577 | dc_ctx->created_bios = true; | |
e8c963d6 | 578 | } |
4562236b HW |
579 | |
580 | /* Create I2C AUX */ | |
581 | dc_ctx->i2caux = dal_i2caux_create(dc_ctx); | |
582 | ||
583 | if (!dc_ctx->i2caux) { | |
584 | ASSERT_CRITICAL(false); | |
65111f25 | 585 | goto fail; |
4562236b HW |
586 | } |
587 | ||
588 | /* Create GPIO service */ | |
589 | dc_ctx->gpio_service = dal_gpio_service_create( | |
590 | dc_version, | |
591 | dc_ctx->dce_environment, | |
592 | dc_ctx); | |
593 | ||
594 | if (!dc_ctx->gpio_service) { | |
595 | ASSERT_CRITICAL(false); | |
65111f25 | 596 | goto fail; |
4562236b HW |
597 | } |
598 | ||
599 | dc->res_pool = dc_create_resource_pool( | |
600 | dc, | |
601 | init_params->num_virtual_links, | |
602 | dc_version, | |
603 | init_params->asic_id); | |
604 | if (!dc->res_pool) | |
65111f25 | 605 | goto fail; |
4562236b | 606 | |
ab8db3e1 AG |
607 | dc_resource_state_construct(dc, dc->current_state); |
608 | ||
4562236b | 609 | if (!create_links(dc, init_params->num_virtual_links)) |
65111f25 | 610 | goto fail; |
4562236b HW |
611 | |
612 | allocate_dc_stream_funcs(dc); | |
613 | ||
614 | return true; | |
615 | ||
65111f25 BL |
616 | fail: |
617 | ||
4562236b HW |
618 | destruct(dc); |
619 | return false; | |
620 | } | |
621 | ||
4562236b HW |
622 | /******************************************************************************* |
623 | * Public functions | |
624 | ******************************************************************************/ | |
625 | ||
626 | struct dc *dc_create(const struct dc_init_data *init_params) | |
627 | { | |
2004f45e | 628 | struct dc *dc = kzalloc(sizeof(*dc), GFP_KERNEL); |
4562236b HW |
629 | unsigned int full_pipe_count; |
630 | ||
15659045 | 631 | if (NULL == dc) |
4562236b HW |
632 | goto alloc_fail; |
633 | ||
15659045 | 634 | if (false == construct(dc, init_params)) |
4562236b HW |
635 | goto construct_fail; |
636 | ||
637 | /*TODO: separate HW and SW initialization*/ | |
15659045 | 638 | dc->hwss.init_hw(dc); |
4562236b | 639 | |
15659045 BL |
640 | full_pipe_count = dc->res_pool->pipe_count; |
641 | if (dc->res_pool->underlay_pipe_index != NO_UNDERLAY_PIPE) | |
4562236b | 642 | full_pipe_count--; |
15659045 | 643 | dc->caps.max_streams = min( |
4562236b | 644 | full_pipe_count, |
15659045 | 645 | dc->res_pool->stream_enc_count); |
4562236b | 646 | |
15659045 BL |
647 | dc->caps.max_links = dc->link_count; |
648 | dc->caps.max_audios = dc->res_pool->audio_count; | |
4562236b | 649 | |
15659045 | 650 | dc->config = init_params->flags; |
4562236b | 651 | |
15659045 | 652 | dm_logger_write(dc->ctx->logger, LOG_DC, |
4562236b HW |
653 | "Display Core initialized\n"); |
654 | ||
655 | ||
656 | /* TODO: missing feature to be enabled */ | |
15659045 | 657 | dc->debug.disable_dfs_bypass = true; |
4562236b | 658 | |
15659045 | 659 | return dc; |
4562236b HW |
660 | |
661 | construct_fail: | |
2004f45e | 662 | kfree(dc); |
4562236b HW |
663 | |
664 | alloc_fail: | |
665 | return NULL; | |
666 | } | |
667 | ||
668 | void dc_destroy(struct dc **dc) | |
669 | { | |
15659045 | 670 | destruct(*dc); |
2004f45e | 671 | kfree(*dc); |
4562236b HW |
672 | *dc = NULL; |
673 | } | |
674 | ||
4562236b | 675 | static void program_timing_sync( |
15659045 | 676 | struct dc *dc, |
608ac7bb | 677 | struct dc_state *ctx) |
4562236b HW |
678 | { |
679 | int i, j; | |
680 | int group_index = 0; | |
15659045 | 681 | int pipe_count = dc->res_pool->pipe_count; |
4562236b HW |
682 | struct pipe_ctx *unsynced_pipes[MAX_PIPES] = { NULL }; |
683 | ||
684 | for (i = 0; i < pipe_count; i++) { | |
685 | if (!ctx->res_ctx.pipe_ctx[i].stream || ctx->res_ctx.pipe_ctx[i].top_pipe) | |
686 | continue; | |
687 | ||
688 | unsynced_pipes[i] = &ctx->res_ctx.pipe_ctx[i]; | |
689 | } | |
690 | ||
691 | for (i = 0; i < pipe_count; i++) { | |
692 | int group_size = 1; | |
693 | struct pipe_ctx *pipe_set[MAX_PIPES]; | |
694 | ||
695 | if (!unsynced_pipes[i]) | |
696 | continue; | |
697 | ||
698 | pipe_set[0] = unsynced_pipes[i]; | |
699 | unsynced_pipes[i] = NULL; | |
700 | ||
701 | /* Add tg to the set, search rest of the tg's for ones with | |
702 | * same timing, add all tgs with same timing to the group | |
703 | */ | |
704 | for (j = i + 1; j < pipe_count; j++) { | |
705 | if (!unsynced_pipes[j]) | |
706 | continue; | |
707 | ||
708 | if (resource_are_streams_timing_synchronizable( | |
709 | unsynced_pipes[j]->stream, | |
710 | pipe_set[0]->stream)) { | |
711 | pipe_set[group_size] = unsynced_pipes[j]; | |
712 | unsynced_pipes[j] = NULL; | |
713 | group_size++; | |
714 | } | |
715 | } | |
716 | ||
717 | /* set first unblanked pipe as master */ | |
718 | for (j = 0; j < group_size; j++) { | |
719 | struct pipe_ctx *temp; | |
720 | ||
6b670fa9 | 721 | if (!pipe_set[j]->stream_res.tg->funcs->is_blanked(pipe_set[j]->stream_res.tg)) { |
4562236b HW |
722 | if (j == 0) |
723 | break; | |
724 | ||
725 | temp = pipe_set[0]; | |
726 | pipe_set[0] = pipe_set[j]; | |
727 | pipe_set[j] = temp; | |
728 | break; | |
729 | } | |
730 | } | |
731 | ||
732 | /* remove any other unblanked pipes as they have already been synced */ | |
733 | for (j = j + 1; j < group_size; j++) { | |
6b670fa9 | 734 | if (!pipe_set[j]->stream_res.tg->funcs->is_blanked(pipe_set[j]->stream_res.tg)) { |
4562236b HW |
735 | group_size--; |
736 | pipe_set[j] = pipe_set[group_size]; | |
737 | j--; | |
738 | } | |
739 | } | |
740 | ||
741 | if (group_size > 1) { | |
15659045 BL |
742 | dc->hwss.enable_timing_synchronization( |
743 | dc, group_index, group_size, pipe_set); | |
4562236b HW |
744 | group_index++; |
745 | } | |
746 | } | |
747 | } | |
748 | ||
e2c7bb12 | 749 | static bool context_changed( |
fb3466a4 | 750 | struct dc *dc, |
608ac7bb | 751 | struct dc_state *context) |
7cf2c840 HW |
752 | { |
753 | uint8_t i; | |
754 | ||
608ac7bb | 755 | if (context->stream_count != dc->current_state->stream_count) |
7cf2c840 HW |
756 | return true; |
757 | ||
608ac7bb JZ |
758 | for (i = 0; i < dc->current_state->stream_count; i++) { |
759 | if (dc->current_state->streams[i] != context->streams[i]) | |
7cf2c840 HW |
760 | return true; |
761 | } | |
762 | ||
763 | return false; | |
764 | } | |
765 | ||
7f5c22d1 VP |
766 | bool dc_enable_stereo( |
767 | struct dc *dc, | |
608ac7bb | 768 | struct dc_state *context, |
0971c40e | 769 | struct dc_stream_state *streams[], |
7f5c22d1 VP |
770 | uint8_t stream_count) |
771 | { | |
772 | bool ret = true; | |
773 | int i, j; | |
774 | struct pipe_ctx *pipe; | |
1663ae1c | 775 | |
7f5c22d1 VP |
776 | for (i = 0; i < MAX_PIPES; i++) { |
777 | if (context != NULL) | |
778 | pipe = &context->res_ctx.pipe_ctx[i]; | |
779 | else | |
608ac7bb | 780 | pipe = &dc->current_state->res_ctx.pipe_ctx[i]; |
7f5c22d1 | 781 | for (j = 0 ; pipe && j < stream_count; j++) { |
4fa086b9 | 782 | if (streams[j] && streams[j] == pipe->stream && |
15659045 BL |
783 | dc->hwss.setup_stereo) |
784 | dc->hwss.setup_stereo(pipe, dc); | |
7f5c22d1 VP |
785 | } |
786 | } | |
1663ae1c | 787 | |
7f5c22d1 VP |
788 | return ret; |
789 | } | |
790 | ||
fa6ecfc6 AG |
791 | |
792 | /* | |
793 | * Applies given context to HW and copy it into current context. | |
794 | * It's up to the user to release the src context afterwards. | |
795 | */ | |
608ac7bb | 796 | static bool dc_commit_state_no_check(struct dc *dc, struct dc_state *context) |
7cf2c840 | 797 | { |
15659045 | 798 | struct dc_bios *dcb = dc->ctx->dc_bios; |
7cf2c840 | 799 | enum dc_status result = DC_ERROR_UNEXPECTED; |
7cf2c840 HW |
800 | struct pipe_ctx *pipe; |
801 | int i, j, k, l; | |
0971c40e | 802 | struct dc_stream_state *dc_streams[MAX_STREAMS] = {0}; |
7cf2c840 | 803 | |
fa6ecfc6 | 804 | for (i = 0; i < context->stream_count; i++) |
4fa086b9 | 805 | dc_streams[i] = context->streams[i]; |
7cf2c840 HW |
806 | |
807 | if (!dcb->funcs->is_accelerated_mode(dcb)) | |
15659045 | 808 | dc->hwss.enable_accelerated_mode(dc); |
7cf2c840 | 809 | |
7cf2c840 | 810 | for (i = 0; i < context->stream_count; i++) { |
b73a22d3 | 811 | const struct dc_sink *sink = context->streams[i]->sink; |
7cf2c840 | 812 | |
13ab1b44 YS |
813 | dc->hwss.apply_ctx_for_surface( |
814 | dc, context->streams[i], | |
815 | context->stream_status[i].plane_count, | |
816 | context); | |
817 | ||
818 | /* | |
819 | * enable stereo | |
820 | * TODO rework dc_enable_stereo call to work with validation sets? | |
821 | */ | |
822 | for (k = 0; k < MAX_PIPES; k++) { | |
823 | pipe = &context->res_ctx.pipe_ctx[k]; | |
824 | ||
825 | for (l = 0 ; pipe && l < context->stream_count; l++) { | |
826 | if (context->streams[l] && | |
827 | context->streams[l] == pipe->stream && | |
828 | dc->hwss.setup_stereo) | |
829 | dc->hwss.setup_stereo(pipe, dc); | |
7cf2c840 HW |
830 | } |
831 | } | |
832 | ||
50d4cfdc | 833 | |
50d4cfdc | 834 | |
7cf2c840 | 835 | CONN_MSG_MODE(sink->link, "{%dx%d, %dx%d@%dKhz}", |
4fa086b9 LSL |
836 | context->streams[i]->timing.h_addressable, |
837 | context->streams[i]->timing.v_addressable, | |
838 | context->streams[i]->timing.h_total, | |
839 | context->streams[i]->timing.v_total, | |
840 | context->streams[i]->timing.pix_clk_khz); | |
7cf2c840 HW |
841 | } |
842 | ||
50d4cfdc YS |
843 | dc->hwss.ready_shared_resources(dc, context); |
844 | ||
845 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
846 | pipe = &context->res_ctx.pipe_ctx[i]; | |
847 | dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe); | |
848 | } | |
849 | result = dc->hwss.apply_ctx_to_hw(dc, context); | |
850 | ||
851 | program_timing_sync(dc, context); | |
852 | ||
fa6ecfc6 AG |
853 | dc_enable_stereo(dc, context, dc_streams, context->stream_count); |
854 | ||
13ab1b44 YS |
855 | for (i = 0; i < context->stream_count; i++) { |
856 | for (j = 0; j < MAX_PIPES; j++) { | |
857 | pipe = &context->res_ctx.pipe_ctx[j]; | |
858 | ||
859 | if (!pipe->top_pipe && pipe->stream == context->streams[i]) | |
860 | dc->hwss.pipe_control_lock(dc, pipe, false); | |
861 | } | |
862 | } | |
863 | ||
608ac7bb | 864 | dc_release_state(dc->current_state); |
8a76708e | 865 | |
608ac7bb | 866 | dc->current_state = context; |
7cf2c840 | 867 | |
608ac7bb | 868 | dc_retain_state(dc->current_state); |
60bf1860 | 869 | |
41f97c07 HW |
870 | dc->hwss.optimize_shared_resources(dc); |
871 | ||
7cf2c840 HW |
872 | return (result == DC_OK); |
873 | } | |
874 | ||
608ac7bb | 875 | bool dc_commit_state(struct dc *dc, struct dc_state *context) |
fa6ecfc6 AG |
876 | { |
877 | enum dc_status result = DC_ERROR_UNEXPECTED; | |
fa6ecfc6 AG |
878 | int i; |
879 | ||
15659045 | 880 | if (false == context_changed(dc, context)) |
fa6ecfc6 AG |
881 | return DC_OK; |
882 | ||
15659045 | 883 | dm_logger_write(dc->ctx->logger, LOG_DC, "%s: %d streams\n", |
fa6ecfc6 AG |
884 | __func__, context->stream_count); |
885 | ||
886 | for (i = 0; i < context->stream_count; i++) { | |
0971c40e | 887 | struct dc_stream_state *stream = context->streams[i]; |
fa6ecfc6 AG |
888 | |
889 | dc_stream_log(stream, | |
15659045 | 890 | dc->ctx->logger, |
fa6ecfc6 AG |
891 | LOG_DC); |
892 | } | |
893 | ||
608ac7bb | 894 | result = dc_commit_state_no_check(dc, context); |
fa6ecfc6 AG |
895 | |
896 | return (result == DC_OK); | |
897 | } | |
898 | ||
899 | ||
ab2541b6 | 900 | bool dc_post_update_surfaces_to_stream(struct dc *dc) |
4562236b | 901 | { |
4562236b | 902 | int i; |
608ac7bb | 903 | struct dc_state *context = dc->current_state; |
4562236b HW |
904 | |
905 | post_surface_trace(dc); | |
de37e273 | 906 | |
15659045 | 907 | for (i = 0; i < dc->res_pool->pipe_count; i++) |
7950f0f9 | 908 | if (context->res_ctx.pipe_ctx[i].stream == NULL |
3be5262e | 909 | || context->res_ctx.pipe_ctx[i].plane_state == NULL) |
15659045 | 910 | dc->hwss.power_down_front_end(dc, i); |
4562236b | 911 | |
7950f0f9 DL |
912 | /* 3rd param should be true, temp w/a for RV*/ |
913 | #if defined(CONFIG_DRM_AMD_DC_DCN1_0) | |
15659045 | 914 | dc->hwss.set_bandwidth(dc, context, dc->ctx->dce_version < DCN_VERSION_1_0); |
7950f0f9 | 915 | #else |
15659045 | 916 | dc->hwss.set_bandwidth(dc, context, true); |
7950f0f9 | 917 | #endif |
de37e273 | 918 | return true; |
4562236b HW |
919 | } |
920 | ||
3be5262e | 921 | bool dc_commit_planes_to_stream( |
4562236b | 922 | struct dc *dc, |
3be5262e HW |
923 | struct dc_plane_state **plane_states, |
924 | uint8_t new_plane_count, | |
bc6828e0 BL |
925 | struct dc_stream_state *dc_stream, |
926 | struct dc_state *state) | |
4562236b | 927 | { |
ab2541b6 AC |
928 | struct dc_surface_update updates[MAX_SURFACES]; |
929 | struct dc_flip_addrs flip_addr[MAX_SURFACES]; | |
930 | struct dc_plane_info plane_info[MAX_SURFACES]; | |
931 | struct dc_scaling_info scaling_info[MAX_SURFACES]; | |
4562236b | 932 | int i; |
c1473558 | 933 | struct dc_stream_update *stream_update = |
2004f45e | 934 | kzalloc(sizeof(struct dc_stream_update), GFP_KERNEL); |
c1473558 AG |
935 | |
936 | if (!stream_update) { | |
937 | BREAK_TO_DEBUGGER(); | |
938 | return false; | |
939 | } | |
4562236b | 940 | |
ab2541b6 AC |
941 | memset(updates, 0, sizeof(updates)); |
942 | memset(flip_addr, 0, sizeof(flip_addr)); | |
943 | memset(plane_info, 0, sizeof(plane_info)); | |
944 | memset(scaling_info, 0, sizeof(scaling_info)); | |
945 | ||
c1473558 AG |
946 | stream_update->src = dc_stream->src; |
947 | stream_update->dst = dc_stream->dst; | |
90114434 | 948 | stream_update->out_transfer_func = dc_stream->out_transfer_func; |
c1473558 | 949 | |
3be5262e HW |
950 | for (i = 0; i < new_plane_count; i++) { |
951 | updates[i].surface = plane_states[i]; | |
89e89630 | 952 | updates[i].gamma = |
3be5262e HW |
953 | (struct dc_gamma *)plane_states[i]->gamma_correction; |
954 | updates[i].in_transfer_func = plane_states[i]->in_transfer_func; | |
955 | flip_addr[i].address = plane_states[i]->address; | |
956 | flip_addr[i].flip_immediate = plane_states[i]->flip_immediate; | |
957 | plane_info[i].color_space = plane_states[i]->color_space; | |
958 | plane_info[i].format = plane_states[i]->format; | |
959 | plane_info[i].plane_size = plane_states[i]->plane_size; | |
960 | plane_info[i].rotation = plane_states[i]->rotation; | |
961 | plane_info[i].horizontal_mirror = plane_states[i]->horizontal_mirror; | |
962 | plane_info[i].stereo_format = plane_states[i]->stereo_format; | |
963 | plane_info[i].tiling_info = plane_states[i]->tiling_info; | |
964 | plane_info[i].visible = plane_states[i]->visible; | |
965 | plane_info[i].per_pixel_alpha = plane_states[i]->per_pixel_alpha; | |
966 | plane_info[i].dcc = plane_states[i]->dcc; | |
967 | scaling_info[i].scaling_quality = plane_states[i]->scaling_quality; | |
968 | scaling_info[i].src_rect = plane_states[i]->src_rect; | |
969 | scaling_info[i].dst_rect = plane_states[i]->dst_rect; | |
970 | scaling_info[i].clip_rect = plane_states[i]->clip_rect; | |
4562236b HW |
971 | |
972 | updates[i].flip_addr = &flip_addr[i]; | |
973 | updates[i].plane_info = &plane_info[i]; | |
974 | updates[i].scaling_info = &scaling_info[i]; | |
975 | } | |
4562236b | 976 | |
bc6828e0 | 977 | dc_commit_updates_for_stream( |
c1473558 AG |
978 | dc, |
979 | updates, | |
3be5262e | 980 | new_plane_count, |
bc6828e0 | 981 | dc_stream, stream_update, plane_states, state); |
c1473558 | 982 | |
2004f45e | 983 | kfree(stream_update); |
cfe4645e | 984 | return true; |
4562236b HW |
985 | } |
986 | ||
608ac7bb | 987 | struct dc_state *dc_create_state(void) |
81c90ec0 | 988 | { |
2004f45e HW |
989 | struct dc_state *context = kzalloc(sizeof(struct dc_state), |
990 | GFP_KERNEL); | |
81c90ec0 LSL |
991 | |
992 | if (!context) | |
993 | return NULL; | |
994 | ||
8ee5702a | 995 | kref_init(&context->refcount); |
81c90ec0 LSL |
996 | return context; |
997 | } | |
998 | ||
608ac7bb | 999 | void dc_retain_state(struct dc_state *context) |
8a76708e | 1000 | { |
8ee5702a | 1001 | kref_get(&context->refcount); |
8a76708e AG |
1002 | } |
1003 | ||
8ee5702a | 1004 | static void dc_state_free(struct kref *kref) |
8a76708e | 1005 | { |
8ee5702a DA |
1006 | struct dc_state *context = container_of(kref, struct dc_state, refcount); |
1007 | dc_resource_state_destruct(context); | |
1008 | kfree(context); | |
1009 | } | |
8a76708e | 1010 | |
8ee5702a DA |
1011 | void dc_release_state(struct dc_state *context) |
1012 | { | |
1013 | kref_put(&context->refcount, dc_state_free); | |
8a76708e AG |
1014 | } |
1015 | ||
e72f0acd | 1016 | static bool is_surface_in_context( |
608ac7bb | 1017 | const struct dc_state *context, |
3be5262e | 1018 | const struct dc_plane_state *plane_state) |
4562236b | 1019 | { |
e72f0acd | 1020 | int j; |
4562236b | 1021 | |
a2b8659d | 1022 | for (j = 0; j < MAX_PIPES; j++) { |
e72f0acd | 1023 | const struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; |
4562236b | 1024 | |
3be5262e | 1025 | if (plane_state == pipe_ctx->plane_state) { |
e72f0acd TC |
1026 | return true; |
1027 | } | |
1028 | } | |
4562236b | 1029 | |
e72f0acd TC |
1030 | return false; |
1031 | } | |
4562236b | 1032 | |
5869b0f6 LE |
1033 | static unsigned int pixel_format_to_bpp(enum surface_pixel_format format) |
1034 | { | |
1035 | switch (format) { | |
0e12c3f6 DL |
1036 | case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: |
1037 | case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: | |
1038 | return 12; | |
5869b0f6 LE |
1039 | case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: |
1040 | case SURFACE_PIXEL_FORMAT_GRPH_RGB565: | |
0e12c3f6 DL |
1041 | case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: |
1042 | case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: | |
5869b0f6 LE |
1043 | return 16; |
1044 | case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: | |
1045 | case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: | |
1046 | case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: | |
1047 | case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: | |
1048 | return 32; | |
1049 | case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: | |
1050 | case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: | |
1051 | case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: | |
1052 | return 64; | |
1053 | default: | |
1054 | ASSERT_CRITICAL(false); | |
1055 | return -1; | |
1056 | } | |
1057 | } | |
1058 | ||
1059 | static enum surface_update_type get_plane_info_update_type( | |
fb9611d2 YS |
1060 | const struct dc_surface_update *u, |
1061 | int surface_index) | |
5869b0f6 | 1062 | { |
80b4c5a8 | 1063 | struct dc_plane_info temp_plane_info; |
ebf055f9 | 1064 | memset(&temp_plane_info, 0, sizeof(temp_plane_info)); |
5869b0f6 LE |
1065 | |
1066 | if (!u->plane_info) | |
1067 | return UPDATE_TYPE_FAST; | |
1068 | ||
80b4c5a8 DL |
1069 | temp_plane_info = *u->plane_info; |
1070 | ||
5869b0f6 LE |
1071 | /* Copy all parameters that will cause a full update |
1072 | * from current surface, the rest of the parameters | |
1073 | * from provided plane configuration. | |
1074 | * Perform memory compare and special validation | |
1075 | * for those that can cause fast/medium updates | |
1076 | */ | |
1077 | ||
1078 | /* Full update parameters */ | |
1079 | temp_plane_info.color_space = u->surface->color_space; | |
1080 | temp_plane_info.dcc = u->surface->dcc; | |
1081 | temp_plane_info.horizontal_mirror = u->surface->horizontal_mirror; | |
1082 | temp_plane_info.plane_size = u->surface->plane_size; | |
1083 | temp_plane_info.rotation = u->surface->rotation; | |
1084 | temp_plane_info.stereo_format = u->surface->stereo_format; | |
5869b0f6 | 1085 | |
fb9611d2 YS |
1086 | if (surface_index == 0) |
1087 | temp_plane_info.visible = u->plane_info->visible; | |
1088 | else | |
1089 | temp_plane_info.visible = u->surface->visible; | |
5869b0f6 LE |
1090 | |
1091 | if (memcmp(u->plane_info, &temp_plane_info, | |
1092 | sizeof(struct dc_plane_info)) != 0) | |
1093 | return UPDATE_TYPE_FULL; | |
1094 | ||
1095 | if (pixel_format_to_bpp(u->plane_info->format) != | |
1096 | pixel_format_to_bpp(u->surface->format)) { | |
4b7e7e2b SC |
1097 | /* different bytes per element will require full bandwidth |
1098 | * and DML calculation | |
1099 | */ | |
5869b0f6 | 1100 | return UPDATE_TYPE_FULL; |
5869b0f6 | 1101 | } |
4b7e7e2b SC |
1102 | |
1103 | if (memcmp(&u->plane_info->tiling_info, &u->surface->tiling_info, | |
1104 | sizeof(union dc_tiling_info)) != 0) { | |
1105 | /* todo: below are HW dependent, we should add a hook to | |
1106 | * DCE/N resource and validated there. | |
1107 | */ | |
1108 | if (u->plane_info->tiling_info.gfx9.swizzle != DC_SW_LINEAR) { | |
1109 | /* swizzled mode requires RQ to be setup properly, | |
1110 | * thus need to run DML to calculate RQ settings | |
1111 | */ | |
1112 | return UPDATE_TYPE_FULL; | |
1113 | } | |
1114 | } | |
1115 | ||
1116 | return UPDATE_TYPE_MED; | |
5869b0f6 LE |
1117 | } |
1118 | ||
1119 | static enum surface_update_type get_scaling_info_update_type( | |
1120 | const struct dc_surface_update *u) | |
1121 | { | |
5869b0f6 LE |
1122 | if (!u->scaling_info) |
1123 | return UPDATE_TYPE_FAST; | |
1124 | ||
b71a0618 DL |
1125 | if (u->scaling_info->src_rect.width != u->surface->src_rect.width |
1126 | || u->scaling_info->src_rect.height != u->surface->src_rect.height | |
1127 | || u->scaling_info->clip_rect.width != u->surface->clip_rect.width | |
1128 | || u->scaling_info->clip_rect.height != u->surface->clip_rect.height | |
1129 | || u->scaling_info->dst_rect.width != u->surface->dst_rect.width | |
1130 | || u->scaling_info->dst_rect.height != u->surface->dst_rect.height) | |
5869b0f6 LE |
1131 | return UPDATE_TYPE_FULL; |
1132 | ||
b71a0618 DL |
1133 | if (u->scaling_info->src_rect.x != u->surface->src_rect.x |
1134 | || u->scaling_info->src_rect.y != u->surface->src_rect.y | |
1135 | || u->scaling_info->clip_rect.x != u->surface->clip_rect.x | |
1136 | || u->scaling_info->clip_rect.y != u->surface->clip_rect.y | |
1137 | || u->scaling_info->dst_rect.x != u->surface->dst_rect.x | |
1138 | || u->scaling_info->dst_rect.y != u->surface->dst_rect.y) | |
1139 | return UPDATE_TYPE_MED; | |
5869b0f6 LE |
1140 | |
1141 | return UPDATE_TYPE_FAST; | |
1142 | } | |
4562236b | 1143 | |
e72f0acd | 1144 | static enum surface_update_type det_surface_update( |
fb3466a4 | 1145 | const struct dc *dc, |
fb9611d2 YS |
1146 | const struct dc_surface_update *u, |
1147 | int surface_index) | |
e72f0acd | 1148 | { |
608ac7bb | 1149 | const struct dc_state *context = dc->current_state; |
5869b0f6 LE |
1150 | enum surface_update_type type = UPDATE_TYPE_FAST; |
1151 | enum surface_update_type overall_type = UPDATE_TYPE_FAST; | |
4562236b | 1152 | |
e72f0acd TC |
1153 | if (!is_surface_in_context(context, u->surface)) |
1154 | return UPDATE_TYPE_FULL; | |
4562236b | 1155 | |
fb9611d2 | 1156 | type = get_plane_info_update_type(u, surface_index); |
5869b0f6 LE |
1157 | if (overall_type < type) |
1158 | overall_type = type; | |
1159 | ||
1160 | type = get_scaling_info_update_type(u); | |
1161 | if (overall_type < type) | |
1162 | overall_type = type; | |
1163 | ||
e72f0acd | 1164 | if (u->in_transfer_func || |
5869b0f6 LE |
1165 | u->hdr_static_metadata) { |
1166 | if (overall_type < UPDATE_TYPE_MED) | |
1167 | overall_type = UPDATE_TYPE_MED; | |
1168 | } | |
1c4e6bce | 1169 | |
5869b0f6 | 1170 | return overall_type; |
e72f0acd | 1171 | } |
4562236b | 1172 | |
5869b0f6 LE |
1173 | enum surface_update_type dc_check_update_surfaces_for_stream( |
1174 | struct dc *dc, | |
e72f0acd TC |
1175 | struct dc_surface_update *updates, |
1176 | int surface_count, | |
ee8f63e1 | 1177 | struct dc_stream_update *stream_update, |
e72f0acd TC |
1178 | const struct dc_stream_status *stream_status) |
1179 | { | |
1180 | int i; | |
1181 | enum surface_update_type overall_type = UPDATE_TYPE_FAST; | |
1182 | ||
3be5262e | 1183 | if (stream_status == NULL || stream_status->plane_count != surface_count) |
e72f0acd TC |
1184 | return UPDATE_TYPE_FULL; |
1185 | ||
ee8f63e1 LE |
1186 | if (stream_update) |
1187 | return UPDATE_TYPE_FULL; | |
1188 | ||
e72f0acd TC |
1189 | for (i = 0 ; i < surface_count; i++) { |
1190 | enum surface_update_type type = | |
15659045 | 1191 | det_surface_update(dc, &updates[i], i); |
e72f0acd TC |
1192 | |
1193 | if (type == UPDATE_TYPE_FULL) | |
1194 | return type; | |
1c4e6bce | 1195 | |
e72f0acd TC |
1196 | if (overall_type < type) |
1197 | overall_type = type; | |
4562236b HW |
1198 | } |
1199 | ||
e72f0acd TC |
1200 | return overall_type; |
1201 | } | |
4562236b | 1202 | |
3e9ad616 | 1203 | static struct dc_stream_status *stream_get_status( |
608ac7bb | 1204 | struct dc_state *ctx, |
3e9ad616 EY |
1205 | struct dc_stream_state *stream) |
1206 | { | |
1207 | uint8_t i; | |
1208 | ||
1209 | for (i = 0; i < ctx->stream_count; i++) { | |
1210 | if (stream == ctx->streams[i]) { | |
1211 | return &ctx->stream_status[i]; | |
1212 | } | |
1213 | } | |
1214 | ||
1215 | return NULL; | |
1216 | } | |
1217 | ||
b8a1d69c | 1218 | static const enum surface_update_type update_surface_trace_level = UPDATE_TYPE_FULL; |
4562236b | 1219 | |
bc6828e0 BL |
1220 | |
1221 | static void commit_planes_for_stream(struct dc *dc, | |
1222 | struct dc_surface_update *srf_updates, | |
1223 | int surface_count, | |
0971c40e | 1224 | struct dc_stream_state *stream, |
bc6828e0 BL |
1225 | struct dc_stream_update *stream_update, |
1226 | enum surface_update_type update_type, | |
1227 | struct dc_state *context) | |
e72f0acd | 1228 | { |
e72f0acd | 1229 | int i, j; |
4562236b | 1230 | |
745cc746 | 1231 | if (update_type == UPDATE_TYPE_FULL) { |
bc6828e0 BL |
1232 | dc->hwss.set_bandwidth(dc, context, false); |
1233 | context_clock_trace(dc, context); | |
45209ef7 | 1234 | } |
e72f0acd | 1235 | |
87480687 | 1236 | if (update_type > UPDATE_TYPE_FAST) { |
15659045 | 1237 | for (j = 0; j < dc->res_pool->pipe_count; j++) { |
6be425f3 | 1238 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; |
87480687 | 1239 | |
15659045 | 1240 | dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe_ctx); |
87480687 EY |
1241 | } |
1242 | } | |
1243 | ||
671a6246 YS |
1244 | if (surface_count == 0) { |
1245 | /* | |
1246 | * In case of turning off screen, no need to program front end a second time. | |
1247 | * just return after program front end. | |
1248 | */ | |
15659045 | 1249 | dc->hwss.apply_ctx_for_surface(dc, stream, surface_count, context); |
671a6246 YS |
1250 | return; |
1251 | } | |
e72f0acd | 1252 | |
f19d5f35 | 1253 | /* Lock pipes for provided surfaces, or all active if full update*/ |
4562236b | 1254 | for (i = 0; i < surface_count; i++) { |
3be5262e | 1255 | struct dc_plane_state *plane_state = srf_updates[i].surface; |
4562236b | 1256 | |
15659045 | 1257 | for (j = 0; j < dc->res_pool->pipe_count; j++) { |
f0828115 | 1258 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; |
92a65e32 | 1259 | |
3be5262e | 1260 | if (update_type != UPDATE_TYPE_FULL && pipe_ctx->plane_state != plane_state) |
f19d5f35 | 1261 | continue; |
3be5262e | 1262 | if (!pipe_ctx->plane_state || pipe_ctx->top_pipe) |
f0828115 | 1263 | continue; |
f19d5f35 | 1264 | |
15659045 BL |
1265 | dc->hwss.pipe_control_lock( |
1266 | dc, | |
d70ccd4a YS |
1267 | pipe_ctx, |
1268 | true); | |
00f02019 | 1269 | } |
f19d5f35 DL |
1270 | if (update_type == UPDATE_TYPE_FULL) |
1271 | break; | |
00f02019 LE |
1272 | } |
1273 | ||
f19d5f35 | 1274 | /* Full fe update*/ |
15659045 | 1275 | for (j = 0; j < dc->res_pool->pipe_count; j++) { |
f19d5f35 | 1276 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; |
3e9ad616 | 1277 | |
3be5262e | 1278 | if (update_type != UPDATE_TYPE_FULL || !pipe_ctx->plane_state) |
f19d5f35 DL |
1279 | continue; |
1280 | ||
3e9ad616 EY |
1281 | if (!pipe_ctx->top_pipe && pipe_ctx->stream) { |
1282 | struct dc_stream_status *stream_status = stream_get_status(context, pipe_ctx->stream); | |
1283 | ||
15659045 BL |
1284 | dc->hwss.apply_ctx_for_surface( |
1285 | dc, pipe_ctx->stream, stream_status->plane_count, context); | |
3e9ad616 | 1286 | } |
f19d5f35 DL |
1287 | } |
1288 | ||
1289 | if (update_type > UPDATE_TYPE_FAST) | |
1290 | context_timing_trace(dc, &context->res_ctx); | |
1291 | ||
00f02019 LE |
1292 | /* Perform requested Updates */ |
1293 | for (i = 0; i < surface_count; i++) { | |
3be5262e | 1294 | struct dc_plane_state *plane_state = srf_updates[i].surface; |
00f02019 | 1295 | |
f19d5f35 | 1296 | if (update_type == UPDATE_TYPE_MED) |
15659045 BL |
1297 | dc->hwss.apply_ctx_for_surface( |
1298 | dc, stream, surface_count, context); | |
00f02019 | 1299 | |
15659045 | 1300 | for (j = 0; j < dc->res_pool->pipe_count; j++) { |
00f02019 | 1301 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; |
00f02019 | 1302 | |
3be5262e | 1303 | if (pipe_ctx->plane_state != plane_state) |
00f02019 | 1304 | continue; |
4562236b | 1305 | |
ee8f63e1 | 1306 | if (srf_updates[i].flip_addr) |
15659045 | 1307 | dc->hwss.update_plane_addr(dc, pipe_ctx); |
4562236b | 1308 | |
e72f0acd TC |
1309 | if (update_type == UPDATE_TYPE_FAST) |
1310 | continue; | |
1311 | ||
309c42ef YS |
1312 | /* work around to program degamma regs for split pipe after set mode. */ |
1313 | if (srf_updates[i].in_transfer_func || (pipe_ctx->top_pipe && | |
1314 | pipe_ctx->top_pipe->plane_state == pipe_ctx->plane_state)) | |
15659045 | 1315 | dc->hwss.set_input_transfer_func( |
3be5262e | 1316 | pipe_ctx, pipe_ctx->plane_state); |
90e508ba | 1317 | |
f19d5f35 DL |
1318 | if (stream_update != NULL && |
1319 | stream_update->out_transfer_func != NULL) { | |
15659045 | 1320 | dc->hwss.set_output_transfer_func( |
f46661dd AZ |
1321 | pipe_ctx, pipe_ctx->stream); |
1322 | } | |
90e508ba | 1323 | |
ee8f63e1 | 1324 | if (srf_updates[i].hdr_static_metadata) { |
fcd2f4bf | 1325 | resource_build_info_frame(pipe_ctx); |
15659045 | 1326 | dc->hwss.update_info_frame(pipe_ctx); |
fcd2f4bf | 1327 | } |
9474980a | 1328 | } |
4562236b HW |
1329 | } |
1330 | ||
00f02019 | 1331 | /* Unlock pipes */ |
15659045 | 1332 | for (i = dc->res_pool->pipe_count - 1; i >= 0; i--) { |
4562236b HW |
1333 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; |
1334 | ||
1335 | for (j = 0; j < surface_count; j++) { | |
f19d5f35 | 1336 | if (update_type != UPDATE_TYPE_FULL && |
3be5262e | 1337 | srf_updates[j].surface != pipe_ctx->plane_state) |
f19d5f35 | 1338 | continue; |
3be5262e | 1339 | if (!pipe_ctx->plane_state || pipe_ctx->top_pipe) |
f19d5f35 DL |
1340 | continue; |
1341 | ||
15659045 BL |
1342 | dc->hwss.pipe_control_lock( |
1343 | dc, | |
d70ccd4a YS |
1344 | pipe_ctx, |
1345 | false); | |
1346 | ||
f19d5f35 | 1347 | break; |
4562236b HW |
1348 | } |
1349 | } | |
bc6828e0 | 1350 | } |
4562236b | 1351 | |
bc6828e0 BL |
1352 | void dc_commit_updates_for_stream(struct dc *dc, |
1353 | struct dc_surface_update *srf_updates, | |
1354 | int surface_count, | |
1355 | struct dc_stream_state *stream, | |
1356 | struct dc_stream_update *stream_update, | |
1357 | struct dc_plane_state **plane_states, | |
1358 | struct dc_state *state) | |
1359 | { | |
1360 | const struct dc_stream_status *stream_status; | |
1361 | enum surface_update_type update_type; | |
1362 | struct dc_state *context; | |
60d671db | 1363 | struct dc_context *dc_ctx = dc->ctx; |
80e80ec8 | 1364 | int i, j; |
bc6828e0 BL |
1365 | |
1366 | stream_status = dc_stream_get_status(stream); | |
1367 | context = dc->current_state; | |
1368 | ||
1369 | update_type = dc_check_update_surfaces_for_stream( | |
1370 | dc, srf_updates, surface_count, stream_update, stream_status); | |
1371 | ||
1372 | if (update_type >= update_surface_trace_level) | |
1373 | update_surface_trace(dc, srf_updates, surface_count); | |
e771aae0 | 1374 | |
e771aae0 | 1375 | |
60d671db JZ |
1376 | if (update_type >= UPDATE_TYPE_FULL) { |
1377 | ||
1378 | /* initialize scratch memory for building context */ | |
1379 | context = dc_create_state(); | |
1380 | if (context == NULL) { | |
1381 | DC_ERROR("Failed to allocate new validate context!\n"); | |
1382 | return; | |
1383 | } | |
1384 | ||
bc6828e0 | 1385 | dc_resource_state_copy_construct(state, context); |
60d671db | 1386 | } |
5aa72db7 | 1387 | |
bc6828e0 BL |
1388 | |
1389 | for (i = 0; i < surface_count; i++) { | |
1390 | struct dc_plane_state *surface = srf_updates[i].surface; | |
1391 | ||
1392 | /* TODO: On flip we don't build the state, so it still has the | |
1393 | * old address. Which is why we are updating the address here | |
1394 | */ | |
1395 | if (srf_updates[i].flip_addr) | |
1396 | surface->address = srf_updates[i].flip_addr->address; | |
5aa72db7 | 1397 | |
80e80ec8 BL |
1398 | if (update_type >= UPDATE_TYPE_MED) { |
1399 | for (j = 0; j < dc->res_pool->pipe_count; j++) { | |
1400 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; | |
1401 | ||
1402 | if (pipe_ctx->plane_state != surface) | |
1403 | continue; | |
bc6828e0 | 1404 | |
80e80ec8 BL |
1405 | resource_build_scaling_params(pipe_ctx); |
1406 | } | |
1407 | } | |
1408 | } | |
bc6828e0 BL |
1409 | |
1410 | commit_planes_for_stream( | |
1411 | dc, | |
1412 | srf_updates, | |
1413 | surface_count, | |
1414 | stream, | |
1415 | stream_update, | |
1416 | update_type, | |
1417 | context); | |
1418 | ||
3df8fcaf LSL |
1419 | if (update_type >= UPDATE_TYPE_FULL) |
1420 | dc_post_update_surfaces_to_stream(dc); | |
bc6828e0 | 1421 | |
60d671db JZ |
1422 | if (dc->current_state != context) { |
1423 | ||
1424 | struct dc_state *old = dc->current_state; | |
1425 | ||
1426 | dc->current_state = context; | |
1427 | dc_release_state(old); | |
1428 | ||
1429 | } | |
1430 | ||
6d9501e4 HW |
1431 | return; |
1432 | ||
4562236b HW |
1433 | } |
1434 | ||
fb3466a4 | 1435 | uint8_t dc_get_current_stream_count(struct dc *dc) |
4562236b | 1436 | { |
608ac7bb | 1437 | return dc->current_state->stream_count; |
4562236b HW |
1438 | } |
1439 | ||
fb3466a4 | 1440 | struct dc_stream_state *dc_get_stream_at_index(struct dc *dc, uint8_t i) |
4562236b | 1441 | { |
608ac7bb JZ |
1442 | if (i < dc->current_state->stream_count) |
1443 | return dc->current_state->streams[i]; | |
4562236b HW |
1444 | return NULL; |
1445 | } | |
1446 | ||
4562236b HW |
1447 | enum dc_irq_source dc_interrupt_to_irq_source( |
1448 | struct dc *dc, | |
1449 | uint32_t src_id, | |
1450 | uint32_t ext_id) | |
1451 | { | |
15659045 | 1452 | return dal_irq_service_to_irq_source(dc->res_pool->irqs, src_id, ext_id); |
4562236b HW |
1453 | } |
1454 | ||
fb3466a4 | 1455 | void dc_interrupt_set(struct dc *dc, enum dc_irq_source src, bool enable) |
4562236b | 1456 | { |
21de3396 RZ |
1457 | |
1458 | if (dc == NULL) | |
1459 | return; | |
21de3396 | 1460 | |
15659045 | 1461 | dal_irq_service_set(dc->res_pool->irqs, src, enable); |
4562236b HW |
1462 | } |
1463 | ||
1464 | void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src) | |
1465 | { | |
15659045 | 1466 | dal_irq_service_ack(dc->res_pool->irqs, src); |
4562236b HW |
1467 | } |
1468 | ||
1469 | void dc_set_power_state( | |
1470 | struct dc *dc, | |
a3621485 | 1471 | enum dc_acpi_cm_power_state power_state) |
4562236b | 1472 | { |
8ee5702a | 1473 | struct kref refcount; |
4562236b | 1474 | |
4562236b HW |
1475 | switch (power_state) { |
1476 | case DC_ACPI_CM_POWER_STATE_D0: | |
ab8db3e1 AG |
1477 | dc_resource_state_construct(dc, dc->current_state); |
1478 | ||
15659045 | 1479 | dc->hwss.init_hw(dc); |
4562236b HW |
1480 | break; |
1481 | default: | |
4562236b | 1482 | |
15659045 | 1483 | dc->hwss.power_down(dc); |
4562236b HW |
1484 | |
1485 | /* Zero out the current context so that on resume we start with | |
1486 | * clean state, and dc hw programming optimizations will not | |
1487 | * cause any trouble. | |
1488 | */ | |
60bf1860 AG |
1489 | |
1490 | /* Preserve refcount */ | |
8ee5702a | 1491 | refcount = dc->current_state->refcount; |
f36cc577 | 1492 | dc_resource_state_destruct(dc->current_state); |
608ac7bb JZ |
1493 | memset(dc->current_state, 0, |
1494 | sizeof(*dc->current_state)); | |
ab8db3e1 | 1495 | |
8ee5702a | 1496 | dc->current_state->refcount = refcount; |
4562236b | 1497 | |
4562236b HW |
1498 | break; |
1499 | } | |
1500 | ||
1501 | } | |
1502 | ||
fb3466a4 | 1503 | void dc_resume(struct dc *dc) |
4562236b | 1504 | { |
4562236b HW |
1505 | |
1506 | uint32_t i; | |
1507 | ||
15659045 BL |
1508 | for (i = 0; i < dc->link_count; i++) |
1509 | core_link_resume(dc->links[i]); | |
4562236b HW |
1510 | } |
1511 | ||
4562236b HW |
1512 | bool dc_submit_i2c( |
1513 | struct dc *dc, | |
1514 | uint32_t link_index, | |
1515 | struct i2c_command *cmd) | |
1516 | { | |
4562236b | 1517 | |
15659045 | 1518 | struct dc_link *link = dc->links[link_index]; |
d0778ebf | 1519 | struct ddc_service *ddc = link->ddc; |
4562236b HW |
1520 | |
1521 | return dal_i2caux_submit_i2c_command( | |
1522 | ddc->ctx->i2caux, | |
1523 | ddc->ddc_pin, | |
1524 | cmd); | |
1525 | } | |
1526 | ||
d0778ebf | 1527 | static bool link_add_remote_sink_helper(struct dc_link *dc_link, struct dc_sink *sink) |
4562236b | 1528 | { |
4562236b HW |
1529 | if (dc_link->sink_count >= MAX_SINKS_PER_LINK) { |
1530 | BREAK_TO_DEBUGGER(); | |
1531 | return false; | |
1532 | } | |
1533 | ||
1534 | dc_sink_retain(sink); | |
1535 | ||
1536 | dc_link->remote_sinks[dc_link->sink_count] = sink; | |
1537 | dc_link->sink_count++; | |
1538 | ||
1539 | return true; | |
1540 | } | |
1541 | ||
1542 | struct dc_sink *dc_link_add_remote_sink( | |
d0778ebf | 1543 | struct dc_link *link, |
4562236b HW |
1544 | const uint8_t *edid, |
1545 | int len, | |
1546 | struct dc_sink_init_data *init_data) | |
1547 | { | |
1548 | struct dc_sink *dc_sink; | |
1549 | enum dc_edid_status edid_status; | |
4562236b HW |
1550 | |
1551 | if (len > MAX_EDID_BUFFER_SIZE) { | |
1552 | dm_error("Max EDID buffer size breached!\n"); | |
1553 | return NULL; | |
1554 | } | |
1555 | ||
1556 | if (!init_data) { | |
1557 | BREAK_TO_DEBUGGER(); | |
1558 | return NULL; | |
1559 | } | |
1560 | ||
1561 | if (!init_data->link) { | |
1562 | BREAK_TO_DEBUGGER(); | |
1563 | return NULL; | |
1564 | } | |
1565 | ||
1566 | dc_sink = dc_sink_create(init_data); | |
1567 | ||
1568 | if (!dc_sink) | |
1569 | return NULL; | |
1570 | ||
1571 | memmove(dc_sink->dc_edid.raw_edid, edid, len); | |
1572 | dc_sink->dc_edid.length = len; | |
1573 | ||
1574 | if (!link_add_remote_sink_helper( | |
d0778ebf | 1575 | link, |
4562236b HW |
1576 | dc_sink)) |
1577 | goto fail_add_sink; | |
1578 | ||
1579 | edid_status = dm_helpers_parse_edid_caps( | |
d0778ebf | 1580 | link->ctx, |
4562236b HW |
1581 | &dc_sink->dc_edid, |
1582 | &dc_sink->edid_caps); | |
1583 | ||
1584 | if (edid_status != EDID_OK) | |
1585 | goto fail; | |
1586 | ||
1587 | return dc_sink; | |
1588 | fail: | |
1589 | dc_link_remove_remote_sink(link, dc_sink); | |
1590 | fail_add_sink: | |
1591 | dc_sink_release(dc_sink); | |
1592 | return NULL; | |
1593 | } | |
1594 | ||
b73a22d3 | 1595 | void dc_link_remove_remote_sink(struct dc_link *link, struct dc_sink *sink) |
4562236b HW |
1596 | { |
1597 | int i; | |
4562236b HW |
1598 | |
1599 | if (!link->sink_count) { | |
1600 | BREAK_TO_DEBUGGER(); | |
1601 | return; | |
1602 | } | |
1603 | ||
d0778ebf HW |
1604 | for (i = 0; i < link->sink_count; i++) { |
1605 | if (link->remote_sinks[i] == sink) { | |
4562236b | 1606 | dc_sink_release(sink); |
d0778ebf | 1607 | link->remote_sinks[i] = NULL; |
4562236b HW |
1608 | |
1609 | /* shrink array to remove empty place */ | |
d0778ebf HW |
1610 | while (i < link->sink_count - 1) { |
1611 | link->remote_sinks[i] = link->remote_sinks[i+1]; | |
4562236b HW |
1612 | i++; |
1613 | } | |
d0778ebf HW |
1614 | link->remote_sinks[i] = NULL; |
1615 | link->sink_count--; | |
4562236b HW |
1616 | return; |
1617 | } | |
1618 | } | |
1619 | } |