Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed to work #1

Merged
merged 1 commit into from Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 1 addition & 2 deletions software/libdvi/audio_ring.c
Expand Up @@ -16,8 +16,7 @@ uint32_t get_write_size(audio_ring_t *audio_ring, bool full) {
if (wp < rp) {
return rp - wp - 1;
} else {
uint32_t size = audio_ring->size - wp;
return full ? ( size - wp + rp - 1) : (size - wp - (rp == 0 ? 1 : 0));
return audio_ring->size - wp + (full ? rp - 1 : (rp == 0 ? -1 : 0));
}
}

Expand Down
4 changes: 2 additions & 2 deletions software/libdvi/data_packet.h
Expand Up @@ -18,8 +18,8 @@
#endif


#define W_DATA_ISLAND W_GUARDBAND * 2 + W_DATA_PACKET
#define N_DATA_ISLAND_WORDS W_DATA_ISLAND / DVI_SYMBOLS_PER_WORD
#define W_DATA_ISLAND (W_GUARDBAND * 2 + W_DATA_PACKET)
mlorenzati marked this conversation as resolved.
Show resolved Hide resolved
#define N_DATA_ISLAND_WORDS (W_DATA_ISLAND / DVI_SYMBOLS_PER_WORD)

typedef enum {
SCAN_INFO_NO_DATA,
Expand Down
71 changes: 51 additions & 20 deletions software/libdvi/dvi.c
Expand Up @@ -42,9 +42,9 @@ void dvi_init(struct dvi_inst *inst, uint spinlock_tmds_queue, uint spinlock_col

dvi_setup_scanline_for_vblank(inst->timing, inst->dma_cfg, true, &inst->dma_list_vblank_sync);
dvi_setup_scanline_for_vblank(inst->timing, inst->dma_cfg, false, &inst->dma_list_vblank_nosync);
dvi_setup_scanline_for_active(inst->timing, inst->dma_cfg, (void*)SRAM_BASE, &inst->dma_list_active);
dvi_setup_scanline_for_active(inst->timing, inst->dma_cfg, NULL, &inst->dma_list_error);
dvi_setup_scanline_for_active(inst->timing, inst->dma_cfg, NULL, &inst->dma_list_active_blank);
dvi_setup_scanline_for_active(inst->timing, inst->dma_cfg, (void*)SRAM_BASE, &inst->dma_list_active, false);
dvi_setup_scanline_for_active(inst->timing, inst->dma_cfg, NULL, &inst->dma_list_error, false);
dvi_setup_scanline_for_active(inst->timing, inst->dma_cfg, NULL, &inst->dma_list_active_blank, true);

for (int i = 0; i < DVI_N_TMDS_BUFFERS; ++i) {
#if DVI_MONOCHROME_TMDS
Expand Down Expand Up @@ -243,47 +243,72 @@ static void __dvi_func(dvi_dma_irq_handler)(struct dvi_inst *inst) {

switch (inst->timing_state.v_state) {
case DVI_STATE_ACTIVE:
if (inst->timing_state.v_ctr < inst->blank_settings.top || inst->timing_state.v_ctr >= (inst->timing->v_active_lines - inst->blank_settings.bottom)) {
{
bool is_blank_line = false;
if (inst->timing_state.v_ctr < inst->blank_settings.top ||
inst->timing_state.v_ctr >= (inst->timing->v_active_lines - inst->blank_settings.bottom))
{
// Is a Blank Line
_dvi_load_dma_op(inst->dma_cfg, &inst->dma_list_active_blank);
} else {
if (queue_try_peek_u32(&inst->q_tmds_valid, &tmdsbuf)) {
if (inst->timing_state.v_ctr % DVI_VERTICAL_REPEAT == DVI_VERTICAL_REPEAT - 1) {
is_blank_line = true;
}
else
{
if (queue_try_peek_u32(&inst->q_tmds_valid, &tmdsbuf))
{
if (inst->timing_state.v_ctr % DVI_VERTICAL_REPEAT == DVI_VERTICAL_REPEAT - 1)
{
queue_remove_blocking_u32(&inst->q_tmds_valid, &tmdsbuf);
inst->tmds_buf_release[0] = tmdsbuf;
}
} else {
}
else
{
// No valid scanline was ready (generates solid red scanline)
tmdsbuf = NULL;
if (inst->timing_state.v_ctr % DVI_VERTICAL_REPEAT == DVI_VERTICAL_REPEAT - 1) {
if (inst->timing_state.v_ctr % DVI_VERTICAL_REPEAT == DVI_VERTICAL_REPEAT - 1)
{
++inst->late_scanline_ctr;
}
}

if (inst->scanline_is_enabled && (inst->timing_state.v_ctr & 1))
{
is_blank_line = true;
}
}
if (tmdsbuf) {
dvi_update_scanline_data_dma(inst->timing, tmdsbuf, &inst->dma_list_active);

if (is_blank_line)
{
_dvi_load_dma_op(inst->dma_cfg, &inst->dma_list_active_blank);
}
else if (tmdsbuf)
{
dvi_update_scanline_data_dma(inst->timing, tmdsbuf, &inst->dma_list_active, inst->data_island_is_enabled);
_dvi_load_dma_op(inst->dma_cfg, &inst->dma_list_active);
} else {
_dvi_load_dma_op(inst->dma_cfg, &inst->dma_list_error);
}
if (inst->scanline_callback && inst->timing_state.v_ctr % DVI_VERTICAL_REPEAT == DVI_VERTICAL_REPEAT - 1) {
inst->scanline_callback(inst->timing_state.v_ctr/DVI_VERTICAL_REPEAT);
else
{
_dvi_load_dma_op(inst->dma_cfg, &inst->dma_list_error);
}
if (inst->scanline_is_enabled && (inst->timing_state.v_ctr & 1)) {
_dvi_load_dma_op(inst->dma_cfg, &inst->dma_list_active_blank);
if (inst->scanline_callback && inst->timing_state.v_ctr % DVI_VERTICAL_REPEAT == DVI_VERTICAL_REPEAT - 1)
{
inst->scanline_callback(inst->timing_state.v_ctr / DVI_VERTICAL_REPEAT);
}
break;
}
break;

case DVI_STATE_SYNC:
_dvi_load_dma_op(inst->dma_cfg, &inst->dma_list_vblank_sync);
if (inst->timing_state.v_ctr == 0) {
++inst->dvi_frame_count;
}
break;

default:
_dvi_load_dma_op(inst->dma_cfg, &inst->dma_list_vblank_nosync);
break;
}

if (inst->data_island_is_enabled) {
dvi_update_data_packet(inst);
}
Expand Down Expand Up @@ -316,6 +341,12 @@ void dvi_audio_init(struct dvi_inst *inst) {
void dvi_enable_data_island(struct dvi_inst *inst) {
inst->data_island_is_enabled = true;

dvi_setup_scanline_for_vblank_with_audio(inst->timing, inst->dma_cfg, true, &inst->dma_list_vblank_sync);
mlorenzati marked this conversation as resolved.
Show resolved Hide resolved
dvi_setup_scanline_for_vblank_with_audio(inst->timing, inst->dma_cfg, false, &inst->dma_list_vblank_nosync);
dvi_setup_scanline_for_active_with_audio(inst->timing, inst->dma_cfg, (void*)SRAM_BASE, &inst->dma_list_active, false);
dvi_setup_scanline_for_active_with_audio(inst->timing, inst->dma_cfg, NULL, &inst->dma_list_error, false);
dvi_setup_scanline_for_active_with_audio(inst->timing, inst->dma_cfg, NULL, &inst->dma_list_active_blank, true);

// Setup internal Data Packet streams
dvi_update_data_island_ptr(&inst->dma_list_vblank_sync, &inst->next_data_stream);
dvi_update_data_island_ptr(&inst->dma_list_vblank_nosync, &inst->next_data_stream);
Expand Down
107 changes: 102 additions & 5 deletions software/libdvi/dvi_timing.c
Expand Up @@ -214,6 +214,20 @@ static uint32_t __attribute__((aligned(8))) __dvi_const(empty_scanline_tmds)[6]
};
#endif

// Black
static uint32_t __dvi_const(black_scanline_tmds)[3] = {
0x7fd00u, // 0x00, 0x00
0x7fd00u, // 0x00, 0x00
0x7fd00u, // 0x00, 0x00
};

// Video Gaurdband
static uint32_t __dvi_const(video_gaurdband_syms)[3] = {
0b10110011001011001100,
0b01001100110100110011,
0b10110011001011001100,
};

void dvi_timing_state_init(struct dvi_timing_state *t) {
t->v_ctr = 0;
t->v_state = DVI_STATE_FRONT_PORCH;
Expand Down Expand Up @@ -278,8 +292,40 @@ void dvi_setup_scanline_for_vblank(const struct dvi_timing *t, const struct dvi_
}
}

void dvi_setup_scanline_for_vblank_with_audio(const struct dvi_timing *t, const struct dvi_lane_dma_cfg dma_cfg[],
bool vsync_asserted, struct dvi_scanline_dma_list *l) {

bool vsync = t->v_sync_polarity == vsync_asserted;
const uint32_t *sym_hsync_off = get_ctrl_sym(vsync, !t->h_sync_polarity);
const uint32_t *sym_hsync_on = get_ctrl_sym(vsync, t->h_sync_polarity);
const uint32_t *sym_no_sync = get_ctrl_sym(false, false );
const uint32_t *sym_preamble_to_data12 = &dvi_ctrl_syms[1];
const uint32_t *data_packet0 = getDefaultDataPacket0(vsync, t->h_sync_polarity);

for (int i = 0; i < N_TMDS_LANES; ++i)
{
dma_cb_t *cblist = dvi_lane_from_list(l, i);
if (i == TMDS_SYNC_LANE)
{
_set_data_cb(&cblist[0], &dma_cfg[i], sym_hsync_off, t->h_front_porch / DVI_SYMBOLS_PER_WORD, 2, false);
_set_data_cb(&cblist[1], &dma_cfg[i], data_packet0, N_DATA_ISLAND_WORDS, 0, false);
_set_data_cb(&cblist[2], &dma_cfg[i], sym_hsync_on, (t->h_sync_width - W_DATA_ISLAND) / DVI_SYMBOLS_PER_WORD, 2, false);
_set_data_cb(&cblist[3], &dma_cfg[i], sym_hsync_off, t->h_back_porch / DVI_SYMBOLS_PER_WORD, 2, true);
_set_data_cb(&cblist[4], &dma_cfg[i], sym_hsync_off, t->h_active_pixels / DVI_SYMBOLS_PER_WORD, 2, false);
}
else
{
_set_data_cb(&cblist[0], &dma_cfg[i], sym_no_sync, (t->h_front_porch - W_PREAMBLE) / DVI_SYMBOLS_PER_WORD, 2, false);
_set_data_cb(&cblist[1], &dma_cfg[i], sym_preamble_to_data12, W_PREAMBLE / DVI_SYMBOLS_PER_WORD, 2, false);
_set_data_cb(&cblist[2], &dma_cfg[i], getDefaultDataPacket12(), N_DATA_ISLAND_WORDS, 0, false);
_set_data_cb(&cblist[3], &dma_cfg[i], sym_no_sync, (t->h_sync_width + t->h_back_porch - W_DATA_ISLAND) / DVI_SYMBOLS_PER_WORD, 2, false);
_set_data_cb(&cblist[4], &dma_cfg[i], sym_no_sync, t->h_active_pixels / DVI_SYMBOLS_PER_WORD, 2, false);
}
}
}

void dvi_setup_scanline_for_active(const struct dvi_timing *t, const struct dvi_lane_dma_cfg dma_cfg[],
uint32_t *tmdsbuf, struct dvi_scanline_dma_list *l) {
uint32_t *tmdsbuf, struct dvi_scanline_dma_list *l, bool black) {

const uint32_t *sym_hsync_off = get_ctrl_sym(!t->v_sync_polarity, !t->h_sync_polarity);
const uint32_t *sym_hsync_on = get_ctrl_sym(!t->v_sync_polarity, t->h_sync_polarity);
Expand All @@ -304,23 +350,74 @@ void dvi_setup_scanline_for_active(const struct dvi_timing *t, const struct dvi_
}
else {
// Use read ring to repeat the correct DC-balanced symbol pair on blank scanlines (4 or 8 byte period)
_set_data_cb(&cblist[target_block], &dma_cfg[i], &empty_scanline_tmds[2 * i / DVI_SYMBOLS_PER_WORD],
_set_data_cb(&cblist[target_block], &dma_cfg[i], &(black ? black_scanline_tmds : empty_scanline_tmds)[2 * i / DVI_SYMBOLS_PER_WORD],
t->h_active_pixels / DVI_SYMBOLS_PER_WORD, DVI_SYMBOLS_PER_WORD == 2 ? 2 : 3, false);
}
}
}

void __dvi_func(dvi_update_scanline_data_dma)(const struct dvi_timing *t, const uint32_t *tmdsbuf, struct dvi_scanline_dma_list *l) {
void dvi_setup_scanline_for_active_with_audio(const struct dvi_timing *t, const struct dvi_lane_dma_cfg dma_cfg[],
uint32_t *tmdsbuf, struct dvi_scanline_dma_list *l, bool black) {

const uint32_t *sym_hsync_off = get_ctrl_sym(!t->v_sync_polarity, !t->h_sync_polarity);
const uint32_t *sym_hsync_on = get_ctrl_sym(!t->v_sync_polarity, t->h_sync_polarity);
const uint32_t *sym_no_sync = get_ctrl_sym(false, false );
const uint32_t *sym_preamble_to_data12 = &dvi_ctrl_syms[1];
const uint32_t *sym_preamble_to_video1 = &dvi_ctrl_syms[1];
const uint32_t *sym_preamble_to_video2 = &dvi_ctrl_syms[0];
const uint32_t *data_packet0 = getDefaultDataPacket0(!t->v_sync_polarity, t->h_sync_polarity);

for (int i = 0; i < N_TMDS_LANES; ++i)
{
dma_cb_t *cblist = dvi_lane_from_list(l, i);

int active_block;
if (i == TMDS_SYNC_LANE)
{
_set_data_cb(&cblist[0], &dma_cfg[i], sym_hsync_off, t->h_front_porch / DVI_SYMBOLS_PER_WORD, 2, false);
_set_data_cb(&cblist[1], &dma_cfg[i], data_packet0, N_DATA_ISLAND_WORDS, 0, false);
_set_data_cb(&cblist[2], &dma_cfg[i], sym_hsync_on, (t->h_sync_width - W_DATA_ISLAND) / DVI_SYMBOLS_PER_WORD, 2, false);
_set_data_cb(&cblist[3], &dma_cfg[i], sym_hsync_off, (t->h_back_porch - W_GUARDBAND) / DVI_SYMBOLS_PER_WORD, 2, false);
_set_data_cb(&cblist[4], &dma_cfg[i], &video_gaurdband_syms[0], W_GUARDBAND / DVI_SYMBOLS_PER_WORD, 2, true);
active_block = 5;
}
else
{
_set_data_cb(&cblist[0], &dma_cfg[i], sym_no_sync, (t->h_front_porch - W_PREAMBLE) / DVI_SYMBOLS_PER_WORD, 2, false);
_set_data_cb(&cblist[1], &dma_cfg[i], sym_preamble_to_data12, W_PREAMBLE / DVI_SYMBOLS_PER_WORD, 2, false);
_set_data_cb(&cblist[2], &dma_cfg[i], getDefaultDataPacket12(), N_DATA_ISLAND_WORDS, 0, false);
_set_data_cb(&cblist[3], &dma_cfg[i], sym_no_sync, (t->h_sync_width + t->h_back_porch - W_DATA_ISLAND - W_PREAMBLE - W_GUARDBAND) / DVI_SYMBOLS_PER_WORD, 2, false);
_set_data_cb(&cblist[4], &dma_cfg[i], i == 1 ? sym_preamble_to_video1 : sym_preamble_to_video2, W_PREAMBLE / DVI_SYMBOLS_PER_WORD, 2, false);
_set_data_cb(&cblist[5], &dma_cfg[i], &video_gaurdband_syms[i], W_GUARDBAND / DVI_SYMBOLS_PER_WORD, 2, false);
active_block = 6;
}

if (tmdsbuf)
{
// Non-repeating DMA for the freshly-encoded TMDS buffer
_set_data_cb(&cblist[active_block], &dma_cfg[i], tmdsbuf + i * (t->h_active_pixels / DVI_SYMBOLS_PER_WORD),
t->h_active_pixels / DVI_SYMBOLS_PER_WORD, 0, false);
}
else
{
// Use read ring to repeat the correct DC-balanced symbol pair on blank scanlines (4 or 8 byte period)
_set_data_cb(&cblist[active_block], &dma_cfg[i], &(black ? black_scanline_tmds : empty_scanline_tmds)[2 * i / DVI_SYMBOLS_PER_WORD],
t->h_active_pixels / DVI_SYMBOLS_PER_WORD, DVI_SYMBOLS_PER_WORD == 2 ? 2 : 3, false);
}
}
}

void __dvi_func(dvi_update_scanline_data_dma)(const struct dvi_timing *t, const uint32_t *tmdsbuf, struct dvi_scanline_dma_list *l, bool audio) {
for (int i = 0; i < N_TMDS_LANES; ++i) {
#if DVI_MONOCHROME_TMDS
const uint32_t *lane_tmdsbuf = tmdsbuf;
#else
const uint32_t *lane_tmdsbuf = tmdsbuf + i * t->h_active_pixels / DVI_SYMBOLS_PER_WORD;
#endif
if (i == TMDS_SYNC_LANE)
dvi_lane_from_list(l, i)[3].read_addr = lane_tmdsbuf;
dvi_lane_from_list(l, i)[audio ? 5 : 3].read_addr = lane_tmdsbuf;
else
dvi_lane_from_list(l, i)[1].read_addr = lane_tmdsbuf;
dvi_lane_from_list(l, i)[audio ? 6 : 1].read_addr = lane_tmdsbuf;
}
}

Expand Down
42 changes: 37 additions & 5 deletions software/libdvi/dvi_timing.h
Expand Up @@ -30,6 +30,29 @@ enum dvi_line_state {
DVI_STATE_COUNT
};

enum dvi_sync_lane_state
{
DVI_SYNC_LANE_STATE_FRONT_PORCH,
DVI_SYNC_LANE_STATE_SYNC_DATA_ISLAND, // leading guardband, header, trailing guardband
DVI_SYNC_LANE_STATE_SYNC,
DVI_SYNC_LANE_STATE_BACK_PORCH,
DVI_SYNC_LANE_STATE_VIDEO_GUARDBAND,
DVI_SYNC_LANE_STATE_VIDEO,
DVI_SYNC_LANE_STATE_COUNT,
};

enum dvi_nosync_lane_state
{
DVI_NOSYNC_LANE_STATE_CTL0,
DVI_NOSYNC_LANE_STATE_PREAMBLE_TO_DATA,
DVI_NOSYNC_LANE_STATE_DATA_ISLAND, // leading guardband, packet, trailing guardband
DVI_NOSYNC_LANE_STATE_CTL1,
DVI_NOSYNC_LANE_STATE_PREAMBLE_TO_VIDEO,
DVI_NOSYNC_LANE_STATE_VIDEO_GUARDBAND,
DVI_NOSYNC_LANE_STATE_VIDEO,
DVI_NOSYNC_LANE_STATE_COUNT,
};

typedef struct dvi_blank {
int left;
int right;
Expand Down Expand Up @@ -58,10 +81,13 @@ static_assert(__builtin_offsetof(dma_cb_t, c.ctrl) == __builtin_offsetof(dma_cha
#define DVI_SYNC_LANE_CHUNKS DVI_STATE_COUNT
#define DVI_NOSYNC_LANE_CHUNKS 2

#define DVI_SYNC_LANE_CHUNKS_WITH_AUDIO DVI_SYNC_LANE_STATE_COUNT
#define DVI_NOSYNC_LANE_CHUNKS_WITH_AUDIO DVI_NOSYNC_LANE_STATE_COUNT

struct dvi_scanline_dma_list {
dma_cb_t l0[DVI_SYNC_LANE_CHUNKS];
dma_cb_t l1[DVI_NOSYNC_LANE_CHUNKS];
dma_cb_t l2[DVI_NOSYNC_LANE_CHUNKS];
dma_cb_t l0[DVI_SYNC_LANE_CHUNKS_WITH_AUDIO];
dma_cb_t l1[DVI_NOSYNC_LANE_CHUNKS_WITH_AUDIO];
dma_cb_t l2[DVI_NOSYNC_LANE_CHUNKS_WITH_AUDIO];
};

static inline dma_cb_t* dvi_lane_from_list(struct dvi_scanline_dma_list *l, int i) {
Expand Down Expand Up @@ -99,9 +125,15 @@ void dvi_setup_scanline_for_vblank(const struct dvi_timing *t, const struct dvi_
bool vsync_asserted, struct dvi_scanline_dma_list *l);

void dvi_setup_scanline_for_active(const struct dvi_timing *t, const struct dvi_lane_dma_cfg dma_cfg[],
uint32_t *tmdsbuf, struct dvi_scanline_dma_list *l);
uint32_t *tmdsbuf, struct dvi_scanline_dma_list *l, bool black);

void dvi_setup_scanline_for_vblank_with_audio(const struct dvi_timing *t, const struct dvi_lane_dma_cfg dma_cfg[],
bool vsync_asserted, struct dvi_scanline_dma_list *l);

void dvi_setup_scanline_for_active_with_audio(const struct dvi_timing *t, const struct dvi_lane_dma_cfg dma_cfg[],
uint32_t *tmdsbuf, struct dvi_scanline_dma_list *l, bool black);

void dvi_update_scanline_data_dma(const struct dvi_timing *t, const uint32_t *tmdsbuf, struct dvi_scanline_dma_list *l);
void dvi_update_scanline_data_dma(const struct dvi_timing *t, const uint32_t *tmdsbuf, struct dvi_scanline_dma_list *l, bool audio);

inline uint32_t dvi_timing_get_pixel_clock(const struct dvi_timing *t) { return t->bit_clk_khz * 100; }
uint32_t dvi_timing_get_pixels_per_frame(const struct dvi_timing *t);
Expand Down