Skip to content

Commit

Permalink
Merge pull request Wren6991#1 from mlorenzati/fix/overrun
Browse files Browse the repository at this point in the history
fixed to work
  • Loading branch information
shuichitakano authored Mar 20, 2023
2 parents 752dafb + 428554d commit 1088cdb
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 34 deletions.
3 changes: 1 addition & 2 deletions software/libdvi/audio_ring.c
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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)
#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
Original file line number Diff line number Diff line change
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);
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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

0 comments on commit 1088cdb

Please sign in to comment.