arm/rp2040: Add RP2040 I2S driver

This commit is contained in:
Yuichi Nakamura 2021-04-18 19:05:37 +09:00 committed by Masayuki Ishikawa
parent 41b193e07f
commit 048802bcd2
6 changed files with 1998 additions and 0 deletions

View File

@ -141,6 +141,49 @@ config RP2040_I2C_DRIVER
endif
config RP2040_I2S
bool "I2S"
select I2S
if RP2040_I2S
config RP2040_I2S_MAXINFLIGHT
int "I2S queue size"
default 16
---help---
This is the total number of transfers that can be enqueue before
the caller is required to wait. This setting determines the number
certain queue data structures that will be pre-allocated.
config RP2040_I2S_DATALEN
int "Data width (bits)"
default 16
---help---
Data width in bits. This is a default value and may be change
via the I2S interface
config RP2040_I2S_DATA
int "I2S DATA GPIO pin assign (0-29)"
default 9
range 0 29
config RP2040_I2S_CLOCK
int "I2S CLOCk GPIO pin assign (0-29)"
default 10
range 0 29
config RP2040_I2S_PIO
int "RP2040 PIO number used for I2S (0-1)"
default 0
range 0 1
config RP2040_I2S_PIO_SM
int "RP2040 PIO state machine number used for I2S (0-3)"
default 0
range 0 3
endif
menuconfig RP2040_SPISD
bool "SPI SD Card"
default n

View File

@ -80,6 +80,11 @@ ifeq ($(CONFIG_RP2040_I2C),y)
CHIP_CSRCS += rp2040_i2c.c
endif
ifeq ($(CONFIG_RP2040_I2S),y)
CHIP_CSRCS += rp2040_i2s.c
CHIP_CSRCS += rp2040_i2s_pio.c
endif
ifeq ($(CONFIG_RP2040_FLASH_BOOT),y)
ifneq ($(PICO_SDK_PATH),)
include chip/boot2/Make.defs

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,74 @@
/****************************************************************************
* arch/arm/src/rp2040/rp2040_i2s.h
*
* 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.
*
****************************************************************************/
#ifndef __ARCH_ARM_SRC_RP2040_RP2040_I2S_H
#define __ARCH_ARM_SRC_RP2040_RP2040_I2S_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/audio/i2s.h>
#include "chip.h"
#ifndef __ASSEMBLY__
/****************************************************************************
* Public Data
****************************************************************************/
#undef EXTERN
#if defined(__cplusplus)
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: rp2040_i2sbus_initialize
*
* Description:
* Initialize the selected I2S port
*
* Input Parameters:
* Port number (for hardware that has multiple I2S interfaces)
*
* Returned Value:
* Valid I2S device structure reference on success; a NULL on failure
*
****************************************************************************/
FAR struct i2s_dev_s *rp2040_i2sbus_initialize(int port);
#undef EXTERN
#if defined(__cplusplus)
}
#endif
#endif /* __ASSEMBLY__ */
#endif /* __ARCH_ARM_SRC_RP2040_RP2040_I2S_H */

View File

@ -0,0 +1,382 @@
/****************************************************************************
* arch/arm/src/rp2040/rp2040_i2s_pio.c
*
* 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;
}

View File

@ -0,0 +1,105 @@
/****************************************************************************
* arch/arm/src/rp2040/rp2040_i2s_pio.h
*
* 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.
*
****************************************************************************/
#ifndef __ARCH_ARM_SRC_RP2040_RP2040_I2S_PIO_H
#define __ARCH_ARM_SRC_RP2040_RP2040_I2S_PIO_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <stdint.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define RP2040_I2S_PIO_16BIT_STEREO 0
#define RP2040_I2S_PIO_16BIT_MONO 1
#define RP2040_I2S_PIO_8BIT_STEREO 2
#define RP2040_I2S_PIO_8BIT_MONO 3
#define RP2040_I2S_PIO_MAX_MODE 4
#ifndef __ASSEMBLY__
#undef EXTERN
#if defined(__cplusplus)
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: rp2040_i2s_pio_configure
*
* Description:
* Configure RP2040 PIO for I2S
*
****************************************************************************/
int rp2040_i2s_pio_configure(int mode, uint32_t samplerate);
/****************************************************************************
* Name: rp2040_i2s_pio_enable
*
* Description:
* Set enable I2S transfer
*
****************************************************************************/
void rp2040_i2s_pio_enable(bool enable);
/****************************************************************************
* Name: rp2040_i2s_pio_getdmaaddr
*
* Description:
* Get DMA peripheral address for I2S transfer
*
****************************************************************************/
uintptr_t rp2040_i2s_pio_getdmaaddr(void);
/****************************************************************************
* Name: rp2040_i2s_pio_getdmaaddr
*
* Description:
* Get DREQ number for I2S transfer
*
****************************************************************************/
uint8_t rp2040_i2s_pio_getdreq(void);
#undef EXTERN
#if defined(__cplusplus)
}
#endif
#endif /* __ASSEMBLY__ */
#endif /* __ARCH_ARM_SRC_RP2040_RP2040_I2S_PIO_H */