dvi_serialiser.c
1 #include "pico.h" 2 #include "hardware/pio.h" 3 #include "hardware/gpio.h" 4 #include "hardware/pwm.h" 5 #include "hardware/structs/padsbank0.h" 6 7 #include "dvi.h" 8 #include "dvi_serialiser.h" 9 #include "dvi_serialiser.pio.h" 10 11 static void dvi_configure_pad(uint gpio, bool invert) { 12 // 2 mA drive, enable slew rate limiting (this seems fine even at 720p30, and 13 // the 3V3 LDO doesn't get warm like when turning all the GPIOs up to 11). 14 // Also disable digital receiver. 15 hw_write_masked( 16 &padsbank0_hw->io[gpio], 17 (0 << PADS_BANK0_GPIO0_DRIVE_LSB), 18 PADS_BANK0_GPIO0_DRIVE_BITS | PADS_BANK0_GPIO0_SLEWFAST_BITS | PADS_BANK0_GPIO0_IE_BITS 19 ); 20 gpio_set_outover(gpio, invert ? GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL); 21 } 22 23 void dvi_serialiser_init(struct dvi_serialiser_cfg *cfg) { 24 #if DVI_SERIAL_DEBUG 25 uint offset = pio_add_program(cfg->pio, &dvi_serialiser_debug_program); 26 #else 27 uint offset = pio_add_program(cfg->pio, &dvi_serialiser_program); 28 #endif 29 cfg->prog_offs = offset; 30 31 for (int i = 0; i < N_TMDS_LANES; ++i) { 32 pio_sm_claim(cfg->pio, cfg->sm_tmds[i]); 33 dvi_serialiser_program_init( 34 cfg->pio, 35 cfg->sm_tmds[i], 36 offset, 37 cfg->pins_tmds[i], 38 DVI_SERIAL_DEBUG 39 ); 40 dvi_configure_pad(cfg->pins_tmds[i], cfg->invert_diffpairs); 41 dvi_configure_pad(cfg->pins_tmds[i] + 1, cfg->invert_diffpairs); 42 } 43 44 // Use a PWM slice to drive the pixel clock. Both GPIOs must be on the same 45 // slice (lower-numbered GPIO must be even). 46 assert(cfg->pins_clk % 2 == 0); 47 uint slice = pwm_gpio_to_slice_num(cfg->pins_clk); 48 // 5 cycles high, 5 low. Invert one channel so that we get complementary outputs. 49 pwm_config pwm_cfg = pwm_get_default_config(); 50 pwm_config_set_output_polarity(&pwm_cfg, true, false); 51 pwm_config_set_wrap(&pwm_cfg, 9); 52 pwm_init(slice, &pwm_cfg, false); 53 pwm_set_both_levels(slice, 5, 5); 54 55 for (uint i = cfg->pins_clk; i <= cfg->pins_clk + 1; ++i) { 56 gpio_set_function(i, GPIO_FUNC_PWM); 57 dvi_configure_pad(i, cfg->invert_diffpairs); 58 } 59 } 60 61 void dvi_serialiser_enable(struct dvi_serialiser_cfg *cfg, bool enable) { 62 uint mask = 0; 63 for (int i = 0; i < N_TMDS_LANES; ++i) 64 mask |= 1u << (cfg->sm_tmds[i] + PIO_CTRL_SM_ENABLE_LSB); 65 if (enable) { 66 hw_set_bits(&cfg->pio->ctrl, mask); 67 pwm_set_enabled(pwm_gpio_to_slice_num(cfg->pins_clk), true); 68 } 69 else { 70 hw_clear_bits(&cfg->pio->ctrl, mask); 71 pwm_set_enabled(pwm_gpio_to_slice_num(cfg->pins_clk), false); 72 } 73 }