xref: /linux/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c (revision 06ed6aa56ffac9241e03a24649e8d048f8f1b10c)
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 	int vready_offset,
439 	int vstartup_start,
440 	int vupdate_offset,
441 	int vupdate_width,
442 	const enum signal_type signal,
443 	bool use_vbios)
444 {
445 	if (use_vbios)
446 		dce110_timing_generator_program_timing_generator(tg, timing);
447 	else
448 		dce110_timing_generator_v_program_blanking(tg, timing);
449 }
450 
451 static void dce110_timing_generator_v_program_blank_color(
452 		struct timing_generator *tg,
453 		const struct tg_color *black_color)
454 {
455 	uint32_t addr = mmCRTCV_BLACK_COLOR;
456 	uint32_t value = dm_read_reg(tg->ctx, addr);
457 
458 	set_reg_field_value(
459 		value,
460 		black_color->color_b_cb,
461 		CRTCV_BLACK_COLOR,
462 		CRTC_BLACK_COLOR_B_CB);
463 	set_reg_field_value(
464 		value,
465 		black_color->color_g_y,
466 		CRTCV_BLACK_COLOR,
467 		CRTC_BLACK_COLOR_G_Y);
468 	set_reg_field_value(
469 		value,
470 		black_color->color_r_cr,
471 		CRTCV_BLACK_COLOR,
472 		CRTC_BLACK_COLOR_R_CR);
473 
474 	dm_write_reg(tg->ctx, addr, value);
475 }
476 
477 static void dce110_timing_generator_v_set_overscan_color_black(
478 	struct timing_generator *tg,
479 	const struct tg_color *color)
480 {
481 	struct dc_context *ctx = tg->ctx;
482 	uint32_t addr;
483 	uint32_t value = 0;
484 
485 	set_reg_field_value(
486 			value,
487 			color->color_b_cb,
488 			CRTC_OVERSCAN_COLOR,
489 			CRTC_OVERSCAN_COLOR_BLUE);
490 
491 	set_reg_field_value(
492 			value,
493 			color->color_r_cr,
494 			CRTC_OVERSCAN_COLOR,
495 			CRTC_OVERSCAN_COLOR_RED);
496 
497 	set_reg_field_value(
498 			value,
499 			color->color_g_y,
500 			CRTC_OVERSCAN_COLOR,
501 			CRTC_OVERSCAN_COLOR_GREEN);
502 
503 	addr = mmCRTCV_OVERSCAN_COLOR;
504 	dm_write_reg(ctx, addr, value);
505 	addr = mmCRTCV_BLACK_COLOR;
506 	dm_write_reg(ctx, addr, value);
507 	/* This is desirable to have a constant DAC output voltage during the
508 	 * blank time that is higher than the 0 volt reference level that the
509 	 * DAC outputs when the NBLANK signal
510 	 * is asserted low, such as for output to an analog TV. */
511 	addr = mmCRTCV_BLANK_DATA_COLOR;
512 	dm_write_reg(ctx, addr, value);
513 
514 	/* TO DO we have to program EXT registers and we need to know LB DATA
515 	 * format because it is used when more 10 , i.e. 12 bits per color
516 	 *
517 	 * m_mmDxCRTC_OVERSCAN_COLOR_EXT
518 	 * m_mmDxCRTC_BLACK_COLOR_EXT
519 	 * m_mmDxCRTC_BLANK_DATA_COLOR_EXT
520 	 */
521 }
522 
523 static void dce110_tg_v_program_blank_color(struct timing_generator *tg,
524 		const struct tg_color *black_color)
525 {
526 	uint32_t addr = mmCRTCV_BLACK_COLOR;
527 	uint32_t value = dm_read_reg(tg->ctx, addr);
528 
529 	set_reg_field_value(
530 		value,
531 		black_color->color_b_cb,
532 		CRTCV_BLACK_COLOR,
533 		CRTC_BLACK_COLOR_B_CB);
534 	set_reg_field_value(
535 		value,
536 		black_color->color_g_y,
537 		CRTCV_BLACK_COLOR,
538 		CRTC_BLACK_COLOR_G_Y);
539 	set_reg_field_value(
540 		value,
541 		black_color->color_r_cr,
542 		CRTCV_BLACK_COLOR,
543 		CRTC_BLACK_COLOR_R_CR);
544 
545 	dm_write_reg(tg->ctx, addr, value);
546 
547 	addr = mmCRTCV_BLANK_DATA_COLOR;
548 	dm_write_reg(tg->ctx, addr, value);
549 }
550 
551 static void dce110_timing_generator_v_set_overscan_color(struct timing_generator *tg,
552 	const struct tg_color *overscan_color)
553 {
554 	struct dc_context *ctx = tg->ctx;
555 	uint32_t value = 0;
556 	uint32_t addr;
557 
558 	set_reg_field_value(
559 		value,
560 		overscan_color->color_b_cb,
561 		CRTCV_OVERSCAN_COLOR,
562 		CRTC_OVERSCAN_COLOR_BLUE);
563 
564 	set_reg_field_value(
565 		value,
566 		overscan_color->color_g_y,
567 		CRTCV_OVERSCAN_COLOR,
568 		CRTC_OVERSCAN_COLOR_GREEN);
569 
570 	set_reg_field_value(
571 		value,
572 		overscan_color->color_r_cr,
573 		CRTCV_OVERSCAN_COLOR,
574 		CRTC_OVERSCAN_COLOR_RED);
575 
576 	addr = mmCRTCV_OVERSCAN_COLOR;
577 	dm_write_reg(ctx, addr, value);
578 }
579 
580 static void dce110_timing_generator_v_set_colors(struct timing_generator *tg,
581 	const struct tg_color *blank_color,
582 	const struct tg_color *overscan_color)
583 {
584 	if (blank_color != NULL)
585 		dce110_tg_v_program_blank_color(tg, blank_color);
586 	if (overscan_color != NULL)
587 		dce110_timing_generator_v_set_overscan_color(tg, overscan_color);
588 }
589 
590 static void dce110_timing_generator_v_set_early_control(
591 		struct timing_generator *tg,
592 		uint32_t early_cntl)
593 {
594 	uint32_t regval;
595 	uint32_t address = mmCRTC_CONTROL;
596 
597 	regval = dm_read_reg(tg->ctx, address);
598 	set_reg_field_value(regval, early_cntl,
599 			CRTCV_CONTROL, CRTC_HBLANK_EARLY_CONTROL);
600 	dm_write_reg(tg->ctx, address, regval);
601 }
602 
603 static uint32_t dce110_timing_generator_v_get_vblank_counter(struct timing_generator *tg)
604 {
605 	uint32_t addr = mmCRTCV_STATUS_FRAME_COUNT;
606 	uint32_t value = dm_read_reg(tg->ctx, addr);
607 	uint32_t field = get_reg_field_value(
608 			value, CRTCV_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT);
609 
610 	return field;
611 }
612 
613 static bool dce110_timing_generator_v_did_triggered_reset_occur(
614 	struct timing_generator *tg)
615 {
616 	DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n");
617 	return false;
618 }
619 
620 static void dce110_timing_generator_v_setup_global_swap_lock(
621 	struct timing_generator *tg,
622 	const struct dcp_gsl_params *gsl_params)
623 {
624 	DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n");
625 	return;
626 }
627 
628 static void dce110_timing_generator_v_enable_reset_trigger(
629 	struct timing_generator *tg,
630 	int source_tg_inst)
631 {
632 	DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n");
633 	return;
634 }
635 
636 static void dce110_timing_generator_v_disable_reset_trigger(
637 	struct timing_generator *tg)
638 {
639 	DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n");
640 	return;
641 }
642 
643 static void dce110_timing_generator_v_tear_down_global_swap_lock(
644 	struct timing_generator *tg)
645 {
646 	DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n");
647 	return;
648 }
649 
650 static void dce110_timing_generator_v_disable_vga(
651 	struct timing_generator *tg)
652 {
653 	return;
654 }
655 
656 /** ********************************************************************************************
657  *
658  * DCE11 Timing Generator Constructor / Destructor
659  *
660  *********************************************************************************************/
661 static const struct timing_generator_funcs dce110_tg_v_funcs = {
662 		.validate_timing = dce110_tg_validate_timing,
663 		.program_timing = dce110_timing_generator_v_program_timing,
664 		.enable_crtc = dce110_timing_generator_v_enable_crtc,
665 		.disable_crtc = dce110_timing_generator_v_disable_crtc,
666 		.is_counter_moving = dce110_timing_generator_v_is_counter_moving,
667 		.get_position = NULL, /* Not to be implemented for underlay*/
668 		.get_frame_count = dce110_timing_generator_v_get_vblank_counter,
669 		.set_early_control = dce110_timing_generator_v_set_early_control,
670 		.wait_for_state = dce110_timing_generator_v_wait_for_state,
671 		.set_blank = dce110_timing_generator_v_set_blank,
672 		.set_colors = dce110_timing_generator_v_set_colors,
673 		.set_overscan_blank_color =
674 				dce110_timing_generator_v_set_overscan_color_black,
675 		.set_blank_color = dce110_timing_generator_v_program_blank_color,
676 		.disable_vga = dce110_timing_generator_v_disable_vga,
677 		.did_triggered_reset_occur =
678 				dce110_timing_generator_v_did_triggered_reset_occur,
679 		.setup_global_swap_lock =
680 				dce110_timing_generator_v_setup_global_swap_lock,
681 		.enable_reset_trigger = dce110_timing_generator_v_enable_reset_trigger,
682 		.disable_reset_trigger = dce110_timing_generator_v_disable_reset_trigger,
683 		.tear_down_global_swap_lock =
684 				dce110_timing_generator_v_tear_down_global_swap_lock,
685 		.enable_advanced_request =
686 				dce110_timing_generator_v_enable_advanced_request
687 };
688 
689 void dce110_timing_generator_v_construct(
690 	struct dce110_timing_generator *tg110,
691 	struct dc_context *ctx)
692 {
693 	tg110->controller_id = CONTROLLER_ID_UNDERLAY0;
694 
695 	tg110->base.funcs = &dce110_tg_v_funcs;
696 
697 	tg110->base.ctx = ctx;
698 	tg110->base.bp = ctx->dc_bios;
699 
700 	tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1;
701 	tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1;
702 
703 	tg110->min_h_blank = 56;
704 	tg110->min_h_front_porch = 4;
705 	tg110->min_h_back_porch = 4;
706 }
707