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