rt-thread/bsp/lpc55sxx/Libraries/drivers/drv_i2s.c

302 lines
8.6 KiB
C

/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-03-12 Vandoul the first version
*/
#include <rtthread.h>
#include "fsl_i2s.h"
#include "fsl_i2s_dma.h"
#include "drv_i2s.h"
#define DBG_TAG "DRVI2S"
#include "rtdbg.h"
#ifdef BSP_USING_I2S
#if !defined(BSP_USING_I2S0) && \
!defined(BSP_USING_I2S1) && \
!defined(BSP_USING_I2S2) && \
!defined(BSP_USING_I2S3) && \
!defined(BSP_USING_I2S4) && \
!defined(BSP_USING_I2S5) && \
!defined(BSP_USING_I2S6) && \
!defined(BSP_USING_I2S7)
#error "Please define at least one I2Sx"
#endif
#include <rtdevice.h>
enum {
#ifdef BSP_USING_I2S0
I2S0_INDEX,
#endif
#ifdef BSP_USING_I2S1
I2S1_INDEX,
#endif
#ifdef BSP_USING_I2S2
I2S2_INDEX,
#endif
#ifdef BSP_USING_I2S3
I2S3_INDEX,
#endif
#ifdef BSP_USING_I2S4
I2S4_INDEX,
#endif
#ifdef BSP_USING_I2S5
I2S5_INDEX,
#endif
#ifdef BSP_USING_I2S6
I2S6_INDEX,
#endif
#ifdef BSP_USING_I2S7
I2S7_INDEX,
#endif
};
struct lpc_i2s_clock_and_irq_param
{
clock_attach_id_t i2s_clock;
reset_ip_name_t i2s_reset_bit;
IRQn_Type irq_type;
};
struct lpc_i2s
{
struct rt_device device;
i2s_handle_t i2s_handle;
struct lpc_i2s_config config;
uint32_t index;
I2S_Type *i2s_base;
const char *device_name;
};
#define LPC_I2S_CONFIG_MODE_IS_SLAVE(dev) ((dev)->config.mode == LPC_I2S_CONFIG_MODE_SLAVE)
#define LPC_I2S_CONFIG_MODE_IS_MASTER(dev) ((dev)->config.mode == LPC_I2S_CONFIG_MODE_MASTER)
#define LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(index) {.i2s_clock = kPLL0_DIV_to_FLEXCOMM##index, .i2s_reset_bit = kFC##index##_RST_SHIFT_RSTn, .irq_type = FLEXCOMM##index##_IRQn,}
const static struct lpc_i2s_clock_and_irq_param lpc_i2s_clock_and_irq_param_table[] =
{
#ifdef BSP_USING_I2S0
// {.i2s_clock = kPLL0_DIV_to_FLEXCOMM0, .i2s_reset_bit = kFC0_RST_SHIFT_RSTn, .irq_type = FLEXCOMM0_IRQn,},
LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(0),
#endif
#ifdef BSP_USING_I2S1
LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(1),
#endif
#ifdef BSP_USING_I2S2
LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(2),
#endif
#ifdef BSP_USING_I2S3
LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(3),
#endif
#ifdef BSP_USING_I2S4
LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(4),
#endif
#ifdef BSP_USING_I2S5
LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(5),
#endif
#ifdef BSP_USING_I2S6
LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(6),
#endif
#ifdef BSP_USING_I2S7
LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(7),
#endif
};
static struct lpc_i2s lpc_i2s_table[] =
{
#ifdef BSP_USING_I2S0
{.index = I2S0_INDEX,.i2s_base = I2S0,.device_name = "i2s0"},
#endif
#ifdef BSP_USING_I2S1
{.index = I2S1_INDEX,.i2s_base = I2S1,.device_name = "i2s1"},
#endif
#ifdef BSP_USING_I2S2
{.index = I2S2_INDEX,.i2s_base = I2S2,.device_name = "i2s2"},
#endif
#ifdef BSP_USING_I2S3
{.index = I2S3_INDEX,.i2s_base = I2S3,.device_name = "i2s3"},
#endif
#ifdef BSP_USING_I2S4
{.index = I2S4_INDEX,.i2s_base = I2S4,.device_name = "i2s4"},
#endif
#ifdef BSP_USING_I2S5
{.index = I2S5_INDEX,.i2s_base = I2S5,.device_name = "i2s5"},
#endif
#ifdef BSP_USING_I2S6
{.index = I2S6_INDEX,.i2s_base = I2S6,.device_name = "i2s6"},
#endif
#ifdef BSP_USING_I2S7
{.index = I2S7_INDEX,.i2s_base = I2S7,.device_name = "i2s7"},
#endif
};
static void transfer_callback(I2S_Type *base, i2s_handle_t *handle, status_t completionStatus, void *userData)
{
struct lpc_i2s *i2s_dev = rt_container_of(handle, struct lpc_i2s, i2s_handle);
if(LPC_I2S_CONFIG_MODE_IS_SLAVE(i2s_dev))
{
if(i2s_dev->device.rx_indicate != RT_NULL)
{
i2s_dev->device.rx_indicate(&i2s_dev->device, completionStatus);
}
}
else
{
if(i2s_dev->device.tx_complete != RT_NULL)
{
i2s_dev->device.tx_complete(&i2s_dev->device, RT_NULL);
}
}
}
static void i2s_clock_and_irq_config(struct lpc_i2s *dev)
{
const struct lpc_i2s_clock_and_irq_param *clock_and_irq_param = &lpc_i2s_clock_and_irq_param_table[dev->index];
// CLOCK_SetClkDiv(kCLOCK_DivPll0Clk, 0U, true);
// CLOCK_SetClkDiv(kCLOCK_DivPll0Clk, 1U, false);
CLOCK_AttachClk(clock_and_irq_param->i2s_clock);
RESET_PeripheralReset(clock_and_irq_param->i2s_reset_bit);
NVIC_ClearPendingIRQ(clock_and_irq_param->irq_type);
/* Enable interrupts for I2S */
EnableIRQ(clock_and_irq_param->irq_type);
}
rt_err_t rt_i2s_init(rt_device_t dev)
{
struct lpc_i2s *i2s_dev = rt_container_of(dev, struct lpc_i2s, device);
i2s_clock_and_irq_config(i2s_dev);
return RT_EOK;
}
rt_err_t rt_i2s_open(rt_device_t dev, rt_uint16_t oflag)
{
struct lpc_i2s *i2s_dev = rt_container_of(dev, struct lpc_i2s, device);
i2s_config_t config;
if(i2s_dev->config.mode == LPC_I2S_CONFIG_MODE_SLAVE)
{
RT_ASSERT(i2s_dev->config.is_blocking == 0);
I2S_RxGetDefaultConfig(&config);
config.divider = CLOCK_GetPll0OutFreq()/i2s_dev->config.sampling_rate/i2s_dev->config.data_bits/i2s_dev->config.channels;
config.masterSlave = kI2S_MasterSlaveNormalSlave;
I2S_RxInit(i2s_dev->i2s_base, &config);
I2S_RxTransferCreateHandle(i2s_dev->i2s_base, &i2s_dev->i2s_handle, transfer_callback, NULL);
}
else if(i2s_dev->config.mode == LPC_I2S_CONFIG_MODE_MASTER)
{
RT_ASSERT(i2s_dev->config.is_blocking == 0);
I2S_TxGetDefaultConfig(&config);
config.divider = CLOCK_GetPll0OutFreq()/i2s_dev->config.sampling_rate/i2s_dev->config.data_bits/i2s_dev->config.channels;
config.masterSlave = kI2S_MasterSlaveNormalMaster;
I2S_TxInit(i2s_dev->i2s_base, &config);
I2S_TxTransferCreateHandle(i2s_dev->i2s_base, &i2s_dev->i2s_handle, transfer_callback, NULL);
}
return RT_EOK;
}
rt_err_t rt_i2s_close(rt_device_t dev)
{
struct lpc_i2s *i2s_dev = rt_container_of(dev, struct lpc_i2s, device);
I2S_Deinit(i2s_dev->i2s_base);
return RT_EOK;
}
rt_ssize_t rt_i2s_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
struct lpc_i2s *i2s_dev = rt_container_of(dev, struct lpc_i2s, device);
if(!LPC_I2S_CONFIG_MODE_IS_SLAVE(i2s_dev))
{
return -RT_ERROR;
}
i2s_transfer_t transfer;
transfer.data = buffer;
transfer.dataSize = size;
if(kStatus_Success == I2S_RxTransferNonBlocking(i2s_dev->i2s_base, &i2s_dev->i2s_handle, transfer))
return size;
else
return -RT_EBUSY;
}
rt_ssize_t rt_i2s_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
struct lpc_i2s *i2s_dev = rt_container_of(dev, struct lpc_i2s, device);
if(!LPC_I2S_CONFIG_MODE_IS_MASTER(i2s_dev))
{
return -RT_ERROR;
}
i2s_transfer_t transfer;
transfer.data = (uint8_t *)buffer;
transfer.dataSize = size;
if(kStatus_Success == I2S_TxTransferNonBlocking(i2s_dev->i2s_base, &i2s_dev->i2s_handle, transfer))
return size;
else
return -RT_EBUSY;
}
rt_err_t rt_i2s_control(rt_device_t dev, int cmd, void *args)
{
struct lpc_i2s *i2s_dev = rt_container_of(dev, struct lpc_i2s, device);
rt_err_t ret = RT_EOK;
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(args != RT_NULL);
switch(cmd)
{
case RT_I2S_CTRL_RESET:
i2s_clock_and_irq_config(i2s_dev);
break;
case RT_I2S_CTRL_SET_CONFIG:
{
struct lpc_i2s_config *config = (struct lpc_i2s_config *)args;
i2s_dev->config = *config;
}
break;
default:
ret = -RT_ERROR;
break;
}
return ret;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops i2s_core_ops =
{
rt_i2s_init,
rt_i2s_open,
rt_i2s_close,
rt_i2s_read,
rt_i2s_write,
rt_i2s_control,
};
#endif /* RT_USING_DEVICE_OPS */
int rt_hw_i2s_init(void)
{
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
int i;
for (i = 0; i < sizeof(lpc_i2s_table) / sizeof(lpc_i2s_table[0]); i++)
{
#ifdef RT_USING_DEVICE_OPS
lpc_i2s_table[i].device.ops = &i2s_core_ops;
#else
lpc_i2s_table[i].device.init = rt_i2s_init;
lpc_i2s_table[i].device.open = rt_i2s_open;
lpc_i2s_table[i].device.close = rt_i2s_close;
lpc_i2s_table[i].device.read = rt_i2s_read;
lpc_i2s_table[i].device.write = rt_i2s_write;
lpc_i2s_table[i].device.control = rt_i2s_control;
#endif
/* register UART device */
rt_device_register(&lpc_i2s_table[i].device,
lpc_i2s_table[i].device_name,
RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
}
return 0;
}
INIT_BOARD_EXPORT(rt_hw_i2s_init);
#endif