nuttx/arch/arm/src/lc823450/lc823450_i2s.c
Xiang Xiao 2e54df0f35 Don't include assert.h from public header file
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2021-06-03 08:36:03 -07:00

1096 lines
29 KiB
C

/****************************************************************************
* arch/arm/src/lc823450/lc823450_i2s.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 <arch/board/board.h>
#include <nuttx/config.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <inttypes.h>
#include <nuttx/sched.h>
#include <nuttx/arch.h>
#include <nuttx/signal.h>
#include <nuttx/kmalloc.h>
#include <nuttx/semaphore.h>
#include <nuttx/audio/audio.h>
#include <nuttx/audio/i2s.h>
#include "arm_arch.h"
#include "lc823450_dma.h"
#include "lc823450_i2s.h"
#include "lc823450_syscontrol.h"
#include "lc823450_clockconfig.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* #define SHOW_BUFFERING */
#define I2S_HAS_IOCTL
#define BUFID(n) (n - 'A')
#define LC823450_AUDIO_REGBASE 0x40060000
#define ABUF_REGBASE (LC823450_AUDIO_REGBASE + 0x0000)
#define SSRC_REGBASE (LC823450_AUDIO_REGBASE + 0x1000)
#define BEEP_REGBASE (LC823450_AUDIO_REGBASE + 0x1200)
#define DGMIC_REGBASE (LC823450_AUDIO_REGBASE + 0x1500)
#define PCKGEN_REGBASE (LC823450_AUDIO_REGBASE + 0x1600)
#define ASRC_REGBASE (LC823450_AUDIO_REGBASE + 0x1a00)
#define MP3DEC_REGBASE (LC823450_AUDIO_REGBASE + 0x2000)
#define AUDCTL_REGBASE (LC823450_AUDIO_REGBASE + 0x4000)
/* Audio Buffer */
#define ABUFCLR (ABUF_REGBASE + 0x0000)
#define ABUFACCEN (ABUF_REGBASE + 0x0004)
#define ABUFACCEN_RDCTEN(n) (1 << (BUFID(n) + 16))
#define ABUFACCEN_CDCEN(n) (1 << (BUFID(n) + 0))
#define ABUFIRQEN0 (ABUF_REGBASE + 0x0008)
#define ABUFIRQEN0_BOLIRQEN(n) (1 << (BUFID(n) + 16))
#define ABUFIRQEN0_BULIRQEN(n) (1 << (BUFID(n) + 0))
#define ABUFSTS1 (ABUF_REGBASE + 0x0034)
#define ABUFSTS1_BOLVL(n) (1 << (BUFID(n) + 16))
#define ABUFSTS1_BULVL(n) (1 << (BUFID(n) + 0))
#define BUF_BASE(n) (ABUF_REGBASE + 0x00c0 + (4 * BUFID(n)))
#define BUF_SIZE(n) (ABUF_REGBASE + 0x0100 + (4 * BUFID(n)))
#define BUF_ULVL(n) (ABUF_REGBASE + 0x0140 + (4 * BUFID(n)))
#define BUF_OLVL(n) (ABUF_REGBASE + 0x0180 + (4 * BUFID(n)))
#define BUF_DTCAP(n) (ABUF_REGBASE + 0x01c0 + (4 * BUFID(n)))
#define BUF_ACCESS(n) (ABUF_REGBASE + 0x0300 + (4 * BUFID(n)))
#define BUFCTL(n) (ABUF_REGBASE + 0x0080 + (4 * BUFID(n)))
#define BUFCTL_MONO (1 << 0)
/* SSRC */
#define SSRC_MODE (SSRC_REGBASE + 0x0000)
#define SSRC_FSI (SSRC_REGBASE + 0x0004)
#define SSRC_FSO (SSRC_REGBASE + 0x0008)
#define SSRC_SRESETB (SSRC_REGBASE + 0x0010)
#define SSRC_STATUS (SSRC_REGBASE + 0x0014)
/* Audio Control */
#define CLOCKEN (AUDCTL_REGBASE + 0x000)
#define CLOCKEN_FCE_ASRC (1 << 29)
#define CLOCKEN_FCE_PCKGEN (1 << 28)
#define CLOCKEN_FCE_PCMPS0 (1 << 17)
#define CLOCKEN_FCE_BEEP (1 << 16)
#define CLOCKEN_FCE_VOLPS0 (1 << 13)
#define CLOCKEN_FCE_VOLSP0 (1 << 11)
#define CLOCKEN_FCE_DGMIC (1 << 7)
#define CLOCKEN_FCE_VOLD (1 << 6)
#define CLOCKEN_FCE_SSRC (1 << 4)
#define CLOCKEN_FCE_MUTED (1 << 3)
#define CLOCKEN_FCE_MP3DEC (1 << 2)
#define AUDSEL (AUDCTL_REGBASE + 0x01c)
#define AUDSEL_PCM0_MODE (1 << 17)
#define AUDSEL_PCM0_MODEM (1 << 16)
#define AUDSEL_PCMSEL (1 << 4)
#define AUDSEL_DECSEL (1 << 0)
#define PSCTL (AUDCTL_REGBASE + 0x110)
/* Volume (SP0) */
#define VOLSP0_CONT (AUDCTL_REGBASE + 0x320)
#define VOL_CONT_DIRECT (1 << 20)
#define PCMOUTEN (AUDCTL_REGBASE + 0x500)
#define PCMOUTEN_DOUT0EN (1 << 3)
#define PCMOUTEN_LRCK0EN (1 << 2)
#define PCMOUTEN_MCLK0EN (1 << 1)
#define PCMOUTEN_BCK0EN (1 << 0)
#define PCMCTL (AUDCTL_REGBASE + 0x504)
/* BEEP */
#define BEEP_CTL (BEEP_REGBASE + 0x0000)
#define BEEP_BYPASS (BEEP_REGBASE + 0x0004)
#define BEEP_COEFF (BEEP_REGBASE + 0x0008)
#define BEEP_TIME (BEEP_REGBASE + 0x000c)
/* Audio PLL */
#define AUDIOPLL_REGBASE (LC823450_OSCSYS_REGBASE + 0x2000)
#define AUDPLLCNT (AUDIOPLL_REGBASE + 0x00)
#define AUDPLLMDIV (AUDIOPLL_REGBASE + 0x04)
#define AUDPLLNDIV (AUDIOPLL_REGBASE + 0x08)
#define DGMICCTL (DGMIC_REGBASE + 0x00)
#define ALCCTL (DGMIC_REGBASE + 0x30)
/* ASRC */
#define ASRC_MODE (ASRC_REGBASE + 0x0000)
#define ASRC_FSI (ASRC_REGBASE + 0x0004)
#define ASRC_FSO (ASRC_REGBASE + 0x0008)
#define ASRC_SRESETB (ASRC_REGBASE + 0x0010)
#define ASRC_STATUS (ASRC_REGBASE + 0x0014)
/* MP3 Decoder */
#define MP3DEC_ERR (MP3DEC_REGBASE + 0x08)
#define MP3DEC_BUFM (MP3DEC_REGBASE + 0x1c)
#define MP3DEC_ERRMODE (MP3DEC_REGBASE + 0x20)
/****************************************************************************
* Private Types
****************************************************************************/
/* The state of the one I2S peripheral */
struct lc823450_i2s_s
{
struct i2s_dev_s dev; /* Externally visible I2S interface */
};
static bool _b_input_started = false;
static uint32_t _i2s_tx_th_bytes;
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static uint32_t lc823450_i2s_rxsamplerate(struct i2s_dev_s *dev,
uint32_t rate);
static uint32_t lc823450_i2s_rxdatawidth(struct i2s_dev_s *dev, int bits);
static int lc823450_i2s_receive(struct i2s_dev_s *dev,
struct ap_buffer_s *apb,
i2s_callback_t callback, void *arg,
uint32_t timeout);
static uint32_t lc823450_i2s_txsamplerate(struct i2s_dev_s *dev,
uint32_t rate);
static uint32_t lc823450_i2s_txdatawidth(struct i2s_dev_s *dev, int bits);
static int lc823450_i2s_send(struct i2s_dev_s *dev,
struct ap_buffer_s *apb,
i2s_callback_t callback, void *arg,
uint32_t timeout);
static void lc823450_i2s_setchannel(char id, uint8_t ch);
static void lc823450_i2s_mp3dec(bool enable);
static int lc823450_i2s_ioctl(struct i2s_dev_s *dev,
int cmd, unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
/* I2S device operations */
static const struct i2s_ops_s g_i2sops =
{
/* Receiver methods */
.i2s_rxsamplerate = lc823450_i2s_rxsamplerate,
.i2s_rxdatawidth = lc823450_i2s_rxdatawidth,
.i2s_receive = lc823450_i2s_receive,
/* Transmitter methods */
.i2s_txsamplerate = lc823450_i2s_txsamplerate,
.i2s_txdatawidth = lc823450_i2s_txdatawidth,
.i2s_send = lc823450_i2s_send,
#ifdef I2S_HAS_IOCTL
/* Ioctl */
.i2s_ioctl = lc823450_i2s_ioctl,
#endif
};
static DMA_HANDLE _hrxdma;
static sem_t _sem_rxdma;
static sem_t _sem_buf_over;
static DMA_HANDLE _htxdma;
static sem_t _sem_txdma;
static sem_t _sem_buf_under;
/****************************************************************************
* Public Data
****************************************************************************/
extern unsigned int XT1OSC_CLK;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: _setup_audio_pll
****************************************************************************/
static void _setup_audio_pll(uint32_t freq)
{
DEBUGASSERT(24000000 == XT1OSC_CLK);
uint32_t m;
uint32_t n;
switch (freq)
{
case 44100:
m = 625;
n = 3528;
break;
case 48000:
m = 125;
n = 768;
break;
default:
DEBUGASSERT(false);
}
/* Set divider */
putreg32(n, AUDPLLNDIV);
putreg32(m, AUDPLLMDIV);
/* Audio PLL standby=off, Audio PLL unreset */
putreg32(0x0503, AUDPLLCNT);
/* TODO: Wait */
nxsig_usleep(50 * 1000);
/* Switch to the PLL */
modifyreg32(AUDCLKCNT,
0x0,
0x03 /* AUDCLKSEL=Audio PLL */
);
/* TODO: Clock divider settings */
modifyreg32(AUDCLKCNT,
0x0,
0x0200 /* AUDDIV=2 */
);
}
/****************************************************************************
* Name: _i2s_semtake
****************************************************************************/
static int _i2s_semtake(FAR sem_t *sem)
{
return nxsem_wait_uninterruptible(sem);
}
/****************************************************************************
* Name: lc823450_i2s_rxsamplerate
****************************************************************************/
static uint32_t lc823450_i2s_rxsamplerate(struct i2s_dev_s *dev,
uint32_t rate)
{
/* Change ASRC FSO rate */
/* Stop/Reset/Unreset ASRC */
putreg32(0, ASRC_MODE);
putreg32(0, ASRC_SRESETB);
putreg32(1, ASRC_SRESETB);
while (getreg32(ASRC_STATUS) & 0x1);
/* Setup FSO */
putreg32((rate) << 0, ASRC_FSO);
/* Restart ASRC */
putreg32(0x1, ASRC_MODE);
while (getreg32(ASRC_STATUS) != 0x1);
return 0;
}
/****************************************************************************
* Name: lc823450_i2s_rxdatawidth
****************************************************************************/
static uint32_t lc823450_i2s_rxdatawidth(struct i2s_dev_s *dev, int bits)
{
return 0;
}
/****************************************************************************
* Name: lc823450_i2s_setchannel
****************************************************************************/
static void lc823450_i2s_setchannel(char id, uint8_t ch)
{
switch (ch)
{
case 1:
modifyreg32(BUFCTL(id), 0, BUFCTL_MONO);
break;
case 2:
modifyreg32(BUFCTL(id), BUFCTL_MONO, 0);
break;
default:
DEBUGASSERT(false);
break;
}
modifyreg32(ABUFCLR, 0, BUFID(id));
}
/****************************************************************************
* Name: _setup_tx_threshold (threshold to start playback)
****************************************************************************/
static void _setup_tx_threshold(uint32_t tx_th)
{
if (0 == tx_th)
{
/* default tx threshould : 1024bytes */
_i2s_tx_th_bytes = 1024;
}
else
{
/* tx_th: 0 to 100 (%) */
_i2s_tx_th_bytes = getreg32(BUF_SIZE('C')) * tx_th / 100;
}
/* NOTE: Buffer Under Level is not controlled by tx threshold */
}
/****************************************************************************
* Name: lc823450_i2s_rxdatawidth
****************************************************************************/
static int lc823450_i2s_ioctl(struct i2s_dev_s *dev, int cmd,
unsigned long arg)
{
FAR const struct audio_caps_desc_s *cap_desc;
uint32_t tx_th;
uint32_t rate[2];
uint8_t ch[2];
uint8_t fmt[2];
switch (cmd)
{
case AUDIOIOC_CONFIGURE:
cap_desc = (FAR const struct audio_caps_desc_s *)((uintptr_t)arg);
DEBUGASSERT(NULL != cap_desc);
tx_th = cap_desc->caps.ac_controls.w >> 24;
rate[1] = cap_desc->caps.ac_controls.w & 0xfffff;
ch[1] = cap_desc->caps.ac_channels;
fmt[1] = cap_desc->caps.ac_format.hw;
if (cap_desc->caps.ac_type & AUDIO_TYPE_OUTPUT)
{
_setup_tx_threshold(tx_th);
rate[0] = getreg32(SSRC_FSI) >> 13;
ch[0] = (getreg32(BUFCTL('C')) & BUFCTL_MONO) ? 1 : 2;
fmt[0] = getreg32(AUDSEL) & AUDSEL_DECSEL ?
AUDIO_FMT_MP3 : AUDIO_FMT_PCM;
if (rate[0] != rate[1])
{
audinfo("change output rate: %" PRId32 " -> %" PRId32 " \n",
rate[0], rate[1]);
lc823450_i2s_txsamplerate(dev, rate[1]);
}
if (ch[0] != ch[1])
{
audinfo("change output ch: %d -> %d \n", ch[0], ch[1]);
lc823450_i2s_setchannel('C', ch[1]);
}
if (fmt[0] != fmt[1])
{
lc823450_i2s_mp3dec(fmt[1] == AUDIO_FMT_MP3 ? true : false);
}
}
if (cap_desc->caps.ac_type & AUDIO_TYPE_INPUT)
{
rate[0] = getreg32(ASRC_FSO);
ch[0] = (getreg32(BUFCTL('J')) & BUFCTL_MONO) ? 1 : 2;
if (rate[0] != rate[1])
{
audinfo("change input rate: %" PRId32 " -> %" PRId32 " \n",
rate[0], rate[1]);
lc823450_i2s_rxsamplerate(dev, rate[1]);
}
if (ch[0] != ch[1])
{
audinfo("change input ch: %d -> %d \n", ch[0], ch[1]);
lc823450_i2s_setchannel('J', ch[1]);
}
}
break;
default:
break;
}
return 0;
}
/****************************************************************************
* Name: _i2s_rxdma_callback
****************************************************************************/
static void _i2s_rxdma_callback(DMA_HANDLE hdma, void *arg, int result)
{
sem_t *waitsem = (sem_t *)arg;
nxsem_post(waitsem);
}
/****************************************************************************
* Name: lc823450_i2s_receive
****************************************************************************/
static int lc823450_i2s_receive(struct i2s_dev_s *dev,
struct ap_buffer_s *apb,
i2s_callback_t callback,
void *arg,
uint32_t timeout)
{
int ret = OK;
#if 1 /* TODO: should move to rxsamplerate later */
if (false == _b_input_started)
{
_b_input_started = true;
/* Start J Buffer */
modifyreg32(ABUFACCEN, 0, ABUFACCEN_CDCEN('J'));
/* J Buffer : ACLTALN=0, ACLTEN=0 */
modifyreg32(BUFCTL('J'), 0x3 << 8, 0);
}
#endif
/* Enable J Buffer Over Level IRQ */
modifyreg32(ABUFIRQEN0, 0, ABUFIRQEN0_BOLIRQEN('J'));
/* Wait for Audio Buffer */
ret = _i2s_semtake(&_sem_buf_over);
if (ret < 0)
{
/* Disable J Buffer Over Level IRQ */
modifyreg32(ABUFIRQEN0, ABUFIRQEN0_BOLIRQEN('J'), 0);
/* Stop J Buffer */
modifyreg32(ABUFACCEN, ABUFACCEN_CDCEN('J'), 0);
return ret;
}
volatile uint32_t *ptr = (uint32_t *)&apb->samp[apb->curbyte];
uint32_t n = apb->nmaxbytes;
/* Setup and start DMA for I2S */
lc823450_dmasetup(_hrxdma,
LC823450_DMA_DSTINC |
LC823450_DMA_SRCWIDTH_WORD |
LC823450_DMA_DSTWIDTH_WORD,
(uint32_t)BUF_ACCESS('J'), (uint32_t)ptr, n / 4);
lc823450_dmastart(_hrxdma,
_i2s_rxdma_callback,
&_sem_rxdma);
ret = _i2s_semtake(&_sem_rxdma);
if (ret < 0)
{
/* Stop DMA because semtake failed */
lc823450_dmastop(_hrxdma);
return ret;
}
/* Invoke the callback handler */
callback(dev, apb, arg, 0);
return OK;
}
/****************************************************************************
* Name: _i2s_txdma_callback
****************************************************************************/
static void _i2s_txdma_callback(DMA_HANDLE hdma, void *arg, int result)
{
sem_t *waitsem = (sem_t *)arg;
nxsem_post(waitsem);
}
/****************************************************************************
* Name: lc823450_i2s_txsamplerate
****************************************************************************/
static uint32_t lc823450_i2s_txsamplerate(struct i2s_dev_s *dev,
uint32_t rate)
{
/* Change SSRC FSI rate */
/* Stop/Reset/Unreset SSRC */
putreg32(0, SSRC_MODE);
putreg32(0, SSRC_SRESETB);
putreg32(1, SSRC_SRESETB);
while (getreg32(SSRC_STATUS) & 0x1);
/* Setup FSI */
putreg32(rate << 13, SSRC_FSI);
/* Restart SSRC */
putreg32(0x1, SSRC_MODE);
while (getreg32(SSRC_STATUS) != 0x1);
return 0;
}
/****************************************************************************
* Name: lc823450_i2s_txdatawidth
****************************************************************************/
static uint32_t lc823450_i2s_txdatawidth(struct i2s_dev_s *dev, int bits)
{
return 0;
}
/****************************************************************************
* Name: _i2s_isr
****************************************************************************/
static int _i2s_isr(int irq, FAR void *context, FAR void *arg)
{
uint32_t status = getreg32(ABUFSTS1);
uint32_t irqen0 = getreg32(ABUFIRQEN0);
/* Check C Buffer Under Level */
if ((irqen0 & ABUFIRQEN0_BULIRQEN('C')) && (status & ABUFSTS1_BULVL('C')))
{
/* Disable C Buffer Under Level IRQ */
modifyreg32(ABUFIRQEN0, ABUFIRQEN0_BULIRQEN('C'), 0);
/* post semaphore for the waiter */
nxsem_post(&_sem_buf_under);
}
/* Check J Buffer Over Level */
if ((irqen0 & ABUFIRQEN0_BOLIRQEN('J')) && (status & ABUFSTS1_BOLVL('J')))
{
/* Disable J Buffer Over Level IRQ */
modifyreg32(ABUFIRQEN0, ABUFIRQEN0_BOLIRQEN('J'), 0);
/* post semaphore for the waiter */
nxsem_post(&_sem_buf_over);
}
return 0;
}
/****************************************************************************
* Name: lc823450_i2s_send
****************************************************************************/
static int lc823450_i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
i2s_callback_t callback, void *arg,
uint32_t timeout)
{
volatile uint32_t *ptr = (uint32_t *)&apb->samp[apb->curbyte];
uint32_t n = apb->nbytes;
uint32_t bufc_enabled;
uint32_t decsel;
int ret = OK;
DEBUGASSERT(0 < n);
decsel = getreg32(AUDSEL) & AUDSEL_DECSEL;
if (0 == getreg32(BUF_DTCAP('C')))
{
/* C buffer is empty. Start buffering by disabling access control */
modifyreg32(ABUFACCEN, ABUFACCEN_CDCEN('C'), 0);
}
bufc_enabled = getreg32(ABUFACCEN) & ABUFACCEN_CDCEN('C');
if (bufc_enabled)
{
/* Enable C Buffer Under Level IRQ */
modifyreg32(ABUFIRQEN0, 0, ABUFIRQEN0_BULIRQEN('C'));
/* Wait for Audio Buffer */
ret = _i2s_semtake(&_sem_buf_under);
if (ret < 0)
{
/* Disable C Buffer Under Level IRQ */
modifyreg32(ABUFIRQEN0, ABUFIRQEN0_BULIRQEN('C'), 0);
return ret;
}
}
if (0 == decsel && (n & 0x3))
{
auderr("** PCM data is not word-aligned (n=%" PRId32 ") ** \n", n);
/* Set size to align on a word boundary */
n &= ~0x3;
if (0 == n)
{
goto out;
}
}
/* Setup and start DMA for I2S */
if (n & 0x3)
{
lc823450_dmasetup(_htxdma,
LC823450_DMA_SRCINC |
LC823450_DMA_SRCWIDTH_BYTE |
LC823450_DMA_DSTWIDTH_BYTE,
(uint32_t)ptr, (uint32_t)BUF_ACCESS('C'), n);
}
else
{
lc823450_dmasetup(_htxdma,
LC823450_DMA_SRCINC |
LC823450_DMA_SRCWIDTH_WORD |
LC823450_DMA_DSTWIDTH_WORD,
(uint32_t)ptr, (uint32_t)BUF_ACCESS('C'), n / 4);
}
lc823450_dmastart(_htxdma,
_i2s_txdma_callback,
&_sem_txdma);
ret = _i2s_semtake(&_sem_txdma);
if (ret < 0)
{
/* Stop DMA because semtake failed */
lc823450_dmastop(_htxdma);
return ret;
}
#ifdef SHOW_BUFFERING
if (0 == bufc_enabled)
{
audinfo("buffering (remain=%d) \n", getreg32(BUF_DTCAP('C')));
}
#endif
/* Start C Buffer */
if (0 == bufc_enabled && _i2s_tx_th_bytes < getreg32(BUF_DTCAP('C')))
{
modifyreg32(ABUFACCEN, 0, ABUFACCEN_CDCEN('C'));
}
out:
/* Invoke the callback handler */
callback(dev, apb, arg, 0);
return OK;
}
/****************************************************************************
* Name: lc823450_dmic_enable
****************************************************************************/
static void lc823450_dmic_enable(void)
{
/* Disable clock for DGMIC */
modifyreg32(CLOCKEN, CLOCKEN_FCE_DGMIC, 0);
/* ALC=off */
modifyreg32(ALCCTL, 0x1, 0x0);
/* DGMICCTL: XFDSP=direct,XEQT=XHPF=off,XMOD=75%,XFS64=64fs */
putreg32(0x70000009, DGMICCTL);
/* Enable clock for DGMIC */
modifyreg32(CLOCKEN, 0, CLOCKEN_FCE_DGMIC);
/* AUDSEL: PCMSEL=1 (dmic) */
modifyreg32(AUDSEL, 0, AUDSEL_PCMSEL);
#if 1
/* TODO: should be moved to another function */
/* Enable clock for VOLUME(SP0) */
modifyreg32(CLOCKEN, 0, CLOCKEN_FCE_VOLSP0);
/* Audio Buffer Access Control: enable redirect E */
modifyreg32(ABUFACCEN, 0, ABUFACCEN_RDCTEN('E'));
/* Set the redirect source of I Buffer to E */
modifyreg32(BUFCTL('I'), 0, BUFID('E') << 16);
/* Enable CDCI */
modifyreg32(ABUFACCEN, 0, ABUFACCEN_CDCEN('I'));
/* Enable ASRC clock */
modifyreg32(CLOCKEN, 0, CLOCKEN_FCE_ASRC);
/* ASRC = 44.1k(in)/44.1k(out) */
putreg32(44100 << 13, ASRC_FSI);
putreg32(44100 << 0, ASRC_FSO);
/* Adjust volume SP0 (+33dB) */
putreg32(VOL_CONT_DIRECT |
33 << 8 | 33,
VOLSP0_CONT);
audinfo("ASRC_FSIO=%" PRId32 " \n", getreg32(ASRC_FSO));
audinfo("DTCAP(I)=0x%" PRIx32 " \n", getreg32(BUF_DTCAP('I')));
audinfo("DTCAP(J)=0x%" PRIx32 " \n", getreg32(BUF_DTCAP('J')));
/* Start ASRC */
putreg32(0x1, ASRC_MODE);
while (getreg32(ASRC_STATUS) != 0x1);
/* J Buffer : ACLTALN=1, ACLTEN=1 */
modifyreg32(BUFCTL('J'), 0, 0x3 << 8);
#endif
}
/****************************************************************************
* Name: lc823450_i2s_mp3dec
****************************************************************************/
static void lc823450_i2s_mp3dec(bool enable)
{
if (enable)
{
modifyreg32(AUDSEL, 0, AUDSEL_DECSEL);
modifyreg32(CLOCKEN, 0, CLOCKEN_FCE_MP3DEC);
putreg32(0x1, MP3DEC_ERRMODE);
}
else
{
modifyreg32(CLOCKEN, CLOCKEN_FCE_MP3DEC, 0);
modifyreg32(AUDSEL, AUDSEL_DECSEL, 0);
}
}
/****************************************************************************
* Name: lc823450_i2s_beeptest
****************************************************************************/
#ifdef BEEP_TEST
static void lc823450_i2s_beeptest(void)
{
/* Enable clock */
modifyreg32(CLOCKEN, 0, CLOCKEN_FCE_BEEP);
/* Set BEEP params */
putreg32(0x0, BEEP_BYPASS);
putreg32(0x123ca6, BEEP_COEFF); /* 1kHz@fs=44.1k */
putreg32(0xffff, BEEP_TIME);
/* Start */
putreg32(0x3, BEEP_CTL);
}
#endif
/****************************************************************************
* Name: lc823450_i2s_configure
****************************************************************************/
static int lc823450_i2s_configure(void)
{
uint32_t base = 0;
_setup_audio_pll(44100);
/* Unreset Audio Buffer */
putreg32(MRSTCNTEXT3_AUDIOBUF_RSTB,
MRSTCNTEXT3);
/* Enable clock to Audio Buffer */
putreg32(MCLKCNTEXT3_AUDIOBUF_CLKEN,
MCLKCNTEXT3);
/* C Buffer : size=56KB, under level=55kB */
putreg32(base, BUF_BASE('C'));
putreg32(1024 * 56, BUF_SIZE('C'));
base += 1024 * 56;
putreg32(1024 * 55, BUF_ULVL('C'));
/* Setup F Buffer : size=512B */
putreg32(base, BUF_BASE('F'));
putreg32(512, BUF_SIZE('F'));
base += 512;
/* Setup I Buffer : size=2KB (TODO) */
putreg32(base, BUF_BASE('I'));
putreg32(2048 * 2, BUF_SIZE('I'));
base += (2048 * 2);
/* Setup J Buffer: size=4KB, over level=1KB */
putreg32(base, BUF_BASE('J'));
putreg32(4096, BUF_SIZE('J'));
base += 4096;
putreg32(1024, BUF_OLVL('J'));
/* Clear Audio Buffer */
putreg32(0xffff, ABUFCLR);
/* Access Enable */
modifyreg32(ABUFACCEN,
0,
ABUFACCEN_RDCTEN('D') |
ABUFACCEN_CDCEN('F')
);
/* Source of F Buffer is D */
modifyreg32(BUFCTL('F'), 0, BUFID('D') << 16);
/* PCM0: BCK0/LRCK0=master, MCLK0=master */
putreg32(AUDSEL_PCM0_MODE |
AUDSEL_PCM0_MODEM,
AUDSEL);
/* LRCK0/BCK0: 1/1fs, BCK0:64fs, BCK1:64fs */
putreg32(0x00001010,
PCMCTL);
/* Enable DOUT0/LRCK0/MCL0/BCK0 */
putreg32(PCMOUTEN_DOUT0EN |
PCMOUTEN_LRCK0EN |
PCMOUTEN_MCLK0EN |
PCMOUTEN_BCK0EN,
PCMOUTEN);
/* Stereo, PCMDLY=1, LRCK active low,
* MSB first and left justified, 32bit
*/
putreg32(0x64, PSCTL);
/* Enable function clocks */
putreg32(CLOCKEN_FCE_PCKGEN |
CLOCKEN_FCE_PCMPS0 |
CLOCKEN_FCE_VOLPS0 |
CLOCKEN_FCE_MUTED |
CLOCKEN_FCE_SSRC |
CLOCKEN_FCE_VOLD,
CLOCKEN);
/* SSRC = 44.1k(in)/44.1k(out) */
putreg32(44100 << 13, SSRC_FSI);
putreg32(44100 << 0, SSRC_FSO);
/* Start SSRC */
putreg32(0x1, SSRC_MODE);
while (getreg32(SSRC_STATUS) != 0x1);
audinfo("DTCAP(C)=0x%08x \n", BUF_DTCAP('C'));
audinfo("DTCAP(I)=0x%08x \n", BUF_DTCAP('I'));
audinfo("DTCAP(J)=0x%08x \n", BUF_DTCAP('J'));
/* Setup default tx threshold */
_setup_tx_threshold(0);
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: lc823450_i2sdev_initialize
****************************************************************************/
FAR struct i2s_dev_s *lc823450_i2sdev_initialize(void)
{
FAR struct lc823450_i2s_s *priv = NULL;
/* The support STM32 parts have only a single I2S port */
/* Allocate a new state structure for this chip select. NOTE that there
* is no protection if the same chip select is used in two different
* chip select structures.
*/
priv = (struct lc823450_i2s_s *)kmm_zalloc(sizeof(struct lc823450_i2s_s));
if (!priv)
{
i2serr("ERROR: Failed to allocate a chip select structure\n");
return NULL;
}
/* Initialize the common parts for the I2S device structure */
priv->dev.ops = &g_i2sops;
lc823450_i2s_configure();
#ifdef BEEP_TEST
lc823450_i2s_beeptest();
#endif
#if 1
/* NOTE: should be moved to another codec driver */
lc823450_dmic_enable();
#endif
_hrxdma = lc823450_dmachannel(DMA_CHANNEL_VIRTUAL);
nxsem_init(&_sem_rxdma, 0, 0);
nxsem_init(&_sem_buf_over, 0, 0);
_htxdma = lc823450_dmachannel(DMA_CHANNEL_VIRTUAL);
nxsem_init(&_sem_txdma, 0, 0);
nxsem_init(&_sem_buf_under, 0, 0);
#ifdef CONFIG_SMP
cpu_set_t cpuset0;
cpu_set_t cpuset1;
CPU_ZERO(&cpuset1);
CPU_SET(0, &cpuset1);
/* Backup the current affinity */
nxsched_get_affinity(getpid(), sizeof(cpuset0), &cpuset0);
/* Set the new affinity which assigns to CPU0 */
nxsched_set_affinity(getpid(), sizeof(cpuset1), &cpuset1);
nxsig_usleep(10 * 1000);
#endif
irq_attach(LC823450_IRQ_AUDIOBUF0, _i2s_isr, NULL);
/* Enable IRQ for Audio Buffer */
up_enable_irq(LC823450_IRQ_AUDIOBUF0);
#ifdef CONFIG_SMP
/* Restore the original affinity */
nxsched_set_affinity(getpid(), sizeof(cpuset0), &cpuset0);
nxsig_usleep(10 * 1000);
#endif
/* Success exit */
return &priv->dev;
}
/****************************************************************************
* Name: up_audio_bufcapacity
****************************************************************************/
uint32_t up_audio_bufcapacity(void)
{
return (100 * getreg32(BUF_DTCAP('C'))) / getreg32(BUF_SIZE('C'));
}