Files
Linus Torvalds 3da719e24b Start doing stereo i2s transfers
This keeps the i2s_dma_buf as a 128-byte ring buffer, but instead of
being organized as 32 words of 32 bits each, it introduces the notion of a

   typedef struct {
	s32 left, right;
   } raw_sample_t;

and makes the DMA ring-buffer be 16 of these entries instead.

The USB output buffer has also been changed to match this sample model,
and renamed to better reflect the fact that it's for USB.

The i2s PIO program is changed to use auto-pull to make it a bit more
streamlined.  It could also have kept doing the explicit 'pull'
instructions, but that would have required another non-looping cycle to
set up.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2026-07-22 17:19:12 -07:00

110 lines
2.8 KiB
Plaintext

;
; We want a target sample frequency of 48kHz, which
; with 32-bit i2s data means a dot clock of 3.072MHz.
;
; We clock the rp2354 to 153.6MHz, which is exactly
; 50x that dot clock frequency.
;
; We then use a PIO divider of 25, and make the PIO
; program do 2 cycles per clock.
;
; i2s: FSYNC and each data bit is transmitted on the
; falling edge of BCLK, so we set those bits the cycle
; when BCLK goes low ("side 0") so that they are stable
; on the rising edge ("side 1").
;
; The actual data is transmitted one cycle after FSYNC
; goes low
;
.pio_version 1
.program i2s_tx
.fifo tx
.clock_div 25
.side_set 2
.wrap_target
out pins, 1 side 0b00 ; Cycle 0: FSYNC low. Output R0 (from previous frame)
public start_tx:
set x, 30 side 0b01
loop_low:
out pins, 1 side 0b00 ; Cycle 2: Output L31..L1
jmp x-- loop_low side 0b01
out pins, 1 side 0b10 ; Cycle 64: FSYNC high. Output L0
set x, 30 side 0b11
loop_high:
out pins, 1 side 0b10 ; Cycle 66: Output R31..R1
jmp x-- loop_high side 0b11
.wrap
% c-sdk {
//
// 'pin' is BCLK (MCU output)
// 'pin+1' is FSYNC (MCU output)
// 'pin+2' is DIN (MCU output! It's DIN from the CODEC standpoint!)
// 'pin+3' is DOUT (MCU input! It's DOUT from the CODEC standpoint!)
//
static inline void i2s_tx_program_init(PIO pio, uint sm, uint offset, uint pin)
{
pio_sm_config c = i2s_tx_program_get_default_config(offset);
pio_gpio_init(pio, pin);
pio_gpio_init(pio, pin+1);
pio_gpio_init(pio, pin+2);
pio_gpio_init(pio, pin+3);
// three output pins, one input
pio_sm_set_pindirs_with_mask(pio, sm, 0b0111 << pin, 0b1111 << pin);
// BCLK and FSYNC using sideset
sm_config_set_sideset_pin_base(&c, pin);
// DOUT using 'OUT' instruction
sm_config_set_out_pins(&c, pin+2, 1);
// Auto-pull, shift left (MSB first), 32 bits
sm_config_set_out_shift(&c, false, true, 32);
// Load and run the program
pio_sm_init(pio, sm, offset + i2s_tx_offset_start_tx, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}
.program i2s_rx
.fifo rx
.clock_div 25
public start_rx:
wait 1 jmppin ; wait for FSYNC going high
wait 0 jmppin [1] ; wait for FSYNC going low, delay to sample middle
.wrap_target
set x, 30
loop_left:
in pins, 1
jmp x-- loop_left
in pins, 1 ; sample LSB of left channel
set x, 30
loop_right:
in pins, 1
jmp x-- loop_right
in pins, 1 ; sample LSB of right channel
.wrap
% c-sdk {
static inline void i2s_rx_program_init(PIO pio, uint sm, uint offset, uint pin)
{
pio_sm_config c = i2s_rx_program_get_default_config(offset);
sm_config_set_jmp_pin(&c, pin+1); // FSYNC
sm_config_set_in_pins(&c, pin+3); // DOUT
// Auto-push, shift left (MSB first), 32 bits
sm_config_set_in_shift(&c, false, true, 32);
// Load and run the program
pio_sm_init(pio, sm, offset + i2s_rx_offset_start_rx, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}