PicoDVI/software/libdvi/dvi_serialiser.c

74 wiersze
2.3 KiB
C

#include "pico.h"
#include "hardware/pio.h"
#include "hardware/gpio.h"
#include "hardware/pwm.h"
#include "hardware/structs/padsbank0.h"
#include "dvi.h"
#include "dvi_serialiser.h"
#include "dvi_serialiser.pio.h"
static void dvi_configure_pad(uint gpio, bool invert) {
// 2 mA drive, enable slew rate limiting (this seems fine even at 720p30, and
// the 3V3 LDO doesn't get warm like when turning all the GPIOs up to 11).
// Also disable digital receiver.
hw_write_masked(
&padsbank0_hw->io[gpio],
(0 << PADS_BANK0_GPIO0_DRIVE_LSB),
PADS_BANK0_GPIO0_DRIVE_BITS | PADS_BANK0_GPIO0_SLEWFAST_BITS | PADS_BANK0_GPIO0_IE_BITS
);
gpio_set_outover(gpio, invert ? GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL);
}
void dvi_serialiser_init(struct dvi_serialiser_cfg *cfg) {
#if DVI_SERIAL_DEBUG
uint offset = pio_add_program(cfg->pio, &dvi_serialiser_debug_program);
#else
uint offset = pio_add_program(cfg->pio, &dvi_serialiser_program);
#endif
cfg->prog_offs = offset;
for (int i = 0; i < N_TMDS_LANES; ++i) {
pio_sm_claim(cfg->pio, cfg->sm_tmds[i]);
dvi_serialiser_program_init(
cfg->pio,
cfg->sm_tmds[i],
offset,
cfg->pins_tmds[i],
DVI_SERIAL_DEBUG
);
dvi_configure_pad(cfg->pins_tmds[i], cfg->invert_diffpairs);
dvi_configure_pad(cfg->pins_tmds[i] + 1, cfg->invert_diffpairs);
}
// Use a PWM slice to drive the pixel clock. Both GPIOs must be on the same
// slice (lower-numbered GPIO must be even).
assert(cfg->pins_clk % 2 == 0);
uint slice = pwm_gpio_to_slice_num(cfg->pins_clk);
// 5 cycles high, 5 low. Invert one channel so that we get complementary outputs.
pwm_config pwm_cfg = pwm_get_default_config();
pwm_config_set_output_polarity(&pwm_cfg, true, false);
pwm_config_set_wrap(&pwm_cfg, 9);
pwm_init(slice, &pwm_cfg, false);
pwm_set_both_levels(slice, 5, 5);
for (uint i = cfg->pins_clk; i <= cfg->pins_clk + 1; ++i) {
gpio_set_function(i, GPIO_FUNC_PWM);
dvi_configure_pad(i, cfg->invert_diffpairs);
}
}
void dvi_serialiser_enable(struct dvi_serialiser_cfg *cfg, bool enable) {
uint mask = 0;
for (int i = 0; i < N_TMDS_LANES; ++i)
mask |= 1u << (cfg->sm_tmds[i] + PIO_CTRL_SM_ENABLE_LSB);
if (enable) {
hw_set_bits(&cfg->pio->ctrl, mask);
pwm_set_enabled(pwm_gpio_to_slice_num(cfg->pins_clk), true);
}
else {
hw_clear_bits(&cfg->pio->ctrl, mask);
pwm_set_enabled(pwm_gpio_to_slice_num(cfg->pins_clk), false);
}
}