;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;

; Transmit a mono or stereo I2S audio stream as stereo
; This is 16 bits per sample; can be altered by modifying the "set" params,
; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register.
;
; Autopull must be enabled, with threshold set to 32.
; Since I2S is MSB-first, shift direction should be to left.
; Hence the format of the FIFO word is:
;
; | 31   :   16 | 15   :    0 |
; | sample ws=0 | sample ws=1 |
;
; Data is output at 1 bit per clock. Use clock divider to adjust frequency.
; Fractional divider will probably be needed to get correct bit clock period,
; but for common syslck freqs this should still give a constant word select period.
;
; One output pin is used for the data output.
; Two side-set pins are used. Two versions of the program are provided, so that
; the clock and word select pins can be in either order.

; Send 16 bit words to the PIO for mono, 32 bit words for stereo

.program audio_i2s
.side_set 2

                    ;        /--- LRCLK
                    ;        |/-- BCLK
bitloop1:           ;        ||
    out pins, 1       side 0b10
    jmp x-- bitloop1  side 0b11
    out pins, 1       side 0b00
    set x, 14         side 0b01

bitloop0:
    out pins, 1       side 0b00
    jmp x-- bitloop0  side 0b01
    out pins, 1       side 0b10
public entry_point:
    set x, 14         side 0b11

.program audio_i2s_swapped
.side_set 2

                    ;        /--- BCLK
                    ;        |/-- LRCLK
bitloop1:           ;        ||
    out pins, 1       side 0b01
    jmp x-- bitloop1  side 0b11
    out pins, 1       side 0b00
    set x, 14         side 0b10
                               
bitloop0:                      
    out pins, 1       side 0b00
    jmp x-- bitloop0  side 0b10
    out pins, 1       side 0b01
public entry_point:            
    set x, 14         side 0b11

% c-sdk {

static inline void audio_i2s_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) {
    pio_sm_config sm_config = audio_i2s_program_get_default_config(offset);
    
    sm_config_set_out_pins(&sm_config, data_pin, 1);
    sm_config_set_sideset_pins(&sm_config, clock_pin_base);
    sm_config_set_out_shift(&sm_config, false, true, 32);
    sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX);

    pio_sm_init(pio, sm, offset, &sm_config);

#if PICO_PIO_USE_GPIO_BASE
    uint64_t pin_mask = (1ull << data_pin) | (3ull << clock_pin_base);
    pio_sm_set_pindirs_with_mask64(pio, sm, pin_mask, pin_mask);
#else
    uint32_t pin_mask = (1u << data_pin) | (3u << clock_pin_base);
    pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
#endif
    pio_sm_set_pins(pio, sm, 0); // clear pins

    pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_offset_entry_point));
}

%}