blob: 41fb628c7cd40edfa7c8e6cb31e0e42b27359439 [file] [log] [blame]
/****************************************************************************
* arch/arm/src/rp2040/rp2040_i2s_pio.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <arch/board/board.h>
#include "rp2040_i2s_pio.h"
#include "rp2040_pio.h"
#include "rp2040_pio_instructions.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
#ifndef CONFIG_RP2040_I2S_PIO
#define CONFIG_RP2040_I2S_PIO 0
#endif
#ifndef CONFIG_RP2040_I2S_PIO_SM
#define CONFIG_RP2040_I2S_PIO_SM 0
#endif
/****************************************************************************
* Private Types
****************************************************************************/
struct rp2040_i2s_pio_config
{
const rp2040_pio_program_t program;
uint32_t entry;
uint32_t wrap_target;
uint32_t wrap;
bool autopull;
uint32_t clocks;
};
/****************************************************************************
* Private Data
****************************************************************************/
/* PIO program for 16bit stereo I2S transfer */
static const uint16_t pio_program_i2s_16s[] =
{
/* .wrap_target */
0x6870, /* 0: out null, 16 side 1 */
0x6001, /* 1: out pins, 1 side 0 */
0xe82d, /* 2: set x, 13 side 1 */
0x6001, /* 3: out pins, 1 side 0 */
0x0843, /* 4: jmp x--, 3 side 1 */
0x7001, /* 5: out pins, 1 side 2 */
0x7870, /* 6: out null, 16 side 3 */
0x7001, /* 7: out pins, 1 side 2 */
0xf82d, /* 8: set x, 13 side 3 */
0x7001, /* 9: out pins, 1 side 2 */
0x1849, /* 10: jmp x--, 9 side 3 */
0x6001, /* 11: out pins, 1 side 0 */
/* .wrap */
};
/* PIO program for 16bit mono I2S transfer */
static const uint16_t pio_program_i2s_16m[] =
{
/* .wrap_target */
0x80a0, /* 0: pull block side 0 */
0x6870, /* 1: out null, 16 side 1 */
0xa847, /* 2: mov y, osr side 1 */
0x6101, /* 3: out pins, 1 side 0 [1] */
0xe92d, /* 4: set x, 13 side 1 [1] */
0x6101, /* 5: out pins, 1 side 0 [1] */
0x0945, /* 6: jmp x--, 5 side 1 [1] */
0x7101, /* 7: out pins, 1 side 2 [1] */
0xb9e2, /* 8: mov osr, y side 3 [1] */
0x7101, /* 9: out pins, 1 side 2 [1] */
0xf92d, /* 10: set x, 13 side 3 [1] */
0x7101, /* 11: out pins, 1 side 2 [1] */
0x194b, /* 12: jmp x--, 11 side 3 [1] */
0x6001, /* 13: out pins, 1 side 0 */
/* .wrap */
};
/* PIO program for 8bit stereo I2S transfer */
static const uint16_t pio_program_i2s_8s[] =
{
/* .wrap_target */
0x80a0, /* 0: pull block side 0 */
0x6078, /* 1: out null, 24 side 0 */
0xa9ef, /* 2: mov osr, !osr side 1 [1] */
0x6101, /* 3: out pins, 1 side 0 [1] */
0xa8ef, /* 4: mov osr, !osr side 1 */
0xe826, /* 5: set x, 6 side 1 */
0x6101, /* 6: out pins, 1 side 0 [1] */
0x0946, /* 7: jmp x--, 6 side 1 [1] */
0xe100, /* 8: set pins, 0 side 0 [1] */
0xe925, /* 9: set x, 5 side 1 [1] */
0xa142, /* 10: nop side 0 [1] */
0x094a, /* 11: jmp x--, 10 side 1 [1] */
0x90a0, /* 12: pull block side 2 */
0x7078, /* 13: out null, 24 side 2 */
0xb9ef, /* 14: mov osr, !osr side 3 [1] */
0x7101, /* 15: out pins, 1 side 2 [1] */
0xb8ef, /* 16: mov osr, !osr side 3 */
0xf826, /* 17: set x, 6 side 3 */
0x7101, /* 18: out pins, 1 side 2 [1] */
0x1952, /* 19: jmp x--, 18 side 3 [1] */
0xf100, /* 20: set pins, 0 side 2 [1] */
0xf925, /* 21: set x, 5 side 3 [1] */
0xb142, /* 22: nop side 2 [1] */
0x1956, /* 23: jmp x--, 22 side 3 [1] */
/* .wrap */
};
/* PIO program for 8bit mono I2S transfer */
static const uint16_t pio_program_i2s_8m[] =
{
/* .wrap_target */
0x80a0, /* 0: pull block side 0 */
0x6078, /* 1: out null, 24 side 0 */
0xa8ef, /* 2: mov osr, !osr side 1 */
0xa847, /* 3: mov y, osr side 1 */
0x6101, /* 4: out pins, 1 side 0 [1] */
0xa8ef, /* 5: mov osr, !osr side 1 */
0xe826, /* 6: set x, 6 side 1 */
0x6101, /* 7: out pins, 1 side 0 [1] */
0x0947, /* 8: jmp x--, 7 side 1 [1] */
0xe100, /* 9: set pins, 0 side 0 [1] */
0xe925, /* 10: set x, 5 side 1 [1] */
0xa142, /* 11: nop side 0 [1] */
0x094b, /* 12: jmp x--, 11 side 1 [1] */
0xb142, /* 13: nop side 2 [1] */
0xb9e2, /* 14: mov osr, y side 3 [1] */
0x7101, /* 15: out pins, 1 side 2 [1] */
0xb8ef, /* 16: mov osr, !osr side 3 */
0xf826, /* 17: set x, 6 side 3 */
0x7101, /* 18: out pins, 1 side 2 [1] */
0x1952, /* 19: jmp x--, 18 side 3 [1] */
0xf100, /* 20: set pins, 0 side 2 [1] */
0xf925, /* 21: set x, 5 side 3 [1] */
0xb142, /* 22: nop side 2 [1] */
0x1956, /* 23: jmp x--, 22 side 3 [1] */
/* .wrap */
};
/* PIO configuration table */
static const struct rp2040_i2s_pio_config g_pio_i2s_configs[] =
{
[RP2040_I2S_PIO_16BIT_STEREO] =
{
{
pio_program_i2s_16s,
sizeof(pio_program_i2s_16s) / sizeof(uint16_t),
-1
},
0, 0, 11,
true, 16 * 2 * 2
},
[RP2040_I2S_PIO_16BIT_MONO] =
{
{
pio_program_i2s_16m,
sizeof(pio_program_i2s_16m) / sizeof(uint16_t),
-1
},
0, 0, 13,
false, 16 * 2 * 4
},
[RP2040_I2S_PIO_8BIT_STEREO] =
{
{
pio_program_i2s_8s,
sizeof(pio_program_i2s_8s) / sizeof(uint16_t),
-1
},
0, 0, 23,
false, 16 * 2 * 4
},
[RP2040_I2S_PIO_8BIT_MONO] =
{
{
pio_program_i2s_8m,
sizeof(pio_program_i2s_8m) / sizeof(uint16_t),
-1
},
0, 0, 23,
false, 16 * 2 * 4
}
};
static const uint32_t g_i2s_pio = CONFIG_RP2040_I2S_PIO;
static const uint32_t g_i2s_pio_sm = CONFIG_RP2040_I2S_PIO_SM;
/* PIO I2S status */
static int g_pio_current_mode = -1;
static uint32_t g_pio_current_samplerate;
static uint32_t g_pio_current_offset;
/****************************************************************************
* Private Functions
****************************************************************************/
static float get_clkdiv(int mode, uint32_t samplerate)
{
float div = (float)BOARD_SYS_FREQ /
(samplerate * g_pio_i2s_configs[mode].clocks);
return div;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: rp2040_i2s_pio_configure
*
* Description:
* Configure RP2040 PIO for I2S
*
****************************************************************************/
int rp2040_i2s_pio_configure(int mode, uint32_t samplerate)
{
const struct rp2040_i2s_pio_config *conf;
rp2040_pio_sm_config sm_config;
uint32_t data_pin = CONFIG_RP2040_I2S_DATA;
uint32_t clock_pin_base = CONFIG_RP2040_I2S_CLOCK;
uint32_t pin_mask = (1u << data_pin) | (3u << clock_pin_base);
/* Check parameters */
if (mode < 0 || mode >= RP2040_I2S_PIO_MAX_MODE ||
samplerate == 0)
{
return -1;
}
if (mode == g_pio_current_mode)
{
if (samplerate == g_pio_current_samplerate)
{
return 0;
}
else
{
/* Only changing the sampling rate */
rp2040_pio_sm_set_clkdiv(g_i2s_pio, g_i2s_pio_sm,
get_clkdiv(mode, samplerate));
rp2040_pio_sm_clkdiv_restart(g_i2s_pio, g_i2s_pio_sm);
return 0;
}
}
if (g_pio_current_mode < 0)
{
/* Claim to use PIO state machine for I2S */
rp2040_pio_sm_claim(g_i2s_pio, g_i2s_pio_sm);
}
else
{
/* Remove existing PIO program to change the I2S mode */
rp2040_pio_remove_program(CONFIG_RP2040_I2S_PIO,
&g_pio_i2s_configs[g_pio_current_mode].program,
g_pio_current_offset);
}
/* Program the PIO */
conf = &g_pio_i2s_configs[mode];
g_pio_current_offset = rp2040_pio_add_program(CONFIG_RP2040_I2S_PIO,
&conf->program);
g_pio_current_mode = mode;
/* Configure the state machine */
sm_config = rp2040_pio_get_default_sm_config();
rp2040_sm_config_set_wrap(&sm_config,
g_pio_current_offset + conf->wrap_target,
g_pio_current_offset + conf->wrap);
rp2040_sm_config_set_sideset(&sm_config, 2, false, false);
rp2040_sm_config_set_out_pins(&sm_config, data_pin, 1);
rp2040_sm_config_set_sideset_pins(&sm_config, clock_pin_base);
rp2040_sm_config_set_out_shift(&sm_config, false, conf->autopull, 32);
rp2040_sm_config_set_set_pins(&sm_config, data_pin, 1);
rp2040_sm_config_set_clkdiv(&sm_config, get_clkdiv(mode, samplerate));
rp2040_pio_sm_init(g_i2s_pio, g_i2s_pio_sm,
g_pio_current_offset, &sm_config);
rp2040_pio_sm_set_pindirs_with_mask(g_i2s_pio, g_i2s_pio_sm,
pin_mask, pin_mask);
rp2040_pio_sm_set_pins(g_i2s_pio, g_i2s_pio_sm, 1); /* clear pins */
rp2040_pio_sm_exec(g_i2s_pio, g_i2s_pio_sm,
pio_encode_jmp(g_pio_current_offset + conf->entry));
return 0;
}
/****************************************************************************
* Name: rp2040_i2s_pio_enable
*
* Description:
* Set enable I2S transfer
*
****************************************************************************/
void rp2040_i2s_pio_enable(bool enable)
{
rp2040_pio_sm_set_enabled(g_i2s_pio, g_i2s_pio_sm, enable);
}
/****************************************************************************
* Name: rp2040_i2s_pio_getdmaaddr
*
* Description:
* Get DMA peripheral address for I2S transfer
*
****************************************************************************/
uintptr_t rp2040_i2s_pio_getdmaaddr(void)
{
return RP2040_PIO_TXF(g_i2s_pio, g_i2s_pio_sm);
}
/****************************************************************************
* Name: rp2040_i2s_pio_getdmaaddr
*
* Description:
* Get DMA peripheral address for I2S transfer
*
****************************************************************************/
uint8_t rp2040_i2s_pio_getdreq(void)
{
return RP2040_DMA_DREQ_PIO0_TX0 + g_i2s_pio_sm + g_i2s_pio * 8;
}