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
53static 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
80static 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
99static 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
119static 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
139static 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
152static 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
190static 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 */
214static 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
224static 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
241static 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
383static 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
426static 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
435static 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, dc_crtc_timing: timing);
446 else
447 dce110_timing_generator_v_program_blanking(tg, timing);
448}
449
450static 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
476static 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
522static 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
550static 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
579static 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, black_color: blank_color);
585 if (overscan_color != NULL)
586 dce110_timing_generator_v_set_overscan_color(tg, overscan_color);
587}
588
589static 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
602static 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
612static 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
619static 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
627static 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
635static 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
642static 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
649static 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 *********************************************************************************************/
660static 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
688void 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

source code of linux/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c