@@ -37,27 +37,30 @@ static void usage(const char * progname)
37
37
fprintf (stderr , "\t-o <goio> Select output GPIO pin number. Default 1\n" );
38
38
fprintf (stderr , "\t-s <us> Set HSync width in microseconds. Default 4.7\n" );
39
39
fprintf (stderr , "\t-t <us> Set line period in microseconds. Default 64.0\n" );
40
- fprintf (stderr , "\t-w <halflines> Set VSync in half-lines when interlaced. Default 6\n" );
40
+ fprintf (stderr , "\t-e <us> Set EQ pulse width when interlaced. Default 0.0\n" );
41
+ fprintf (stderr , "\t-w <halflines> Set VSW in half-lines when interlaced. Default 6\n" );
41
42
fprintf (stderr , "\t-i | --interlace Generate serrated CSync for an interlaced mode\n" );
42
- fprintf (stderr , "\t-p | --pal Equivalent to -i -s 4.7 -t 64.0 -w 5\n" );
43
- fprintf (stderr , "\t-n | --ntsc Equivalent to -i -s 4.7 -t 63.56 -w 6\n" );
43
+ fprintf (stderr , "\t-p | --pal Equivalent to -i -s 4.7 -t 64.0 -e 2.35 - w 5\n" );
44
+ fprintf (stderr , "\t-n | --ntsc Equivalent to -i -s 4.7 -t 63.56 -e 2.3 - w 6\n" );
44
45
}
45
46
46
47
unsigned int opt_hpos = 0 ;
47
48
unsigned int opt_vpos = 0 ;
48
49
unsigned int opt_cpos = 0 ;
49
50
unsigned int opt_gpio = 1 ;
50
51
unsigned int opt_ilace = 0 ;
52
+ unsigned int opt_vsw = 6 ;
51
53
double opt_hsw = 4.7 ;
52
54
double opt_period = 64.0 ;
53
- double opt_vsw = 6 ;
55
+ double opt_eqp = 0.0 ;
54
56
55
57
static void setpal ()
56
58
{
57
59
opt_ilace = 1 ;
58
60
opt_hsw = 4.7 ;
59
61
opt_period = 64.0 ;
60
62
opt_vsw = 5 ;
63
+ opt_eqp = 2.35 ;
61
64
}
62
65
63
66
static void setntsc ()
@@ -66,6 +69,7 @@ static void setntsc()
66
69
opt_hsw = 4.7 ;
67
70
opt_period = 63.56 ;
68
71
opt_vsw = 6 ;
72
+ opt_eqp = 2.3 ;
69
73
}
70
74
71
75
static int getopts (int argc , const char * * argv )
@@ -109,6 +113,11 @@ static int getopts(int argc, const char **argv)
109
113
argv ++ ;
110
114
opt_period = atof (argv [1 ]);
111
115
break ;
116
+ case 'e' :
117
+ if (-- argc < 2 ) return -1 ;
118
+ argv ++ ;
119
+ opt_eqp = atof (argv [1 ]);
120
+ break ;
112
121
case 'w' :
113
122
if (-- argc < 2 ) return -1 ;
114
123
argv ++ ;
@@ -335,17 +344,135 @@ static int setup_pio_for_csync_ilace(PIO pio)
335
344
return 0 ;
336
345
}
337
346
347
+ /*
348
+ * COMPOSITE SYNC (TV-STYLE) for 625/25i [-w 5] and 525/30i [-w 6] only.
349
+ *
350
+ * DPI VSYNC (GPIO2) must be a modified signal which is always active-low.
351
+ * It should go low for 1 or 2 scanlines, VSyncWidth/2.0 or (VSyncWidth+1)/2.0
352
+ * lines before Vsync-start, i.e. just after the last full active TV line
353
+ * (noting that RP1 DPI does not generate half-lines).
354
+ *
355
+ * This will push the image up by 1 line compared to customary DRM timings in
356
+ * "PAL" mode, or 2 lines in "NTSC" mode (which is arguably too low anyway),
357
+ * but avoids a collision between an active line and an equalizing pulse.
358
+ *
359
+ * Another wrinkle is that when the first equalizing pulse aligns with HSync,
360
+ * it becomes a normal-width sync pulse. This was a deliberate simplification.
361
+ * It is unlikely that any TV will notice or care.
362
+ */
363
+
364
+ static int setup_pio_for_csync_tv (PIO pio )
365
+ {
366
+ static const int wrap_target = 6 ;
367
+ static const int wrap = 27 ;
368
+ unsigned short instructions [] = { /* This is mutable */
369
+ 0x3703 , // 0: wait 0 gpio, 3 side 1 [7] ; while (HSync) delay;
370
+ 0x3083 , // 1: wait 1 gpio, 3 side 1 ; do { @HSync
371
+ 0xa7e6 , // 2: mov osr, isr side 0 [7] ; CSYNC: rewind sequence
372
+ 0x2003 , // 3: wait 0 gpio, 3 side 0 ; CSYNC: HSync->CSync
373
+ 0xb7e6 , // 4: mov osr, isr side 1 [7] ; delay
374
+ 0x10c1 , // 5: jmp pin, 1 side 1 ; } while (VSync)
375
+ // .wrap_target ; while (true) {
376
+ 0xd042 , // 6: irq clear 2 side 1 ; flush stale IRQ
377
+ 0xd043 , // 7: irq clear 3 side 1 ; flush stale IRQ
378
+ 0xb022 , // 8: mov x, y side 1 ; X = EQWidth - 3
379
+ 0x30c2 , // 9: wait 1 irq, 2 side 1 ; @midline
380
+ 0x004a , // 10: jmp x--, 10 side 0 ; CSYNC: while (x--) ;
381
+ 0x6021 , // 11: out x, 1 side 0 ; CSYNC: next pulse broad?
382
+ 0x002e , // 12: jmp !x, 14 side 0 ; CSYNC: if (broad)
383
+ 0x20c3 , // 13: wait 1 irq, 3 side 0 ; CSYNC: @BroadRight
384
+ 0x7021 , // 14: out x, 1 side 1 ; sequence not finished?
385
+ 0x1020 , // 15: jmp !x, 0 side 1 ; if (finished) break
386
+ 0xd041 , // 16: irq clear 1 side 1 ; flush stale IRQ
387
+ 0xb022 , // 17: mov x, y side 1 ; X = EQWidth - 3
388
+ 0x3083 , // 18: wait 1 gpio, 3 side 1 ; @HSync
389
+ 0x0053 , // 19: jmp x--, 19 side 0 ; CSYNC: while (x--) ;
390
+ 0x6021 , // 20: out x, 1 side 0 ; CSYNC: next pulse broad?
391
+ 0x0037 , // 21: jmp !x, 23 side 0 ; CSYNC: if (broad)
392
+ 0x20c1 , // 22: wait 1 irq, 1 side 0 ; CSYNC: @BroadLeft
393
+ 0x7021 , // 23: out x, 1 side 1 ; sequence not finished?
394
+ 0x1020 , // 24: jmp !x, 0 side 1 ; if (finished) break
395
+ 0x10c6 , // 25: jmp pin, 6 side 1 ; if (VSync) continue
396
+ 0xb0e6 , // 26: mov osr, isr side 1 ; rewind sequence
397
+ 0x7022 , // 27: out x, 2 side 1 ; skip 2 bits
398
+ // .wrap ; }
399
+ };
400
+ struct pio_program prog = {
401
+ .instructions = instructions ,
402
+ .length = sizeof (instructions )/sizeof (instructions [0 ]),
403
+ .origin = -1
404
+ };
405
+ pio_sm_config cfg = pio_get_default_sm_config ();
406
+ unsigned int i , offset ;
407
+ unsigned int tc [3 ];
408
+ unsigned int sm = 0 ;
409
+
410
+ pio_claim_sm_mask (pio , 1 );
411
+
412
+ /* Compute mid-line and broad-sync time constants and start the 3 "timer" SMs */
413
+ tc [1 ] = 5.0e-7 * opt_period * (double )clock_get_hz (clk_sys );
414
+ tc [0 ] = tc [1 ] - 1.0e-6 * opt_hsw * (double )clock_get_hz (clk_sys );
415
+ tc [2 ] = tc [1 ] + tc [0 ];
416
+ if (start_timers (pio , opt_hpos ? 0 : 1 , tc ) < 0 ) {
417
+ pio_sm_unclaim (pio , sm );
418
+ return -1 ;
419
+ }
420
+
421
+ /* Adapt program code according to CSync polarity; configure program */
422
+ pio_sm_set_enabled (pio , sm , false);
423
+ for (i = 0 ; i < prog .length ; i ++ ) {
424
+ if (opt_cpos )
425
+ instructions [i ] ^= 0x1000 ;
426
+ if (!opt_hpos && (instructions [i ] & 0xe07f ) == 0x2003 )
427
+ instructions [i ] ^= 0x0080 ;
428
+ }
429
+ offset = pio_add_program (pio , & prog );
430
+ if (offset == PIO_ORIGIN_ANY )
431
+ return -1 ;
432
+
433
+ /* Configure pins and SM */
434
+ sm_config_set_wrap (& cfg , offset + wrap_target , offset + wrap );
435
+ sm_config_set_sideset (& cfg , 1 , false, false);
436
+ sm_config_set_sideset_pins (& cfg , opt_gpio );
437
+ pio_gpio_init (pio , opt_gpio );
438
+ sm_config_set_jmp_pin (& cfg , 2 ); /* DPI VSync "helper" signal is GPIO 2 */
439
+ pio_sm_init (pio , sm , offset , & cfg );
440
+ pio_sm_set_consecutive_pindirs (pio , sm , opt_gpio , 1 , true);
441
+
442
+ /* Load parameters (Vsync pattern; EQ pulse width) into ISR and Y */
443
+ tc [0 ] = (unsigned )(1.0e-6 * opt_eqp * (double ) clock_get_hz (clk_sys ));
444
+ pio_sm_put (pio , sm , (opt_vsw <= 5 ) ? 0x02ABFFAA : 0xAABFFEAA );
445
+ pio_sm_put (pio , sm , tc [0 ] - 3 );
446
+ pio_sm_exec (pio , sm , pio_encode_pull (false, false));
447
+ pio_sm_exec (pio , sm , pio_encode_out (pio_y , 32 ));
448
+ pio_sm_exec (pio , sm , pio_encode_in (pio_y , 32 ));
449
+ pio_sm_exec (pio , sm , pio_encode_pull (false, false));
450
+ pio_sm_exec (pio , sm , pio_encode_out (pio_y , 32 ));
451
+ pio_sm_set_enabled (pio , sm , true);
452
+
453
+ /* Start the SM */
454
+ pio_sm_set_enabled (pio , sm , true);
455
+
456
+ return 0 ;
457
+ }
458
+
338
459
int main (int argc , const char * * argv )
339
460
{
461
+ int r = 0 ;
462
+
340
463
if (getopts (argc , argv )) {
341
464
const char * progname = (argc > 0 && argv [0 ]) ? argv [0 ] : "dpi_csync" ;
342
465
usage (progname );
343
466
return 1 ;
344
467
}
345
468
346
- int r = opt_ilace ?
347
- setup_pio_for_csync_ilace (pio0 ) :
348
- setup_pio_for_csync_prog (pio0 );
469
+ if (!opt_ilace )
470
+ r = setup_pio_for_csync_prog (pio0 );
471
+ else if (opt_eqp <= 0 || opt_vsw < 5 || opt_vsw > 6 )
472
+ r = setup_pio_for_csync_ilace (pio0 );
473
+ else
474
+ r = setup_pio_for_csync_tv (pio0 );
475
+
349
476
if (r ) {
350
477
fprintf (stderr , "PIO setup failed\n" );
351
478
return 1 ;
0 commit comments