1 /*
2  * Copyright 2017 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  */
23 
24 #include "dm_services.h"
25 
26 /* include DCE11 register header files */
27 #include "dce/dce_11_0_d.h"
28 #include "dce/dce_11_0_sh_mask.h"
29 
30 #include "dc_types.h"
31 #include "dc_bios_types.h"
32 #include "dc.h"
33 
34 #include "include/grph_object_id.h"
35 #include "include/logger_interface.h"
36 #include "dce110_timing_generator.h"
37 #include "dce110_timing_generator_v.h"
38 
39 #include "timing_generator.h"
40 
41 #define DC_LOGGER \
42 	tg->ctx->logger
43 /** ********************************************************************************
44  *
45  * DCE11 Timing Generator Implementation
46  *
47  **********************************************************************************/
48 
49 /**
50 * Enable CRTCV
51 */
52 
53 static bool dce110_timing_generator_v_enable_crtc(struct timing_generator *tg)
54 {
55 /*
56 * Set MASTER_UPDATE_MODE to 0
57 * This is needed for DRR, and also suggested to be default value by Syed.
58 */
59 
60 	uint32_t value;
61 
62 	value = 0;
63 	set_reg_field_value(value, 0,
64 			CRTCV_MASTER_UPDATE_MODE, MASTER_UPDATE_MODE);
65 	dm_write_reg(tg->ctx,
66 			mmCRTCV_MASTER_UPDATE_MODE, value);
67 
68 	/* TODO: may want this on for looking for underflow */
69 	value = 0;
70 	dm_write_reg(tg->ctx, mmCRTCV_MASTER_UPDATE_MODE, value);
71 
72 	value = 0;
73 	set_reg_field_value(value, 1,
74 			CRTCV_MASTER_EN, CRTC_MASTER_EN);
75 	dm_write_reg(tg->ctx,
76 			mmCRTCV_MASTER_EN, value);
77 
78 	return true;
79 }
80 
81 static bool dce110_timing_generator_v_disable_crtc(struct timing_generator *tg)
82 {
83 	uint32_t value;
84 
85 	value = dm_read_reg(tg->ctx,
86 			mmCRTCV_CONTROL);
87 	set_reg_field_value(value, 0,
88 			CRTCV_CONTROL, CRTC_DISABLE_POINT_CNTL);
89 	set_reg_field_value(value, 0,
90 				CRTCV_CONTROL, CRTC_MASTER_EN);
91 	dm_write_reg(tg->ctx,
92 			mmCRTCV_CONTROL, value);
93 	/*
94 	 * TODO: call this when adding stereo support
95 	 * tg->funcs->disable_stereo(tg);
96 	 */
97 	return true;
98 }
99 
100 static void dce110_timing_generator_v_blank_crtc(struct timing_generator *tg)
101 {
102 	uint32_t addr = mmCRTCV_BLANK_CONTROL;
103 	uint32_t value = dm_read_reg(tg->ctx, addr);
104 
105 	set_reg_field_value(
106 		value,
107 		1,
108 		CRTCV_BLANK_CONTROL,
109 		CRTC_BLANK_DATA_EN);
110 
111 	set_reg_field_value(
112 		value,
113 		0,
114 		CRTCV_BLANK_CONTROL,
115 		CRTC_BLANK_DE_MODE);
116 
117 	dm_write_reg(tg->ctx, addr, value);
118 }
119 
120 static void dce110_timing_generator_v_unblank_crtc(struct timing_generator *tg)
121 {
122 	uint32_t addr = mmCRTCV_BLANK_CONTROL;
123 	uint32_t value = dm_read_reg(tg->ctx, addr);
124 
125 	set_reg_field_value(
126 		value,
127 		0,
128 		CRTCV_BLANK_CONTROL,
129 		CRTC_BLANK_DATA_EN);
130 
131 	set_reg_field_value(
132 		value,
133 		0,
134 		CRTCV_BLANK_CONTROL,
135 		CRTC_BLANK_DE_MODE);
136 
137 	dm_write_reg(tg->ctx, addr, value);
138 }
139 
140 static bool dce110_timing_generator_v_is_in_vertical_blank(
141 		struct timing_generator *tg)
142 {
143 	uint32_t addr = 0;
144 	uint32_t value = 0;
145 	uint32_t field = 0;
146 
147 	addr = mmCRTCV_STATUS;
148 	value = dm_read_reg(tg->ctx, addr);
149 	field = get_reg_field_value(value, CRTCV_STATUS, CRTC_V_BLANK);
150 	return field == 1;
151 }
152 
153 static bool dce110_timing_generator_v_is_counter_moving(struct timing_generator *tg)
154 {
155 	uint32_t value;
156 	uint32_t h1 = 0;
157 	uint32_t h2 = 0;
158 	uint32_t v1 = 0;
159 	uint32_t v2 = 0;
160 
161 	value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION);
162 
163 	h1 = get_reg_field_value(
164 			value,
165 			CRTCV_STATUS_POSITION,
166 			CRTC_HORZ_COUNT);
167 
168 	v1 = get_reg_field_value(
169 			value,
170 			CRTCV_STATUS_POSITION,
171 			CRTC_VERT_COUNT);
172 
173 	value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION);
174 
175 	h2 = get_reg_field_value(
176 			value,
177 			CRTCV_STATUS_POSITION,
178 			CRTC_HORZ_COUNT);
179 
180 	v2 = get_reg_field_value(
181 			value,
182 			CRTCV_STATUS_POSITION,
183 			CRTC_VERT_COUNT);
184 
185 	if (h1 == h2 && v1 == v2)
186 		return false;
187 	else
188 		return true;
189 }
190 
191 static void dce110_timing_generator_v_wait_for_vblank(struct timing_generator *tg)
192 {
193 	/* We want to catch beginning of VBlank here, so if the first try are
194 	 * in VBlank, we might be very close to Active, in this case wait for
195 	 * another frame
196 	 */
197 	while (dce110_timing_generator_v_is_in_vertical_blank(tg)) {
198 		if (!dce110_timing_generator_v_is_counter_moving(tg)) {
199 			/* error - no point to wait if counter is not moving */
200 			break;
201 		}
202 	}
203 
204 	while (!dce110_timing_generator_v_is_in_vertical_blank(tg)) {
205 		if (!dce110_timing_generator_v_is_counter_moving(tg)) {
206 			/* error - no point to wait if counter is not moving */
207 			break;
208 		}
209 	}
210 }
211 
212 /**
213 * Wait till we are in VActive (anywhere in VActive)
214 */
215 static void dce110_timing_generator_v_wait_for_vactive(struct timing_generator *tg)
216 {
217 	while (dce110_timing_generator_v_is_in_vertical_blank(tg)) {
218 		if (!dce110_timing_generator_v_is_counter_moving(tg)) {
219 			/* error - no point to wait if counter is not moving */
220 			break;
221 		}
222 	}
223 }
224 
225 static void dce110_timing_generator_v_wait_for_state(struct timing_generator *tg,
226 	enum crtc_state state)
227 {
228 	switch (state) {
229 	case CRTC_STATE_VBLANK:
230 		dce110_timing_generator_v_wait_for_vblank(tg);
231 		break;
232 
233 	case CRTC_STATE_VACTIVE:
234 		dce110_timing_generator_v_wait_for_vactive(tg);
235 		break;
236 
237 	default:
238 		break;
239 	}
240 }
241 
242 static void dce110_timing_generator_v_program_blanking(
243 	struct timing_generator *tg,
244 	const struct dc_crtc_timing *timing)
245 {
246 	uint32_t vsync_offset = timing->v_border_bottom +
247 			timing->v_front_porch;
248 	uint32_t v_sync_start = timing->v_addressable + vsync_offset;
249 
250 	uint32_t hsync_offset = timing->h_border_right +
251 			timing->h_front_porch;
252 	uint32_t h_sync_start = timing->h_addressable + hsync_offset;
253 
254 	struct dc_context *ctx = tg->ctx;
255 	uint32_t value = 0;
256 	uint32_t addr = 0;
257 	uint32_t tmp = 0;
258 
259 	addr = mmCRTCV_H_TOTAL;
260 	value = dm_read_reg(ctx, addr);
261 	set_reg_field_value(
262 		value,
263 		timing->h_total - 1,
264 		CRTCV_H_TOTAL,
265 		CRTC_H_TOTAL);
266 	dm_write_reg(ctx, addr, value);
267 
268 	addr = mmCRTCV_V_TOTAL;
269 	value = dm_read_reg(ctx, addr);
270 	set_reg_field_value(
271 		value,
272 		timing->v_total - 1,
273 		CRTCV_V_TOTAL,
274 		CRTC_V_TOTAL);
275 	dm_write_reg(ctx, addr, value);
276 
277 	addr = mmCRTCV_H_BLANK_START_END;
278 	value = dm_read_reg(ctx, addr);
279 
280 	tmp = timing->h_total -
281 		(h_sync_start + timing->h_border_left);
282 
283 	set_reg_field_value(
284 		value,
285 		tmp,
286 		CRTCV_H_BLANK_START_END,
287 		CRTC_H_BLANK_END);
288 
289 	tmp = tmp + timing->h_addressable +
290 		timing->h_border_left + timing->h_border_right;
291 
292 	set_reg_field_value(
293 		value,
294 		tmp,
295 		CRTCV_H_BLANK_START_END,
296 		CRTC_H_BLANK_START);
297 
298 	dm_write_reg(ctx, addr, value);
299 
300 	addr = mmCRTCV_V_BLANK_START_END;
301 	value = dm_read_reg(ctx, addr);
302 
303 	tmp = timing->v_total - (v_sync_start + timing->v_border_top);
304 
305 	set_reg_field_value(
306 		value,
307 		tmp,
308 		CRTCV_V_BLANK_START_END,
309 		CRTC_V_BLANK_END);
310 
311 	tmp = tmp + timing->v_addressable + timing->v_border_top +
312 		timing->v_border_bottom;
313 
314 	set_reg_field_value(
315 		value,
316 		tmp,
317 		CRTCV_V_BLANK_START_END,
318 		CRTC_V_BLANK_START);
319 
320 	dm_write_reg(ctx, addr, value);
321 
322 	addr = mmCRTCV_H_SYNC_A;
323 	value = 0;
324 	set_reg_field_value(
325 		value,
326 		timing->h_sync_width,
327 		CRTCV_H_SYNC_A,
328 		CRTC_H_SYNC_A_END);
329 	dm_write_reg(ctx, addr, value);
330 
331 	addr = mmCRTCV_H_SYNC_A_CNTL;
332 	value = dm_read_reg(ctx, addr);
333 	if (timing->flags.HSYNC_POSITIVE_POLARITY) {
334 		set_reg_field_value(
335 			value,
336 			0,
337 			CRTCV_H_SYNC_A_CNTL,
338 			CRTC_H_SYNC_A_POL);
339 	} else {
340 		set_reg_field_value(
341 			value,
342 			1,
343 			CRTCV_H_SYNC_A_CNTL,
344 			CRTC_H_SYNC_A_POL);
345 	}
346 	dm_write_reg(ctx, addr, value);
347 
348 	addr = mmCRTCV_V_SYNC_A;
349 	value = 0;
350 	set_reg_field_value(
351 		value,
352 		timing->v_sync_width,
353 		CRTCV_V_SYNC_A,
354 		CRTC_V_SYNC_A_END);
355 	dm_write_reg(ctx, addr, value);
356 
357 	addr = mmCRTCV_V_SYNC_A_CNTL;
358 	value = dm_read_reg(ctx, addr);
359 	if (timing->flags.VSYNC_POSITIVE_POLARITY) {
360 		set_reg_field_value(
361 			value,
362 			0,
363 			CRTCV_V_SYNC_A_CNTL,
364 			CRTC_V_SYNC_A_POL);
365 	} else {
366 		set_reg_field_value(
367 			value,
368 			1,
369 			CRTCV_V_SYNC_A_CNTL,
370 			CRTC_V_SYNC_A_POL);
371 	}
372 	dm_write_reg(ctx, addr, value);
373 
374 	addr = mmCRTCV_INTERLACE_CONTROL;
375 	value = dm_read_reg(ctx, addr);
376 	set_reg_field_value(
377 		value,
378 		timing->flags.INTERLACE,
379 		CRTCV_INTERLACE_CONTROL,
380 		CRTC_INTERLACE_ENABLE);
381 	dm_write_reg(ctx, addr, value);
382 }
383 
384 static void dce110_timing_generator_v_enable_advanced_request(
385 	struct timing_generator *tg,
386 	bool enable,
387 	const struct dc_crtc_timing *timing)
388 {
389 	uint32_t addr = mmCRTCV_START_LINE_CONTROL;
390 	uint32_t value = dm_read_reg(tg->ctx, addr);
391 
392 	if (enable) {
393 		if ((timing->v_sync_width + timing->v_front_porch) <= 3) {
394 			set_reg_field_value(
395 				value,
396 				3,
397 				CRTCV_START_LINE_CONTROL,
398 				CRTC_ADVANCED_START_LINE_POSITION);
399 		} else {
400 			set_reg_field_value(
401 				value,
402 				4,
403 				CRTCV_START_LINE_CONTROL,
404 				CRTC_ADVANCED_START_LINE_POSITION);
405 		}
406 		set_reg_field_value(
407 			value,
408 			0,
409 			CRTCV_START_LINE_CONTROL,
410 			CRTC_LEGACY_REQUESTOR_EN);
411 	} else {
412 		set_reg_field_value(
413 			value,
414 			2,
415 			CRTCV_START_LINE_CONTROL,
416 			CRTC_ADVANCED_START_LINE_POSITION);
417 		set_reg_field_value(
418 			value,
419 			1,
420 			CRTCV_START_LINE_CONTROL,
421 			CRTC_LEGACY_REQUESTOR_EN);
422 	}
423 
424 	dm_write_reg(tg->ctx, addr, value);
425 }
426 
427 static void dce110_timing_generator_v_set_blank(struct timing_generator *tg,
428 		bool enable_blanking)
429 {
430 	if (enable_blanking)
431 		dce110_timing_generator_v_blank_crtc(tg);
432 	else
433 		dce110_timing_generator_v_unblank_crtc(tg);
434 }
435 
436 static void dce110_timing_generator_v_program_timing(struct timing_generator *tg,
437 	const struct dc_crtc_timing *timing,
438 	bool use_vbios)
439 {
440 	if (use_vbios)
441 		dce110_timing_generator_program_timing_generator(tg, timing);
442 	else
443 		dce110_timing_generator_v_program_blanking(tg, timing);
444 }
445 
446 static void dce110_timing_generator_v_program_blank_color(
447 		struct timing_generator *tg,
448 		const struct tg_color *black_color)
449 {
450 	uint32_t addr = mmCRTCV_BLACK_COLOR;
451 	uint32_t value = dm_read_reg(tg->ctx, addr);
452 
453 	set_reg_field_value(
454 		value,
455 		black_color->color_b_cb,
456 		CRTCV_BLACK_COLOR,
457 		CRTC_BLACK_COLOR_B_CB);
458 	set_reg_field_value(
459 		value,
460 		black_color->color_g_y,
461 		CRTCV_BLACK_COLOR,
462 		CRTC_BLACK_COLOR_G_Y);
463 	set_reg_field_value(
464 		value,
465 		black_color->color_r_cr,
466 		CRTCV_BLACK_COLOR,
467 		CRTC_BLACK_COLOR_R_CR);
468 
469 	dm_write_reg(tg->ctx, addr, value);
470 }
471 
472 static void dce110_timing_generator_v_set_overscan_color_black(
473 	struct timing_generator *tg,
474 	const struct tg_color *color)
475 {
476 	struct dc_context *ctx = tg->ctx;
477 	uint32_t addr;
478 	uint32_t value = 0;
479 
480 	set_reg_field_value(
481 			value,
482 			color->color_b_cb,
483 			CRTC_OVERSCAN_COLOR,
484 			CRTC_OVERSCAN_COLOR_BLUE);
485 
486 	set_reg_field_value(
487 			value,
488 			color->color_r_cr,
489 			CRTC_OVERSCAN_COLOR,
490 			CRTC_OVERSCAN_COLOR_RED);
491 
492 	set_reg_field_value(
493 			value,
494 			color->color_g_y,
495 			CRTC_OVERSCAN_COLOR,
496 			CRTC_OVERSCAN_COLOR_GREEN);
497 
498 	addr = mmCRTCV_OVERSCAN_COLOR;
499 	dm_write_reg(ctx, addr, value);
500 	addr = mmCRTCV_BLACK_COLOR;
501 	dm_write_reg(ctx, addr, value);
502 	/* This is desirable to have a constant DAC output voltage during the
503 	 * blank time that is higher than the 0 volt reference level that the
504 	 * DAC outputs when the NBLANK signal
505 	 * is asserted low, such as for output to an analog TV. */
506 	addr = mmCRTCV_BLANK_DATA_COLOR;
507 	dm_write_reg(ctx, addr, value);
508 
509 	/* TO DO we have to program EXT registers and we need to know LB DATA
510 	 * format because it is used when more 10 , i.e. 12 bits per color
511 	 *
512 	 * m_mmDxCRTC_OVERSCAN_COLOR_EXT
513 	 * m_mmDxCRTC_BLACK_COLOR_EXT
514 	 * m_mmDxCRTC_BLANK_DATA_COLOR_EXT
515 	 */
516 }
517 
518 static void dce110_tg_v_program_blank_color(struct timing_generator *tg,
519 		const struct tg_color *black_color)
520 {
521 	uint32_t addr = mmCRTCV_BLACK_COLOR;
522 	uint32_t value = dm_read_reg(tg->ctx, addr);
523 
524 	set_reg_field_value(
525 		value,
526 		black_color->color_b_cb,
527 		CRTCV_BLACK_COLOR,
528 		CRTC_BLACK_COLOR_B_CB);
529 	set_reg_field_value(
530 		value,
531 		black_color->color_g_y,
532 		CRTCV_BLACK_COLOR,
533 		CRTC_BLACK_COLOR_G_Y);
534 	set_reg_field_value(
535 		value,
536 		black_color->color_r_cr,
537 		CRTCV_BLACK_COLOR,
538 		CRTC_BLACK_COLOR_R_CR);
539 
540 	dm_write_reg(tg->ctx, addr, value);
541 
542 	addr = mmCRTCV_BLANK_DATA_COLOR;
543 	dm_write_reg(tg->ctx, addr, value);
544 }
545 
546 static void dce110_timing_generator_v_set_overscan_color(struct timing_generator *tg,
547 	const struct tg_color *overscan_color)
548 {
549 	struct dc_context *ctx = tg->ctx;
550 	uint32_t value = 0;
551 	uint32_t addr;
552 
553 	set_reg_field_value(
554 		value,
555 		overscan_color->color_b_cb,
556 		CRTCV_OVERSCAN_COLOR,
557 		CRTC_OVERSCAN_COLOR_BLUE);
558 
559 	set_reg_field_value(
560 		value,
561 		overscan_color->color_g_y,
562 		CRTCV_OVERSCAN_COLOR,
563 		CRTC_OVERSCAN_COLOR_GREEN);
564 
565 	set_reg_field_value(
566 		value,
567 		overscan_color->color_r_cr,
568 		CRTCV_OVERSCAN_COLOR,
569 		CRTC_OVERSCAN_COLOR_RED);
570 
571 	addr = mmCRTCV_OVERSCAN_COLOR;
572 	dm_write_reg(ctx, addr, value);
573 }
574 
575 static void dce110_timing_generator_v_set_colors(struct timing_generator *tg,
576 	const struct tg_color *blank_color,
577 	const struct tg_color *overscan_color)
578 {
579 	if (blank_color != NULL)
580 		dce110_tg_v_program_blank_color(tg, blank_color);
581 	if (overscan_color != NULL)
582 		dce110_timing_generator_v_set_overscan_color(tg, overscan_color);
583 }
584 
585 static void dce110_timing_generator_v_set_early_control(
586 		struct timing_generator *tg,
587 		uint32_t early_cntl)
588 {
589 	uint32_t regval;
590 	uint32_t address = mmCRTC_CONTROL;
591 
592 	regval = dm_read_reg(tg->ctx, address);
593 	set_reg_field_value(regval, early_cntl,
594 			CRTCV_CONTROL, CRTC_HBLANK_EARLY_CONTROL);
595 	dm_write_reg(tg->ctx, address, regval);
596 }
597 
598 static uint32_t dce110_timing_generator_v_get_vblank_counter(struct timing_generator *tg)
599 {
600 	uint32_t addr = mmCRTCV_STATUS_FRAME_COUNT;
601 	uint32_t value = dm_read_reg(tg->ctx, addr);
602 	uint32_t field = get_reg_field_value(
603 			value, CRTCV_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT);
604 
605 	return field;
606 }
607 
608 static bool dce110_timing_generator_v_did_triggered_reset_occur(
609 	struct timing_generator *tg)
610 {
611 	DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n");
612 	return false;
613 }
614 
615 static void dce110_timing_generator_v_setup_global_swap_lock(
616 	struct timing_generator *tg,
617 	const struct dcp_gsl_params *gsl_params)
618 {
619 	DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n");
620 	return;
621 }
622 
623 static void dce110_timing_generator_v_enable_reset_trigger(
624 	struct timing_generator *tg,
625 	int source_tg_inst)
626 {
627 	DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n");
628 	return;
629 }
630 
631 static void dce110_timing_generator_v_disable_reset_trigger(
632 	struct timing_generator *tg)
633 {
634 	DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n");
635 	return;
636 }
637 
638 static void dce110_timing_generator_v_tear_down_global_swap_lock(
639 	struct timing_generator *tg)
640 {
641 	DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n");
642 	return;
643 }
644 
645 static void dce110_timing_generator_v_disable_vga(
646 	struct timing_generator *tg)
647 {
648 	return;
649 }
650 
651 /** ********************************************************************************************
652  *
653  * DCE11 Timing Generator Constructor / Destructor
654  *
655  *********************************************************************************************/
656 static const struct timing_generator_funcs dce110_tg_v_funcs = {
657 		.validate_timing = dce110_tg_validate_timing,
658 		.program_timing = dce110_timing_generator_v_program_timing,
659 		.enable_crtc = dce110_timing_generator_v_enable_crtc,
660 		.disable_crtc = dce110_timing_generator_v_disable_crtc,
661 		.is_counter_moving = dce110_timing_generator_v_is_counter_moving,
662 		.get_position = NULL, /* Not to be implemented for underlay*/
663 		.get_frame_count = dce110_timing_generator_v_get_vblank_counter,
664 		.set_early_control = dce110_timing_generator_v_set_early_control,
665 		.wait_for_state = dce110_timing_generator_v_wait_for_state,
666 		.set_blank = dce110_timing_generator_v_set_blank,
667 		.set_colors = dce110_timing_generator_v_set_colors,
668 		.set_overscan_blank_color =
669 				dce110_timing_generator_v_set_overscan_color_black,
670 		.set_blank_color = dce110_timing_generator_v_program_blank_color,
671 		.disable_vga = dce110_timing_generator_v_disable_vga,
672 		.did_triggered_reset_occur =
673 				dce110_timing_generator_v_did_triggered_reset_occur,
674 		.setup_global_swap_lock =
675 				dce110_timing_generator_v_setup_global_swap_lock,
676 		.enable_reset_trigger = dce110_timing_generator_v_enable_reset_trigger,
677 		.disable_reset_trigger = dce110_timing_generator_v_disable_reset_trigger,
678 		.tear_down_global_swap_lock =
679 				dce110_timing_generator_v_tear_down_global_swap_lock,
680 		.enable_advanced_request =
681 				dce110_timing_generator_v_enable_advanced_request
682 };
683 
684 void dce110_timing_generator_v_construct(
685 	struct dce110_timing_generator *tg110,
686 	struct dc_context *ctx)
687 {
688 	tg110->controller_id = CONTROLLER_ID_UNDERLAY0;
689 
690 	tg110->base.funcs = &dce110_tg_v_funcs;
691 
692 	tg110->base.ctx = ctx;
693 	tg110->base.bp = ctx->dc_bios;
694 
695 	tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1;
696 	tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1;
697 
698 	tg110->min_h_blank = 56;
699 	tg110->min_h_front_porch = 4;
700 	tg110->min_h_back_porch = 4;
701 }
702